From c082f5044eedd2294d72f920d1018e40d73aab33 Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Fri, 23 Oct 2026 13:20:11 +0300 Subject: restore, step 2: resume agents, retype command lines - the pi extension reports its session file; the daemon keeps it in the pane option @agent_session, and snapshots carry it. restore runs `pi --session ` in the rebuilt pane, one agent every 0.5s, then reports which agents run and which were interrupted mid-turn. - other panes: the command line running in them is typed at the new prompt but not run. exactly as typed with the zsh hook (dingger install zsh: preexec/precmd keep @dingger_cmd), else the foreground program's exact argv from the kernel (KERN_PROCARGS2), shell-quoted. - "at a prompt" is the pane's own shell owning the terminal's foreground process group, not a name check: `sh -c ...` is a running program. - an agent or command that exits keeps its place in snapshots for 60s, so a shutdown that stops programs before tmux still restores them. - typing waits for each new prompt (text typed earlier echoes twice). e2e step 16 covers resume with a session path containing a space, the interrupted report, an agent that exits just before the server dies, a quoted command retyped not run, and the hook's text; dropping the grace period, judging prompts by name, running retyped commands, or not storing the session reference each fail it. the test's fake pi is guarded so a real pi never starts. --- crates/dinggerd/src/hooks.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'crates/dinggerd/src/hooks.rs') diff --git a/crates/dinggerd/src/hooks.rs b/crates/dinggerd/src/hooks.rs index 46077cf..191309a 100644 --- a/crates/dinggerd/src/hooks.rs +++ b/crates/dinggerd/src/hooks.rs @@ -6,6 +6,9 @@ //! {"method":"pane.report_agent","params":{"pane_id":"%3","agent":"pi","state":"working"}} //! ``` //! +//! A report may carry the agent's session reference (`agent_session_path` +//! or `agent_session_id`, herdr's names), which restore uses to resume it. +//! //! Reports are state-change *events*: the watcher applies every one in //! order, so a turn that starts and finishes between two polls is still a //! completion, and each report wakes the watcher immediately. @@ -40,6 +43,8 @@ struct PaneReports { last: Option, /// Reports not yet applied by the watcher, oldest first. pending: Vec, + /// The agent's latest session reference. + session: Option, } fn reports() -> MutexGuard<'static, HashMap> { @@ -92,6 +97,10 @@ struct HookMessage { struct HookParams { pane_id: String, state: String, + #[serde(default)] + agent_session_path: Option, + #[serde(default)] + agent_session_id: Option, } fn handle_stream(stream: UnixStream) { @@ -123,6 +132,10 @@ fn handle_stream(stream: UnixStream) { let pane = reports.entry(params.pane_id).or_default(); pane.last = Some(state); pane.pending.push(state); + let session = params.agent_session_path.or(params.agent_session_id); + if let Some(session) = session.filter(|s| !s.is_empty() && !s.chars().any(char::is_control)) { + pane.session = Some(session); + } } wake(); } @@ -168,6 +181,11 @@ pub fn last_state(pane_id: &str) -> Option { reports().get(pane_id).and_then(|pane| pane.last) } +/// The pane's agent session reference, if its extension reported one. +pub fn session(pane_id: &str) -> Option { + reports().get(pane_id).and_then(|pane| pane.session.clone()) +} + /// Drop a pane's reports (the agent exited). pub fn drop_reports(pane_id: &str) { reports().remove(pane_id); -- cgit v1.2.3