Skip to content

Tools and approvals

A tool is a named export from your deployed handler bundle. The agent’s config.tools declares the tool’s effect and trust posture. Register tools explicitly and use requiresApproval for actions that need a project owner’s review.

{
"tools": {
"listOrders": { "effect": "read" },
"refundOrder": { "effect": "write", "requiresApproval": true }
}
}

This object belongs in an agent’s config. The tool’s name must match its exported handler. A tool step references it as { "tool": "refundOrder", "input": { "item": "$input" } }.

Export an async function from a single JavaScript ESM bundle. Its arguments are input and context. Return a serializable result. Bundle dependencies before deployment; the runtime does not install npm packages for each run.

The context includes the run and step identity, endUserId, a stable idempotencyKey, the broker channel, recordCost, and markUntrusted. Use the broker for scoped application data. Treat its token as a secret and never return it in a result or log it.

recordCost({ tokensInput, tokensOutput, costUsd }) reports costs for a tool’s work. Only report measured usage. Gateway model steps record their own provider-reported usage; do not report it a second time from a handler.

Tool posture accepts source: 'internal' | 'external', effect: 'read' | 'write', and requiresApproval: boolean. Content from an external source can lower the run’s trust; a handler that relays untrusted content can call context.markUntrusted(origin). This is a one-way reduction. A later write may require an approval even if the tool was not explicitly marked requiresApproval.

Trust posture is a declaration used by the runtime, not an inspection of your handler’s source. Label effects honestly and validate input. An approval does not make malicious SQL, external content, or a handler implementation safe.

When the run reaches a gated step, it enters awaiting-approval before executing that tool. The customer UI can show that it is waiting. The project owner reviews it in Approvals in the console or with the CLI:

Terminal window
npx --yes getstead@0.2.1 approvals
npx --yes getstead@0.2.1 approve "$APPROVAL_ID"
# Or reject it:
npx --yes getstead@0.2.1 deny "$APPROVAL_ID"

Set APPROVAL_ID to the ID returned by the queue. Review the action’s input, reason, run, and expiry before deciding. Resolving a pending approval automatically resumes its run. Customers cannot approve their own actions; the API requires project or owning developer authority.

Denial or expiry prevents the gated action and ends the run as failed, with an approval-denied or approval-expired reason in its trace. A decision is accepted only while the approval is pending and unexpired; a stale or foreign ID is refused. Check the run trace instead of repeatedly submitting a decision.

A process can fail after an external service accepted a write but before Stead records its result. Pass context.idempotencyKey to external services that support idempotency, and implement your own deduplication when necessary. Do not describe a run log as an exactly-once guarantee for outside effects.

The starter’s refund changes a demo order’s amount to zero. It does not contact a payment provider or return money. Replace it with your own reviewed integration before using a real refund flow.