mirror of
https://github.com/openai/codex.git
synced 2026-05-16 09:12:54 +00:00
# Why Revert #20524 for now because the computer use plugin has not migrated off legacy `notify` yet. Keeping the deprecation in place today would show users a warning before the plugin path is ready to move, so this rolls the change back until that migration is complete. # What - revert the legacy `notify` deprecation change from #20524 - restore the prior `notify` behavior and remove the temporary deprecation metrics/docs from that change Once the computer use plugin has migrated, we can land the same deprecation again.
154 lines
5.5 KiB
Rust
154 lines
5.5 KiB
Rust
use std::process::Stdio;
|
|
use std::sync::Arc;
|
|
|
|
use serde::Serialize;
|
|
|
|
use crate::Hook;
|
|
use crate::HookEvent;
|
|
use crate::HookPayload;
|
|
use crate::HookResult;
|
|
use crate::command_from_argv;
|
|
|
|
/// Legacy notify payload appended as the final argv argument for backward compatibility.
|
|
#[derive(Debug, Clone, PartialEq, Serialize)]
|
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
|
enum UserNotification {
|
|
#[serde(rename_all = "kebab-case")]
|
|
AgentTurnComplete {
|
|
thread_id: String,
|
|
turn_id: String,
|
|
cwd: String,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
client: Option<String>,
|
|
|
|
/// Messages that the user sent to the agent to initiate the turn.
|
|
input_messages: Vec<String>,
|
|
|
|
/// The last message sent by the assistant in the turn.
|
|
last_assistant_message: Option<String>,
|
|
},
|
|
}
|
|
|
|
pub fn legacy_notify_json(payload: &HookPayload) -> Result<String, serde_json::Error> {
|
|
match &payload.hook_event {
|
|
HookEvent::AfterAgent { event } => {
|
|
serde_json::to_string(&UserNotification::AgentTurnComplete {
|
|
thread_id: event.thread_id.to_string(),
|
|
turn_id: event.turn_id.clone(),
|
|
cwd: payload.cwd.display().to_string(),
|
|
client: payload.client.clone(),
|
|
input_messages: event.input_messages.clone(),
|
|
last_assistant_message: event.last_assistant_message.clone(),
|
|
})
|
|
}
|
|
_ => Err(serde_json::Error::io(std::io::Error::other(
|
|
"legacy notify payload is only supported for after_agent",
|
|
))),
|
|
}
|
|
}
|
|
|
|
pub fn notify_hook(argv: Vec<String>) -> Hook {
|
|
let argv = Arc::new(argv);
|
|
Hook {
|
|
name: "legacy_notify".to_string(),
|
|
func: Arc::new(move |payload: &HookPayload| {
|
|
let argv = Arc::clone(&argv);
|
|
Box::pin(async move {
|
|
let mut command = match command_from_argv(&argv) {
|
|
Some(command) => command,
|
|
None => return HookResult::Success,
|
|
};
|
|
if let Ok(notify_payload) = legacy_notify_json(payload) {
|
|
command.arg(notify_payload);
|
|
}
|
|
|
|
// Backwards-compat: match legacy notify behavior (argv + JSON arg, fire-and-forget).
|
|
command
|
|
.stdin(Stdio::null())
|
|
.stdout(Stdio::null())
|
|
.stderr(Stdio::null());
|
|
|
|
match command.spawn() {
|
|
Ok(_) => HookResult::Success,
|
|
Err(err) => HookResult::FailedContinue(err.into()),
|
|
}
|
|
})
|
|
}),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use anyhow::Result;
|
|
use codex_protocol::ThreadId;
|
|
use codex_utils_absolute_path::test_support::PathBufExt;
|
|
use codex_utils_absolute_path::test_support::test_path_buf;
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::Value;
|
|
use serde_json::json;
|
|
|
|
use super::*;
|
|
|
|
fn expected_notification_json() -> Value {
|
|
let cwd = test_path_buf("/Users/example/project");
|
|
json!({
|
|
"type": "agent-turn-complete",
|
|
"thread-id": "b5f6c1c2-1111-2222-3333-444455556666",
|
|
"turn-id": "12345",
|
|
"cwd": cwd.display().to_string(),
|
|
"client": "codex-tui",
|
|
"input-messages": ["Rename `foo` to `bar` and update the callsites."],
|
|
"last-assistant-message": "Rename complete and verified `cargo build` succeeds.",
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn test_user_notification() -> Result<()> {
|
|
let notification = UserNotification::AgentTurnComplete {
|
|
thread_id: "b5f6c1c2-1111-2222-3333-444455556666".to_string(),
|
|
turn_id: "12345".to_string(),
|
|
cwd: test_path_buf("/Users/example/project")
|
|
.display()
|
|
.to_string(),
|
|
client: Some("codex-tui".to_string()),
|
|
input_messages: vec!["Rename `foo` to `bar` and update the callsites.".to_string()],
|
|
last_assistant_message: Some(
|
|
"Rename complete and verified `cargo build` succeeds.".to_string(),
|
|
),
|
|
};
|
|
let serialized = serde_json::to_string(¬ification)?;
|
|
let actual: Value = serde_json::from_str(&serialized)?;
|
|
assert_eq!(actual, expected_notification_json());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn legacy_notify_json_matches_historical_wire_shape() -> Result<()> {
|
|
let payload = HookPayload {
|
|
session_id: ThreadId::new(),
|
|
cwd: test_path_buf("/Users/example/project").abs(),
|
|
client: Some("codex-tui".to_string()),
|
|
triggered_at: chrono::Utc::now(),
|
|
hook_event: HookEvent::AfterAgent {
|
|
event: crate::HookEventAfterAgent {
|
|
thread_id: ThreadId::from_string("b5f6c1c2-1111-2222-3333-444455556666")
|
|
.expect("valid thread id"),
|
|
turn_id: "12345".to_string(),
|
|
input_messages: vec![
|
|
"Rename `foo` to `bar` and update the callsites.".to_string(),
|
|
],
|
|
last_assistant_message: Some(
|
|
"Rename complete and verified `cargo build` succeeds.".to_string(),
|
|
),
|
|
},
|
|
},
|
|
};
|
|
|
|
let serialized = legacy_notify_json(&payload)?;
|
|
let actual: Value = serde_json::from_str(&serialized)?;
|
|
assert_eq!(actual, expected_notification_json());
|
|
|
|
Ok(())
|
|
}
|
|
}
|