waiting_for_human and everything needed to continue. Save the whole result, collect the decision in your application, then pass the saved result and the decision to the agent’s continue method.
Phaseo doesn’t store Agent SDK runs. Save resumable state in your own application when it must survive a request or process restart.
Approve or reject tool calls
Approval-gated, manual and human-in-the-loop tools pause the run before they execute. Each pending call has the model’scall (with its id) and a kind: approval, manual or hitl. Answer every call by its exact call ID, not its tool name, so two calls to the same tool stay separate.
An approved call runs its tool. A rejected call sends {"error": "<reason>"} to the model as the tool result, with the default reason Tool call rejected.
The pause itself also has a
kind. Go and C# set it to tool_approval for every tool pause. PHP, Ruby and Java set tool_approval when any pending call needs approval, otherwise the first call’s kind. Rust uses tool_approval for approval calls, external_output when every pending call needs an external result, and tool_input for a mix of the two.
To require approval for every tool call instead of per tool, set requireApproval on the agent definition (require_approval in Python and Ruby, RequireApproval in Go and C#). In TypeScript it’s a function requireApproval(call, context). Rust only supports approval per tool, with Tool::require_approval(). Tools shows how each kind of tool is defined.
Supply results for manual and human-in-the-loop tools
Formanual and hitl calls, pass the result your application produced, keyed by call ID. In Rust, these are tools built with Tool::external(...).
Missing output for tool call <id> (lowercase missing in Go, a ValueError in Python, an AgentError in Rust). A human input message is not a substitute for a tool output.
Pause for human review
Set a human review callback on the agent definition to check each model response before the run continues. Return a review request with areason (and optionally a payload) to pause, or nothing to carry on. The pause kind is human_review. The callback receives the run ID, step index, messages, model response and parsed output (Rust: the run and agent IDs, step index, messages, response and application context).
Run <id> is waiting for human input (Go: run <id> is waiting for human input; Java: Run is waiting for human input). Rust requires a non-empty human_input. TypeScript also accepts structured humanMessages.
Save run state between requests
The run result is plain data: store it in your database, object store or job record, and keep applicationcontext serializable. A run can only be continued by an agent with the same ID. The durable agent loop cookbook shows a complete save-and-resume flow.
To let the SDK save and load runs for you, pass a state accessor with load(runId) and save(result). The SDK then saves after each run and continuation, and you can continue by run ID instead of passing the record through every layer.
AgentDefinition aren’t part of the serialized run. The effective model and step limit are stored in RunRecord, so per-run overrides stay active after continuation.
Whatever you store, your application owns encryption and retention of saved runs, authorization for approvals and human input, and idempotency around side effects that run again after a resume.