Merge remote-tracking branch 'origin/master' into fix/run-code-description-contract
# Conflicts: # examples/headless-agent/tests/snapshots/advanced-toolchain/session.1.jsonl # examples/headless-agent/tests/snapshots/advanced-toolchain/session.2.jsonl # examples/headless-agent/tests/snapshots/advanced-toolchain/session.jsonl
This commit is contained in:
@@ -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 packages/core/tools/README.md
|
||||
README.md: 3dddeb51a9f5b98a01a5e0170dacde471be67094
|
||||
README.zh.md: deb01899fa909e5d430415fa98ea49bd543b2749
|
||||
README.md: 60841513ad5ad439ae8851dffc34e8d250acaa67
|
||||
README.zh.md: ba2947337285ec5bba3a124cfeb4f105ad623877
|
||||
|
||||
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
Tool registry and execution pipeline. Tool plugins register their schemas and executors; the agent loop executes each call through `tools/pre-execute` (the extensible allow/deny gate) → monotonic registered guards → `tools/execute` (an around-dispatch wrapper for timeout/retry/metrics plugins) → `tools/post-execute` (inspect/replace the result, attach context) → the definition-owned `finalizeContent` boundary → the observe-only `tools/result` notification. The registry also owns HOW its tools are presented to the model — its `mode` config selects native function calling, [Code Mode](#code-mode), or both, and one agent shadows that default for itself with `presentAs`.
|
||||
|
||||
## Service: `ToolRegistry` (ctx key: `tools`)
|
||||
## Service: `ToolRuntime` (ctx key: `tools`)
|
||||
|
||||
### Config
|
||||
|
||||
@@ -13,7 +13,7 @@ tools:
|
||||
mode: native # native (default) | code | both
|
||||
```
|
||||
|
||||
`native` contributes visible tools as function definitions. `code` contributes the reserved `run_code` transport, the generated `tools:sdk` section, and the `tools:code-only` rule stating that only `run_code` may be called directly — which the executor then enforces, resolving a model-direct call naming any other tool to `UNKNOWN_TOOL` before policy runs; `both` contributes both forms and states no such rule, because its native calls do execute. This is the default for agents that declare none of their own — an agent preset selects its own with [`dsh-agent-tool-mode`](../agent-tool-mode/README.md). The reserved transport cannot be registered, shadowed, restricted, or removed, and its name is reserved whatever the configured mode, because any agent may select a code mode. Non-native modes require a `ctx.codeRuntime` whose `language` has a registered SDK renderer — TypeScript ships via [`dsh-code-runtime-worker`](../../code-runtime/code-runtime-worker/README.md); a Python renderer is built in and drives any runtime that reports `language: 'python'` (a first-party `dsh-code-runtime-python` backend is delivered separately). A runtime language with no renderer fails prompt assembly loudly, and a `systemPrompt.toolOrder` entry for a tool the mode does not contribute rejects prompt assembly. A `system-prompt/assemble` listener may replace the registry's contributions; its returned assembly is authoritative, so that listener owns preserving a usable Code Mode protocol.
|
||||
`native` contributes visible tools as function definitions. `code` contributes the reserved `run_code` transport, the generated `tools:sdk` section, and the `tools:code-only` rule stating that only `run_code` may be called directly — which the executor then enforces, resolving a model-direct call naming any other tool to `UNKNOWN_TOOL` before policy runs; `both` contributes both forms and states no such rule, because its native calls do execute. This is the default for agents that declare none of their own — an agent preset selects its own with [`dsh-agent-tool-presentation`](../agent-tool-presentation/README.md). The reserved transport cannot be registered, shadowed, restricted, or removed, and its name is reserved whatever the configured mode, because any agent may select a code mode. Non-native modes require a `ctx.codeRuntime` whose `language` has a registered SDK renderer — TypeScript ships via [`dsh-code-runtime-worker-thread`](../../code-runtime/code-runtime-worker-thread/README.md); a Python renderer is built in and drives any runtime that reports `language: 'python'` (a first-party `dsh-code-runtime-python` backend is delivered separately). A runtime language with no renderer fails prompt assembly loudly, and a `systemPrompt.toolOrder` entry for a tool the mode does not contribute rejects prompt assembly. A `system-prompt/assemble` listener may replace the registry's contributions; its returned assembly is authoritative, so that listener owns preserving a usable Code Mode protocol.
|
||||
|
||||
### Public API
|
||||
|
||||
@@ -148,7 +148,7 @@ Prefix-stable while visible definitions and their order are unchanged. Registrat
|
||||
|
||||
#### What the model sees
|
||||
|
||||
Code Mode exposes the generated [`run_code` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tools), the SDK instructions below, and the generated exact SDK block for the loaded runtime's language (the TypeScript `declare const tools` block, or the Python `tools` declaration). `both` exposes normal schemas and this Code Mode API. Under `code` the prompt also carries the `tools:code-only` rule, ordered ahead of the per-tool guidance band so the model reads which tools it may call before it reads what each one is for; `both` renders it empty. The instructions and SDK block match the loaded runtime's language; the TypeScript version (via [`dsh-code-runtime-worker`](../../code-runtime/code-runtime-worker/README.md)) is shown below, and the Python version (for any runtime reporting `language: 'python'`) has the same operations and types in Python syntax (`await tools.name(args)`, subscript access for exotic names, `print(...)` and top-level `return`).
|
||||
Code Mode exposes the generated [`run_code` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tools), the SDK instructions below, and the generated exact SDK block for the loaded runtime's language (the TypeScript `declare const tools` block, or the Python `tools` declaration). `both` exposes normal schemas and this Code Mode API. Under `code` the prompt also carries the `tools:code-only` rule, ordered ahead of the per-tool guidance band so the model reads which tools it may call before it reads what each one is for; `both` renders it empty. The instructions and SDK block match the loaded runtime's language; the TypeScript version (via [`dsh-code-runtime-worker-thread`](../../code-runtime/code-runtime-worker-thread/README.md)) is shown below, and the Python version (for any runtime reporting `language: 'python'`) has the same operations and types in Python syntax (`await tools.name(args)`, subscript access for exotic names, `print(...)` and top-level `return`).
|
||||
|
||||
##### Code Mode SDK instructions
|
||||
|
||||
@@ -192,7 +192,7 @@ Append-only; newly visible content follows the reusable request prefix and does
|
||||
- **Concurrency policy is not an event gate** — `executionMode()` reads the resolved tool definition directly; plugins can only declare a classifier on definitions they own.
|
||||
- **`tools/pre-execute` deliberately cannot rewrite `exec.arguments`** — logged and rendered args would desync from what ran; the rewrite design is [a proposed Agent Note](../../../.agents/notes/proposed/feature/2026-06-30-pre-tool-input-rewrite.md).
|
||||
- **Caller-defined subagent and workflow structured outputs remain object-rooted** — this is a consumer-level guard; the shared schema vocabulary and tool outputs support every JSON root.
|
||||
- **`timeoutMs` on a definition is declarative only** — the registry never enforces deadlines; enforcement requires the `@deepseek-ai/dsh-timeout-policy` wrapper.
|
||||
- **`timeoutMs` on a definition is declarative only** — the registry never enforces deadlines; enforcement requires the `@deepseek-ai/dsh-tool-call-timeout-policy` wrapper.
|
||||
- **Code Mode's SDK language follows the one loaded runtime, and a presentation is per agent rather than per tool** — `mode: code`/`both` rejects prompt assembly unless `ctx.codeRuntime.language` has a registered SDK renderer (TypeScript or Python); scoped restrictions/shadows and `presentAs` choose each agent's visible bindings and their form, but within one agent no tool can be native-only while another is code-only.
|
||||
- **Code Mode intermediate values are execution-local and unbounded by bytes** — the canonical typed values cannot be reconstructed from session replay and may exhaust process or worker memory; only the outer `run_code` output has the worker's configurable hard cap. The durable log copy of each sub-call IS bounded: the `tools/code-dispatch-log` waterfall lets the spill policy replace an oversized `tool/code-dispatch` content with a preview + locator ([rationale](../../../.agents/notes/implemented/feature/2026-07-26-code-dispatch-log-spill.md)).
|
||||
- **`run_code` state is fresh per run** — a persistent REPL-style kernel is rejected for the MVP (cross-call state would be invisible to the log); see [the Code Mode Agent Note](../../../.agents/notes/implemented/feature/2026-06-15-code-mode.md).
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
工具注册表与执行流水线。工具插件注册各自的 schema 和执行器;agent loop(智能体循环)依次让每次调用经过 `tools/pre-execute`(可扩展的允许/拒绝门禁)→ 已注册的单调守卫 → `tools/execute`(供超时/重试/指标插件使用的环绕分发包装层)→ `tools/post-execute`(检查/替换结果、附加上下文)→ 定义自身的 `finalizeContent` 终结步骤 → 仅观测的 `tools/result` 通知。注册表还负责决定如何向模型呈现其工具:`mode` 配置可以选择原生 Function Calling(函数调用)、[Code Mode](#code-mode),或同时选择两者;单个 agent 可用 `presentAs` 为自己遮蔽该默认值。
|
||||
|
||||
## 服务:`ToolRegistry`(ctx 键:`tools`)
|
||||
## 服务:`ToolRuntime`(ctx 键:`tools`)
|
||||
|
||||
### 配置
|
||||
|
||||
@@ -13,7 +13,7 @@ tools:
|
||||
mode: native # native (default) | code | both
|
||||
```
|
||||
|
||||
`native` 以函数定义的形式贡献可见工具。`code` 贡献保留的 `run_code` 传输、生成的 `tools:sdk` 段,以及声明「只有 `run_code` 可被直接调用」的 `tools:code-only` 规则——执行器随后强制该规则,模型直呼其他任何工具名都会在策略运行之前解析为 `UNKNOWN_TOOL`;`both` 同时贡献两种形式,且不声明该规则,因为它的原生调用确实会执行。这是「未作声明的 agent」的默认值——agent preset 用 [`dsh-agent-tool-mode`](../agent-tool-mode/README.md) 为自己选择。不能注册、遮蔽、限制或移除该保留传输,且无论配置何种模式,该名称都是保留的,因为任何 agent 都可能选择 code 模式。非原生模式要求所加载 `ctx.codeRuntime` 的 `language` 有已注册的 SDK 渲染器——TypeScript 经 [`dsh-code-runtime-worker`](../../code-runtime/code-runtime-worker/README.md) 交付;Python 渲染器内置,驱动任何报告 `language: 'python'` 的运行时(第一方 `dsh-code-runtime-python` 后端另行交付)。没有渲染器的运行时语言会导致提示词组装明确失败;如果 `systemPrompt.toolOrder` 条目指向当前模式未贡献的工具,系统会拒绝组装提示词。`system-prompt/assemble` 监听器可以替换注册表贡献;它返回的组装结果具有权威性,因此该监听器负责保留可用的 Code Mode 协议。
|
||||
`native` 以函数定义的形式贡献可见工具。`code` 贡献保留的 `run_code` 传输、生成的 `tools:sdk` 段,以及声明「只有 `run_code` 可被直接调用」的 `tools:code-only` 规则——执行器随后强制该规则,模型直呼其他任何工具名都会在策略运行之前解析为 `UNKNOWN_TOOL`;`both` 同时贡献两种形式,且不声明该规则,因为它的原生调用确实会执行。这是「未作声明的 agent」的默认值——agent preset 用 [`dsh-agent-tool-presentation`](../agent-tool-presentation/README.md) 为自己选择。不能注册、遮蔽、限制或移除该保留传输,且无论配置何种模式,该名称都是保留的,因为任何 agent 都可能选择 code 模式。非原生模式要求所加载 `ctx.codeRuntime` 的 `language` 有已注册的 SDK 渲染器——TypeScript 经 [`dsh-code-runtime-worker-thread`](../../code-runtime/code-runtime-worker-thread/README.md) 交付;Python 渲染器内置,驱动任何报告 `language: 'python'` 的运行时(第一方 `dsh-code-runtime-python` 后端另行交付)。没有渲染器的运行时语言会导致提示词组装明确失败;如果 `systemPrompt.toolOrder` 条目指向当前模式未贡献的工具,系统会拒绝组装提示词。`system-prompt/assemble` 监听器可以替换注册表贡献;它返回的组装结果具有权威性,因此该监听器负责保留可用的 Code Mode 协议。
|
||||
|
||||
### 公开 API
|
||||
|
||||
@@ -148,7 +148,7 @@ agent loop 将连续的 `parallel` 调用归入有界滚动池,并把每个 `e
|
||||
|
||||
#### 模型看到的内容
|
||||
|
||||
Code Mode 会公开生成的 [`run_code` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tools)、下方 SDK 说明,以及按所加载运行时语言生成的精确 SDK 块(TypeScript 的 `declare const tools` 块,或 Python 的 `tools` 声明)。`both` 会同时公开普通 schema 与此 Code Mode API。在 `code` 下,提示词还会带上 `tools:code-only` 规则,其顺序排在逐工具指导段之前,让模型先读到「可以调用哪些工具」再读「每个工具做什么」;`both` 下它渲染为空。说明与 SDK 块随所加载运行时的语言切换;下方展示 TypeScript 版本(经 [`dsh-code-runtime-worker`](../../code-runtime/code-runtime-worker/README.md)),Python 版本(用于任何报告 `language: 'python'` 的运行时)以 Python 语法提供相同操作和类型(`await tools.name(args)`、特殊名称用下标访问、`print(...)` 与顶层 `return`)。
|
||||
Code Mode 会公开生成的 [`run_code` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tools)、下方 SDK 说明,以及按所加载运行时语言生成的精确 SDK 块(TypeScript 的 `declare const tools` 块,或 Python 的 `tools` 声明)。`both` 会同时公开普通 schema 与此 Code Mode API。在 `code` 下,提示词还会带上 `tools:code-only` 规则,其顺序排在逐工具指导段之前,让模型先读到「可以调用哪些工具」再读「每个工具做什么」;`both` 下它渲染为空。说明与 SDK 块随所加载运行时的语言切换;下方展示 TypeScript 版本(经 [`dsh-code-runtime-worker-thread`](../../code-runtime/code-runtime-worker-thread/README.md)),Python 版本(用于任何报告 `language: 'python'` 的运行时)以 Python 语法提供相同操作和类型(`await tools.name(args)`、特殊名称用下标访问、`print(...)` 与顶层 `return`)。
|
||||
|
||||
##### Code Mode SDK 说明
|
||||
|
||||
@@ -192,7 +192,7 @@ The available tools:
|
||||
- **并发策略不是事件门禁**:`executionMode()` 直接读取已解析的工具定义;插件只能在自身拥有的定义上声明分类器。
|
||||
- **`tools/pre-execute` 有意不允许改写 `exec.arguments`**:否则日志记录和呈现的参数会与实际运行内容失去同步;改写设计记录在[拟议的 Agent Note](../../../.agents/notes/proposed/feature/2026-06-30-pre-tool-input-rewrite.md)中。
|
||||
- **调用方定义的 subagent 与工作流结构化输出仍要求对象根**:这是消费方层面的守卫;共享 schema 词汇和工具输出支持任意 JSON 根。
|
||||
- **定义上的 `timeoutMs` 仅为声明**:注册表绝不会强制执行截止时间;要强制执行,必须使用 `@deepseek-ai/dsh-timeout-policy` 包装层。
|
||||
- **定义上的 `timeoutMs` 仅为声明**:注册表绝不会强制执行截止时间;要强制执行,必须使用 `@deepseek-ai/dsh-tool-call-timeout-policy` 包装层。
|
||||
- **Code Mode 的 SDK 语言跟随已加载的那个运行时,且呈现方式按 agent 而非按工具**:`mode: code`/`both` 会拒绝组装提示词,除非 `ctx.codeRuntime.language` 有已注册的 SDK 渲染器(TypeScript 或 Python);作用域限制/遮蔽与 `presentAs` 会选择每个 agent 的可见绑定及其形态,但在同一个 agent 内不能让一个工具仅使用 Native,而另一个仅使用 Code。
|
||||
- **Code Mode 中间值只存在于执行局部,且没有字节上限**:这些规范的类型化值无法从会话回放重建,并可能耗尽进程或 worker 内存;只有外层 `run_code` 输出受 worker 可配置的硬上限约束。每个子调用的持久日志副本则确实有上限:`tools/code-dispatch-log` waterfall 允许 spill 策略把过大的 `tool/code-dispatch` 内容替换为预览加定位符([原理](../../../.agents/notes/implemented/feature/2026-07-26-code-dispatch-log-spill.md))。
|
||||
- **每次运行都会获得全新的 `run_code` 状态**:MVP 不采用持久 REPL 风格内核(跨调用状态不会出现在日志中);参见 [Code Mode Agent Note](../../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)。
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-tools",
|
||||
"description": "Tool registry and execution pipeline for the DeepSeek Harness",
|
||||
"version": "0.0.1-rc.2",
|
||||
"version": "0.0.1-rc.5",
|
||||
"publishConfig": {
|
||||
"access": "restricted"
|
||||
},
|
||||
|
||||
@@ -12,8 +12,8 @@ import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-
|
||||
import { snapshotJsonValue } from '@deepseek-ai/dsh-session'
|
||||
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
||||
import { defineTool, parameterSchemaSpecToJsonSchema } from './schema.ts'
|
||||
import { TOOL_REGISTRY_SCHEDULER } from './index.ts'
|
||||
import type { CodeDispatchLog, ToolDefinition, ToolExecutionResult, ToolRegistry, ToolRunContext } from './index.ts'
|
||||
import { TOOL_RUNTIME_SCHEDULER } from './index.ts'
|
||||
import type { CodeDispatchLog, ToolDefinition, ToolExecutionResult, ToolRuntime, ToolRunContext } from './index.ts'
|
||||
import type {} from './types.ts'
|
||||
|
||||
/** The model-facing name of the Code Mode tool. */
|
||||
@@ -291,7 +291,7 @@ export interface RunCodeBridgeOptions {
|
||||
* @param options - the registry-private capabilities described above.
|
||||
* @returns the registry-ready definition.
|
||||
*/
|
||||
export function createRunCodeTool(registry: ToolRegistry, options: RunCodeBridgeOptions): ToolDefinition {
|
||||
export function createRunCodeTool(registry: ToolRuntime, options: RunCodeBridgeOptions): ToolDefinition {
|
||||
const { requireRuntime, peekRuntime, maxParallel, shapeDispatchLog } = options
|
||||
const definition = defineTool({
|
||||
name: RUN_CODE_NAME,
|
||||
@@ -478,7 +478,7 @@ export function createRunCodeTool(registry: ToolRegistry, options: RunCodeBridge
|
||||
signal: runController.signal,
|
||||
}
|
||||
type DispatchOutcome = { isError: true; message: string } | { isError: false; value: JsonValue }
|
||||
const scheduler = registry[TOOL_REGISTRY_SCHEDULER]
|
||||
const scheduler = registry[TOOL_RUNTIME_SCHEDULER]
|
||||
const outcome = await new Promise<DispatchOutcome>((resolve, reject) => {
|
||||
// Set by the dispatch stage (or start() for a pre-settled result): what commit() finalizes in submission order.
|
||||
let parked:
|
||||
|
||||
@@ -136,7 +136,7 @@ export type {
|
||||
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
tools: ToolRegistry
|
||||
tools: ToolRuntime
|
||||
}
|
||||
|
||||
interface Events {
|
||||
@@ -149,7 +149,7 @@ declare module '@deepseek-ai/cordis' {
|
||||
* @param exec - the pending call (name, parsed arguments, caller agent).
|
||||
* @mode waterfall
|
||||
*/
|
||||
'tools/pre-execute'(this: Scoped<ToolRegistry>, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>
|
||||
'tools/pre-execute'(this: Scoped<ToolRuntime>, exec: ToolExecution, next: () => Promise<PreToolDecision>): Promise<PreToolDecision>
|
||||
/**
|
||||
* Around-dispatch waterfall for timeout, retry, or metrics. `next()` returns
|
||||
* a normalized result; wrappers may change only `exec.signal`, while call
|
||||
@@ -160,7 +160,7 @@ declare module '@deepseek-ai/cordis' {
|
||||
* @param exec - the allowed call about to dispatch (name, parsed arguments, caller agent, signal).
|
||||
* @mode waterfall
|
||||
*/
|
||||
'tools/execute'(this: Scoped<ToolRegistry>, exec: ToolDispatchExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>
|
||||
'tools/execute'(this: Scoped<ToolRuntime>, exec: ToolDispatchExecution, next: () => Promise<ToolExecutionResult>): Promise<ToolExecutionResult>
|
||||
/**
|
||||
* Accept, replace, enrich, or block a normalized dispatch result. `next()`
|
||||
* accepts it unchanged; thrown tools still reach this waterfall as errors. Async
|
||||
@@ -172,7 +172,7 @@ declare module '@deepseek-ai/cordis' {
|
||||
* @param result - the dispatch outcome a listener may accept, replace, or block.
|
||||
* @mode waterfall
|
||||
*/
|
||||
'tools/post-execute'(this: Scoped<ToolRegistry>, exec: ToolExecution, result: Readonly<ToolExecutionResult>, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>
|
||||
'tools/post-execute'(this: Scoped<ToolRuntime>, exec: ToolExecution, result: Readonly<ToolExecutionResult>, next: () => Promise<PostToolDecision>): Promise<PostToolDecision>
|
||||
/**
|
||||
* Allow a listener to replace content in the DURABLE LOG COPY of one
|
||||
* `run_code` sub-dispatch outcome before the bridge appends its
|
||||
@@ -186,7 +186,7 @@ declare module '@deepseek-ai/cordis' {
|
||||
* @param dispatch - the parent execution, sub-call identity, and the settled content to log.
|
||||
* @mode waterfall
|
||||
*/
|
||||
'tools/code-dispatch-log'(this: Scoped<ToolRegistry>, dispatch: CodeDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>
|
||||
'tools/code-dispatch-log'(this: Scoped<ToolRuntime>, dispatch: CodeDispatchLog, next: () => Promise<ContentBlock[]>): Promise<ContentBlock[]>
|
||||
/**
|
||||
* Observe the frozen, lossless-JSON final outcome. Listener failures are contained.
|
||||
* Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): keyed by `exec.agent`.
|
||||
@@ -194,7 +194,7 @@ declare module '@deepseek-ai/cordis' {
|
||||
* @param result - a deep-frozen snapshot of the final returned result.
|
||||
* @mode emit
|
||||
*/
|
||||
'tools/result'(this: Scoped<ToolRegistry>, exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): undefined
|
||||
'tools/result'(this: Scoped<ToolRuntime>, exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): undefined
|
||||
/**
|
||||
* A tool was registered or unregistered, or a scoped restriction changed
|
||||
* (the available tool set changed — possibly for one scope only). An
|
||||
@@ -247,7 +247,7 @@ export interface ToolDefinition extends ToolSchema {
|
||||
finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined
|
||||
/**
|
||||
* Cooperative tool-call timeout budget in milliseconds. Omit for no deadline.
|
||||
* Enforced by `@deepseek-ai/dsh-timeout-policy` (a `tools/execute` wrapper); it
|
||||
* Enforced by `@deepseek-ai/dsh-tool-call-timeout-policy` (a `tools/execute` wrapper); it
|
||||
* is NEVER sent to the model — `schemas()` whitelists only name/description/
|
||||
* parameters. Declaring it asserts this tool forwards `exec.signal` to a
|
||||
* cooperative implementation that can reach quiescence when the signal aborts.
|
||||
@@ -307,7 +307,7 @@ declare const toolExecutionTokenBrand: unique symbol
|
||||
export type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
|
||||
|
||||
/**
|
||||
* Caller-supplied description of one tool call. {@link ToolRegistry.execute}
|
||||
* Caller-supplied description of one tool call. {@link ToolRuntime.execute}
|
||||
* adds the registry-owned token to form a pipeline {@link ToolExecution};
|
||||
* callers do not choose that token.
|
||||
*/
|
||||
@@ -330,7 +330,7 @@ export interface ToolExecutionInput {
|
||||
* The token also marks the call as a transport sub-dispatch rather than a
|
||||
* model-direct call: under `mode: 'code'`, only calls WITH a parent may
|
||||
* execute a native tool name — a model-direct call (no parent) is denied as
|
||||
* `UNKNOWN_TOOL` before the policy pipeline. See {@link ToolRegistry.execute}.
|
||||
* `UNKNOWN_TOOL` before the policy pipeline. See {@link ToolRuntime.execute}.
|
||||
*/
|
||||
readonly parent?: ToolExecutionToken
|
||||
/** Required caller-owned cancellation for this invocation. */
|
||||
@@ -435,7 +435,7 @@ export type ScheduledToolPreparation =
|
||||
|
||||
/**
|
||||
* Scheduler-only dispatch result. A `post-result` still receives post-execute;
|
||||
* a `final-result` already matches {@link ToolRegistry.execute} failure semantics.
|
||||
* a `final-result` already matches {@link ToolRuntime.execute} failure semantics.
|
||||
* @internal
|
||||
*/
|
||||
export type ScheduledToolDispatch =
|
||||
@@ -444,11 +444,11 @@ export type ScheduledToolDispatch =
|
||||
|
||||
/**
|
||||
* Symbol-keyed scheduler view that keeps pre/post policy ordered while
|
||||
* overlapping dispatch. Ordinary callers use {@link ToolRegistry.execute};
|
||||
* overlapping dispatch. Ordinary callers use {@link ToolRuntime.execute};
|
||||
* this is not a plugin extension point.
|
||||
* @internal
|
||||
*/
|
||||
export interface ToolRegistryScheduler {
|
||||
export interface ToolRuntimeScheduler {
|
||||
/** Materialize input, run the ordered pre-execute/guard gate, and decide what stage follows. */
|
||||
prepare(exec: ToolExecutionInput): Promise<ScheduledToolPreparation>
|
||||
/** Run only the around-dispatch/body stage. */
|
||||
@@ -463,7 +463,7 @@ export interface ToolRegistryScheduler {
|
||||
* Scheduler entry point omitted from the generated named service API.
|
||||
* @internal
|
||||
*/
|
||||
export const TOOL_REGISTRY_SCHEDULER: unique symbol = Symbol('@deepseek-ai/dsh-tools.scheduler')
|
||||
export const TOOL_RUNTIME_SCHEDULER: unique symbol = Symbol('@deepseek-ai/dsh-tools.scheduler')
|
||||
|
||||
/** Canonical error code for cancellation after a tool body was invoked. */
|
||||
export const TOOL_ABORTED = 'ABORTED'
|
||||
@@ -784,7 +784,7 @@ function resolveMaxParallelSubCalls(value: number | undefined): number {
|
||||
* Tool registry and execution pipeline. Scoped registrations shadow globals;
|
||||
* one visibility resolver feeds presentation, lookup, and dispatch.
|
||||
*/
|
||||
export class ToolRegistry extends Service {
|
||||
export class ToolRuntime extends Service {
|
||||
static inject = ['systemPrompt']
|
||||
|
||||
static Config: z<Config> = z.object({
|
||||
@@ -793,7 +793,7 @@ export class ToolRegistry extends Service {
|
||||
})
|
||||
|
||||
/** Internal staged view consumed by `dsh-agent-loop`'s parallel scheduler. */
|
||||
readonly [TOOL_REGISTRY_SCHEDULER]: ToolRegistryScheduler = {
|
||||
readonly [TOOL_RUNTIME_SCHEDULER]: ToolRuntimeScheduler = {
|
||||
prepare: exec => this.prepareScheduledExecution(exec),
|
||||
dispatch: exec => this.dispatchScheduledExecution(exec),
|
||||
finalize: (exec, result) => this.finalizeScheduledExecution(exec, result),
|
||||
@@ -948,7 +948,7 @@ export class ToolRegistry extends Service {
|
||||
if (scopeOf(ctx) === undefined) {
|
||||
throw new Error('tools.presentAs() requires a scoped context (agent.ctx): a context-global presentation is the `mode` config field on the tools row')
|
||||
}
|
||||
const dispose = ctx.effect(function* (this: ToolRegistry) {
|
||||
const dispose = ctx.effect(function* (this: ToolRuntime) {
|
||||
yield this.layers.effect(
|
||||
ctx,
|
||||
(layer) => {
|
||||
@@ -1019,7 +1019,7 @@ export class ToolRegistry extends Service {
|
||||
private requireCodeRuntime(mode: ToolPresentationMode): CodeRuntime {
|
||||
const runtime = this.ctx.get('codeRuntime')
|
||||
if (!runtime) {
|
||||
throw new Error(`dsh-tools: mode "${mode}" requires a code runtime — load a ctx.codeRuntime implementation (e.g. @deepseek-ai/dsh-code-runtime-worker) or set tools mode to "native"`)
|
||||
throw new Error(`dsh-tools: mode "${mode}" requires a code runtime — load a ctx.codeRuntime implementation (e.g. @deepseek-ai/dsh-code-runtime-worker-thread) or set tools mode to "native"`)
|
||||
}
|
||||
if (!Object.hasOwn(SDK_RENDERERS, runtime.language)) {
|
||||
const known = Object.keys(SDK_RENDERERS).map(name => JSON.stringify(name)).join(', ')
|
||||
@@ -1314,7 +1314,7 @@ export class ToolRegistry extends Service {
|
||||
*
|
||||
* 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
|
||||
* `dsh-agent-tool-presentation` 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.
|
||||
@@ -1943,4 +1943,4 @@ function toolAbortedBeforeDispatchResult(prior?: ToolExecutionResult): ToolExecu
|
||||
}
|
||||
}
|
||||
|
||||
export default ToolRegistry
|
||||
export default ToolRuntime
|
||||
|
||||
@@ -61,7 +61,7 @@ export interface GenericCallView {
|
||||
kind?: ToolCallKind
|
||||
/**
|
||||
* The salient input to show in a detail/expanded view (e.g. a background
|
||||
* task id). Omit to show nothing; a string renders as-is, an object as pretty
|
||||
* job id). Omit to show nothing; a string renders as-is, an object as pretty
|
||||
* JSON. NOT the full raw args object unless that is genuinely what a reader wants.
|
||||
*/
|
||||
rawInput?: unknown
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { Scope } from '@deepseek-ai/dsh-scope'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
|
||||
import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
|
||||
import ToolRegistry, { CodeRunFailedError, RUN_CODE_NAME, TOOL_ABORTED_BEFORE_DISPATCH, defineContentToolFixture, defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import ToolRuntime, { CodeRunFailedError, RUN_CODE_NAME, TOOL_ABORTED_BEFORE_DISPATCH, defineContentToolFixture, defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import type { Config, JsonSchemaNode, PostToolDecision, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
@@ -50,7 +50,7 @@ interface SetupOptions {
|
||||
async function setup(options: SetupOptions = {}) {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, { ...options.toolOrder ? { toolOrder: options.toolOrder } : {} })
|
||||
await ctx.plugin(ToolRegistry, { mode: options.mode ?? 'code', ...options.maxParallelSubCalls !== undefined ? { maxParallelSubCalls: options.maxParallelSubCalls } : {} })
|
||||
await ctx.plugin(ToolRuntime, { mode: options.mode ?? 'code', ...options.maxParallelSubCalls !== undefined ? { maxParallelSubCalls: options.maxParallelSubCalls } : {} })
|
||||
let runtime: FakeRuntime | undefined
|
||||
if (options.runtime !== false) {
|
||||
await ctx.plugin(FakeRuntime, options.runtime ?? {})
|
||||
@@ -461,7 +461,7 @@ describe('mode-aware wire contribution', () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
await ctx.plugin(FakeRuntime, {})
|
||||
const fiber = await ctx.plugin(ToolRegistry, { mode: 'code' })
|
||||
const fiber = await ctx.plugin(ToolRuntime, { mode: 'code' })
|
||||
expect(ctx.tools.get(RUN_CODE_NAME)).toBeDefined()
|
||||
await fiber.dispose()
|
||||
const assembly = await ctx.systemPrompt.assemble()
|
||||
@@ -1221,7 +1221,7 @@ describe('the run_code dispatch bridge', () => {
|
||||
it('executing run_code under a missing runtime is a structured isError, not a crash', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
await ctx.plugin(ToolRegistry, { mode: 'code' })
|
||||
await ctx.plugin(ToolRuntime, { mode: 'code' })
|
||||
const result = await runCode(ctx, 'program')
|
||||
expect(result.isError).toBe(true)
|
||||
expect((result.content[0] as { text: string }).text).toContain('requires a code runtime')
|
||||
@@ -1573,21 +1573,21 @@ describe('the run_code dispatch bridge', () => {
|
||||
it('direct construction rejects a non-positive parallel sub-call cap at load', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
expect(() => new ToolRegistry(ctx, { mode: 'code', maxParallelSubCalls: 0 }))
|
||||
expect(() => new ToolRuntime(ctx, { mode: 'code', maxParallelSubCalls: 0 }))
|
||||
.toThrow('maxParallelSubCalls must be a positive integer')
|
||||
})
|
||||
|
||||
it('direct construction in code mode defaults the parallel sub-call cap', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
const registry = new ToolRegistry(ctx, { mode: 'code' })
|
||||
const registry = new ToolRuntime(ctx, { mode: 'code' })
|
||||
expect(registry.get(RUN_CODE_NAME)).toBeDefined()
|
||||
})
|
||||
|
||||
it('defaults to native mode under direct construction with no config', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
const registry = new ToolRegistry(ctx)
|
||||
const registry = new ToolRuntime(ctx)
|
||||
expect(registry.get(RUN_CODE_NAME)).toBeUndefined()
|
||||
const assembly = await ctx.systemPrompt.assemble()
|
||||
expect(assembly.sections.some(section => section.name === 'tools:sdk')).toBe(false)
|
||||
@@ -1595,7 +1595,7 @@ describe('the run_code dispatch bridge', () => {
|
||||
it('denies a model-direct native-tool call under code mode as UNKNOWN_TOOL', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
const registry = new ToolRegistry(ctx, { mode: 'code' })
|
||||
const registry = new ToolRuntime(ctx, { mode: 'code' })
|
||||
registerEcho(ctx, 'write')
|
||||
const result = await registry.execute({
|
||||
signal: testToolSignal,
|
||||
@@ -1615,7 +1615,7 @@ describe('the run_code dispatch bridge', () => {
|
||||
it('routes a pre-aborted collapsed call through ABORTED_BEFORE_DISPATCH', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
const registry = new ToolRegistry(ctx, { mode: 'code' })
|
||||
const registry = new ToolRuntime(ctx, { mode: 'code' })
|
||||
registerEcho(ctx, 'write')
|
||||
const aborted = new AbortController()
|
||||
aborted.abort()
|
||||
@@ -1686,7 +1686,7 @@ describe('per-agent presentation', () => {
|
||||
// `native` here, so a collapse predicate reading it instead of this
|
||||
// scope's effective mode would announce [run_code] and still execute the
|
||||
// native call — the bypass, reopened for exactly the preset composition
|
||||
// `dsh-agent-tool-mode` produces.
|
||||
// `dsh-agent-tool-presentation` produces.
|
||||
expect(ctx.tools.executionMode({
|
||||
signal: testToolSignal,
|
||||
callId: CallId('preset-coded-schedule'),
|
||||
|
||||
@@ -4,7 +4,7 @@ import { describe, expect, expectTypeOf, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry, {
|
||||
import ToolRuntime, {
|
||||
defineContentToolFixture,
|
||||
type ToolDefinition,
|
||||
type ToolExecutionInput,
|
||||
@@ -16,7 +16,7 @@ const testToolSignal = new AbortController().signal
|
||||
async function setup() {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ function exec(name: string, args: unknown): ToolExecutionInput {
|
||||
return { signal: testToolSignal, callId: CallId('c1'), name, arguments: args }
|
||||
}
|
||||
|
||||
describe('ToolRegistry.executionMode', () => {
|
||||
describe('ToolRuntime.executionMode', () => {
|
||||
it('returns parallel only for an explicit true classifier', async () => {
|
||||
const ctx = await setup()
|
||||
ctx.tools.register(defineContentToolFixture({
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
assertManifestComplete,
|
||||
assertToolsHarvested,
|
||||
collectToolCatalog,
|
||||
render,
|
||||
type ToolCatalog,
|
||||
type ToolPackage,
|
||||
} from '../../../../scripts/gen-tool-catalog.ts'
|
||||
|
||||
/** JSON Schema shape enough to reach the values AST extraction can't. */
|
||||
@@ -23,7 +25,7 @@ describe('gen-tool-catalog collectToolCatalog', () => {
|
||||
it('boots every shipped tool package and harvests its model-facing schemas', async () => {
|
||||
const catalog = await collectToolCatalog()
|
||||
const names = catalog.flatMap(entry => entry.schemas.map(s => s.name)).sort()
|
||||
expect(names).toEqual(['ask_user_question', 'bash', 'bash', 'cordis_inspect', 'cordis_mount', 'cordis_unmount', 'create_goal', 'edit', 'exit_plan_mode', 'get_goal', 'glob', 'grep', 'interrupt_agent', 'list_agents', 'lsp', 'pwsh', 'ralph', 'read', 'read_image', 'report', 'run_code', 'schedule_create', 'schedule_delete', 'schedule_list', 'send_message', 'session_event_read', 'session_event_search', 'session_event_trace', 'session_search', 'session_trace', 'skill', 'str_replace_editor', 'subagent', 'task_kill', 'task_list', 'task_output', 'terminal_close', 'terminal_list', 'terminal_open', 'terminal_read', 'terminal_send', 'terminal_signal', 'todo_write', 'update_goal', 'web_fetch', 'web_search', 'workflow', 'write'])
|
||||
expect(names).toEqual(['ask_user_question', 'bash', 'bash', 'cordis_define', 'cordis_inspect_list', 'cordis_inspect_query', 'cordis_inspect_self', 'cordis_run', 'cordis_stop', 'cordis_undefine', 'create_goal', 'edit', 'exit_plan_mode', 'get_goal', 'glob', 'grep', 'interrupt_agent', 'job_kill', 'job_list', 'job_output', 'list_agents', 'lsp', 'pwsh', 'ralph', 'read', 'read_image', 'report', 'run_code', 'schedule_create', 'schedule_delete', 'schedule_list', 'send_message', 'session_event_read', 'session_event_search', 'session_event_trace', 'session_search', 'session_trace', 'skill', 'str_replace_editor', 'subagent', 'terminal_close', 'terminal_list', 'terminal_open', 'terminal_read', 'terminal_send', 'terminal_signal', 'todo_write', 'update_goal', 'web_fetch', 'web_search', 'workflow', 'write'])
|
||||
// Every tool carries a JSON-Schema `parameters` object (what the model sees).
|
||||
for (const entry of catalog) {
|
||||
for (const schema of entry.schemas) {
|
||||
@@ -46,7 +48,7 @@ describe('gen-tool-catalog collectToolCatalog', () => {
|
||||
it('attributes each harvested tool with its registering plugin source', async () => {
|
||||
const catalog = await collectToolCatalog()
|
||||
const bash = catalog.find(entry => entry.pkg === '@deepseek-ai/dsh-tool-bash')
|
||||
expect(bash?.sources.bash).toBe('packages/bash/tool-bash/src/index.ts')
|
||||
expect(bash?.sources.bash).toBe('packages/shell/tool-bash/src/index.ts')
|
||||
const control = catalog.find(entry => entry.pkg === '@deepseek-ai/dsh-tool-subagent-control')
|
||||
expect(control?.sources).toEqual({
|
||||
interrupt_agent: 'packages/subagent/tool-subagent-control/src/index.ts',
|
||||
@@ -91,6 +93,29 @@ describe('gen-tool-catalog assertManifestComplete', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('gen-tool-catalog assertToolsHarvested', () => {
|
||||
const entry: ToolPackage = {
|
||||
pkg: '@deepseek-ai/dsh-tool-demo',
|
||||
dir: 'tool-demo',
|
||||
source: 'packages/demo/tool-demo/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.somethingUnmounted'],
|
||||
writes: ['tool/result'],
|
||||
mount: () => Promise.resolve(),
|
||||
}
|
||||
|
||||
it('accepts a boot that registered at least one tool', () => {
|
||||
expect(() => { assertToolsHarvested(entry, 1) }).not.toThrow()
|
||||
})
|
||||
|
||||
it('throws, naming the package and its requirements, when a boot registers nothing', () => {
|
||||
// The failure this guards is silent by construction: the package is in the
|
||||
// manifest, its plugin merely stays PENDING on an unmounted service, and the
|
||||
// catalog would ship without its tools while every gate stays green.
|
||||
expect(() => { assertToolsHarvested(entry, 0) }).toThrow(/@deepseek-ai\/dsh-tool-demo booted without registering a single tool/)
|
||||
expect(() => { assertToolsHarvested(entry, 0) }).toThrow(/ctx.somethingUnmounted/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('gen-tool-catalog render', () => {
|
||||
it('emits a package heading, a tool heading, and a json schema fence', () => {
|
||||
const catalog: ToolCatalog = [
|
||||
|
||||
@@ -5,14 +5,14 @@ import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { ToolExecution, ToolExecutionResult, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
|
||||
import * as ToolsInvariant from '@deepseek-ai/dsh-tools/invariant'
|
||||
import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const testToolSignal = new AbortController().signal
|
||||
|
||||
async function setup(): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(InvariantRegistry)
|
||||
await ctx.plugin(ToolsInvariant)
|
||||
return ctx
|
||||
}
|
||||
@@ -219,7 +219,7 @@ describe('tool-pipeline invariants', () => {
|
||||
content: [{ type: 'text', text: 'ok' }],
|
||||
})
|
||||
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(InvariantRegistry)
|
||||
await expect(ctx.plugin(ToolsInvariant).then(() => undefined)).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -233,7 +233,7 @@ describe('tool-pipeline invariants', () => {
|
||||
name: 'echo',
|
||||
arguments: {},
|
||||
})
|
||||
await ctx.plugin(InvariantService)
|
||||
await ctx.plugin(InvariantRegistry)
|
||||
await expect(ctx.plugin(ToolsInvariant).then(() => undefined)).rejects.toThrow(/outside any open turn/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Events } from '@deepseek-ai/cordis'
|
||||
import { bindScopeParent, createScope } from '@deepseek-ai/dsh-scope'
|
||||
import type { Scope } from '@deepseek-ai/dsh-scope'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||
import ToolRuntime from '@deepseek-ai/dsh-tools'
|
||||
import type { PreToolDecision, ToolDefinition, ToolExecution, ToolExecutionInput, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
|
||||
@@ -17,7 +17,7 @@ const testToolSignal = new AbortController().signal
|
||||
async function mount(): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt, {})
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createUserMessage, CallId, HarnessError, type ContentBlock } from '@de
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import ApprovalService, { type ApprovalOutcome, type ApprovalRequest } from '@deepseek-ai/dsh-user-approval'
|
||||
import ToolRegistry, {
|
||||
import ToolRuntime, {
|
||||
defineContentToolFixture, defineTool, JsonSchemaError, parameterSchemaSpecToJsonSchema, validateArgs, ToolArgsError, ToolNotFoundError,
|
||||
TOOL_ABORTED, TOOL_ABORTED_BEFORE_DISPATCH,
|
||||
type InferArgs, type JsonValue, type ParameterSchemaSpec, type PreToolDecision, type PostToolDecision,
|
||||
@@ -16,7 +16,7 @@ const testToolSignal = new AbortController().signal
|
||||
async function setup() {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(ToolRuntime)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const echoTool = defineTool({
|
||||
},
|
||||
})
|
||||
|
||||
describe('ToolRegistry', () => {
|
||||
describe('ToolRuntime', () => {
|
||||
it('registers tools, exposes schemas, and feeds the system-prompt assembly', async () => {
|
||||
const ctx = await setup()
|
||||
ctx.tools.register(echoTool)
|
||||
@@ -2449,7 +2449,7 @@ describe('schema DSL optional and nested contracts', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('ToolRegistry.get', () => {
|
||||
describe('ToolRuntime.get', () => {
|
||||
it('get() returns the registered tool definition', async () => {
|
||||
const ctx = await setup()
|
||||
ctx.tools.register(echoTool)
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"path": "../../interaction/user-approval"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user