From 0911a87409a77309b3da82549dfeb7e698f34555 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:29:01 +0800 Subject: [PATCH] docs(i18n): refresh core translations for latest master --- .../code-runtime.i18n.yaml | 4 +- docs/core-data-structures/code-runtime.zh.md | 54 +++- docs/core-data-structures/core.i18n.yaml | 4 +- docs/core-data-structures/core.zh.md | 4 +- .../core-data-structures/filesystem.i18n.yaml | 4 +- docs/core-data-structures/filesystem.zh.md | 6 +- docs/core-data-structures/session.i18n.yaml | 4 +- docs/core-data-structures/session.zh.md | 26 +- docs/core-data-structures/subagent.i18n.yaml | 4 +- docs/core-data-structures/subagent.zh.md | 4 +- docs/core-data-structures/tools.i18n.yaml | 4 +- docs/core-data-structures/tools.zh.md | 243 ++++++++++-------- scripts/type-equiv.manifest.json | 52 +++- 13 files changed, 257 insertions(+), 156 deletions(-) diff --git a/docs/core-data-structures/code-runtime.i18n.yaml b/docs/core-data-structures/code-runtime.i18n.yaml index f534e6a821..218ef4eea9 100644 --- a/docs/core-data-structures/code-runtime.i18n.yaml +++ b/docs/core-data-structures/code-runtime.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -code-runtime.md: a984c0f6422defc879086ff95eb94048aaa6e285 -code-runtime.zh.md: 95287f2917c8fd409944569a2f5c3e8417a18efe +code-runtime.md: 64de3c45d4f1d1d981daa6c6f074abb667e0aa52 +code-runtime.zh.md: 4b14aeb2183010e8140540258ce8109df9f59910 diff --git a/docs/core-data-structures/code-runtime.zh.md b/docs/core-data-structures/code-runtime.zh.md index 95287f2917..4b14aeb218 100644 --- a/docs/core-data-structures/code-runtime.zh.md +++ b/docs/core-data-structures/code-runtime.zh.md @@ -2,7 +2,7 @@ [English](code-runtime.md) | 中文 -代码执行 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md):其接口([dsh-code-runtime](../../packages/code-runtime/code-runtime),`ctx.codeRuntime`)针对宿主提供的异步 binding 运行一段模型编写的程序,并报告其打印内容与返回值。代码执行是**一项可选能力**,不属于 agent loop(智能体循环)主干,因此其词汇定义在此而非 [core.md](core.md) 中。各后端的执行基底与源语言不同,这两项均为服务上的只读描述符;worker-thread 后端和工具注册表消费方(Code Mode)由 [Code Mode Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md) 规定。 +代码执行 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md):其接口([dsh-code-runtime](../../packages/code-runtime/code-runtime),`ctx.codeRuntime`)针对宿主提供的异步 binding 运行一段模型编写的程序,并报告其打印内容与返回值。代码执行是**一项可选能力**,不属于 agent loop(智能体循环)主干,因此其词汇定义在此而非 [core.md](core.md) 中。各后端的执行基底与源语言不同,这两项均为服务上的只读描述符;worker-thread 后端与工具注册表消费方的契约见 [Code Mode 基础设计](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)和[类型化返回契约](../../.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.md)。 源码:[`packages/code-runtime/code-runtime/src/types.ts`](../../packages/code-runtime/code-runtime/src/types.ts) @@ -47,12 +47,12 @@ interface CodeRunRequest { interface CodeRunResult { /** * The program's completion value (its top-level `return`), when it ran to - * completion and the value survived the runtime's serialization boundary; - * a non-transferable value is replaced by a string rendering, and a failed - * or value-less run leaves this absent. + * completion and the value crossed the runtime's lossless-JSON boundary. + * Invalid or over-limit completions fail the run instead of substituting a + * rendered string; a failed or value-less run leaves this absent. */ - value?: unknown - /** Text the program emitted, in order (capped by the implementation). */ + value?: CodeJsonValue + /** Text the program emitted, in order, bounded only as part of the outer result. */ logs: string[] /** Present iff the run failed; see {@link CodeRunFailure} for the taxonomy. */ error?: CodeRunFailure @@ -61,7 +61,23 @@ interface CodeRunResult { ## 绑定:宿主函数作为程序全局变量 -每个 `CodeBindingNamespace` 在程序内成为一个由异步可调用函数组成的全局对象(Code Mode 消费方传入一个:`tools`)。参数与返回值必须可 structured-clone(运行时可能跨序列化边界桥接调用),且运行时将绑定名视为不可信输入(`__proto__` 是普通自有属性,绝不会发生原型碰撞): +每个 `CodeBindingNamespace` 在程序内成为一个由异步可调用函数组成的全局对象(Code Mode 消费方传入一个:`tools`)。参数与返回值必须是无损 JSON,且跨越边界时不受 seam 层字节上限约束;运行时可以通过结构化克隆桥接它们。命名空间可以声明程序可见的错误类,而无需让运行时知道消费方的名称:运行时会注入真实构造函数,并将被拒绝的调用转为该类的实例。运行时也将绑定名视为不可信输入(`__proto__` 是普通自有属性,绝不会发生原型碰撞): + +```ts type-equiv +/** + * Program-visible typed rejection for one binding namespace. The runtime + * injects a real error constructor under `name`; rejected member calls become + * its instances and expose the exact member name through + * `memberNameProperty`. Both strings are runtime data rather than knowledge + * of a particular consumer such as Code Mode. + */ +interface CodeBindingErrorClass { + /** Constructor global and resulting `Error.name` (must be a usable JS identifier). */ + name: string + /** Non-empty own property for the member name; cannot replace `name`, `message`, or `stack`. */ + memberNameProperty: string +} +``` ```ts type-equiv /** @@ -76,24 +92,32 @@ interface CodeBindingNamespace { global: string /** The callable members, keyed by the exact name the program calls. */ functions: Record + /** Optional program-visible typed rejection contract for this namespace. */ + errorClass?: CodeBindingErrorClass } ``` +```ts type-equiv +/** A lossless JSON value transferable across the dependency-light code-runtime seam. */ +type CodeJsonValue = null | boolean | number | string | CodeJsonValue[] | { [key: string]: CodeJsonValue } +``` + ```ts type-equiv /** * One host-side function exposed to the program as an async callable. The * runtime bridges calls to it (possibly across a serialization boundary), so - * `args` and the resolution value MUST be structured-cloneable; a runtime - * rejects a non-cloneable value with a descriptive error rather than - * corrupting the run. A rejection of this function surfaces inside the - * program as a rejection of the corresponding call. + * `args` and the resolution value MUST be lossless JSON. A runtime rejects a + * lossy or non-cloneable value with a descriptive error rather than corrupting + * the run. No seam-level byte cap applies to a binding resolution. A rejection + * of this function surfaces inside the program as a rejection of the + * corresponding call. */ -type CodeBindingFunction = (args: unknown) => Promise +type CodeBindingFunction = (args: unknown) => Promise ``` ## 捕获的输出与失败分类体系 -日志是按发出顺序排列的纯字符串。运行时捕获程序的 console 与流输出,但通道和 console 方法的元数据不属于 seam 的一部分,因为消费方只渲染文本。实现对聚合输出设上限,并在输出内标记截断。 +日志是按发出顺序排列的纯字符串。运行时捕获程序的 console 与流输出,但通道和 console 方法的元数据不属于 seam,因为消费方只渲染文本。实现会对序列化后的外层日志数组,以及完成值或失败消息的组合载荷设置上限;固定的结果封装语法与消费方展示空白不计入这份可变载荷计量。超限会显式失败,而不会在值中插入替代内容。 失败类型是**正交的结果,独立报告**(见 [defensive-patterns](../defensive-patterns.md)):预算耗尽不是异常,中止不是超时,基底崩溃(如 OOM)也不是二者中的任何一个: @@ -107,10 +131,12 @@ type CodeBindingFunction = (args: unknown) => Promise * - `'timeout'` — an implementation-owned budget expired; the message says which. * - `'abort'` — {@link CodeRunRequest.signal} fired. * - `'worker-exit'` — the execution substrate died without settling (e.g. OOM). + * - `'invalid-output'` — the completion value was not lossless JSON. + * - `'output-limit'` — the serialized outer logs/value/diagnostic exceeded the configured cap. */ interface CodeRunFailure { /** The failure class (see the interface doc for each kind's meaning). */ - kind: 'exception' | 'timeout' | 'abort' | 'worker-exit' + kind: 'exception' | 'timeout' | 'abort' | 'worker-exit' | 'invalid-output' | 'output-limit' /** Human-readable detail, suitable for feeding back to a model to self-correct. */ message: string } diff --git a/docs/core-data-structures/core.i18n.yaml b/docs/core-data-structures/core.i18n.yaml index 32019f6002..a01c020939 100644 --- a/docs/core-data-structures/core.i18n.yaml +++ b/docs/core-data-structures/core.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -core.md: 7d0f9503dfd4c47f185d2bcef84528145d66fa70 -core.zh.md: 21f417bdf8ddb6c4d629de037d95a0196961aeb3 +core.md: 9446152b909cd3e0105fe44321b16353228c0730 +core.zh.md: 55b04e3ef4215fc984824015761f5f41537656a0 diff --git a/docs/core-data-structures/core.zh.md b/docs/core-data-structures/core.zh.md index 21f417bdf8..55b04e3ef4 100644 --- a/docs/core-data-structures/core.zh.md +++ b/docs/core-data-structures/core.zh.md @@ -13,7 +13,7 @@ harness 是一个微内核:一个极小的核心加上众多插件。大多数 1. 它流经 agent loop 主干——循环在每个轮次中持有、派生、流式输出或记录它(`Message`、`StreamChunk`、`SessionEvent`、`Agent` 句柄本身),与当前加载了哪些插件无关;**或者** 2. 它是插件作者面向某条流水线编写的唯一标题类型——`ToolDefinition`(每个工具*是什么*)。 -其他一切都记录在**子页面**上,而非本页。划线的规则是:*你编写、持有或接收的类型是核心;为它提供类型推导、渲染或持久化的机制是子页面细节*。因此 `ToolDefinition` 是核心,但为它提供类型推导的 `SchemaSpec`/`InferArgs` DSL、为它提供渲染意图的 `ToolCallView`/`ToolResultView` 词汇,以及存储事件日志的 `SessionPersistence` seam 都不是——它们分别在下列子页面中。 +其他一切都记录在**子页面**上,而非本页。划线的规则是:*你编写、持有或接收的类型是核心;为它提供类型推导、渲染或持久化的机制是子页面细节*。因此 `ToolDefinition` 是核心,但为它提供类型推导的 `ValueSchemaSpec`/`ParameterSchemaSpec` 机制、为它提供渲染意图的 `ToolCallView`/`ToolResultView` 词汇,以及存储事件日志的 `SessionPersistence` seam 都不是——它们分别在下列子页面中。 | 子页面 | 负责内容 | |---|---| @@ -552,4 +552,4 @@ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact' 唯一属于核心的流水线编写类型:每个已注册工具*是什么*——一个面向模型的 `ToolSchema` 加上一个 `execute` 函数和可选的 UI 展示器。工具作者很少手动构造它(`defineTool` DSL 会用类型化参数构建),但它是注册表持有、循环分发所经过的契约。 -其完整字段、`defineTool`/`SchemaSpec`/`InferArgs` 类型化 schema DSL、`ToolExecution`/`ToolExecutionResult` waterfall 形状,以及工具展示 UI 词汇在 **[tools.md](tools.md)** 中。 +其完整字段、`defineTool`/`ValueSchemaSpec`/`ParameterSchemaSpec` 类型化 schema DSL、`ToolExecution`/`ToolExecutionResult` waterfall 形状,以及工具展示 UI 词汇在 **[tools.md](tools.md)** 中。 diff --git a/docs/core-data-structures/filesystem.i18n.yaml b/docs/core-data-structures/filesystem.i18n.yaml index 6713615776..e360dd99d3 100644 --- a/docs/core-data-structures/filesystem.i18n.yaml +++ b/docs/core-data-structures/filesystem.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -filesystem.md: 3c9b041da92e71cd20429e8b1549c0b8e6f2436d -filesystem.zh.md: 612b90427c6813823d38515257acfdfafcc2ba1e +filesystem.md: 110c1fd428b15c5094f9dcc94050cad61c324373 +filesystem.zh.md: aca450364c05c6f756c36fccc11be7246767f3a4 diff --git a/docs/core-data-structures/filesystem.zh.md b/docs/core-data-structures/filesystem.zh.md index 612b90427c..aca450364c 100644 --- a/docs/core-data-structures/filesystem.zh.md +++ b/docs/core-data-structures/filesystem.zh.md @@ -207,7 +207,7 @@ interface FsPolicyExec { ## 读取结果(消费方 / 读取渲染) -文本读取受行窗口、字节上限和后端限制约束。面向模型的 `read` 工具渲染的结果纯粹是展示性的;不存在 `full`/`partial` 视图区分——授权基于新鲜度(工具直接用 stat 的版本 emit `fs/observed`),因此任何窗口化读取在文件未变时都能授权后续的 write/edit。读取窗口化与此结果形状位于 `dsh-tool-fs`(拥有读取操作的执行器)中,而非策略插件中。 +文本读取受行窗口、字节上限和后端限制约束。达到字节上限后,扫描仍会继续,但不再保留更多行,因此 `totalLines` 仍为精确值。面向模型的 `read` 工具渲染的结果纯粹是展示性的;不存在 `full`/`partial` 视图区分——授权基于新鲜度(工具直接用 stat 的版本 emit `fs/observed`),因此任何窗口化读取在文件未变时都能授权后续的 write/edit。读取窗口化与此结果形状位于 `dsh-tool-fs`(拥有读取操作的执行器)中,而非策略插件中。 ```ts type-equiv /** Outcome of a bounded text read — what {@link formatReadOutput} renders. */ @@ -216,9 +216,9 @@ interface FileReadOutcome { offset: number /** Returned lines, already numbered. */ lines: FileTextLine[] - /** Total line count in the file, unless `truncatedByBytes` stopped scanning early. */ + /** Exact total line count in the file. */ totalLines: number - /** Whether selected output hit the byte cap before EOF or the requested limit. */ + /** Whether selected output hit the byte cap. */ truncatedByBytes?: true } ``` diff --git a/docs/core-data-structures/session.i18n.yaml b/docs/core-data-structures/session.i18n.yaml index 8dbb8a8ae2..ab8a534829 100644 --- a/docs/core-data-structures/session.i18n.yaml +++ b/docs/core-data-structures/session.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -session.md: e07e38037e51c74886db88d24353266f6035b15a -session.zh.md: 8d3de643a42bdd173528b024276d0f83e90d2d11 +session.md: b342e1c5c3bff030d67c61a6f1daa0c8167182c1 +session.zh.md: 8e8a3f923b2f4ec1dd5d86ebe6bb7eff0511474c diff --git a/docs/core-data-structures/session.zh.md b/docs/core-data-structures/session.zh.md index 8d3de643a4..8e8a3f923b 100644 --- a/docs/core-data-structures/session.zh.md +++ b/docs/core-data-structures/session.zh.md @@ -87,15 +87,25 @@ interface SessionEventMap { */ 'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string } /** - * A completed tool call's model-facing result, plus an optional tool-private - * `meta` presentation payload. `meta` is opaque to the core (`unknown` — the - * producing tool owns its shape and reads it back in `presentResult`) but MUST - * be JSON-serializable: `Session.append` runtime-validates all event data with - * `isJsonValue`, so a non-serializable `meta` is rejected at the source, and the - * durable log reproduces the identical card on replay. Absent unless the tool - * attaches one (e.g. `dsh-tool-fs` carries its result-time contextual diff here). + * A completed tool call's model-facing result, optional internal failure + * identity, and optional tool-private `meta` presentation payload. `meta` is + * opaque to the core (the producing tool owns its shape and reads it back in + * `presentResult`) but MUST be JSON-serializable: `Session.append` + * runtime-validates all event data with `isJsonValue`, so a non-serializable + * `meta` is rejected at the source, and the durable log reproduces the + * identical card on replay. Absent + * unless the tool attaches one (e.g. `dsh-tool-fs` carries its result-time + * contextual diff here). */ - 'tool/result': { turn: number; step: number; callId: CallId; content: ContentBlock[]; isError: boolean; error?: { name: string; code: string }; meta?: unknown } + 'tool/result': { + turn: number + step: number + callId: CallId + content: ContentBlock[] + isError: boolean + error?: { name: string; code: string } + meta?: JsonValue + } /** Steering content injected between steps of a running turn. */ 'steering/message': PromptMessageData & { turn: number } /** Whole-list snapshot; latest write wins on replay. Log-only UI state; never derived history. */ diff --git a/docs/core-data-structures/subagent.i18n.yaml b/docs/core-data-structures/subagent.i18n.yaml index e0cd108b13..0b6410142f 100644 --- a/docs/core-data-structures/subagent.i18n.yaml +++ b/docs/core-data-structures/subagent.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -subagent.md: 97d6862c10a0757c41472f207f857c25f3f5d50f -subagent.zh.md: d28e18df6361c4a87a66b027f20cf584b8b14ecb +subagent.md: 0335a3f0780ae17b57ae730f5a49a269261c8073 +subagent.zh.md: 0f255ac79258d91a3305c4e2a9c9f943f5674c1c diff --git a/docs/core-data-structures/subagent.zh.md b/docs/core-data-structures/subagent.zh.md index d28e18df63..0f255ac792 100644 --- a/docs/core-data-structures/subagent.zh.md +++ b/docs/core-data-structures/subagent.zh.md @@ -68,11 +68,11 @@ interface SubagentStartRequest { /** Per-child agent options (model and plugin-defined extension fields). */ readonly agentOptions?: AgentOptions /** - * Object-rooted JSON Schema within `assertSupportedOutputSchema`'s enforced subset. Start rejects + * Object-rooted JSON Schema within `assertObjectJsonSchema`'s enforced subset. Start rejects * unsupported schemas or providers without the capability. Data must be plain host-realm JSON; * a successful child returns the matching value as {@link SubagentResult.structured}. */ - readonly outputSchema?: StructuredOutputSchema + readonly outputSchema?: ObjectJsonSchema /** * Optional absolute delegation-depth cap for the child being started: its * computed depth must be less than or equal to this non-negative safe diff --git a/docs/core-data-structures/tools.i18n.yaml b/docs/core-data-structures/tools.i18n.yaml index 4f6dd0c8d6..4ef6231caa 100644 --- a/docs/core-data-structures/tools.i18n.yaml +++ b/docs/core-data-structures/tools.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write -tools.md: ce14a37da33f89b8b90d6d8e70756f94e3d690dd -tools.zh.md: 77b1c9534c5eae8843e458eedb6af0e8950375ae +tools.md: 4612800ce0b2b2d718cb32c1aad9fb2e592c337b +tools.zh.md: 5663ecef53654c1fc0f8c1d8661ff34cbcd5eb88 diff --git a/docs/core-data-structures/tools.zh.md b/docs/core-data-structures/tools.zh.md index 77b1c9534c..5663ecef53 100644 --- a/docs/core-data-structures/tools.zh.md +++ b/docs/core-data-structures/tools.zh.md @@ -8,21 +8,36 @@ ## `ToolDefinition` — 一个已注册的工具 -由一个 `ToolSchema`(面向模型的字段)、`execute` 函数、仅供宿主使用的调度器元数据和可选 UI 展示函数组成。注册表持有这些定义,循环通过它们分派调用。注册表的 `schemas()` 通过显式允许列表构建面向模型的 `ToolSchema[]`;`execute`/`timeoutMs`/`isConcurrencySafe`/`presentCall`/`presentResult` 绝不能泄漏到模型请求中。 +由一个 `ToolSchema`(面向模型的字段)、必需的规范输出声明、`execute` 函数、仅供宿主使用的调度器元数据和可选 UI 展示函数组成。注册表持有这些定义,循环通过它们分派调用。注册表的 `schemas()` 通过显式允许列表构建面向模型的 `ToolSchema[]`;`output`/`execute`/`timeoutMs`/`isConcurrencySafe`/`presentCall`/`presentResult` 绝不能泄漏到模型请求中。 + +```ts type-equiv +/** Tool-owned canonical output contract used after the body returns a JSON value. */ +interface ToolOutputDefinition { + /** Raw supported JSON Schema enforced against every successful canonical value. */ + readonly schema: JsonSchemaNode + /** Pure projection from validated arguments and value to Native/model content. */ + render(args: unknown, value: JsonValue): ContentBlock[] + /** Pure replayable presentation projection, computed only for surface calls. */ + presentationMeta?(args: unknown, value: JsonValue): JsonValue +} +``` ```ts type-equiv /** A registered tool: its schema plus the execution function. */ interface ToolDefinition extends ToolSchema { + /** Mandatory canonical output declaration. */ + readonly output: ToolOutputDefinition /** - * Run one accepted call. Async work must observe or forward `exec.signal` and - * settle only after its owned work reaches quiescence. The registry preserves - * caller cancellation through around-dispatch signal replacement and does - * not abandon this promise, but it cannot hard-kill same-process code. + * Run one accepted call and return only its canonical lossless-JSON value. + * Async work must observe or forward `exec.signal` and settle only after its + * owned work reaches quiescence. The registry preserves caller cancellation + * through around-dispatch signal replacement and does not abandon this + * promise, but it cannot hard-kill same-process code. * @param args - losslessly snapshotted, frozen model arguments. * @param exec - execution identity, cancellation signal, and context deferral. - * @returns model-facing content plus optional private presentation metadata. + * @returns the canonical value declared by `output.schema`. */ - execute(args: unknown, exec: ToolRunContext): Promise + execute(args: unknown, exec: ToolRunContext): Promise /** * Cooperative tool-call timeout budget in milliseconds. Omit for no deadline. * Enforced by `@deepseek-ai/dsh-timeout-policy` (a `tools/execute` wrapper); it @@ -57,7 +72,7 @@ interface ToolDefinition extends ToolSchema { presentCall?(args: unknown): ToolCallView | undefined /** * Optional: how to present the COMPLETED state, given the same `args` and the - * `result` (`execute`'s content + whether it errored). Returns a + * durable result projection (`content`, failure state, and optional `meta`). Returns a * {@link ToolResultView}, or `undefined` (or omit the method) to keep the * pending title and render the raw result content. Pure and side-effect-free * for the same replay reason. @@ -66,69 +81,62 @@ interface ToolDefinition extends ToolSchema { } ``` -`execute` 接收 `args: unknown`——原始的 `ToolDefinition` 自行校验输入。第一方工具不需要手写校验;它们使用 `defineTool`,由后者代为校验并收窄类型。 +`execute` 接收 `args: unknown`——原始的 `ToolDefinition` 自行校验输入。第一方工具不需要手写校验;它们使用 `defineTool`,由后者代为校验并收窄参数类型、根据 `output.schema` 推导函数体返回类型,并为两个输出投影器提供类型约束。 -## 类型化 schema DSL +## 统一的 JSON 值 schema DSL -插件作者为每个属性编写带有布尔值 `required: true` 的规格,类型层面的辅助工具将规格映射为 `execute` 的参数类型——零类型断言。该 DSL 是*提供类型推导的机制*,作用于 `ToolDefinition`;它有意作为子页面细节,而非核心内容。 +插件作者使用同一套词汇描述类型化参数和类型化输出值。`ValueSchemaSpec` 支持 `string`、`number`、`integer`、`boolean`、`null`、`array`、`object`、仅作者侧可用的 `json`,以及要求恰好命中一个分支的 `oneOf`;标量 `enum` 和 `const` 值必须与节点类型匹配。显式对象节点始终声明 `additionalProperties: true | false`。参数定义仍是隐式的开放对象属性映射,每个必填属性都附带 `required: true`。 源码:[`packages/core/tools/src/schema.ts`](../../packages/core/tools/src/schema.ts) ```ts type-equiv -/** One schema-spec property entry. */ -interface SchemaProp { - type: SchemaType - /** Per-property required flag (NOT the JSON Schema top-level required array). */ - required?: true - /** Human-readable description, surfaced in the JSON Schema as well. */ - description?: string - /** Enum of allowed values (strings only). */ - enum?: string[] - /** - * Model-visible JSON Schema default annotation. Validation does not apply it; - * dynamic tool mounts may supply it even though first-party definitions do not. - */ - default?: unknown - /** Nested properties for type: 'object'. */ - properties?: SchemaSpec - /** Items schema for type: 'array'. */ - items?: SchemaProp +/** One author-facing schema for any lossless JSON value root. */ +type ValueSchemaSpec = + | StringValueSchemaSpec + | NumberValueSchemaSpec + | IntegerValueSchemaSpec + | BooleanValueSchemaSpec + | NullValueSchemaSpec + | ArrayValueSchemaSpec + | ObjectValueSchemaSpec + | JsonValueSchemaSpec + | OneOfValueSchemaSpec +``` + +```ts type-equiv +/** One implicit parameter-root property, optionally required. */ +type ParameterPropertySpec = ValueSchemaSpec & { required?: true } +``` + +```ts type-equiv +/** + * Tool parameter schema. The map itself is an implicit open object root; + * requiredness remains a per-property `required: true` annotation. + */ +type ParameterSchemaSpec = { + [key: string]: ParameterPropertySpec + [key: symbol]: never } ``` -```ts type-equiv -/** - * The author-facing parameter schema: a shallow map of property name to - * {@link SchemaProp}. Required-ness is a per-property boolean (`required: - * true`), not a separate array. - */ -type SchemaSpec = Record -``` - -`SchemaType` 是原始联合类型 `'string' | 'number' | 'boolean' | 'object' | 'array'`。`InferArgs` 将一个 `SchemaSpec` 映射为 TS 参数类型——`required: true` 的属性成为必选键,其余为真正的可选: +`{ type: 'json' }` 推导为 `JsonValue`,并编译成仅含注解、不施加约束的原始 schema。输出根可以是对象、数组、标量或 null。`InferValue` 在 16 层容器内保留字面量约束与对象开放性,之后回退为 `JsonValue`,避免耗尽 TypeScript 的类型实例化栈。`InferArgs

` 依据逐属性的必填标记生成必填和可选的字符串键: ```ts type-equiv /** - * Infer the TS argument type for a complete {@link SchemaSpec}. - * - * Properties marked `required: true` are required keys; all others are - * genuinely optional keys (`?`), so callers may omit them entirely. - * - * Example: - * ```ts - * type Args = InferArgs<{ path: { type: 'string'; required: true }; limit: { type: 'number' } }> - * // → { path: string; limit?: number } - * ``` + * Infer the TypeScript value accepted by an author-facing value schema. Exact + * inference is bounded to 16 container levels, then falls back to `JsonValue`. */ -type InferArgs = Simplify< - & { [K in RequiredKeys]: InferPropValue } - & { [K in Exclude>]?: InferPropValue } -> +type InferValue = InferValueAt ``` -`defineTool({ name, description, parameters, execute, … })` 将各部分串联:`parameters` 是一个 `SchemaSpec`,`execute(args, exec)` 获得 `args: InferArgs`,辅助函数将规格转换为 JSON Schema(`schemaSpecToJsonSchema`)用于协议传输,并在类型化函数体运行前校验模型生成的参数(`validateArgs`)。校验不通过时抛出 `ToolArgsError`(`code: 'INVALID_ARGS'`),注册表将其转为 `isError` 结果以便模型自行修正。为何用自定义 DSL 而非 schemastery:工具参数需要 JSON Schema(LLM(大语言模型)的协议格式),而非校验/转换——轻量 DSL 以最小的接口面积提供最佳的编写体验。 +```ts type-equiv +/** Infer the TypeScript argument object for an implicit parameter schema. */ +type InferArgs = InferProperties +``` -注册是一个受信任的同进程契约。注册表以 readonly 输入借用类型化定义,仅校验语义要求(如 `timeoutMs` 必须为正有限值);`schemas()` 在模型边界处物化显式的面向模型投影,使执行和展示共享同一份已解析定义,而不会将回调泄漏到协议上。 +`defineTool({ name, description, parameters, output, execute, … })` 将参数推导与 `parameterSchemaSpecToJsonSchema()` 和 `validateArgs()` 绑定,并将 `execute`/`render`/`presentationMeta` 与 `InferValue` 绑定。Schema 记录只包含自有且可枚举的字符串键,schema 数组是稠密的内建数组,因此推导、编译与校验观察到的是同一份声明。精确推导保持到 16 层容器,之后放宽为 `JsonValue`;运行时校验仍会继续遍历完整 schema。`valueSchemaSpecToJsonSchema()` 通过同一套已强制执行的原始子集编译输出声明。参数不匹配时抛出 `ToolArgsError`(`INVALID_ARGS`);函数体或后置策略产生的值无效时抛出 `ToolOutputError`(`INVALID_TOOL_OUTPUT`)。两者都经由常规工具错误路径处理。原始 JSON Schema 默认保持开放;不支持的关键字会被拒绝,而不会在未强制执行的情况下获准进入。 + +注册是一个受信任的同进程契约。注册表以 readonly 输入借用类型化定义,要求它声明 `output`,校验其原始 schema,并检查 `timeoutMs` 必须为正有限值等语义要求;`schemas()` 在模型边界处物化显式的面向模型投影,使执行和展示共享同一份已解析定义,而不会将回调泄漏到协议上。 ## `ToolRestriction` — 单个作用域的实时全局过滤器 @@ -254,34 +262,48 @@ type ToolGuard = (execution: Readonly) => string | undefined ``` ```ts type-equiv -/** The outcome of one tool call. */ -interface ToolExecutionResult { - content: ContentBlock[] - isError: boolean - /** - * Set when the call failed with a {@link HarnessError}: machine-routable - * `{ name, code }` for retry/sandbox plugins and replay. The model-facing - * text in `content` is always present; this is extra structure for code. - */ - error?: ToolErrorInfo - /** - * Model-facing context for the next request, separate from this tool result. The loop - * accepts it into the active-batch FIFO, then appends after recorded results even if interrupted. - */ - additionalContexts?: HookContext[] - /** - * The tool-private presentation payload from a successful `execute` (the object - * return form). Threaded onto the `tool/result` session event and back into - * {@link ToolResult} for `presentResult`. Opaque (`unknown`); absent when the - * tool attached none or the call failed. - */ - meta?: unknown +/** Canonical failure detail; internal routing information remains optional. */ +interface ToolFailure { + /** Human-readable failure message without the Native `Error: ` envelope. */ + message: string + /** Internal error class/code used by policy and durable diagnostics. */ + info?: ToolErrorInfo } ``` -结果仅承载产出。调用身份保留在不可变的 `ToolExecution` 上,后者伴随结果经过每个钩子,并出现在持久化的 `tool/call` / `tool/result` 会话事件上,因此包装层无法创建第二个相互矛盾的身份。 +```ts type-equiv +/** Successful canonical tool execution, including its Native/model projection. */ +interface ToolExecutionSuccess { + readonly isError: false + /** Execution-local canonical value; deliberately omitted from durable events. */ + readonly value: JsonValue + readonly content: ContentBlock[] + readonly error?: never + readonly meta?: JsonValue + readonly additionalContexts?: HookContext[] +} +``` -注册表在 `tools/result` 之前立即物化并冻结最终接受的结果。其内容、结构化错误、附加上下文和展示元数据必须通过 JSON 无损往返;无效的产出会被转为 JSON 安全的 `isError` 结果,从而保证被观察到的实时产出对后续持久化的 `tool/result` 追加是安全的。 +```ts type-equiv +/** Failed canonical tool execution; failures never carry a successful value. */ +interface ToolExecutionFailure { + readonly isError: true + readonly error: ToolFailure + readonly value?: never + readonly content: ContentBlock[] + readonly meta?: JsonValue + readonly additionalContexts?: HookContext[] +} +``` + +```ts type-equiv +/** The discriminated, execution-local outcome of one tool call. */ +type ToolExecutionResult = ToolExecutionSuccess | ToolExecutionFailure +``` + +结果仅承载产出。调用身份保留在不可变的 `ToolExecution` 上,后者伴随结果经过每个钩子,并出现在持久化的 `tool/call` / `tool/result` 会话事件上,因此包装层无法创建第二个相互矛盾的身份。规范的 `value` 仅存在于执行期间:循环只持久化 `content`、`error` 和 `meta`,`tool/code-dispatch` 则存储有界摘要。回放可以重现展示,却无法重建中间值。 + +成功时,注册表会快照并校验函数体返回值,将其冻结,然后调用纯渲染器;对于直接的外层调用,还会调用可选的元数据投影器。注册表会在 `tools/result` 之前另行物化持久展示字段;无效值、渲染器/投影器失败或非 JSON 展示都会转为 JSON 安全的 `isError`。因此,最终实时观察者能看到精确的执行期值,以及可安全用于后续持久追加的字段。 每个拦截 waterfall 返回一个类型化的 **Decision**(与 `agent/*` seam 共享的惯用模式)。`tools/pre-execute` 监听器接收 `(exec, next)` 并返回 `PreToolDecision`;`tools/execute` 包装层返回 `ToolExecutionResult`;`tools/post-execute` 监听器接收 `(exec, result, next)` 并返回 `PostToolDecision`: @@ -300,67 +322,70 @@ type PreToolDecision = ```ts type-equiv /** - * Post-dispatch decision: accept or replace content, attach context for the next - * request, or block by turning corrective feedback into an error result. + * Post-dispatch decision: accept, replace one projection, attach context for the + * next request, or block by turning corrective feedback into an error result. */ type PostToolDecision = - | { kind: 'accept'; content?: ContentBlock[]; additionalContexts?: HookContext[] } + | { kind: 'accept'; content?: ContentBlock[]; value?: never; additionalContexts?: HookContext[] } + | { kind: 'accept'; value: JsonValue; content?: never; additionalContexts?: HookContext[] } | { kind: 'block'; feedback: ContentBlock[]; additionalContexts?: HookContext[] } ``` 调用 `next()` 获取默认决策,或直接返回一个决策以短路。前置策略可以 deny 或 ask;只有 `allowed-once` 才继续执行,而未授权、缺少审批通道或服务、或无 agent 的请求都会变为拒绝。Guard 仍可施加最终拒绝。参数不可被改写,因为历史记录、审计、UI 和执行必须保持一致。 -后置策略可以替换内容;阻止决策会变为包含纠正反馈的 `isError` 结果。`tools/result` 在归一化后接收冻结的执行和结果;观察者无法对其进行变换,观察者的失败也会被隔离。未知工具和抛出异常的工具都会变为结构化错误(`ToolNotFoundError` 映射为 `UNKNOWN_TOOL`),调用失败但不终止当前轮次。 +后置策略可以替换内容或值,但不能同时替换两者。替换内容会保留规范值和现有元数据;替换值会重新校验并重新计算内容/元数据;阻止会移除值,并转为包含纠正反馈的 `isError`。内容替换是展示策略,而非保密策略;需要隐藏程序化值的监听器必须阻止或替换该值。`tools/result` 在归一化后接收冻结的执行和结果;观察者无法对其进行变换,观察者的失败也会被隔离。未知工具和抛出异常的工具都会变为结构化错误(`ToolNotFoundError` 映射为 `UNKNOWN_TOOL`),调用失败但不终止当前轮次。 -## 结构化输出 schema 子集 +## 已强制执行的原始 JSON Schema 子集 -调用方用来向 subagent 要求机器可读结果的词汇(`SubagentStartRequest.outputSchema`,见 [subagent.md](subagent.md#the-start-request)),或工作流 `agent()` 调用使用的词汇。它有意不是完整的 JSON Schema:schema 原样传给模型作为强制工具的 `parameters`,产出的值由 `validateStructuredValue` 在客户端校验——因此每个被接受的关键字都必须是校验器实际执行的,`assertSupportedOutputSchema` 会大声拒绝其他任何内容(`OutputSchemaError`,列出所有违规项)。两个遍历器仅推理自有可枚举属性(JSON 不携带其他内容),并拒绝会有损序列化的非纯对象(`Date`、`Map`)。 +subagent、工作流、MCP 和动态注册提供的原始 schema 使用作者侧 DSL 在协议层的对应表示。`assertSupportedJsonSchema()` 接受任意 JSON 根,`validateJsonSchemaValue()` 强制执行该 schema,`JsonSchemaError` 则报告每条不受支持或格式错误的 schema 路径。仅含注解的空节点表示不受约束的无损 JSON。`oneOf` 至少要求两个分支,且一个值必须恰好匹配其中一个。仍要求对象根的消费方调用 `assertObjectJsonSchema()` 并携带 `ObjectJsonSchema`;这样,subagent/工作流中由调用方定义的结构化输出可以继续以对象为根,而不会限制共享词汇。 ```ts type-equiv -/** The scalar values `enum`/`const` may carry (finite numbers only). */ -type StructuredScalar = string | number | boolean | null +/** Scalar JSON values supported by `enum` and `const`. */ +type JsonSchemaScalar = string | number | boolean | null ``` ```ts type-equiv -/** The `type` keywords the subset accepts. */ -type StructuredSchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null' +/** Single-type keywords accepted by the enforced subset. */ +type JsonSchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null' ``` ```ts type-equiv /** - * One node of the structured-output schema subset. Recursive via `properties` - * and `items`; see the module doc for the exact keyword semantics. + * One raw JSON Schema node in the enforced subset. The optional fields express + * the external wire shape; {@link assertSupportedJsonSchema} rejects invalid + * combinations before a caller treats the node as trusted. */ -interface StructuredSchemaNode { - type: StructuredSchemaType +interface JsonSchemaNode { + /** Omit with no constraints for any JSON value, or use `oneOf`. */ + type?: JsonSchemaType + /** Exactly one branch must validate; at least two branches are required. */ + oneOf?: JsonSchemaNode[] /** Nested property schemas (`type: 'object'` only). */ - properties?: Record + properties?: Record /** Required property names; each must appear in `properties`. */ required?: string[] - /** `false` rejects undeclared keys; absent/`true` allows them (JSON Schema default). */ + /** `false` rejects undeclared keys; absent/`true` follows JSON Schema's open default. */ additionalProperties?: boolean - /** Item schema (`type: 'array'` only); absent ⇒ any JSON items. */ - items?: StructuredSchemaNode - /** Allowed values (scalar types only). */ - enum?: StructuredScalar[] - /** The single allowed value (scalar types only). */ - const?: StructuredScalar + /** Item schema (`type: 'array'` only); absent accepts any JSON item. */ + items?: JsonSchemaNode + /** Allowed values for a scalar node. */ + enum?: JsonSchemaScalar[] + /** The single allowed value for a scalar node. */ + const?: JsonSchemaScalar /** Annotation, ignored for validation. */ description?: string /** Annotation, ignored for validation. */ title?: string - /** Annotation, ignored for validation (must still be JSON data). */ - default?: unknown - /** Annotation, ignored for validation (must still be JSON data). */ - examples?: unknown + /** Annotation, ignored for validation but required to be lossless JSON. */ + default?: JsonValue + /** Annotation, ignored for validation but required to be lossless JSON. */ + examples?: JsonValue } ``` -schema 是一个以 object 为根的节点(`enum`/`const` 仅限标量;`description`/`title`/`default`/`examples` 是注解,允许但忽略,但仍要求为 JSON 数据——它们随协议传输): - ```ts type-equiv -/** A structured-output schema: an OBJECT-rooted {@link StructuredSchemaNode}. */ -type StructuredOutputSchema = StructuredSchemaNode & { type: 'object' } +/** A consumer-constrained object-rooted schema. */ +type ObjectJsonSchema = JsonSchemaNode & { type: 'object' } ``` ## 工具展示 UI 词汇 diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json index b7970091fe..c97162ec29 100644 --- a/scripts/type-equiv.manifest.json +++ b/scripts/type-equiv.manifest.json @@ -1487,6 +1487,11 @@ "symbol": "SessionEventTrace", "source": "packages/session-query/session-query/src/types.ts" }, + { + "doc": "docs/core-data-structures/tools.zh.md", + "symbol": "ToolOutputDefinition", + "source": "packages/core/tools/src/index.ts" + }, { "doc": "docs/core-data-structures/tools.zh.md", "symbol": "ToolDefinition", @@ -1494,12 +1499,22 @@ }, { "doc": "docs/core-data-structures/tools.zh.md", - "symbol": "SchemaProp", + "symbol": "ValueSchemaSpec", "source": "packages/core/tools/src/schema.ts" }, { "doc": "docs/core-data-structures/tools.zh.md", - "symbol": "SchemaSpec", + "symbol": "ParameterPropertySpec", + "source": "packages/core/tools/src/schema.ts" + }, + { + "doc": "docs/core-data-structures/tools.zh.md", + "symbol": "ParameterSchemaSpec", + "source": "packages/core/tools/src/schema.ts" + }, + { + "doc": "docs/core-data-structures/tools.zh.md", + "symbol": "InferValue", "source": "packages/core/tools/src/schema.ts" }, { @@ -1547,6 +1562,21 @@ "symbol": "ToolRestriction", "source": "packages/core/tools/src/index.ts" }, + { + "doc": "docs/core-data-structures/tools.zh.md", + "symbol": "ToolFailure", + "source": "packages/core/tools/src/index.ts" + }, + { + "doc": "docs/core-data-structures/tools.zh.md", + "symbol": "ToolExecutionSuccess", + "source": "packages/core/tools/src/index.ts" + }, + { + "doc": "docs/core-data-structures/tools.zh.md", + "symbol": "ToolExecutionFailure", + "source": "packages/core/tools/src/index.ts" + }, { "doc": "docs/core-data-structures/tools.zh.md", "symbol": "ToolExecutionResult", @@ -1564,22 +1594,22 @@ }, { "doc": "docs/core-data-structures/tools.zh.md", - "symbol": "StructuredScalar", + "symbol": "JsonSchemaScalar", "source": "packages/core/tools/src/json-schema.ts" }, { "doc": "docs/core-data-structures/tools.zh.md", - "symbol": "StructuredSchemaType", + "symbol": "JsonSchemaType", "source": "packages/core/tools/src/json-schema.ts" }, { "doc": "docs/core-data-structures/tools.zh.md", - "symbol": "StructuredSchemaNode", + "symbol": "JsonSchemaNode", "source": "packages/core/tools/src/json-schema.ts" }, { "doc": "docs/core-data-structures/tools.zh.md", - "symbol": "StructuredOutputSchema", + "symbol": "ObjectJsonSchema", "source": "packages/core/tools/src/json-schema.ts" }, { @@ -1717,6 +1747,11 @@ "symbol": "ConfinedArgv", "source": "packages/sandbox/sandbox/src/index.ts" }, + { + "doc": "docs/core-data-structures/code-runtime.zh.md", + "symbol": "CodeJsonValue", + "source": "packages/code-runtime/code-runtime/src/types.ts" + }, { "doc": "docs/core-data-structures/code-runtime.zh.md", "symbol": "CodeRunRequest", @@ -1732,6 +1767,11 @@ "symbol": "CodeBindingNamespace", "source": "packages/code-runtime/code-runtime/src/types.ts" }, + { + "doc": "docs/core-data-structures/code-runtime.zh.md", + "symbol": "CodeBindingErrorClass", + "source": "packages/code-runtime/code-runtime/src/types.ts" + }, { "doc": "docs/core-data-structures/code-runtime.zh.md", "symbol": "CodeBindingFunction",