Tool Approval & State Persistence
Tool Approval & State Persistence
Add human-in-the-loop approval gates for sensitive tools and persist conversation state across callModel invocations.
Tool Approval & State Persistence
Add human-in-the-loop approval gates for sensitive tools and persist conversation state across callModel invocations.
Some tools — sending emails, making payments, deleting records — should not auto-execute without human review. The SDK provides two mechanisms to control this:
requireApproval — pause execution when the model calls sensitive tools, giving users a chance to approve or reject each callStateAccessor — persist conversation state between callModel invocations so approval decisions, message history, and tool results survive across runsTogether, these enable human-in-the-loop workflows where a user reviews tool calls before they execute, even across separate request/response cycles (e.g., in a web application).
Add requireApproval directly on a tool definition. It accepts a boolean or a function:
Pass a function to require approval only in certain cases:
The function receives the parsed tool arguments and a TurnContext, and can return a boolean or Promise<boolean>.
Override tool-level settings with a requireApproval callback on callModel itself:
The call-level callback takes priority over tool-level requireApproval settings when both are present.
When tools with approval gates are called by the model, the SDK follows this flow:
requireApproval and split into two groups: those requiring approval and those that can auto-executestatus: 'awaiting_approval' with the pending tool calls storedresult.requiresApproval() and inspect pending calls with result.getPendingToolCalls()callModel again with the same state, passing approveToolCalls and/or rejectToolCalls arrays of tool call IDsThe StateAccessor interface enables any storage backend:
For production use, implement StateAccessor with a persistent backend like Redis, a database, or file storage to survive process restarts.
The state object tracks everything needed to resume a conversation:
Here is an end-to-end example showing approval gates with state persistence:
When the state has status: 'awaiting_approval', pass approveToolCalls and/or rejectToolCalls to resume:
If a conversation was interrupted (status: 'interrupted'), calling callModel with the same state resumes automatically. The SDK clears the interruption flag and continues where it left off:
Messages accumulate automatically across callModel runs that share the same StateAccessor. Each run appends its input and response to the state’s message history:
tool() helper