feat: unify worker lifecycle restore semantics

This commit is contained in:
2026-09-03 13:23:31 +09:00
parent 6b1b8a8846
commit 85e1ea320a
21 changed files with 681 additions and 393 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
use agen::llm_client::scheme::{Scheme, anthropic::AnthropicScheme};
use agen::llm_client::transport::{HttpTransport, ResolvedAuth};
use agen::{Engine, EngineRunExit, StopReason};
use agen::{Engine, EngineRunExit, RunInterruptionReason};
use std::time::Duration;
#[tokio::main]
@@ -51,7 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
EngineRunExit::Finished => println!("✅ Task completed normally"),
EngineRunExit::Paused => println!("⏸️ Task paused"),
EngineRunExit::Yielded => println!("↩️ Task yielded"),
EngineRunExit::Interrupted(StopReason::LimitReached) => {
EngineRunExit::Interrupted(RunInterruptionReason::LimitReached) => {
println!("🔒 Turn limit reached")
}
EngineRunExit::Interrupted(reason) => println!("❌ Task interrupted: {reason:?}"),
+4 -3
View File
@@ -39,7 +39,7 @@ use tracing::info;
use tracing_subscriber::EnvFilter;
use agen::{
Engine, EngineRunExit, StopReason,
Engine, EngineRunExit, RunInterruptionReason,
interceptor::{Interceptor, PostToolAction, ToolResultInfo},
llm_client::{
LlmClient,
@@ -478,7 +478,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// One-shot mode
if let Some(prompt) = args.prompt {
let output = engine.run(&mut history, &prompt).await;
if let EngineRunExit::Interrupted(StopReason::Unexpected(error)) = output.result {
if let EngineRunExit::Interrupted(RunInterruptionReason::Unexpected(error)) = output.result
{
eprintln!("\n❌ Error: {error}");
}
@@ -518,7 +519,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
break;
}
if let EngineRunExit::Interrupted(StopReason::Unexpected(error)) =
if let EngineRunExit::Interrupted(RunInterruptionReason::Unexpected(error)) =
locked.run(&mut history, input).await
{
eprintln!("\n❌ Error: {error}");
+9 -7
View File
@@ -147,12 +147,12 @@ pub enum EngineRunExit {
Finished,
Paused,
Yielded,
Interrupted(StopReason),
Interrupted(RunInterruptionReason),
}
/// A typed reason why an engine run could not finish normally.
#[derive(Debug)]
pub enum StopReason {
pub enum RunInterruptionReason {
LimitReached,
ContextWindowExceeded,
Cancelled,
@@ -165,13 +165,15 @@ impl From<Result<EngineResult, EngineError>> for EngineRunExit {
Ok(EngineResult::Finished) => Self::Finished,
Ok(EngineResult::Paused) => Self::Paused,
Ok(EngineResult::Yielded) => Self::Yielded,
Ok(EngineResult::LimitReached) => Self::Interrupted(StopReason::LimitReached),
Err(EngineError::Client(ClientError::ContextWindowExceeded)) => {
Self::Interrupted(StopReason::ContextWindowExceeded)
Ok(EngineResult::LimitReached) => {
Self::Interrupted(RunInterruptionReason::LimitReached)
}
Err(EngineError::Cancelled) => Self::Interrupted(StopReason::Cancelled),
Err(EngineError::Client(ClientError::ContextWindowExceeded)) => {
Self::Interrupted(RunInterruptionReason::ContextWindowExceeded)
}
Err(EngineError::Cancelled) => Self::Interrupted(RunInterruptionReason::Cancelled),
Err(EngineError::PauseRequested) => Self::Paused,
Err(error) => Self::Interrupted(StopReason::Unexpected(error)),
Err(error) => Self::Interrupted(RunInterruptionReason::Unexpected(error)),
}
}
}
+1 -1
View File
@@ -22,7 +22,7 @@ pub use agen_macros::{description, tool, tool_registry};
pub use callback::{TextBlockScope, ThinkingBlockScope, ToolUseBlockScope};
pub use engine::{
Engine, EngineConfig, EngineError, EngineResult, EngineRunExit, EngineRunOutput,
LlmRetryNotice, StopReason, ToolRegistryError,
LlmRetryNotice, RunInterruptionReason, ToolRegistryError,
};
pub use handler::ToolUseBlockStart;
pub use history::{History, HistoryEntry};
+5 -5
View File
@@ -14,7 +14,7 @@ use agen::interceptor::{
};
use agen::llm_client::event::{Event, ResponseStatus, StatusEvent};
use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput};
use agen::{Engine, EngineError, EngineRunExit, History, StopReason};
use agen::{Engine, EngineError, EngineRunExit, History, RunInterruptionReason};
use async_trait::async_trait;
use common::MockLlmClient;
@@ -205,7 +205,7 @@ async fn history_append_failure_stops_before_tool_execution() {
let exit = engine.run(&mut history, "use the tool").await;
assert!(
matches!(exit, EngineRunExit::Interrupted(StopReason::Unexpected(EngineError::HistoryAppend(ref message))) if message == "simulated ENOSPC")
matches!(exit, EngineRunExit::Interrupted(RunInterruptionReason::Unexpected(EngineError::HistoryAppend(ref message))) if message == "simulated ENOSPC")
);
assert_eq!(tool.call_count(), 0);
assert_eq!(history.len(), 1);
@@ -730,7 +730,7 @@ async fn paused_tool_resume_does_not_reset_the_consumed_turn_budget() {
assert!(matches!(
engine.resume(&mut history).await,
EngineRunExit::Interrupted(StopReason::LimitReached)
EngineRunExit::Interrupted(RunInterruptionReason::LimitReached)
));
assert_eq!(engine.turn_count(), 1);
assert_eq!(engine.active_run_turn_count(), None);
@@ -785,7 +785,7 @@ async fn interceptor_continuation_consumes_the_logical_run_budget() {
assert!(matches!(
engine.run(&mut history, "start").await,
EngineRunExit::Interrupted(StopReason::LimitReached)
EngineRunExit::Interrupted(RunInterruptionReason::LimitReached)
));
assert_eq!(engine.turn_count(), 1);
assert_eq!(engine.llm_call_count(), 1);
@@ -803,7 +803,7 @@ async fn restored_active_run_budget_is_enforced_before_another_llm_call() {
assert!(matches!(
engine.resume(&mut history).await,
EngineRunExit::Interrupted(StopReason::LimitReached)
EngineRunExit::Interrupted(RunInterruptionReason::LimitReached)
));
assert_eq!(engine.turn_count(), 7);
assert_eq!(engine.llm_call_count(), 0);
+2 -2
View File
@@ -580,7 +580,7 @@ async fn cooperative_cancellation_commits_bounded_terminal_output() {
);
assert!(matches!(
output.result,
agen::EngineRunExit::Interrupted(agen::StopReason::Cancelled)
agen::EngineRunExit::Interrupted(agen::RunInterruptionReason::Cancelled)
));
}
@@ -1214,7 +1214,7 @@ async fn post_tool_abort_commits_confirmed_result_before_stopping_run() {
);
assert!(matches!(
output.result,
agen::EngineRunExit::Interrupted(agen::StopReason::Unexpected(
agen::EngineRunExit::Interrupted(agen::RunInterruptionReason::Unexpected(
agen::EngineError::Aborted(ref reason)
)) if reason == "policy stopped the run"
));