diff options
| author | Mark Jaquith <mark@jaquith.me> | 2026-09-05 17:08:19 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-06 00:08:19 +0300 |
| commit | ec05328a4413e38ebdcb84365938345e9cf47431 (patch) | |
| tree | 3f280531f2c81d00d2204ca7e7fb0d3b161072a6 /src/terminal | |
| parent | 6f57f2f62c5d2591647262caf15bc3c962b9c9f8 (diff) | |
fix: prevent OpenCode child prompts from leaving status blocked (#3669)
Child permission and question reports omitted the session ID, clearing
Herdr’s root session identity and preventing later lifecycle reports
from clearing the blocked state.
Track child-to-parent relationships and report prompt transitions with
their root session ID, including for nested children. This preserves
identity and lets existing ownership checks reject unrelated sessions.
Add regression coverage for prompt routing and lifecycle transitions,
and bump the OpenCode integration version to 11.
Diffstat (limited to 'src/terminal')
| -rw-r--r-- | src/terminal/state.rs | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/src/terminal/state.rs b/src/terminal/state.rs index ee170198..dc28af10 100644 --- a/src/terminal/state.rs +++ b/src/terminal/state.rs @@ -4786,6 +4786,60 @@ mod tests { } #[test] + fn opencode_child_prompt_reports_with_root_id_preserve_lifecycle_authority() { + let mut terminal = test_terminal(); + let root = crate::agent_resume::AgentSessionRef::id("opencode-root").unwrap(); + anchor_full_lifecycle_session( + &mut terminal, + Agent::OpenCode, + "herdr:opencode", + "opencode", + root.clone(), + ); + + // The plugin projects child permission/question events onto their root. + for (seq, state) in [ + (20, AgentState::Working), + (21, AgentState::Blocked), + (22, AgentState::Working), + (23, AgentState::Idle), + ] { + let mutation = terminal + .set_hook_authority_with_session_ref( + "herdr:opencode".into(), + "opencode".into(), + state, + None, + Some(root.clone()), + Some(seq), + ) + .expect("root-scoped lifecycle report should be accepted"); + assert!(!mutation.session_ref_changed); + assert_eq!(terminal.state, state); + assert_eq!( + terminal + .hook_authority + .as_ref() + .unwrap() + .session_ref + .as_ref(), + Some(&root) + ); + } + + let foreign_child_prompt = terminal.set_hook_authority_with_session_ref( + "herdr:opencode".into(), + "opencode".into(), + AgentState::Blocked, + None, + crate::agent_resume::AgentSessionRef::id("opencode-other-root"), + Some(24), + ); + assert!(foreign_child_prompt.is_none()); + assert_eq!(terminal.state, AgentState::Idle); + } + + #[test] fn opencode_tui_selection_reanchors_full_lifecycle_authority() { let mut terminal = test_terminal(); let old_session = crate::agent_resume::AgentSessionRef::id("opencode-newer").unwrap(); |
