Interface: ToolPolicy
Defined in: internal/tool-policy/ToolPolicy.ts:65
ToolPolicy -- which tools a run may see, and the verdict on each call before it runs
About
The registry says what exists; the policy says what this user, this profile, this step and this call may use.
Implements IToolPolicy: pure logic over the tool definitions, no I/O, no state beyond its config.
resolve, once per run: registry ∩ profile'sallowedTools∩ user's permissions ∩ model caps, narrowed to the call'sactiveTools(D48), the call'sadditionalToolsadded (D66), the built-ins always in (D61); with the set of tools that pause for a human (D87).narrowForStep, before every model step: guarded workflows (requires, D15) and each tool'svisibleWhen(D89).assertOfferedInStep, before a call runs: a tool the step hid is refused with the reason (D88).decide, before a call runs: ALLOW, DENY with a reason, or NEEDS_APPROVAL, from the tool'spolicyand the profile'sapprovals(D87).endsRun, after a call ran: whether the result is the final answer (D86).
It sits between the ToolRegistry (the catalog) and the agent loop (runAgentLoop), which asks it through the hooks
of createRunHooks.
Who needs it
prepareCall and AIGateway.resume (resolve); createRunHooks for the loop's prepareStep, executeTool,
decideToolCall and endsRun hooks. A host replaces it with its own IToolPolicy in overrides.toolPolicy.
What needs
Nothing shared: only its config (permissionsToTools, requires).
Usage
When to use it
Always, through the gateway: createGatewayDeps creates it from IAIGatewayConfig.toolPolicy.
When shouldn't use it
A host with its own authorisation model (roles, ABAC) implements IToolPolicy over it and passes it in overrides.
Pitfalls
The policy never executes and never refuses a tool outside the run: ToolRegistry.execute does (D72). A rule here is
a rule on the model's tool calls only; the application's own calls to a tool do not pass through it.
Implements
Methods
assertOfferedInStep()
assertOfferedInStep(
__namedParameters):void
Defined in: internal/tool-policy/ToolPolicy.ts:223
assertOfferedInStep -- refuses a call to a tool hidden for this step, naming what must run first when requires hid it
About
"Not yet" with the reason, instead of a bare "not available".
A tool of the run that is not among the step's tools was hidden by requires (D88) or by its visibleWhen (D89). The
403 thrown here says it is not offered in this step and, when requires names tools, which ones the model must run first,
so the model can recover in the next step instead of guessing. A tool outside the run is left to ToolRegistry.execute,
which refuses it as before (D72). A tool of the step passes.
Who needs it
createRunHooks, in the loop's executeTool, before the registry runs the call.
Parameters
__namedParameters
name
string
resolved
The run's tools.
tools
IToolDef<unknown, unknown>[]
The step's tools, as the model saw them.
Returns
void
Implementation of
IToolPolicy.assertOfferedInStep
decide()
decide(
__namedParameters):Promise<TToolCallPolicyResult>
Defined in: internal/tool-policy/ToolPolicy.ts:275
decide -- the verdict on one tool call before it runs
About
Allow it, deny it with a reason, or hand it to a human.
Applies the tool's policy (D87) and the profile's approvals to one call: a static verdict as it is; a predicate on the
parsed input and the user context; then the static approvals of resolve (the profile's approvals, the tools with
policy: NEEDS_APPROVAL) as the floor, which turn an ALLOW into NEEDS_APPROVAL. DENY wins over the floor: a human is never
asked to approve a call that would be refused. The agent loop asks before every call of a step, through the decideToolCall hook.
Who needs it
createRunHooks, for the loop's decideToolCall.
Pitfalls
A tool outside resolved.tools is ALLOW here: ToolRegistry.execute refuses it (D72). An input the schema rejects is ALLOW
too: it never reaches the predicate, ToolRegistry.execute reports it to the model (D50).
Parameters
__namedParameters
input
unknown
The model's input, as requested.
name
string
resolved
userContext
Returns
Promise<TToolCallPolicyResult>
Implementation of
IToolPolicy.decide
endsRun()
endsRun(
__namedParameters):Promise<boolean>
Defined in: internal/tool-policy/ToolPolicy.ts:314
endsRun -- whether an executed tool call is the run's final answer
About
Some tools are the end of the conversation: a handover, an order placed.
True when the tool's endsRun is true, or when its predicate says so on the parsed input and the output (D86). A
tool outside resolved.tools or an input the schema rejects never ends the run: neither can have executed.
Who needs it
createRunHooks, for the loop's endsRun hook, after every successful execution.
Pitfalls
Ask only after a successful execution: a failed, refused or rejected call must not reach here, the loop keeps that rule.
Parameters
__namedParameters
input
unknown
The model's input, as requested.
name
string
output
unknown
What the tool returned.
resolved
userContext
Returns
Promise<boolean>
Implementation of
IToolPolicy.endsRun
narrowForStep()
narrowForStep(
__namedParameters):IResolvedTools
Defined in: internal/tool-policy/ToolPolicy.ts:181
narrowForStep -- the tools of one step of a run
About
Not every tool of the run is on the table at every step.
A tool listed in requires stays hidden until every tool it requires has been executed in this run (D15, D88); a tool
with visibleWhen is shown only when its predicate says so for this step (D89); a tool must pass both. The approvals
follow the visible tools. Returns the same object when nothing was hidden.
Who needs it
createRunHooks, for the loop's prepareStep hook, before every model call of a run.
Pitfalls
executed is the run's list: tools run in earlier runs of the thread do not count, and a rejected or failed call is
not an execution (D76).
Parameters
__namedParameters
executed
string[]
Tool names already executed in this run.
resolved
stepIndex
number
Returns
Implementation of
IToolPolicy.narrowForStep
resolve()
resolve(
__namedParameters):IResolvedTools
Defined in: internal/tool-policy/ToolPolicy.ts:98
resolve -- the tool set of one run
About
Everything the profile allows and the user may use, cut to what the call asked for; the built-ins always.
The tools of allTools the profile allows and the user's permissions unlock, plus the call's additionalTools
(allowed because the app passed them, the permissions still apply, D66), narrowed to activeTools when the call
names them (D48); none when the model cannot call tools (caps.tools). The gateway's built-in tools (D61) are always
in the set, in the order of the registry: no profile, permission or activeTools filters them, and the profile's
approvals do not apply to them; a built-in's own policy still pauses (dyna_aigw_ask_user_question, D62).
approvals: the tools whose policy is NEEDS_APPROVAL, plus the tools the profile lists (D87).
Throws a 400 when activeTools names a tool the profile or the permissions do not allow (error 5373897180054): a call
narrows the set, never widens it. An additional tool whose name is in allTools, or given twice, is a 400 (error
5374506419213).
Who needs it
prepareCall, once per run(); AIGateway.resume, with the paused run's tool names, so the resumed run offers what
the paused one did.
Pitfalls
The checks run before caps.tools is read: a bad activeTools is a 400 even on a model without tools. resolved.tools
is the run's whole set; what the model sees in a step is narrowForStep's cut.
Parameters
__namedParameters
activeTools?
string[]
The call's own selection; omitted = every tool the profile and the permissions allow.
additionalTools?
IToolDef<unknown, unknown>[] = []
Tools added for this call, on top of the registry's (D66).
allTools
IToolDef<unknown, unknown>[]
builtInTools?
string[] = []
Names of the gateway's own tools among allTools; offered whatever the profile and the call say (D61).
caps
profile
userContext
Returns
Implementation of
IToolPolicy.resolve