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" } }.
Handler contract
Section titled “Handler contract”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.
Review an action
Section titled “Review an action”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:
npx --yes getstead@0.2.1 approvalsnpx --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.
Make effects safe to retry
Section titled “Make effects safe to retry”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.