feat(extensions): add ctx.invokeTool for native built-in delegation

A tool's execute context now carries invokeTool(name, params, options?),
which runs the native built-in of `name` and returns its result. A tool
that re-registers a built-in (e.g. wrapping write to add logging or a
policy check) can delegate to the original instead of reimplementing it.

The native implementation is captured before extension re-registration
replaces the registry entry and before the ExtensionToolWrapper pass, so
invokeTool reaches the unwrapped native execute: it does not recurse into
the caller's own wrapper, and it inherits the caller's already-granted
approval rather than re-running the gate. Delegation depth is guarded
against accidental self-recursion, and it resolves to undefined when no
native tool of that name exists.

Wired through ToolContextStore with a lazy native-tool resolver, so it is
coding-agent-only (no agent-loop change) and sees the fully-assembled
built-in set at call time.
This commit is contained in:
Larry Gordon
2026-07-27 15:12:28 -07:00
parent 4df68d6043
commit 6acf957dd8
5 changed files with 213 additions and 8 deletions
+19
View File
@@ -312,6 +312,25 @@ execute(
): Promise<AgentToolResult>
```
### Delegating to a native built-in (`ctx.invokeTool`)
A tool that re-registers a built-in name (e.g. wrapping `write` to add logging or a policy check) can
run the original instead of reimplementing it. The `ctx` passed to `execute` carries:
```ts
ctx.invokeTool?<TDetails>(
name: string,
params: Record<string, unknown>,
options?: { signal?: AbortSignal; onUpdate?: AgentToolUpdateCallback },
): Promise<AgentToolResult<TDetails> | undefined>
```
It runs the **native** built-in of `name` (bypassing your own re-registration, so it does not recurse
into your wrapper) and returns its result, including the native tool's own side effects and internal
bookkeeping. It resolves to `undefined` when there is no native tool of that name. The invoked tool's
approval gate is not re-run — your call already passed approval — and delegation depth is guarded
against accidental self-recursion.
Template:
```ts