Behaviour for episode strategies. A strategy defines the investigation logic for a specific expectation — how to gather data, classify, and produce outputs.
Summary
Callbacks
Optional hook invoked when the turn/token budget is exhausted between steps.
Callbacks
@callback converge(state :: map(), episode_ctx :: map()) :: {:ok, Cyclium.ConvergeResult.t()} | {:partial, Cyclium.ConvergeResult.t(), failures :: [term()]}
@callback handle_budget_exhausted(state :: map(), episode_ctx :: map()) :: {:converge, new_state :: map()} | :fail
Optional hook invoked when the turn/token budget is exhausted between steps.
Lets a strategy convert a hard budget failure into a graceful convergence —
e.g. an interactive actor can emit a "ran out of budget for this turn" summary
instead of leaving the episode :failed with no user-facing message.
Return {:converge, new_state} to run the normal converge path with the given
state, or :fail to keep the default behaviour (episode fails with
error_class: "budget_exceeded"). Strategies that don't implement this
callback always fail. This is not called for wall-time exhaustion
(max_wall_ms), which can fire mid-step and is always a hard failure.
@callback handle_result( state :: map(), step :: %Cyclium.Schemas.EpisodeStep{ __meta__: term(), args_hash: term(), args_redacted: term(), cost_ms: term(), cost_tokens: term(), created_at: term(), episode_id: term(), error_class: term(), error_detail: term(), id: term(), kind: term(), result_ref: term(), side_effect_key: term(), step_no: term(), tool_name: term() }, result :: term() ) :: {:ok, new_state :: map()} | {:retry, new_state :: map()} | {:abort, reason :: term()}
@callback init( episode :: %Cyclium.Schemas.Episode{ __meta__: term(), actor_id: term(), archived_at: term(), attempts: term(), budget: term(), checkpoints: term(), classification: term(), confidence: term(), conversation_id: term(), dedupe_key: term(), dry_run_opts: term(), error_class: term(), error_detail: term(), expectation_id: term(), finished_at: term(), id: term(), log_strategy: term(), max_attempts: term(), mode: term(), outputs: term(), parent_episode_id: term(), phase: term(), queued_at: term(), source_env: term(), source_stack: term(), spec_rev: term(), started_at: term(), status: term(), steps: term(), summary: term(), tokens_used: term(), trigger_ref: term(), trigger_type: term(), turns_used: term(), workflow_instance_id: term(), workflow_step_id: term(), workflow_step_no: term() }, trigger :: Cyclium.Trigger.t() ) :: {:ok, state :: map()}
@callback workflow_result(state :: map(), converge_result :: Cyclium.ConvergeResult.t()) :: map()