aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-26 17:10:28 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-26 17:10:28 +0300
commit3750e0bae21c54da3947d337584f180a21d0bc67 (patch)
tree4542d8af76b4fee0c428ca5e9378a06946ef62ca /src/server
parent1b980eaa9d4cbc050b6b81525e4b6b72ae853be5 (diff)
feat: add pane metadata reporting
refs #36
Diffstat (limited to 'src/server')
-rw-r--r--src/server/headless.rs102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs
index e65b741c..37c05bf0 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -2479,6 +2479,18 @@ impl HeadlessServer {
self.app.save_session_now();
}
+ if let Some(deadline) = self
+ .app
+ .agent_metadata_deadline
+ .filter(|deadline| now >= *deadline)
+ {
+ for update in self.app.state.expire_agent_metadata_at(deadline, now) {
+ self.app.emit_pane_state_update(&update);
+ }
+ self.app.sync_agent_metadata_deadline();
+ changed = true;
+ }
+
self.app.sync_headless_animation_timer(now);
changed
}
@@ -3452,6 +3464,96 @@ next_tab = ""
}
#[test]
+ fn headless_scheduled_tasks_expire_agent_metadata() {
+ let mut server = test_headless_server();
+ let workspace = crate::workspace::Workspace::test_new("metadata");
+ let pane_id = workspace.tabs[0].root_pane;
+ server.app.state.workspaces = vec![workspace];
+ server.app.state.ensure_test_terminals();
+
+ assert!(
+ server.handle_internal_event_with_forwarding(AppEvent::HookStateReported {
+ pane_id,
+ source: "herdr:pi".into(),
+ agent_label: "pi".into(),
+ state: crate::detect::AgentState::Working,
+ message: None,
+ custom_status: None,
+ seq: None,
+ session_ref: None,
+ })
+ );
+ assert!(
+ server.handle_internal_event_with_forwarding(AppEvent::HookMetadataReported {
+ pane_id,
+ source: "user:pi-display".into(),
+ agent_label: Some("pi".into()),
+ applies_to_source: Some("herdr:pi".into()),
+ title: None,
+ display_agent: None,
+ custom_status: Some("short lived".into()),
+ state_labels: HashMap::new(),
+ clear_title: false,
+ clear_display_agent: false,
+ clear_custom_status: false,
+ clear_state_labels: false,
+ seq: None,
+ ttl: Some(Duration::from_millis(1)),
+ })
+ );
+
+ let deadline = server
+ .app
+ .agent_metadata_deadline
+ .expect("metadata deadline");
+ let terminal_id = server.app.state.workspaces[0]
+ .pane_state(pane_id)
+ .expect("pane")
+ .attached_terminal_id
+ .clone();
+ assert_eq!(
+ server
+ .app
+ .state
+ .terminals
+ .get(&terminal_id)
+ .expect("terminal")
+ .effective_custom_status()
+ .as_deref(),
+ Some("short lived")
+ );
+
+ assert!(server.handle_scheduled_tasks_headless(deadline + Duration::from_millis(1)));
+
+ assert_eq!(server.app.agent_metadata_deadline, None);
+ assert_eq!(
+ server
+ .app
+ .state
+ .terminals
+ .get(&terminal_id)
+ .expect("terminal")
+ .effective_custom_status(),
+ None
+ );
+ assert!(server
+ .app
+ .event_hub
+ .events_after(0)
+ .iter()
+ .any(|(_, event)| {
+ event.event == crate::api::schema::EventKind::PaneAgentStatusChanged
+ && matches!(
+ &event.data,
+ crate::api::schema::EventData::PaneAgentStatusChanged {
+ custom_status,
+ ..
+ } if custom_status.is_none()
+ )
+ }));
+ }
+
+ #[test]
fn virtual_render_produces_nonempty_buffer() {
let mut state = AppState::test_new();
let area = Rect::new(0, 0, 80, 24);