feat(tools): let one agent choose its tool presentation, and ship code
Code Mode was a deployment-wide field on the host `tools` row: a deployment ran every session that way or none. The obvious product shape — 代码模式 beside 标准/极简/创造 in the preset picker — had nothing to hang on. The registry itself cannot move into a preset; the agent loop's scheduler, the api-proxy's presenters, and every tool plugin are its consumers. So split the registry from its projection: `presentAs(mode)` writes one cell on the calling agent's scope layer, exactly as `restrict()` does, and the three reads that decided presentation take that scope's mode instead of the service's. The config `mode` becomes the default agents shadow rather than a process-wide fact. Two consequences are load-bearing. `run_code` now enters a view only for scopes whose own mode presents it — a native agent must not find it dispatchable because another agent in the process does — and the reserved name holds whatever the configured mode, since any agent may select a code mode later. `dsh-agent-tool-mode` is the row a preset carries to declare this. A code mode waits for the host's `codeRuntime` rather than assuming it, so a runtime-less deployment fails the preset at mount, naming the row, instead of at the session's first request. The shipped `code` preset is `standard` plus that row, ordered second.
This commit is contained in:
@@ -1124,6 +1124,10 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
key: 'tools',
|
||||
summary: 'Tool registry and execution pipeline.',
|
||||
methods: [
|
||||
{
|
||||
signature: 'presentAs(mode: ToolPresentationMode): () => void',
|
||||
jsDoc: '/**\n * Present this agent\'s tools in `mode` instead of the deployment default.\n *\n * Scoped only, and one declaration per agent: this is how an agent preset\n * composes a Code Mode agent beside native ones in the same process, and a\n * process-global override would be the `mode` config field instead.\n * @param mode - the presentation this agent\'s model sees.\n * @returns the exact disposer that restores the deployment default.\n */',
|
||||
},
|
||||
{
|
||||
signature: 'register(definition: ToolDefinition): () => void',
|
||||
jsDoc: '/**\n * Register globally or in the calling agent scope. Scoped tools shadow\n * globals; duplicates within one layer and the reserved `run_code` name fail.\n * @param definition - tool schema, execution, and optional finalization/presentation callbacks.\n * @returns the exact disposer that unregisters the tool.\n */',
|
||||
@@ -3043,6 +3047,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
name: 'ToolOutputDefinition',
|
||||
declaration: 'export interface ToolOutputDefinition {\n readonly schema: JsonSchemaNode;\n render(args: unknown, value: JsonValue): ContentBlock[];\n presentationMeta?(args: unknown, value: JsonValue): JsonValue;\n}',
|
||||
},
|
||||
{
|
||||
name: 'ToolPresentationMode',
|
||||
declaration: 'export type ToolPresentationMode = \'native\' | \'code\' | \'both\';',
|
||||
},
|
||||
{
|
||||
name: 'ToolProviderResult',
|
||||
declaration: 'export interface ToolProviderResult {\n readonly schemas: readonly ToolSchema[];\n readonly knownNames?: readonly string[];\n}',
|
||||
|
||||
Reference in New Issue
Block a user