diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-24 02:49:33 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-24 02:50:58 +0300 |
| commit | e608a75159fc5cca70485d83ffb8ee681c9da385 (patch) | |
| tree | 5ffdec62d0bee9a798418d6414ecbddae7ff8ae5 /src/app/api/panes.rs | |
| parent | 169a4fd90c20a00aec77d684ecec98b9f0f996cc (diff) | |
fix: make known-agent exit process-owned
refs #1792
refs #1648
Diffstat (limited to 'src/app/api/panes.rs')
| -rw-r--r-- | src/app/api/panes.rs | 142 |
1 files changed, 141 insertions, 1 deletions
diff --git a/src/app/api/panes.rs b/src/app/api/panes.rs index 2da6d308..43206d1e 100644 --- a/src/app/api/panes.rs +++ b/src/app/api/panes.rs @@ -1357,9 +1357,21 @@ impl App { let Some(terminal) = self.state.terminals.get_mut(&terminal_id) else { return pane_not_found(id, ¶ms.pane_id); }; + if terminal.metadata_report_blocked_by_process_exit( + &source, + agent_label.as_deref(), + applies_to_source.as_deref(), + ) { + return encode_success(id, ResponseResult::Ok {}); + } if !terminal.metadata_report_sequence_is_fresh(&source, params.seq) { return encode_success(id, ResponseResult::Ok {}); } + let metadata_agent = crate::terminal::TerminalState::metadata_report_agent( + &source, + agent_label.as_deref(), + applies_to_source.as_deref(), + ); if let Some(tokens) = tokens.as_ref() { if terminal.metadata_tokens.key_count_after_patch(tokens) > MAX_METADATA_TOKEN_KEYS_PER_RESOURCE @@ -1373,7 +1385,8 @@ impl App { ); } } - match terminal.accept_metadata_report(&source, params.seq, tokens.is_some()) { + match terminal.accept_metadata_report(&source, params.seq, tokens.is_some(), metadata_agent) + { Ok(true) => {} Ok(false) => return encode_success(id, ResponseResult::Ok {}), Err(()) => { @@ -3819,6 +3832,133 @@ mod tests { } #[test] + fn pane_metadata_ignored_after_process_exit_does_not_poison_sequence() { + let (mut app, pane_id) = app_with_test_workspace(); + let (_, internal_pane_id) = app.parse_pane_id(&pane_id).unwrap(); + let terminal_id = app.state.workspaces[0] + .pane_state(internal_pane_id) + .unwrap() + .attached_terminal_id + .clone(); + app.state + .terminals + .get_mut(&terminal_id) + .unwrap() + .set_detected_state(Some(Agent::Pi), AgentState::Idle); + + let mut initial = metadata_params(pane_id.clone()); + initial.source = "custom:pi-metadata".into(); + initial.agent = Some("pi".into()); + initial.seq = Some(100); + let response = app.handle_pane_report_metadata("initial".into(), initial); + let _: SuccessResponse = serde_json::from_str(&response).unwrap(); + + let mut initial_tokens = metadata_params(pane_id.clone()); + initial_tokens.source = "custom:pi-tokens".into(); + initial_tokens.agent = Some("pi".into()); + initial_tokens.title = None; + initial_tokens.tokens = + std::collections::HashMap::from([("generation".into(), Some("old".into()))]); + initial_tokens.seq = Some(100); + let response = app.handle_pane_report_metadata("initial-tokens".into(), initial_tokens); + let _: SuccessResponse = serde_json::from_str(&response).unwrap(); + + let exit_at = std::time::Instant::now() + std::time::Duration::from_millis(1); + app.state + .terminals + .get_mut(&terminal_id) + .unwrap() + .set_detected_state_with_screen_signals_at( + Some(Agent::Pi), + AgentState::Idle, + false, + false, + false, + true, + exit_at, + ); + app.state + .terminals + .get_mut(&terminal_id) + .unwrap() + .set_detected_state_with_screen_signals_at( + None, + AgentState::Unknown, + false, + false, + false, + false, + exit_at + std::time::Duration::from_millis(1), + ); + + let mut stale = metadata_params(pane_id.clone()); + stale.source = "custom:pi-metadata".into(); + stale.agent = Some("pi".into()); + stale.title = Some("stale".into()); + stale.seq = Some(200); + let response = app.handle_pane_report_metadata("stale".into(), stale); + let _: SuccessResponse = serde_json::from_str(&response).unwrap(); + + let mut official = metadata_params(pane_id.clone()); + official.source = "herdr:pi".into(); + official.seq = Some(200); + let response = app.handle_pane_report_metadata("official".into(), official); + let _: SuccessResponse = serde_json::from_str(&response).unwrap(); + + let terminal = &app.state.terminals[&terminal_id]; + assert!(terminal.metadata_report_sequence_is_fresh("custom:pi-metadata", Some(1))); + assert!(terminal.metadata_report_sequence_is_fresh("custom:pi-tokens", Some(1))); + assert!(terminal.metadata_report_sequence_is_fresh("herdr:pi", Some(1))); + + app.state + .terminals + .get_mut(&terminal_id) + .unwrap() + .set_detected_state_with_screen_signals_at( + Some(Agent::Pi), + AgentState::Idle, + false, + false, + false, + false, + exit_at + std::time::Duration::from_millis(2), + ); + let mut fresh = metadata_params(pane_id.clone()); + fresh.source = "custom:pi-metadata".into(); + fresh.agent = Some("pi".into()); + fresh.title = Some("fresh".into()); + fresh.seq = Some(1); + let response = app.handle_pane_report_metadata("fresh".into(), fresh); + let _: SuccessResponse = serde_json::from_str(&response).unwrap(); + + let mut fresh_tokens = metadata_params(pane_id); + fresh_tokens.source = "custom:pi-tokens".into(); + fresh_tokens.agent = Some("pi".into()); + fresh_tokens.title = None; + fresh_tokens.tokens = + std::collections::HashMap::from([("generation".into(), Some("new".into()))]); + fresh_tokens.seq = Some(1); + let response = app.handle_pane_report_metadata("fresh-tokens".into(), fresh_tokens); + let _: SuccessResponse = serde_json::from_str(&response).unwrap(); + + let terminal = &app.state.terminals[&terminal_id]; + assert_eq!( + terminal.agent_metadata["custom:pi-metadata"] + .title + .as_deref(), + Some("fresh") + ); + assert_eq!( + terminal + .metadata_tokens + .values() + .get("generation") + .map(String::as_str), + Some("new") + ); + } + + #[test] fn pane_report_metadata_accepts_documented_source_chars_and_max_ttl() { let (mut app, pane_id) = app_with_test_workspace(); let mut params = metadata_params(pane_id); |
