fix(agents): drop the previous session id when the foreground agent changes (#957)

同一 pane 里前台从 Claude 换成 omp,或别的 agent 的 hook 写进这个 TTY 时,旧的 session id 会留在新 agent 上,Fork 就会拿错 id。

Co-authored-by: stevelliu <stevelliu@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
LiuSir
2026-09-25 13:52:36 +08:00
committed by GitHub
co-authored by stevelliu Cursor
parent 3a52e34b02
commit 575d2cd68e
+44 -1
View File
@@ -3057,6 +3057,14 @@ fn apply_agent_signals(
let before = st.agent_session.clone();
for event in &events {
// A hook names the agent that emitted it. Once this pane has a
// foreground agent, a different one's session id must not land here:
// omp forking with a Claude UUID is "session not found".
if let (Some(current), Some(from)) = (st.agent, event.agent)
&& current != from
{
continue;
}
if st.agent.is_none() && event.agent.is_some() {
st.agent = event.agent;
notify(st, DaemonMsg::Agent(st.agent));
@@ -3183,7 +3191,9 @@ fn apply_agent(
stamp_launch_argv(st, argv);
return;
}
if agent.is_none() && st.agent_session.is_some() {
// The session belongs to whoever was in the foreground. Switching from
// Claude to omp (or back to the shell) must not keep the previous id.
if st.agent_session.is_some() {
st.agent_session = None;
notify(st, DaemonMsg::AgentStatus(None));
}
@@ -5378,6 +5388,39 @@ mod tests {
assert!(matches!(rx.try_recv(), Ok(DaemonMsg::Agent(None))));
}
#[test]
fn a_foreign_agent_hook_does_not_replace_the_foreground_session() {
use crate::core::cli_agent::CLIAgent;
let mut st = test_state(true);
let mut sniffer = OscSniffer::new();
let omp = concat!(
"\x1b]777;notify;tty7://cli-agent;",
r#"{"v":1,"agent":"omp","event":"session-start","session_id":"01a0d21f-2f79-72e0-bb16-d6d908aaa6e0"}"#,
"\x07",
);
apply_signals(&mut st, sniffer.feed(omp.as_bytes()));
assert_eq!(st.agent, Some(CLIAgent::OhMyPi));
let claude = concat!(
"\x1b]777;notify;tty7://cli-agent;",
r#"{"v":1,"agent":"claude","event":"session-start","session_id":"e18a20ff-4c8c-4b94-867b-dd4b79032a6c"}"#,
"\x07",
);
apply_signals(&mut st, sniffer.feed(claude.as_bytes()));
assert_eq!(
st.agent_session.as_ref().unwrap().session_id.as_deref(),
Some("01a0d21f-2f79-72e0-bb16-d6d908aaa6e0"),
"a Claude hook on an omp pane must not steal the session id"
);
apply_agent(&mut st, Some((CLIAgent::Claude, vec!["claude".into()])));
assert!(
st.agent_session.is_none(),
"switching the foreground agent drops the previous session"
);
}
#[test]
fn opaque_notifications_only_fall_back_when_no_rich_state() {
use crate::core::cli_agent::{AgentSessionState, AgentStatus, CLIAgent};