mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
fix(hooks): record OMP tool calls through the Pi tool schema
OMP was left out of the closed-tool agents introduced with the safe tool-context capture (#190), so every OMP pre-tool-use/post-tool-use observation was stored with the event name as its title and an empty body: session pages, handoffs and the auto-improve reviewer only saw the prompts. The generated Pi extension is the OMP extension with its AGENT constant renamed (build_pi_extension), so OMP posts the exact tool/callID/args/output/isError payload Pi does. Route it through the same metadata schema, closed-summary path and isError outcome; unknown tool families still keep no output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1417119eec
commit
85edc2a5af
@@ -174,7 +174,9 @@ pub(crate) fn tool_observation_metadata(
|
||||
object.get("tool")?.as_str()?,
|
||||
object.get("callID").and_then(Value::as_str),
|
||||
),
|
||||
AgentKind::Pi => (
|
||||
// The Pi extension is generated from the OMP one (`build_pi_extension`),
|
||||
// so both post the same `tool` / `callID` / `args` payload.
|
||||
AgentKind::Pi | AgentKind::Omp => (
|
||||
object.get("tool")?.as_str()?,
|
||||
object.get("callID").and_then(Value::as_str),
|
||||
),
|
||||
@@ -230,7 +232,7 @@ pub(crate) fn tool_observation_metadata(
|
||||
/// Extracts an outcome only where the adapter protocol proves its meaning.
|
||||
pub(crate) fn tool_observation_outcome(agent: AgentKind, raw: &Value) -> ToolOutcome {
|
||||
match agent {
|
||||
AgentKind::Pi => match raw.get("isError").and_then(Value::as_bool) {
|
||||
AgentKind::Pi | AgentKind::Omp => match raw.get("isError").and_then(Value::as_bool) {
|
||||
Some(true) => ToolOutcome::Error,
|
||||
Some(false) => ToolOutcome::Success,
|
||||
None => ToolOutcome::Unknown,
|
||||
|
||||
@@ -628,6 +628,7 @@ const fn closed_tool_agent(agent: AgentKind) -> bool {
|
||||
| AgentKind::Codex
|
||||
| AgentKind::OpenCode
|
||||
| AgentKind::Pi
|
||||
| AgentKind::Omp
|
||||
| AgentKind::AntigravityCli
|
||||
| AgentKind::Hermes
|
||||
| AgentKind::Pool
|
||||
@@ -2747,40 +2748,78 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pi_post_outcomes_and_stable_id_are_rendered() {
|
||||
for (is_error, outcome) in [
|
||||
(Some(false), "success"),
|
||||
(Some(true), "error"),
|
||||
(None, "unknown"),
|
||||
] {
|
||||
let mut raw = serde_json::json!({"tool":"bash","args":{},"callID":"pi-stable-190","output":"result"});
|
||||
if let Some(is_error) = is_error {
|
||||
raw["isError"] = serde_json::json!(is_error);
|
||||
fn pi_and_omp_post_outcomes_and_stable_id_are_rendered() {
|
||||
for agent in ["pi", "omp"] {
|
||||
for (is_error, outcome) in [
|
||||
(Some(false), "success"),
|
||||
(Some(true), "error"),
|
||||
(None, "unknown"),
|
||||
] {
|
||||
let mut raw = serde_json::json!({"tool":"bash","args":{},"callID":"pi-stable-190","output":"result"});
|
||||
if let Some(is_error) = is_error {
|
||||
raw["isError"] = serde_json::json!(is_error);
|
||||
}
|
||||
let pre = HookEnvelope::from_query_and_body(
|
||||
HookQuery {
|
||||
event: "pre-tool-use".into(),
|
||||
agent: Some(agent.into()),
|
||||
..Default::default()
|
||||
},
|
||||
raw.clone(),
|
||||
);
|
||||
let post = HookEnvelope::from_query_and_body(
|
||||
HookQuery {
|
||||
event: "post-tool-use".into(),
|
||||
agent: Some(agent.into()),
|
||||
..Default::default()
|
||||
},
|
||||
raw,
|
||||
);
|
||||
assert!(
|
||||
pre.body_excerpt
|
||||
.unwrap()
|
||||
.contains("tool_call_id: pi-stable-190")
|
||||
);
|
||||
let body = post.body_excerpt.unwrap();
|
||||
assert!(body.contains("tool_call_id: pi-stable-190"));
|
||||
assert!(
|
||||
body.contains(&format!("outcome: {outcome}")),
|
||||
"{agent}: {body}"
|
||||
);
|
||||
}
|
||||
let pre = HookEnvelope::from_query_and_body(
|
||||
HookQuery {
|
||||
event: "pre-tool-use".into(),
|
||||
agent: Some("pi".into()),
|
||||
..Default::default()
|
||||
},
|
||||
raw.clone(),
|
||||
);
|
||||
let post = HookEnvelope::from_query_and_body(
|
||||
HookQuery {
|
||||
event: "post-tool-use".into(),
|
||||
agent: Some("pi".into()),
|
||||
..Default::default()
|
||||
},
|
||||
raw,
|
||||
);
|
||||
assert!(
|
||||
pre.body_excerpt
|
||||
.unwrap()
|
||||
.contains("tool_call_id: pi-stable-190")
|
||||
);
|
||||
let body = post.body_excerpt.unwrap();
|
||||
assert!(body.contains("tool_call_id: pi-stable-190"));
|
||||
assert!(body.contains(&format!("outcome: {outcome}")));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn omp_tool_events_keep_family_call_id_and_output_but_not_arguments() {
|
||||
// `build_pi_extension` in the CLI derives the Pi extension from the OMP
|
||||
// one, so OMP posts this exact tool payload shape.
|
||||
let pre = HookEnvelope::from_query_and_body(
|
||||
HookQuery {
|
||||
event: "pre-tool-use".into(),
|
||||
agent: Some("omp".into()),
|
||||
..Default::default()
|
||||
},
|
||||
serde_json::json!({"tool":"bash","callID":"omp-1","args":{"command":"SENTINEL_COMMAND"}}),
|
||||
);
|
||||
assert_eq!(pre.title_hint.as_deref(), Some("tool non-file"));
|
||||
assert_eq!(
|
||||
pre.body_excerpt.as_deref(),
|
||||
Some("tool_family: non-file\ntool_call_id: omp-1")
|
||||
);
|
||||
|
||||
let post = HookEnvelope::from_query_and_body(
|
||||
HookQuery {
|
||||
event: "post-tool-use".into(),
|
||||
agent: Some("omp".into()),
|
||||
..Default::default()
|
||||
},
|
||||
serde_json::json!({"tool":"bash","callID":"omp-1","args":{"command":"SENTINEL_COMMAND"},"output":"tests passed","details":{"diff":"SENTINEL_DETAILS"}}),
|
||||
);
|
||||
assert_eq!(post.title_hint.as_deref(), Some("tool non-file"));
|
||||
assert_eq!(
|
||||
post.body_excerpt.as_deref(),
|
||||
Some("tool_family: non-file\ntool_call_id: omp-1\noutcome: unknown\n---\ntests passed")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -273,7 +273,7 @@ printf '%s\n' '{"session_id":"demo","cwd":"/example/workspace","tool_name":"Edit
|
||||
```
|
||||
|
||||
The normal capture contract is intentionally narrow: supported Claude Code,
|
||||
OpenCode, Pi, and Antigravity tool events retain only canonical tool family,
|
||||
OpenCode, Pi, OMP, and Antigravity tool events retain only canonical tool family,
|
||||
an agent-provided validated call ID when their documented schema proves one,
|
||||
and a PostToolUse outcome class. `PreToolUse` never retains commands,
|
||||
arguments, paths, input bodies, or arbitrary tool names. `PostToolUse` appends
|
||||
|
||||
Reference in New Issue
Block a user