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
+40 -6
View File
@@ -7,6 +7,7 @@ use tokio::sync::broadcast;
use crate::agent_log::AgentLogWriter;
use crate::io::watcher::WatcherEvent;
use crate::slog_warn;
use super::{AgentEvent, AgentRuntime, RuntimeContext, RuntimeResult, RuntimeStatus};
@@ -49,18 +50,51 @@ impl AgentRuntime for ClaudeCodeRuntime {
&ctx.cwd,
&tx,
&event_log,
log_writer,
log_writer.clone(),
ctx.inactivity_timeout_secs,
Arc::clone(&self.child_killers),
self.watcher_tx.clone(),
ctx.session_id_to_resume.as_deref(),
)
.await?;
.await;
Ok(RuntimeResult {
session_id: pty_result.session_id,
token_usage: pty_result.token_usage,
})
match pty_result {
Ok(result) => Ok(RuntimeResult {
session_id: result.session_id,
token_usage: result.token_usage,
}),
Err(e) if ctx.session_id_to_resume.is_some() && ctx.fresh_prompt.is_some() => {
// Resume failed — fall back to a fresh session without --resume.
slog_warn!(
"[agents] Resume failed for {}:{}, retrying without --resume: {}",
ctx.story_id,
ctx.agent_name,
e
);
let fresh = ctx.fresh_prompt.unwrap();
let fallback_result = super::super::pty::run_agent_pty_streaming(
&ctx.story_id,
&ctx.agent_name,
&ctx.command,
&ctx.args,
&fresh,
&ctx.cwd,
&tx,
&event_log,
log_writer,
ctx.inactivity_timeout_secs,
Arc::clone(&self.child_killers),
self.watcher_tx.clone(),
None, // no --resume on fallback
)
.await?;
Ok(RuntimeResult {
session_id: fallback_result.session_id,
token_usage: fallback_result.token_usage,
})
}
Err(e) => Err(e),
}
}
fn stop(&self) {
+3
View File
@@ -705,6 +705,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
let instruction = build_system_instruction(&ctx);
@@ -723,6 +724,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
let instruction = build_system_instruction(&ctx);
@@ -803,6 +805,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
// The model extraction logic is inside start(), but we test the
+8
View File
@@ -32,6 +32,13 @@ pub struct RuntimeContext {
/// than `claude -p <full_prompt>`. The agent re-enters the previous
/// conversation and receives the `prompt` (if non-empty) as a new message.
pub session_id_to_resume: Option<String>,
/// Full rendered prompt for a fresh session, kept as fallback if resume fails.
///
/// When `session_id_to_resume` is `Some`, `prompt` contains only the
/// resume context (e.g. gate failure output). If the CLI rejects the
/// resume (session expired, file missing, version mismatch), the runtime
/// retries with this full prompt and no `--resume` flag.
pub fresh_prompt: Option<String>,
}
/// Result returned by a runtime after the agent session completes.
@@ -101,6 +108,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
assert_eq!(ctx.story_id, "42_story_foo");
assert_eq!(ctx.agent_name, "coder-1");
+4
View File
@@ -617,6 +617,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
assert_eq!(build_system_text(&ctx), "Custom system prompt");
@@ -634,6 +635,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
let text = build_system_text(&ctx);
@@ -684,6 +686,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
assert!(ctx.command.starts_with("gpt"));
}
@@ -700,6 +703,7 @@ mod tests {
inactivity_timeout_secs: 300,
mcp_port: 3001,
session_id_to_resume: None,
fresh_prompt: None,
};
assert!(ctx.command.starts_with("o"));
}