huskies: merge 652_story_pass_resume_session_id_on_agent_respawn_so_new_sessions_inherit_prior_reasoning

This commit is contained in:
dave
2026-04-27 11:23:28 +00:00
parent 144f07f412
commit ac85cfce5d
8 changed files with 319 additions and 10 deletions
+17 -1
View File
@@ -292,6 +292,22 @@ impl AgentPool {
.map(|a| a.inactivity_timeout_secs)
.unwrap_or(300);
// If no explicit session_id_to_resume was provided, look up from the
// persistent session store. The key includes the model so a model
// change (e.g. sonnet → opus) produces a cache miss — intentional.
let effective_session_id = session_id_to_resume.or_else(|| {
let model = config
.find_agent(&resolved_name)
.and_then(|a| a.model.clone())
.unwrap_or_default();
crate::agents::session_store::lookup_session(
project_root,
story_id,
&resolved_name,
&model,
)
});
// Clone all values needed inside the background spawn.
// Spawn the background task. Worktree creation and agent launch happen here
// so `start_agent` returns immediately after registering the agent as
@@ -300,7 +316,7 @@ impl AgentPool {
project_root.to_path_buf(),
config.clone(),
resume_context.map(str::to_string),
session_id_to_resume,
effective_session_id,
story_id.to_string(),
resolved_name.clone(),
tx.clone(),
+25 -3
View File
@@ -155,13 +155,17 @@ pub(super) async fn run_agent_spawn(
// (which would re-read CLAUDE.md and README) and send only the gate
// failure context as a new message. On a fresh start, append the
// failure context to the original prompt as before.
let effective_prompt = match &session_id_to_resume_owned {
Some(_) => resume_context_owned.unwrap_or_default(),
let (effective_prompt, fresh_prompt) = match &session_id_to_resume_owned {
Some(_) => {
// Keep the full rendered prompt as fallback if resume fails.
let fallback = prompt;
(resume_context_owned.unwrap_or_default(), Some(fallback))
}
None => {
if let Some(ctx) = resume_context_owned {
prompt.push_str(&ctx);
}
prompt
(prompt, None)
}
};
@@ -200,6 +204,7 @@ pub(super) async fn run_agent_spawn(
inactivity_timeout_secs,
mcp_port: port_for_task,
session_id_to_resume: session_id_to_resume_owned.clone(),
fresh_prompt: fresh_prompt.clone(),
};
runtime
.start(ctx, tx_clone.clone(), log_clone.clone(), log_writer_clone)
@@ -217,6 +222,7 @@ pub(super) async fn run_agent_spawn(
inactivity_timeout_secs,
mcp_port: port_for_task,
session_id_to_resume: session_id_to_resume_owned.clone(),
fresh_prompt: fresh_prompt.clone(),
};
runtime
.start(ctx, tx_clone.clone(), log_clone.clone(), log_writer_clone)
@@ -234,6 +240,7 @@ pub(super) async fn run_agent_spawn(
inactivity_timeout_secs,
mcp_port: port_for_task,
session_id_to_resume: session_id_to_resume_owned,
fresh_prompt,
};
runtime
.start(ctx, tx_clone.clone(), log_clone.clone(), log_writer_clone)
@@ -266,6 +273,21 @@ pub(super) async fn run_agent_spawn(
}
}
// Persist session_id so respawns can resume prior reasoning.
if let Some(ref sess_id) = result.session_id {
let model = config_clone
.find_agent(&aname)
.and_then(|a| a.model.clone())
.unwrap_or_default();
crate::agents::session_store::record_session(
&project_root_clone,
&sid,
&aname,
&model,
sess_id,
);
}
// Mergemaster agents have their own completion path via
// start_merge_agent_work / run_merge_pipeline and must NOT go
// through server-owned gates. When a mergemaster exits early