From 9575ead9c637a2dc482c00f417a1d784d46e076a Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Thu, 5 Nov 2026 19:45:08 +0200 Subject: name pi agents by their reported session name, not the terminal title; scroll a panel list taller than the screen --- crates/dinggerd/src/hooks.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'crates/dinggerd/src/hooks.rs') diff --git a/crates/dinggerd/src/hooks.rs b/crates/dinggerd/src/hooks.rs index aeb3285..c9825dd 100644 --- a/crates/dinggerd/src/hooks.rs +++ b/crates/dinggerd/src/hooks.rs @@ -32,6 +32,9 @@ //! Or that it failed, with the `error` as one readable line: the agent is //! done, and the alert says it failed and why. //! +//! A pi report carries the session's `name` ("" for none): the agent's +//! name, whatever its terminal title says. +//! //! It also carries the `draft` in the agent's input box ("" for none), for //! marking a draft you left behind; never logged. And `context`: how full //! the agent's context window is, in percent, as pi's footer shows it ("" @@ -83,6 +86,8 @@ struct PaneReports { draft: Option, /// How full its context window is: whole percent ("" when unknown). context: Option, + /// The agent's session name, as reported ("" for none). + name: Option, /// The error that ended its last failed run, as one line. error: Option, /// When the last report arrived. @@ -189,6 +194,8 @@ struct HookParams { aborted: Option, #[serde(default)] error: Option, + #[serde(default)] + name: Option, } fn handle_stream(stream: UnixStream) { @@ -259,6 +266,7 @@ fn handle_stream(stream: UnixStream) { context: params.context, aborted: params.aborted == Some(true), error: params.error, + name: params.name, }; record(params.pane_id, state, reported); wake(); @@ -278,6 +286,7 @@ struct Reported { aborted: bool, /// The error the run this report ends failed with. error: Option, + name: Option, } /// A reported state, and whether it ends a run the user cancelled. @@ -344,6 +353,9 @@ fn record(pane_id: String, state: Option, reported: Reported) { if let Some(draft) = reported.draft { pane.draft = Some(draft_line(&draft)); } + if let Some(name) = reported.name { + pane.name = Some(draft_line(&name)); + } if let Some(context) = reported.context { pane.context = Some(context_percent(&context)); } @@ -358,6 +370,12 @@ fn context_percent(value: &str) -> String { } } +/// The pane's agent's session name ("" for none), if its extension +/// reports it. +pub fn name(pane_id: &str) -> Option { + reports().get(pane_id).and_then(|pane| pane.name.clone()) +} + /// The error the pane's agent's last failed run ended with. pub fn error(pane_id: &str) -> Option { reports().get(pane_id).and_then(|pane| pane.error.clone()) @@ -527,6 +545,18 @@ mod tests { assert_eq!(take_observations("%t7"), Some(vec![Observation::plain(AgentState::Idle)])); } + #[test] + fn a_reported_name_is_kept_as_one_line_until_reported_again() { + let named = |name: &str| Reported { name: Some(name.into()), ..Default::default() }; + assert_eq!(super::name("%t9"), None, "an extension that does not report it"); + record("%t9".into(), None, named("Fix the\nsidecar")); + assert_eq!(super::name("%t9").as_deref(), Some("Fix the sidecar")); + record("%t9".into(), None, Reported::default()); + assert_eq!(super::name("%t9").as_deref(), Some("Fix the sidecar"), "kept by a report without one"); + record("%t9".into(), None, named("")); + assert_eq!(super::name("%t9").as_deref(), Some(""), "unnamed"); + } + #[test] fn a_failed_run_ends_in_a_failed_idle_observation_with_its_error() { let failed = |error: &str| Reported { error: Some(error.into()), ..Default::default() }; -- cgit v1.2.3