fix(tools): resolve the collapse through the scope, not the deployment default
`collapses()` read `defaultMode`, so the collapse only applied when the DEPLOYMENT was `code`. An agent handed `code` by an agent preset under a native default announced `[run_code]` on the wire and still executed a model-direct native call -- the bypass this collapse exists to close, reopened for exactly the composition `dsh-agent-tool-mode` produces. `modeFor(scope)` is the same resolution `wireSchemas` and the SDK section already use, so presentation and execution cannot disagree, and a mode inherited from a standing preset scope collapses like a declared one. The per-agent and preset tests asserted only the wire, which is why the regression passed them. They now assert through the executor: the body never runs, the call resolves UNKNOWN_TOOL, and the native sibling beside it still executes.
This commit is contained in:
@@ -1221,7 +1221,7 @@ export class ToolRegistry extends Service {
|
||||
private resolveExecution(name: string, scope: ScopeKey | undefined, nested: boolean): ToolDefinition | undefined {
|
||||
const tool = this.get(name, scope)
|
||||
if (tool === undefined) return undefined
|
||||
if (this.collapses(name, nested)) return undefined
|
||||
if (this.collapses(name, scope, nested)) return undefined
|
||||
return tool
|
||||
}
|
||||
|
||||
@@ -1311,11 +1311,18 @@ export class ToolRegistry extends Service {
|
||||
* `parent` token set) bypass the collapse. One home for the
|
||||
* security-relevant predicate, shared by {@link resolveExecution} and
|
||||
* {@link createExecution} so the two can never drift apart.
|
||||
*
|
||||
* Resolved through {@link modeFor}, NOT `defaultMode`: an agent given `code`
|
||||
* by an agent preset under a native deployment is the composition
|
||||
* `dsh-agent-tool-mode` exists for, and reading the deployment default would
|
||||
* leave exactly that agent uncollapsed — announcing one surface while
|
||||
* executing another, which is the bypass this collapse closes.
|
||||
* @param name - the tool name as registered.
|
||||
* @param scope - the viewing scope whose effective presentation mode applies.
|
||||
* @param nested - whether the call is a transport sub-dispatch, not a model-direct call.
|
||||
*/
|
||||
private collapses(name: string, nested: boolean): boolean {
|
||||
return !nested && this.defaultMode === 'code' && name !== RUN_CODE_NAME
|
||||
private collapses(name: string, scope: ScopeKey | undefined, nested: boolean): boolean {
|
||||
return !nested && this.modeFor(scope) === 'code' && name !== RUN_CODE_NAME
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1371,7 +1378,7 @@ export class ToolRegistry extends Service {
|
||||
// keeps the historical dispatch-stage `UNKNOWN_TOOL` path so policy
|
||||
// listeners still see every name that reaches the registry.
|
||||
const visible = this.get(name, agent)
|
||||
const collapsed = visible !== undefined && this.collapses(name, parent !== undefined)
|
||||
const collapsed = visible !== undefined && this.collapses(name, agent, parent !== undefined)
|
||||
const concludingExecutions = this.concludingExecutions
|
||||
const base = {
|
||||
token,
|
||||
|
||||
Reference in New Issue
Block a user