Deferred tools

A tool doesn't have to answer in the same decision. Your worker can acknowledge the call, do the work on its own schedule, and report the result later. The turn stays open, and the run resumes when the answer arrives.

Example

Answer tool.execute without settling the call: start the work, return an empty decision, and the engine leaves the call in flight.

server.mjs
const tools = [
    {
        name: "render_report",
        description: "Render a report. This takes a while.",
        input: {
            type: "object",
            properties: { topic: { type: "string" } },
            required: ["topic"]
        }
    }
];

function decide({ trigger, proposed }) {
    if (trigger.type === "session.start") {
        return { agent: { model: "claude-haiku-4-5-20251001", stream: true, tools } };
    }

    // Kick off the work, keyed by the call id, and leave the call pending.
    if (trigger.type === "tool.execute") {
        startRender(trigger.id, trigger.input.value);   // runs elsewhere, on its own schedule
        return {};                                       // no tool.result yet
    }

    return proposed;
}

When the render finishes, settle the call out of band by its id:

{ "type": "tool.result", "id": "<toolCallId>", "result": "https://reports/42.pdf" }

Deferring

Answering a tool.execute with no tool.result or tool.error leaves the call in flight. It is the same state a client-side tool sits in while the browser works (see Client-side tools); a deferred tool is the worker-handled version. The declaration is an ordinary tool, run on your own schedule.

Waiting

While a call is in flight the turn stays open. Sibling tool calls that finish first only record their results; the model is not re-prompted until every in-flight call has settled, so it never sees a half-finished turn. The wait is just persisted state: it holds no compute and survives a restart of the engine or your worker.

Settling

Report the outcome with a tool.result or tool.error, addressed by the call id:

{ type: "tool.result"; id: string; attempt?: number; result?: unknown }
{ type: "tool.error"; id: string; error: string; retryable: boolean; attempt?: number }

Your service submits it to the engine's client API, or calls settle_effect in an embedded runtime. attempt is optional; include it to fence a settle from a stale executor that a retry has superseded. The engine records the result and, once nothing is in flight, re-prompts the model and the turn continues.

Timeouts

By default a deferred call waits indefinitely. Give the tool.call a retry policy with a timeout_secs to bound the wait: when it lapses the call fails, then retries or settles as a terminal error per the policy. See Retries.

Next