From 6be9dc9d5cbb0b99ea01455bae8528d63157f30e Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Thu, 1 Oct 2026 10:18:59 +0800 Subject: [PATCH] fix(core): take Prime Agent's hook reports from its background daemon Prime Agent runs each session in a detached daemon, started by the first session and outliving it, and the daemon runs tty7's bridge there with the environment of the TUI that asked for the session. The report names the right pane, but its process never runs under that pane's shell, so the daemon turned every one away and a Prime Agent pane never left idle. A report about Prime Agent is now taken by the pane it names when that pane is running Prime Agent. --- crates/tty7-core/src/daemon/pane.rs | 40 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/tty7-core/src/daemon/pane.rs b/crates/tty7-core/src/daemon/pane.rs index 73afe684..a48d994a 100644 --- a/crates/tty7-core/src/daemon/pane.rs +++ b/crates/tty7-core/src/daemon/pane.rs @@ -2510,11 +2510,14 @@ impl DaemonPane { .pty() .and_then(|pty| pty.shell_pid) .ok_or_else(|| format!("pane {} runs no local process", self.id))?; - if !crate::daemon::procinfo::descends_from(pid, shell) { - return Err(format!("process {pid} does not run in pane {}", self.id)); - } let event = crate::core::cli_agent::parse_agent_event_body(body.as_bytes()) .ok_or_else(|| "not an agent event".to_string())?; + let running = self.state.lock().unwrap().agent; + if !crate::daemon::procinfo::descends_from(pid, shell) + && !owns_detached_report(event.agent, running) + { + return Err(format!("process {pid} does not run in pane {}", self.id)); + } self.apply_reported_agent_event(event) } @@ -3547,6 +3550,23 @@ fn apply_probed_cwd(st: &mut PaneState, probed: Option) { st.cwd = Some(probed); } +/// Whether a report from a process outside the pane it names still belongs +/// to it, because the agent it is about runs its hooks there by design. +/// +/// Prime Agent's TUI hands each session to a background daemon — started by +/// the first session, detached, and outliving it — which runs that session's +/// extensions, tty7's bridge among them, with the environment of the TUI that +/// asked for it. The report's `$TTY7_PANE` is therefore right while the +/// process is never under the pane's shell. Taking it on the pane's word is +/// safe as long as that pane is running Prime Agent itself. +fn owns_detached_report( + reported: Option, + running: Option, +) -> bool { + use crate::core::cli_agent::CLIAgent; + reported == Some(CLIAgent::PrimeAgent) && running == reported +} + /// A pane as a Codex hook report sees it: whether Codex runs in it, where, /// and which session it last reported. #[derive(Debug, Clone)] @@ -4056,6 +4076,20 @@ fn hex_val(b: u8) -> Option { #[cfg(test)] mod tests { + /// Only Prime Agent's daemon reports from outside the pane, and only to a + /// pane running Prime Agent. + #[test] + fn a_detached_report_is_taken_only_from_prime_agent_in_its_own_pane() { + use super::owns_detached_report; + use crate::core::cli_agent::CLIAgent; + let prime = Some(CLIAgent::PrimeAgent); + assert!(owns_detached_report(prime, prime)); + assert!(!owns_detached_report(prime, None)); + assert!(!owns_detached_report(prime, Some(CLIAgent::Pi))); + assert!(!owns_detached_report(Some(CLIAgent::Pi), Some(CLIAgent::Pi))); + assert!(!owns_detached_report(None, prime)); + } + /// A pane inherits the directory the last one *reported* — the logical /// path, `/tmp/x` and not the `/private/tmp/x` the kernel resolves it to. /// `cwd()` alone loses that: the shell falls back to `getcwd()` and one