feat(missions): keep the tool's arguments, not just its name
The container tier's first measured mission recorded `Bash × 6` and not one of them said what it ran. Every behavioural question about the phase — did it run the tests, did it commit, did it call an API a skill forbids — was unanswerable from a record that looked complete. `vm_tool_tap::parse` already read `tool_input` to pull the path out of it, then dropped the rest on the floor. It now keeps it, bounded: file bodies (`content`, `new_string`, `old_string`, `edits`) become a byte count, and any other over-long string is truncated with a marker saying so. Bounded rather than whitelisted, because a whitelist silently loses the one argument that matters the first time a tool grows a field. `file.touch` keeps the absolute path in `detail.abs` alongside the repo-relative `target`. Normalising is what the map needs and exactly what destroys "did this write land outside the checkout". `tool.call` also gains `detail.path`, which the World's SSE has been reading and getting a null from on every container-tier call. `mission_events::tool_evidence_for_mission` is the reader — the counterpart to `narrative_for_mission`, and the reason it exists: the narrative is what an agent SAID it did. Host-side only. No image rebuild: the arguments were always in the tap file, the first parse threw them away. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018i9Ten1LU4jUr5d7TAWda9
This commit is contained in:
co-authored by
Claude Opus 5
parent
0b4d91889a
commit
8cb38d1320
@@ -2077,7 +2077,11 @@ pub(crate) async fn record_vm_tools(
|
||||
agent_id: None,
|
||||
kind: crate::mission_events::TOOL_CALL.to_string(),
|
||||
target: Some(t.tool.clone()),
|
||||
detail: serde_json::Value::Null,
|
||||
// `path` because the World's SSE reads `detail.path` for this kind
|
||||
// and was handed a null on every container-tier call; `input`
|
||||
// because the tool name alone cannot answer a single behavioural
|
||||
// question about the phase.
|
||||
detail: serde_json::json!({ "path": t.path, "input": t.input }),
|
||||
});
|
||||
if let Some(path) = &t.path {
|
||||
events.push(crate::mission_events::MissionEvent {
|
||||
@@ -2090,7 +2094,13 @@ pub(crate) async fn record_vm_tools(
|
||||
path,
|
||||
&["/mission/repo", "/workspace"],
|
||||
)),
|
||||
detail: serde_json::json!({ "tool": t.tool }),
|
||||
// The ABSOLUTE path as well as the repo-relative one. `target`
|
||||
// is normalised for the map, where a `mission` → `repo` pair of
|
||||
// directory orbs means nothing to a reader — but normalising is
|
||||
// exactly what destroys the question "did this write land
|
||||
// outside the checkout", which is the one boundary a skill can
|
||||
// be scored on.
|
||||
detail: serde_json::json!({ "tool": t.tool, "abs": path }),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user