docs: include JSDoc in type-equiv blocks

This commit is contained in:
Tianyi Cui
2026-07-19 12:25:40 +08:00
parent dde62f454e
commit 5dcf6095cb
29 changed files with 1068 additions and 282 deletions

View File

@@ -9,6 +9,7 @@ Source: [`packages/core/tools/src/index.ts`](../../packages/core/tools/src/index
A `ToolSchema` (the model-facing fields) plus the `execute` function, host-only scheduler metadata, and optional UI presenters. The registry holds these; the loop dispatches calls through them. The registry's `schemas()` builds the model-facing `ToolSchema[]` by an explicit allowlist — `execute`/`timeoutMs`/`isConcurrencySafe`/`presentCall`/`presentResult` must never leak into a model request.
```ts type-equiv
/** A registered tool: its schema plus the execution function. */
interface ToolDefinition extends ToolSchema {
execute(args: unknown, exec: ToolRunContext): Promise<ToolExecuteReturn>
/**
@@ -26,7 +27,9 @@ interface ToolDefinition extends ToolSchema {
*
* Opted-in executions must not mutate parent-owned state. Shared state must
* tolerate concurrent dispatch; recorder races are permitted only when they
* commute or fail closed. See the parallel-tool-call RFC for the full contract.
* commute or fail closed. See the
* [parallel-tool-call RFC](../../../../docs/rfc/implemented/feature/2026-07-10-parallel-tool-call-execution.md)
* for the full contract.
* @param args - parsed arguments; `defineTool` validates before calling.
* @returns Whether this call may join a parallel group.
*/
@@ -61,6 +64,7 @@ Plugin authors write per-property specs with a boolean `required: true`, and a t
Source: [`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). */
@@ -69,7 +73,10 @@ interface SchemaProp {
description?: string
/** Enum of allowed values (strings only). */
enum?: string[]
/** Default value. */
/**
* 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
@@ -79,12 +86,29 @@ interface SchemaProp {
```
```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<string, SchemaProp>
```
`SchemaType` is the primitive union `'string' | 'number' | 'boolean' | 'object' | 'array'`. `InferArgs<S>` maps a `SchemaSpec` to the TS argument type — `required: true` props become required keys, everything else genuinely optional:
```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 }
* ```
*/
type InferArgs<S extends SchemaSpec> = Simplify<
& { [K in RequiredKeys<S>]: InferPropValue<S[K]> }
& { [K in Exclude<keyof S, RequiredKeys<S>>]?: InferPropValue<S[K]> }
@@ -100,8 +124,14 @@ Registration is a trusted same-process contract. The registry borrows the typed
`ToolRestriction` applies only to the live deployment-global tool layer. The registry compiles readonly names into private sets, intersects multiple restrictions, then overlays scope-local tools. A deny-only filter admits later unlisted globals, while an allow-list excludes them.
```ts type-equiv
/**
* Per-scope filter over global tools. Restrictions intersect and do not affect
* scoped registrations or the reserved Code Mode transport.
*/
interface ToolRestriction {
/** Global tool names that stay visible; everything else is removed. */
readonly allow?: readonly string[]
/** Global tool names removed from visibility. */
readonly deny?: readonly string[]
}
```
@@ -111,14 +141,20 @@ interface ToolRestriction {
`ctx.tools.execute()` accepts a caller-owned `ToolExecutionInput`, materializes its parsed JSON arguments once into a pipeline-owned `ToolExecution`, and runs that call through `tools/pre-execute` (the reorderable allow/deny/ask waterfall) → registered monotonic guards → `tools/execute` (around-dispatch wrappers) → `tools/post-execute` (inspect/replace the result) → `tools/result` (the immutable authoritative outcome). The outcome is a `ToolExecutionResult`.
```ts type-equiv
/** Opaque call identity that permits correlation without exposing mutable execution state. */
type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
```
```ts type-equiv
/**
* Caller-supplied description of one tool call. {@link ToolRegistry.execute}
* adds the registry-owned token to form a pipeline {@link ToolExecution};
* callers do not choose that token.
*/
interface ToolExecutionInput {
readonly callId: CallId
readonly name: string
/** Parsed JSON arguments (unknown — tools validate their own input). */
/** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
readonly arguments: unknown
/** The agent on whose behalf the call runs (set by the agent loop). */
readonly agent?: Agent
@@ -135,6 +171,12 @@ interface ToolExecutionInput {
A tool body receives the runtime extension. `deferContext()` is the composite-tool channel: it records nested-dispatch context without injecting inside the still-open outer call.
```ts type-equiv
/**
* Runtime context handed to a tool implementation after the registry has
* accepted a {@link ToolExecution}. A composite tool uses
* {@link deferContext} to ferry context produced by nested dispatches back to
* the outer result; the loop appends it only after the outer `tool/result`.
*/
interface ToolRunContext extends ToolExecution {
/**
* Defer one nested-dispatch context until this tool's final result reaches
@@ -148,12 +190,23 @@ interface ToolRunContext extends ToolExecution {
The agent loop asks the registry for each pending call's execution mode and uses it to form exclusive barriers and rolling-pool parallel runs:
```ts type-equiv
/**
* Scheduling mode for one pending call. `parallel` may overlap with siblings;
* `exclusive` runs alone and forms an ordering barrier.
*/
type ToolExecutionMode =
| { kind: 'parallel' }
| { kind: 'exclusive' }
```
```ts type-equiv
/**
* One pending tool call inside the registry pipeline. Parsed arguments cross
* one lossless-JSON materialization boundary before policy and are deep-frozen;
* call identity and the registry-assigned {@link token} are readonly. An
* around-dispatch wrapper may set, replace, or remove `signal`. The registry
* freezes the complete object before `tools/result` observers run.
*/
interface ToolExecution extends ToolExecutionInput {
/** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
readonly token: ToolExecutionToken
@@ -165,10 +218,19 @@ interface ToolExecution extends ToolExecutionInput {
A `ToolGuard` is scope-aware final pre-dispatch policy. Its shape deliberately has no allow result: `undefined` preserves the waterfall decision, while a returned reason can only reduce permission, so a later listener cannot undo it.
```ts type-equiv
/**
* A monotonic execution guard evaluated after every `tools/pre-execute`
* listener and before the tool body. Returning a reason denies the call;
* returning `undefined` leaves it unchanged. Because guards have no allow
* result, listener ordering cannot turn a denial back into permission.
* @param execution - the identity-protected call after extensible pre-execute policy completed.
* @returns a final denial reason, or `undefined` to leave the call allowed.
*/
type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined
```
```ts type-equiv
/** The outcome of one tool call. */
interface ToolExecutionResult {
content: ContentBlock[]
isError: boolean
@@ -179,14 +241,8 @@ interface ToolExecutionResult {
*/
error?: ToolErrorInfo
/**
* Extra model-facing contexts deferred by a composite tool or attached by
* `tools/post-execute` listeners for the NEXT request. They are not part of
* this call's `content`: the loop accepts them into the active-batch FIFO and
* appends them after every recorded `tool/result` when the batch settles, even
* when execution is interrupted. The array preserves each context's source,
* envelope, metadata, and production order. An accepted outer call keeps
* deferred contexts before decision contexts; a block retains only contexts
* supplied by the blocking decision.
* 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[]
/**
@@ -206,6 +262,12 @@ The registry materializes and freezes the final accepted result immediately befo
Each interception waterfall returns a typed **Decision** (the idiom shared with the `agent/*` seams). `tools/pre-execute` listeners receive `(exec, next)` and return a `PreToolDecision`; `tools/execute` wrappers return a `ToolExecutionResult`; `tools/post-execute` listeners receive `(exec, result, next)` and return a `PostToolDecision`:
```ts type-equiv
/**
* Pre-dispatch decision. `allow` runs the call; `deny` materializes an error;
* `ask` runs only after an approval service returns `allowed-once` and otherwise
* denies. Input rewriting is excluded because arguments are already logged and
* presented.
*/
type PreToolDecision =
| { kind: 'allow' }
| { kind: 'deny'; reason: string }
@@ -213,6 +275,10 @@ 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.
*/
type PostToolDecision =
| { kind: 'accept'; content?: ContentBlock[]; additionalContexts?: HookContext[] }
| { kind: 'block'; feedback: ContentBlock[]; additionalContexts?: HookContext[] }
@@ -227,25 +293,41 @@ Post-policy may replace content; a block becomes an `isError` result containing
The vocabulary a caller uses to demand a machine-readable result from a subagent (`SubagentStartRequest.outputSchema`, [subagent.md](subagent.md#the-start-request)) or a workflow `agent()` call. It is deliberately NOT full JSON Schema: the schema travels verbatim to the model as a forced tool's `parameters`, and the produced value is validated client-side by `validateStructuredValue` — so every accepted keyword must be one the validator actually enforces, and `assertSupportedOutputSchema` rejects anything else loud (`OutputSchemaError`, listing every violation). Both walkers reason over own enumerable properties only (JSON carries nothing else) and reject non-plain objects (`Date`, `Map`) that would serialize lossily.
```ts type-equiv
/** The scalar values `enum`/`const` may carry (finite numbers only). */
type StructuredScalar = string | number | boolean | null
```
```ts type-equiv
/** The `type` keywords the subset accepts. */
type StructuredSchemaType = '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.
*/
interface StructuredSchemaNode {
type: StructuredSchemaType
/** Nested property schemas (`type: 'object'` only). */
properties?: Record<string, StructuredSchemaNode>
/** Required property names; each must appear in `properties`. */
required?: string[]
/** `false` rejects undeclared keys; absent/`true` allows them (JSON Schema 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
/** 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
}
```
@@ -253,6 +335,7 @@ interface StructuredSchemaNode {
A schema is an object-rooted node (`enum`/`const` are scalar-only; `description`/`title`/`default`/`examples` are annotations, allowed and ignored but still required to be JSON data — they ride the wire):
```ts type-equiv
/** A structured-output schema: an OBJECT-rooted {@link StructuredSchemaNode}. */
type StructuredOutputSchema = StructuredSchemaNode & { type: 'object' }
```