From 31c08b5387afa9e7afcdee21a3c59a5cfa747475 Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Mon, 15 Jun 2026 16:05:53 +0700 Subject: [PATCH] =?UTF-8?q?feat(daemon):=20RestartSurface=20honors=20resum?= =?UTF-8?q?e=20=E2=80=94=20swap=20to=20resume=5Fargs=20when=20mapped?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/spaceshd/src/server.rs | 41 ++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/crates/spaceshd/src/server.rs b/crates/spaceshd/src/server.rs index 1d68456..148fa56 100644 --- a/crates/spaceshd/src/server.rs +++ b/crates/spaceshd/src/server.rs @@ -291,6 +291,23 @@ fn spawn_env(sid: &SurfaceId, spec: &spacesh_proto::workspace::SurfaceSpec) -> ( (env, active) } +/// Build the spawn spec for a (re)start. When `resume` and the command has a +/// resume mapping, its args are replaced with the resume args; otherwise the +/// original spec args are kept. +fn resume_spec( + spec: &spacesh_proto::workspace::SurfaceSpec, + resume: bool, + cfg: &crate::config::Config, +) -> spacesh_proto::workspace::SurfaceSpec { + let mut out = spec.clone(); + if resume { + if let Some(args) = cfg.resume_args(&spec.command) { + out.args = args; + } + } + out +} + /// Emit a `layout_changed` event for a workspace's current tree. fn emit_layout(reg: &Registry, ws_id: &WorkspaceId, clients: &HashMap) { if let Some(w) = reg.workspace(ws_id) { @@ -475,13 +492,14 @@ async fn handle_request( let _ = out.send(ok(id, serde_json::json!({ "surface_ids": new_ids.iter().map(|s| s.0.clone()).collect::>() }))).await; } - Cmd::RestartSurface { surface_id } => { + Cmd::RestartSurface { surface_id, resume } => { if reg.is_running(&surface_id) { let _ = out.send(ok(id, serde_json::Value::Null)).await; return; // already running } let Some(spec) = reg.surface_spec(&surface_id) else { let _ = out.send(err(id, "NOT_FOUND", "surface")).await; return; }; + let spec = resume_spec(&spec, resume, config); let ws_id = reg.workspace_of(&surface_id).unwrap(); let (env, hooks_active) = spawn_env(&surface_id, &spec); match crate::surface::spawn_from_spec(surface_id.clone(), ws_id.clone(), &spec, env, hooks_active, state_tx.clone(), exit_tx.clone(), snapshot_tx.clone()) { @@ -1607,4 +1625,25 @@ mod tests { let snap = data["snapshot"].as_str().unwrap_or(""); assert!(snap.contains("SNAPDISK"), "on-disk snapshot should contain SNAPDISK, got: {snap:?}"); } + + #[test] + fn resume_spec_swaps_args_when_mapped() { + use spacesh_proto::workspace::SurfaceSpec; + let spec = SurfaceSpec { + command: "claude".into(), args: vec!["--foo".into()], cwd: "/tmp".into(), + agent_label: Some("claude".into()), cols: 80, rows: 24, autostart: false, + }; + let cfg = crate::config::Config::default(); + // resume=false → original args + let plain = resume_spec(&spec, false, &cfg); + assert_eq!(plain.args, vec!["--foo".to_string()]); + // resume=true with a default mapping → resume args + let resumed = resume_spec(&spec, true, &cfg); + assert_eq!(resumed.args, vec!["--continue".to_string()]); + // resume=true for an unmapped command → original args (graceful fallback) + let mut shell = spec.clone(); + shell.command = "bash".into(); + let resumed_shell = resume_spec(&shell, true, &cfg); + assert_eq!(resumed_shell.args, shell.args); + } }