Merge remote-tracking branch 'origin/master' into claude/unified-environment-credentials-c8841a

# Conflicts:
#	.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-24-web-config-tree-boot-and-transport-layering.zh.md
#	.agents/notes/implemented/architecture/2026-07-29-request-level-llm-config-credentials.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-30-credential-boundaries-and-atomic-registration.i18n.yaml
#	apps/cli/package.json
#	apps/cli/src/web.ts
#	examples/mcp-memory/README.i18n.yaml
#	packages/cordis/repository-plugin/README.i18n.yaml
#	packages/credentials/credentials-local/README.i18n.yaml
#	packages/credentials/credentials-local/README.zh.md
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/web/web-search-deepseek/README.i18n.yaml
This commit is contained in:
Yichen Jiang
2026-08-05 12:40:20 +08:00
1363 changed files with 12118 additions and 4336 deletions

View File

@@ -105,6 +105,9 @@ flowchart LR
pkg_subagent_acp["subagent-acp"]
pkg_bash["bash"]
svc_bash["ctx.bash<br/>Bash executor seam"]
pkg_pwsh_local["pwsh-local"]
pkg_tool_pwsh["tool-pwsh"]
pkg_bash_env["bash-env"]
svc_bashEnv["ctx.bashEnv<br/>Managed bash environment registry"]
pkg_pty["pty"]
svc_pty["ctx.pty<br/>Persistent PTY session registry"]
@@ -167,6 +170,7 @@ flowchart LR
pkg_agent_loop --> svc_agentLoop
pkg_approval --> svc_approval
pkg_bash --> svc_bash
pkg_bash_env --> svc_bashEnv
pkg_bash_local --> svc_bash
pkg_bash_sandbox --> svc_bash
pkg_code_runtime --> svc_codeRuntime
@@ -194,6 +198,7 @@ flowchart LR
pkg_plan_mode --> svc_planMode
pkg_pty --> svc_pty
pkg_pty_local --> svc_pty
pkg_pwsh_local --> svc_bash
pkg_sandbox --> svc_sandbox
pkg_sandbox_local --> svc_sandbox
pkg_sandbox_policy --> svc_sandboxPolicy
@@ -231,7 +236,6 @@ flowchart LR
pkg_tasks --> svc_tasks
pkg_tasks_local --> svc_tasks
pkg_token_meter --> svc_tokenMeter
pkg_tool_bash --> svc_bashEnv
pkg_tools --> svc_tools
pkg_typert_registry --> svc_typert
pkg_user_interaction --> svc_userInteraction
@@ -254,6 +258,9 @@ flowchart LR
svc_bash --> pkg_hooks_claude
svc_bash --> pkg_hooks_codex
svc_bash --> pkg_tool_bash
svc_bash --> pkg_tool_pwsh
svc_bashEnv --> pkg_tool_bash
svc_bashEnv --> pkg_tool_pwsh
svc_clientModuleHost --> pkg_hmr
svc_codeRuntime --> pkg_tools
svc_compact --> pkg_compact_basic
@@ -371,8 +378,8 @@ flowchart LR
| `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-spine-demo`](../packages/examples/agent-spine-demo) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
| `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | Folds revisioned objective state from the session log and keeps live continuation activation process-local. |
| `ctx.subprocess` | `seam` | [`subprocess`](../packages/subprocess/subprocess) | [`subprocess-local`](../packages/subprocess/subprocess-local) | [`bash-local`](../packages/bash/bash-local), [`bash-sandbox`](../packages/bash/bash-sandbox), [`lsp-local`](../packages/lsp/lsp-local), [`subagent-acp`](../packages/subagent/subagent-acp) | - | The bash executors, the LSP host, and the ACP subagent backend spawn their children through ctx.subprocess; the service owns tree lifetime, stdio dispositions (pipes, inherit, bounded spill-backed collection), and kill escalation. |
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local), [`bash-sandbox`](../packages/bash/bash-sandbox) | [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors replace bash-local without touching them. |
| `ctx.bashEnv` | `core` | [`tool-bash`](../packages/bash/tool-bash) | - | - | - | Plugins declare effect-scoped DSH_* facts; tool-bash collects one trusted snapshot per execution and the executor rebuilds the namespace. |
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local), [`bash-sandbox`](../packages/bash/bash-sandbox), [`pwsh-local`](../packages/bash/pwsh-local) | [`tool-bash`](../packages/bash/tool-bash), [`tool-pwsh`](../packages/bash/tool-pwsh), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing shell tools and hook bridges consume this seam; sandboxed, remote, or PowerShell executors replace bash-local without touching them. |
| `ctx.bashEnv` | `core` | [`bash-env`](../packages/bash/bash-env) | - | [`tool-bash`](../packages/bash/tool-bash), [`tool-pwsh`](../packages/bash/tool-pwsh) | - | Plugins declare effect-scoped DSH_* facts; each shell tool collects one trusted snapshot per execution and its executor rebuilds the namespace. |
| `ctx.pty` | `seam` | [`pty`](../packages/pty/pty) | [`pty-local`](../packages/pty/pty-local) | [`tool-pty`](../packages/pty/tool-pty) | - | The registry owns exact-Agent session identity and cleanup; backends own terminal mechanics, while tool-pty exposes the owner-scoped model surface. |
| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/bash/bash-sandbox), [`pty-local`](../packages/pty/pty-local) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. |
| `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/bash/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox), [`pty-local`](../packages/pty/pty-local) | - | The one home for the deployment default mode + workspace root; only the sandboxed executor and provider read the service (the tool layers use the pure `sandbox/mode` fold it also exports). Both enforcing families read it so bash and fs cannot confine to different roots. |

View File

@@ -192,7 +192,19 @@ export interface GoalConfig {
Depends on: [`AgentLoopConfig`](#deepseek-aidsh-agent-loop) · [`GoalDomainConfig`](#deepseek-aidsh-goal) · [`InvariantConfig`](#deepseek-aidsh-invariants) · [`SessionTitleConfig`](#deepseek-aidsh-session-title) · [`SkillLocal`](../packages/skill/skill-local/src/index.ts) · [`SkillRegistryConfig`](#deepseek-aidsh-skill) · [`SystemPromptConfig`](#deepseek-aidsh-system-prompt) · [`toolBash`](../packages/bash/tool-bash/src/index.ts) · [`toolGoal`](../packages/goal/tool-goal/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`toolSkill`](../packages/skill/tool-skill/src/index.ts) · [`toolTasks`](../packages/tasks/tool-tasks/src/index.ts) · [`workspaceContext`](../packages/context/workspace-context/src/index.ts)
Source: [`packages/examples/agent-spine-demo/src/index.ts:89`](../packages/examples/agent-spine-demo/src/index.ts)
Source: [`packages/examples/agent-spine-demo/src/index.ts:90`](../packages/examples/agent-spine-demo/src/index.ts)
## `@deepseek-ai/dsh-bash-env`
```ts config-catalog
/** Plugin config (all optional — the built-in facts resolve without defaults). */
export interface Config {
/** DeepSeek Harness home directory exposed as `DSH_HOME`; defaults to `$DSH_HOME` or `~/.dsh`. */
dshHome?: string
}
```
Source: [`packages/bash/bash-env/src/index.ts:29`](../packages/bash/bash-env/src/index.ts)
## `@deepseek-ai/dsh-bash-local`
@@ -296,7 +308,7 @@ export interface ConnectionConfig {
}
```
Source: [`packages/client/connection/src/index.ts:20`](../packages/client/connection/src/index.ts)
Source: [`packages/client/connection/src/index.ts:21`](../packages/client/connection/src/index.ts)
## `@deepseek-ai/dsh-client-hmr`
@@ -574,7 +586,7 @@ export interface Config {
}
```
Source: [`packages/host/webserver/src/index.ts:39`](../packages/host/webserver/src/index.ts)
Source: [`packages/host/webserver/src/index.ts:47`](../packages/host/webserver/src/index.ts)
## `@deepseek-ai/dsh-invariants`
@@ -959,6 +971,37 @@ export interface Config {
Source: [`packages/pty/pty-local/src/config.ts:6`](../packages/pty/pty-local/src/config.ts)
## `@deepseek-ai/dsh-pwsh-local`
Requires: `subprocess`
```ts config-catalog
/** Plugin config (all optional — `static Config` supplies the defaults). */
export interface Config {
/** Default working directory for commands (default: process.cwd()). */
cwd?: string
/** Default foreground timeout in milliseconds. */
timeoutMs?: number
/** Upper bound for per-call timeout overrides. */
maxTimeoutMs?: number
/** Per-stream in-memory output cap; overflow spills to a temp file. */
maxOutputBytes?: number
/** Per-stream spill-file cap; larger streams retain only their in-memory tail. */
maxSpillBytes?: number
/** Grace period for kill escalation and for inherited pipes after shell exit. */
graceMs?: number
/**
* Explicit pwsh executable. When omitted, well-known Windows install
* locations and PATH entries are probed in order (PowerShell 7 install,
* PATH entries such as the Microsoft Store install, then Windows
* PowerShell 5.1), falling back to a bare `pwsh` resolved through PATH.
*/
pwshPath?: string
}
```
Source: [`packages/bash/pwsh-local/src/index.ts:54`](../packages/bash/pwsh-local/src/index.ts)
## `@deepseek-ai/dsh-repeat-tool-guard`
```ts config-catalog
@@ -1659,19 +1702,17 @@ Source: [`packages/llm/token-meter/src/types.ts:12`](../packages/llm/token-meter
## `@deepseek-ai/dsh-tool-bash`
Requires: `tools` · `bash` · `systemPrompt`
Requires: `tools` · `bash` · `systemPrompt` · `bashEnv`
```ts config-catalog
/** Configuration for the bash tool and its managed child environment. */
/** Configuration for the bash tool. */
export interface Config {
/** Expose `run_in_background` (default true); disabled calls are also rejected. */
enableRunInBackground?: boolean
/** DeepSeek Harness home directory exposed as `DSH_HOME`; defaults to `$DSH_HOME` or `~/.dsh`. */
dshHome?: string
}
```
Source: [`packages/bash/tool-bash/src/index.ts:41`](../packages/bash/tool-bash/src/index.ts)
Source: [`packages/bash/tool-bash/src/index.ts:34`](../packages/bash/tool-bash/src/index.ts)
## `@deepseek-ai/dsh-tool-bash-persistent`
@@ -1809,6 +1850,20 @@ export interface Config {
Source: [`packages/pty/tool-pty/src/index.ts:35`](../packages/pty/tool-pty/src/index.ts)
## `@deepseek-ai/dsh-tool-pwsh`
Requires: `tools` · `bash` · `systemPrompt` · `bashEnv`
```ts config-catalog
/** Configuration for the pwsh tool. */
export interface Config {
/** Expose `run_in_background` (default true); disabled calls are also rejected. */
enableRunInBackground?: boolean
}
```
Source: [`packages/bash/tool-pwsh/src/index.ts:41`](../packages/bash/tool-pwsh/src/index.ts)
## `@deepseek-ai/dsh-tool-ralph`
Requires: `tools` · `workflows` · `subagents` · `systemPrompt`

View File

@@ -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 docs/cookbook/adding-a-package.md
adding-a-package.md: 2dd9165c4b5a7e04ecc7af0507f364fe89b294bb
adding-a-package.zh.md: 79f022531de500eed1d53b0915ee933b047121ff
adding-a-package.md: 5df22b23a69e2a33a5d963970c771088ed7f3b5d
adding-a-package.zh.md: e573657988bd67f5ff9b9191550c10bc654ef116

View File

@@ -23,7 +23,7 @@ packages/<group>/<pkg>/
Choose an existing group when one matches the package's role (`core`, `llm`, `bash`, `compact`, `subagent`, `todo`, `session-persistence`, `ui`, `util`, or `support`). A new group is allowed, but it is a pure container: no `package.json`, no source files, and packages still sit exactly one level below it.
package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, a `version` matching the root `package.json`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list is precise: `lib/index.js`, `lib/types/**/*.d.ts`, `lib/types/**/*.d.ts.map`, and `src`; do not publish `lib/types` JS or JS-map intermediates or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`.
package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, a `version` matching the root `package.json`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list contains exactly `lib/index.js`, `lib/invariant.js`, `lib/types/**/*.d.ts`, and package-specific runtime artifacts recognized by the gate; a package whose runtime export points into the emitted tree also includes `lib/types/**/*.js`. Do not publish `src`, declaration maps, JS maps, or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`.
In-package relative imports use explicit `.ts` specifiers in source (for example, `export * from './types.ts'`). The compiler rewrites those to `.js` in emitted JS and leaves explicit `.ts` specifiers in declarations, which standard NodeNext/Node16 TypeScript consumers resolve to the sibling `.d.ts` files.

View File

@@ -23,7 +23,7 @@ packages/<group>/<pkg>/
当已有分组与包的角色匹配时,选择该分组(`core``llm``bash``compact``subagent``todo``session-persistence``ui``util``support`)。允许新建分组,但分组只是纯容器:没有 `package.json`,没有源文件,包仍然恰好位于其下一层。
package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-constraints.ts` 强制执行):`private: true``version` 与根 `package.json` 一致,`type: module``main: "lib/index.js"``types: "lib/types/index.d.ts"``exports["."].types: "./lib/types/index.d.ts"``exports["."].default: "./lib/index.js"``cordis` 同时出现在 peerDependencies 和 devDependencies 中(相同范围)。每个 dsh 对等依赖peer dependency都要在 devDependencies 中镜像。`schemastery` 放在 `dependencies` 中(它是运行时校验器),与 agent-loop 保持一致。`files` 列表精确`lib/index.js``lib/types/**/*.d.ts``lib/types/**/*.d.ts.map``src`;不要发布 `lib/types` 下的 JS 或 JS-map 中间产物,也不要发布陈旧的根声明文件。带有 `bin` 的 CLI 应用包在 `files` 中将 `lib/bin.js` 紧跟在 `lib/index.js` 之后。
package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-constraints.ts` 强制执行):`private: true``version` 与根 `package.json` 一致,`type: module``main: "lib/index.js"``types: "lib/types/index.d.ts"``exports["."].types: "./lib/types/index.d.ts"``exports["."].default: "./lib/index.js"``cordis` 同时出现在 peerDependencies 和 devDependencies 中(相同范围)。每个 dsh 对等依赖peer dependency都要在 devDependencies 中镜像。`schemastery` 放在 `dependencies` 中(它是运行时校验器),与 agent-loop 保持一致。`files` 列表精确包含 `lib/index.js``lib/invariant.js``lib/types/**/*.d.ts` 以及门禁认可的包专用运行时产物;如果包的运行时 export 指向输出树,还要包含 `lib/types/**/*.js`。不要发布 `src`、声明映射、JS map 或陈旧的根声明文件。带有 `bin` 的 CLI 应用包在 `files` 中将 `lib/bin.js` 紧跟在 `lib/index.js` 之后。
包内的相对导入在源码中使用显式 `.ts` 后缀(例如 `export * from './types.ts'`)。编译器在输出的 JS 中将其重写为 `.js`,在声明文件中保留显式 `.ts` 后缀;标准的 NodeNext/Node16 TypeScript 消费方会将其解析到同目录的 `.d.ts` 文件。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-vendored-package.md
adding-a-vendored-package.md: a951a96f62d2ea3aa693a24d83bf46a1a12070cd
adding-a-vendored-package.zh.md: 878adbb203f8c79db0f127cb1ac58cd9e7a09171
adding-a-vendored-package.zh.md: 66b71d9ac7901d40074a21213c4962d1db96db8f

View File

@@ -1,8 +1,8 @@
# 实操手册:添加一个 vendored 包package
# 实操手册:添加一个 vendored 包
[English](adding-a-vendored-package.md) | 中文
当 harness 需要引入另一个上游 Cordis 包(如 `@cordisjs/plugin-http`)时,应将其作为固定版本的源码 **vendor**`vendor/` 下,而非作为 npm 依赖添加——原因见[vendoring 决策](../../.agents/notes/implemented/process/2026-06-11-vendor-cordis-as-source.md)。[vendor/README.md](../../vendor/README.md) 介绍如何*更新*已有的 vendored 包;本指南是添加**新** vendored 包的逐文件清单。(已对照现有 vendored 集合验证;如有偏差,请在此修正。)
当 harness 需要引入另一个上游 Cordis 包(如 `@cordisjs/plugin-http`)时,应将其作为固定版本的源码 **vendor**`vendor/` 下,而非作为 NPM 依赖添加——原因见[vendoring 决策](../../.agents/notes/implemented/process/2026-06-11-vendor-cordis-as-source.md)。[vendor/README.md](../../vendor/README.md) 介绍如何*更新*已有的 vendored 包;本指南是添加**新** vendored 包的逐文件清单。(已对照现有 vendored 集合验证;如有偏差,请在此修正。)
## 1. 复制源码

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/cookbook/adding-an-llm-adapter.md
adding-an-llm-adapter.md: a7f9dced70041653a0cb815147a07b6386d79e3e
adding-an-llm-adapter.zh.md: 3515927585201326b713bb03cd863886ce7846bd
adding-an-llm-adapter.zh.md: 494115bdd5cc2365feb0964f2bef4d6dc070d3f6

View File

@@ -1,8 +1,8 @@
# 实操手册:添加 LLM 适配器
# 实操手册:添加 LLM(大语言模型)适配器
[English](adding-an-llm-adapter.md) | 中文
如何接入一个新的模型提供方。参考实现:`packages/llm/llm-deepseek`(直接 HTTPSSE `eventsource-parser` 分帧)与 `packages/llm/llm-pi-ai`(封装 LLM 库)。请先阅读 `packages/llm/llm/src/types.ts` 中的 `StreamChunk` 文档——它记录了两个适配器都经过验证的协议约定。
如何接入一个新的模型提供方。参考实现:`packages/llm/llm-deepseek`(直接 HTTPSSEServer-Sent Events`eventsource-parser` 分帧)与 `packages/llm/llm-pi-ai`(封装 LLM 库)。请先阅读 `packages/llm/llm/src/types.ts` 中的 `StreamChunk` 文档——它记录了两个适配器都经过验证的协议约定。
## 基本形态
@@ -20,7 +20,7 @@ export function apply(ctx: Context, config: Config) {
}
```
注册基于副作用HMR 安全);每个提供方路由仅对应一个适配器,重复注册会抛出异常,多路由注册要么全部成功,要么全部失败。`options.provider` 用于选择适配器,`options.model` 是提供方模型 ID因此动态模型目录适配器无需重新配置生命周期即可提供新模型。密钥采用 Cordis 原生方式管理schemastery Config 带环境变量回退,通过 cordis.yml 的 `!!js process.env.MY_KEY` 注入。代码中禁止临时读取密钥文件。
注册基于副作用,可安全支持 HMR热模块替换);每个提供方路由仅对应一个适配器,重复注册会抛出异常,多路由注册要么全部成功,要么全部失败。`options.provider` 用于选择适配器,`options.model` 是提供方模型 ID因此动态模型目录适配器无需重新配置生命周期即可提供新模型。密钥采用 Cordis 原生方式管理schemastery Config 带环境变量回退,通过 cordis.yml 的 `!!js process.env.MY_KEY` 注入。切勿在代码中读取自行约定的密钥文件。
## 协议义务(两个实现共同验证的契约)
@@ -32,7 +32,7 @@ export function apply(ctx: Context, config: Config) {
- 如果 `GenerateOptions` 中某个字段你的提供方无法支持(例如提供方不支持 stop sequences 时收到 `stop` 列表):抛出 `LlmError(..., 'UNSUPPORTED')`,而非静默丢弃。
- 如果提供方在后续调用中需要响应 ID、签名或其他原生元数据请将其最小无损 JSON 投影作为 `finish.replayState` 发出。重建历史时验证该状态。只有历史提供方路由和目标提供方路由当前由完全相同的适配器实例拥有时,`LlmService` 才会传递该状态;由适配器决定同模型、跨模型或跨提供方恢复是否合法。状态缺失时,切勿仅根据提供方/模型名称推断原生回放。
提供方特有的 thinking 模式开关仍放在适配器的 Config 中。确切模型元数据使用一处提供方无关的能力 seam实现 `resolveModel()`,返回提供方/模型身份以及可选的 `context` 和 `reasoning` 字段;仅当存在配置指定的默认值时才声明 `defaultEffort`响应传给解析器的可选 `AbortSignal`。推理强度是由适配器映射到提供方请求的有序不透明 ID。请保留适配器给出的权威可选列表包括适配器在支持时定义的 `off`不得暴露最终协议值的具体拼写也不得自动调整不支持的值。ID 无需与其协议表示相同。
提供方特有的思考模式开关仍放在适配器的 Config 中。确切模型元数据使用一处提供方无关的能力 seam实现 `resolveModel()`,返回提供方/模型身份以及可选的 `context` 和 `reasoning` 字段;仅当存在配置指定的默认值时才声明 `defaultEffort`遵守解析模型时传入的可选 `AbortSignal`。推理reasoning强度是由适配器映射到提供方请求的有序不透明 ID。请保留适配器给出的权威可选列表包括适配器在支持时定义的 `off`不得暴露最终协议值的具体拼写也不得自动调整不支持的值。ID 无需与其协议表示相同。
## 经验证有效的结构
@@ -42,5 +42,5 @@ export function apply(ctx: Context, config: Config) {
- **单元测试mock 提供方,而非 harness。** 用脚本化的 `node:http` 服务器模拟提供方的协议格式,覆盖正常路径、所有错误状态码、畸形载荷、连接提前关闭和中止——无需网络,且能满足 100% 逐文件覆盖率门禁。对基于 SDK 的适配器同样适用(将 SDK 的 baseURL 指向 mock 服务器)。
- **恶意分帧测试。** 在任意字节位置(包括 UTF-8 字符中间)切割流载荷——真实网络环境正是如此。
- **E2E`tests/*.e2e.ts`**,通过 `pnpm run test:e2e` 运行,以 `describe.skipIf(!process.env.MY_KEY)` 守卫,确保无密钥的 CI 保持绿色。覆盖具有代表性的模型/提供方/API 系列以及你映射的每种提供方模式、一次包含后续轮次(历史中带工具结果)的工具调用往返,以及仅做宽松断言(子串/结构匹配、有界的 maxTokens——真实模型是非确定性的
- **e2e`tests/*.e2e.ts`**,通过 `pnpm run test:e2e` 运行,以 `describe.skipIf(!process.env.MY_KEY)` 守卫,确保无密钥的 CI 保持绿色。覆盖具有代表性的模型/提供方/API 系列以及你映射的每种提供方模式、一次务必包含后续轮次(历史中带工具结果)的工具调用往返,以及仅做宽松断言(子串/结构匹配、有界的 maxTokens——真实模型是非确定性的
- 在 `knip.json` 中注册 e2e 文件模式per-workspace `entry` 覆盖),否则 knip 会将其标记为未使用。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cookbook/maintaining-dsh-code-review.md
maintaining-dsh-code-review.md: 2b5d0d926ae922f2650daac33cf35991cb71c5e5
maintaining-dsh-code-review.zh.md: c0e8b64fde3a67174878b4b0665712c9ba2e67c0
maintaining-dsh-code-review.zh.md: 56d274c080fcd95b6d18219f4bf0e0e7485f624e

View File

@@ -1,8 +1,8 @@
# 维护 dsh-code-review skill
# 维护 dsh-code-review skill(技能)
[English](maintaining-dsh-code-review.md) | 中文
[`dsh-code-review`](../../.agents/skills/dsh-code-review/SKILL.md) skill(技能)由一名指定操作员通过私有的周期维护工具持续更新。本实操手册cookbook既是该操作员和接任者的入口,也帮助仓库贡献者理解为何 skill 更新会以小型周期 PRPull Request的形式出现而不是一次性审计。工作流本身由[人工评审 skill 维护 Agent Noteagent 决策记录)](../../.agents/notes/proposed/process/2026-07-13-human-review-skill-maintenance.md)规定。
[`dsh-code-review`](../../.agents/skills/dsh-code-review/SKILL.md) skill 由一名指定操作员通过私有的周期维护工具持续更新。本实操手册既是该操作员和接任者的入口,也帮助仓库贡献者理解为何 skill 更新会以小型周期 PRPull Request的形式出现而不是一次性审计。工作流本身由[人工评审 skill 维护 Agent Note](../../.agents/notes/proposed/process/2026-07-13-human-review-skill-maintenance.md)规定。
## 维护者会收到什么
@@ -11,7 +11,7 @@
1. 选择指定窗口内合并、且合并 commit 可从 `origin/master` 到达的 PR每天运行默认选择 2 个 UTC 日,每周运行选择 7 日)。合并 commit 无法到达的 PR例如父分支被 squash 的堆叠分支),或超出 250 个 commit 获取上限的 PR会记录到 `skipped-pulls.json` 并跳过,不会中止本次运行。
2. 收集合并前带 commit 锚点的人工评审反馈(行内评论和评审提交),然后比较反馈时与最终落地的 PR patch。它不获取 PR 会话评论,因为 GitHub 当前状态无法为这些评论提供可抵抗 force-push 的反馈时基线;它也不会把只存在于目标分支的变更作为采纳证据。
3. 两个独立配置的评审适配器先对来源和采纳情况分类,再根据当前 skill 对双方一致认定已采纳的条目分类。
4. 主适配器起草完整修订版 `SKILL.md`;两个适配器评审同一份 diff只要仍有阻塞发现,循环就会继续,直到双方批准。
4. 主适配器起草完整修订版 `SKILL.md`;两个适配器评审同一份 diff只要仍有阻塞性问题,循环就会继续,直到双方批准。
5. 工具声明成功前,会针对候选版本运行 `pnpm run doc-sync``pnpm run lint`
每次运行都把产物保存在操作员的机器上。保存的 diff、候选 `SKILL.md` 和提升 manifest元数据清单按时间戳命名存放在 `~/dsh-code-review-outputs/` 下。manifest 记录源 master commit 与 skill blob、源反馈 ID 和 URL、已落地证据范围、适配器裁决和门禁结果每个适配器的原始 I/O 留在私有临时目录中,该目录路径会写入通知和 `~/Library/Logs/dsh-code-review-maintainer/` 下的每日日志。维护 worktree 在每次运行后都会恢复为干净状态,避免操作员直接在维护副本中编辑。
@@ -37,7 +37,7 @@
```sh
rm ~/dsh-code-review-outputs/2026-07-16T02-00-00Z.{diff,SKILL.md,manifest.json}
```
- **暂存成批。** 如果更新很小,可以把候选版本留待与后续版本合并。源 skill 检查仍然适用;如果 `master` 先发生变化,请重新运行分析,或手动 rebase 并重新评审 diff。
- **留待成批处理。** 如果更新很小,可以把候选版本留待与后续版本合并。源 skill 检查仍然适用;如果 `master` 先发生变化,请重新运行分析,或手动 rebase 并重新评审 diff。
- **提升。** 在仓库的干净 `master` checkout 中运行提升辅助工具。它会刷新 `master`、验证当前 skill 与记录的源 blob 一致、应用保存的 diff并创建一份 draft PR其正文包含 manifest 的来源摘要。如果 skill 已发生漂移,它会停止而不是覆盖更新后的指导;操作员仍需在 GitHub 上评审 PR并选择合并或关闭。
```sh

View File

@@ -297,7 +297,7 @@ Source: [`packages/bash/bash/src/index.ts:51`](../../packages/bash/bash/src/inde
## `ctx.bashEnv` — `BashEnvRegistry`
Registry (`ctx.bashEnv`) for trusted, per-execution `DSH_*` variables. The namespace is rebuilt for every model bash call: ambient `DSH_*` values are discarded by the executor, then the registry's current snapshot is injected. Built-in shell facts remain owned by the registry itself while plugins can register additional, enumerable facts with effect-scoped disposal.
Registry (`ctx.bashEnv`) for trusted, per-execution `DSH_*` variables. The namespace is rebuilt for every model shell call: ambient `DSH_*` values are discarded by the executor, then the registry's current snapshot is injected. Built-in shell facts remain owned by the registry itself while plugins can register additional, enumerable facts with effect-scoped disposal.
```ts cordis-catalog
/**
@@ -309,7 +309,7 @@ Registry (`ctx.bashEnv`) for trusted, per-execution `DSH_*` variables. The names
register(contributor: BashEnvContributor): () => void
/**
* Build the trusted `DSH_*` snapshot for one bash tool execution.
* Build the trusted `DSH_*` snapshot for one shell tool execution.
* @param execution - the current tool execution.
* @returns an immutable environment overlay containing built-ins and current contributions.
*/
@@ -324,7 +324,7 @@ list(): BashEnvVariableInfo[]
Types: [DshEnvironment](../core-data-structures/subprocess.md) · [ToolExecution](../core-data-structures/tools.md)
Source: [`packages/bash/tool-bash/src/index.ts:104`](../../packages/bash/tool-bash/src/index.ts)
Source: [`packages/bash/bash-env/src/index.ts:89`](../../packages/bash/bash-env/src/index.ts)
## `ctx.clientModuleHost` — `ClientModuleHostService`
@@ -770,6 +770,14 @@ The web-shape HTTP carrier service. Activation listens immediately (route regist
*/
register(route: WebRoute): () => void
/**
* Register an exact-path HTTP upgrade route. Duplicate paths throw because
* one socket can have only one protocol owner.
* @param route - pathname and handler owning negotiation plus socket use.
* @returns the disposer removing the route.
*/
registerUpgrade(route: WebUpgradeRoute): () => void
/**
* Register an index.html transform, applied to every index response in
* registration order.
@@ -779,7 +787,7 @@ register(route: WebRoute): () => void
tapIndex(transform: (html: string) => string): () => void
```
Source: [`packages/host/webserver/src/index.ts:55`](../../packages/host/webserver/src/index.ts)
Source: [`packages/host/webserver/src/index.ts:63`](../../packages/host/webserver/src/index.ts)
## `ctx.invariants` — `InvariantService`
@@ -2097,7 +2105,7 @@ abstract spawn(spec: SubprocessSpawnSpec): SubprocessHandle
Types: [SubprocessHandle](../core-data-structures/subprocess.md) · [SubprocessSpawnSpec](../core-data-structures/subprocess.md)
Source: [`packages/subprocess/subprocess/src/index.ts:88`](../../packages/subprocess/subprocess/src/index.ts)
Source: [`packages/subprocess/subprocess/src/index.ts:91`](../../packages/subprocess/subprocess/src/index.ts)
## `ctx.systemPrompt` — `SystemPrompt`

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-primer.md
cordis-primer.md: ee65e6e702ecaeb506ce7334032c38e09c936cda
cordis-primer.zh.md: ee4f6864ba7864fc95b5eb8e31acbcaea6e99825
cordis-primer.zh.md: 051dd7c956a4db107a4ef7d1414435f9c2b2603d

View File

@@ -10,7 +10,7 @@ Cordis 是 DeepSeek Harness SDK 底层以 vendor 方式引入的插件框架。
- **上下文是服务的容器。** 一个服务占据一个稳定的 `ctx.<key>`(如 `ctx.tools``ctx.llm``ctx.sessions`);其他插件通过 key 查找服务,而非导入具体实现。
- **通过 `inject` 声明服务依赖。** 插件声明所需的服务后,会等待这些服务就绪才启动;加载顺序通过服务依赖表达,而非手动编排启动序列。
- **类型化事件用于通信。** 服务通过 TypeScript 声明合并注册事件名,然后以 `emit``waterfall`(瀑布式事件)、`parallel``serial` 方式分发,分别对应监听者观察、包装、并行扇出或按序执行。
- **注册是可逆的副作用。** 提示词片段、工具 schema、适配器、提供方和监听器通过 `ctx.effect()``ctx.on()` 安装reload 和 teardown 时可预期地回卷
- **注册是可逆的副作用。** 提示词片段、工具 schema、适配器、提供方和监听器通过 `ctx.effect()``ctx.on()` 安装reload 和 teardown 时会按预期撤销
## 分发模式
@@ -45,4 +45,4 @@ Cordis 是 DeepSeek Harness SDK 底层以 vendor 方式引入的插件框架。
将行为封装为插件:工具流水线事件属于 `ctx.tools`,模型流式输出属于 `ctx.llm`,实时 agent智能体协调属于 `ctx.agents`。拦截和策略优先使用事件;直接能力调用优先使用服务方法。
每个注册都应有对应的 disposerdispose资源释放函数):要么从 `ctx.effect()` 返回一个,要么使用 Cordis 提供的辅助方法自动处理。如果 teardown 顺序有要求,请将相关工作放在同一个 effect 中,以确保资源释放按预期顺序回卷
每个注册都应有对应的 disposer资源释放函数要么从 `ctx.effect()` 返回一个,要么使用 Cordis 提供的辅助方法自动处理。如果 teardown 顺序有要求,请将相关工作放在同一个 effect 中,以确保资源按预期顺序释放

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/02-lifecycle-and-effects.md
02-lifecycle-and-effects.md: f1b39e06e9d25c51ab2d76503025e2b6ffe90c73
02-lifecycle-and-effects.zh.md: a6021ed7475a0045d480810747244274eb5b4198
02-lifecycle-and-effects.zh.md: 2e98e3af6d2f2b1b9cbb8ea38559bc1ffbf7e43b

View File

@@ -2,11 +2,11 @@
[English](02-lifecycle-and-effects.md) | 中文
Cordis 插件可能因配置编辑、热重载、显式资源释放或所需服务消失而卸载。通过 Cordis API 建立的注册属于 effect会在所属插件卸载时撤销在这些 API 之外管理的资源必须包装在 `ctx.effect()` 中。
Cordis 插件可能因修改配置、热重载、显式资源释放或所需服务消失而卸载。通过 Cordis API 建立的注册属于 effect会在所属插件卸载时撤销在这些 API 之外管理的资源必须包装在 `ctx.effect()` 中。
## Effect
对于 Cordis 尚未管理的资源,例如定时器、连接或 watcher应将其包装在 `ctx.effect()` 中并返回 disposerdispose资源释放函数):
对于 Cordis 尚未管理的资源,例如定时器、连接或 watcher应将其包装在 `ctx.effect()` 中并返回 disposer资源释放函数
创建 `lifecycle.ts`,将它放在 `tmp/cordis-tutorial` 中:
@@ -67,7 +67,7 @@ disposed
## Fiber 状态机
每个已加载插件实例都拥有一个 fiber依次经过以下状态
每个已加载插件实例都拥有一个 fiber在以下状态之间转换
```
PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
@@ -86,8 +86,8 @@ PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
你很少需要亲自编写 `ctx.effect()`,因为内置注册 API 本身已经是 effect
- `ctx.on(event, listener)`:监听器会在卸载时移除([第 4 章](04-events.md))。
- `ctx.plugin(child)`:子插件会随父插件一同 dispose。
- 服务注册属于 effect。`ctx.tools.register(...)` 等 harness 注册表也会把返回的 disposer 附着到调用插件上,因此会自动回卷[第 7 章](07-into-the-harness.md))。
- `ctx.plugin(child)`:子插件会随父插件一同 dispose(资源释放)
- 服务注册属于 effect。`ctx.tools.register(...)` 等 harness 注册表也会把返回的 disposer 附着到调用插件上,因此会自动撤销[第 7 章](07-into-the-harness.md))。
对于 Cordis 不管理的资源,应在 `ctx.effect()` 内获取它,并返回用于释放资源的 disposer。此后 Cordis 会在卸载期间调用该释放逻辑,热重载时也不例外。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/03-services.md
03-services.md: 5848132c6ad18338fa893954d45fc20005db6199
03-services.zh.md: 3c77d0451df9062f1a344e7474e6be141b709197
03-services.zh.md: 0f599f082573364e6ad38278e1914d8faf67faa1

View File

@@ -2,7 +2,7 @@
[English](03-services.md) | 中文
**服务**是一个插件提供、其他插件通过 `ctx` 消费的命名功能。在 harness 中,`ctx.tools``ctx.llm``ctx.agents` 都是服务。消费方只命名 `'tools'` 之类的能,而不导入其提供方,因此配置可以选择提供方,无需修改消费方。
**服务**是一个插件提供、其他插件通过 `ctx` 消费的具名能力。在 harness 中,`ctx.tools``ctx.llm``ctx.agents` 都是服务。消费方只指定 `'tools'` 之类的能,而不导入其提供方,因此配置可以选择提供方,无需修改消费方。
## 提供服务
@@ -73,9 +73,9 @@ Hello, world!
## 加载后仍会跟踪依赖关系
`inject` 并非一次性的启动检查。如果应用运行期间所需服务消失,例如提供方被卸载或热替换,每个依赖插件也会随之卸载,并在服务恢复后再次加载。结合 effect[第 2 章](02-lifecycle-and-effects.md)),这能防止运行中的消费方保留对不可用服务的引用:依赖消失时,它自己的注册也会回卷
`inject` 并非一次性的启动检查。如果应用运行期间所需服务消失,例如提供方被卸载或热替换,每个依赖插件也会随之卸载,并在服务恢复后再次加载。结合 effect[第 2 章](02-lifecycle-and-effects.md)),这能防止运行中的消费方保留对不可用服务的引用:依赖消失时,它自己的注册也会撤销
这也是配置中可以替换服务的原因:卸载 `dsh-bash-local` 配置项,挂载另一个 `bash` 提供方,所有注入 `'bash'` 的插件都会干净地重启并使用新实现。
这也是配置中可以替换服务的原因:卸载 Cordis 配置项 `dsh-bash-local`,挂载另一个 `bash` 提供方,所有注入 `'bash'` 的插件都会重新启动并使用新实现。
## 可选依赖

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/04-events.md
04-events.md: 18f39dc1b693e5fb7e1793ec4b7dcac9cf24db95
04-events.zh.md: f55a61ff2f43ea42968893d07eb92ea0613b921a
04-events.zh.md: 3fdafb50303f49dca179bcaea32db211a66241f6

View File

@@ -91,7 +91,7 @@ export function apply(ctx: Context) {
每个 harness 事件都会在生成的[事件目录](../cordis-catalog/events.md)中记录其模式。
## Waterfall转换或短路
## waterfall转换或短路
waterfall 是实现拦截的模式。每个监听器都会收到参数和一个 `next()` continuation它可以转换 `next()` 的返回值,也可以不调用 `next()` 就直接返回从而短路链条的其余部分。Cordis 文档把后一种行为称为否决。创建 `waterfall-demo.ts`

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/05-config.md
05-config.md: fc19add239636fa9e7071d9c77e48595caec1f08
05-config.zh.md: 52a75e40672c9a08d285677dd14dcd404b925e5a
05-config.zh.md: 0c8170f518f0c87ab5c754606436496a4ff9d51e

View File

@@ -2,7 +2,7 @@
[English](05-config.md) | 中文
每个 `cordis.yml` 配置项都可以携带 `config` 块,插件则声明一个 schema在运行 `apply` 前验证该块。错误配置会导致加载失败,并给出准确的错误:插件绝不会在配置不完整时启动。
`cordis.yml` 中的每个 Cordis 配置项都可以携带 `config` 块,插件则声明一个 schema在运行 `apply` 前验证该块。错误配置会导致加载失败,并给出准确的错误:插件绝不会在配置不完整时启动。
## 可配置插件
@@ -65,7 +65,7 @@ ValidationError: invalid config:
- $.targets expected array but got not-an-array (at targets)
```
插件的 fiber 进入 FAILED 状态,本教程的启动器打印错误后以状态码 1 退出。如果某个插件的 schema 有效配置命名了不可用的资源或提供方,该插件也应当在能解析该引用时立即拒绝。
插件的 fiber 进入 FAILED 状态,本教程的启动器打印错误后以状态码 1 退出。如果某个插件的配置通过了 schema 验证,但其中指定的资源或提供方不可用,该插件也应当在能解析该引用时立即拒绝。
## 计算得到的配置值
@@ -77,8 +77,8 @@ ValidationError: invalid config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
```
`!!js` **仅在 `config` 内有效**。配置项元数据(`name``id``disabled``inject` 等)是静态的;`disabled: !!js ...` 会生成一个真值表达式对象,始终禁用该配置项。详见 [loader 配置](../cordis-primer.md#loader-configuration)。
`!!js` **仅在 `config` 内有效**Cordis 配置项元数据(`name``id``disabled``inject` 等)是静态的;`disabled: !!js ...` 会生成一个真值表达式对象,始终禁用该 Cordis 配置项。详见 [loader 配置](../cordis-primer.md#loader-configuration)。
下一章:[组合与 HMR](06-composition-and-hmr.md):将 `cordis.yml` 视为应用。
下一章:[组合与 HMR(热模块替换)](06-composition-and-hmr.md):将 `cordis.yml` 视为应用。
[![](https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat-square&logo=deepseek&logoColor=white)](https://github.com/deepseek-harness/deepseek-harness)

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/06-composition-and-hmr.md
06-composition-and-hmr.md: 66d6a9d93fe39baa881940ba32388979e2678505
06-composition-and-hmr.zh.md: ebe63fc26607ae6d9344c4795a7975496ed901b5
06-composition-and-hmr.zh.md: 7c0a94b0abcc0f153f59391fd009f1e0b40500e5

View File

@@ -2,11 +2,11 @@
[English](06-composition-and-hmr.md) | 中文
到目前为止构建的每项能都是插件,`cordis.yml` 则选择应用的插件树。本章会改变这种组合、热重载一个插件,并诊断始终无法加载的插件。
到目前为止构建的每项能都是插件,`cordis.yml` 则选择应用的插件树。本章会改变这种组合、热重载一个插件,并诊断始终无法加载的插件。
## 配置项不只有名称
## Cordis 配置项不只有名称
配置项除了 `name``config`,还接受其他元数据:
Cordis 配置项除了 `name``config`,还接受其他元数据:
```yaml
- id: greeter # stable identity for this entry
@@ -16,9 +16,9 @@
disabled: true # keep the entry, skip mounting it
```
`id` 为配置项提供稳定标识,使 loader 能区分修改现有配置项与先删除再添加。`disabled: true` 会卸载插件而不删除其配置项;改回原值后,插件以及所有因依赖其服务而处于 PENDING 的插件都会再次加载。
`id` Cordis 配置项提供稳定标识,使 loader 能区分修改现有 Cordis 配置项与先删除再添加。`disabled: true` 会卸载插件而不删除其 Cordis 配置项;改回原值后,插件以及所有因依赖其服务而处于 PENDING 的插件都会再次加载。
组可以嵌套一份配置项子列表,并将其作为一个单元加载和卸载;`isolate` 则为一个组提供某项服务名称的独立实例,因此两个组可以各自看到配置不同的 `bash`,互不影响。这些概念值得在用到之前先了解;[Cordis 入门](../cordis-primer.md)和[服务隔离示例](../user/develop/framework/service.md#service-isolation)介绍了详细内容。
组可以嵌套一份 Cordis 配置项子列表,并将其作为一个单元加载和卸载;`isolate` 则为一个组提供某项服务名称的独立实例,因此两个组可以各自看到配置不同的 `bash`,互不影响。这些概念值得在用到之前先了解;[Cordis 入门](../cordis-primer.md)和[服务隔离示例](../user/develop/framework/service.md#service-isolation)介绍了详细内容。
## 热模块替换
@@ -39,7 +39,7 @@
name: './hello.ts'
```
列表中增加了两个支持插件HMR 通过 Cordis logger 服务记录日志,因此没有 console exporter 时看不到其消息;它还会 `inject` `timer` 服务来实现去抖,如果没有 `@cordisjs/plugin-timer`,它就会永远停在 PENDING而且不发出任何提示。下一节就讨论这种静默状态。
列表中增加了两个辅助插件HMR 通过 Cordis logger 服务记录日志,因此没有控制台导出器时看不到其消息;它还会 `inject` `timer` 服务来实现去抖,如果没有 `@cordisjs/plugin-timer`,它就会永远停在 PENDING而且不发出任何提示。下一节就讨论这种静默状态。
HMR 通过 Loader 的原生辅助工具读取 Node 的 loader 内部结构。请在 tsx 下运行 Cordis
@@ -56,7 +56,7 @@ hello from my first plugin
hello from my EDITED plugin
```
旧实例先卸载(其所有 effect 都会回卷),新代码随后加载,`apply` 再次运行。按 Ctrl-C 停止进程。编辑 `cordis.yml` 本身也会触发更新loader 按 `id` 比较配置项,只挂载、卸载或重新配置发生变化的部分。这就是上述配置项显式携带 `id` 的原因:不带该字段的配置项在每次读取时都会获得一个新生成的 id所以只要配置文件发生任何编辑即使自身文本未变它也会被视为先删除再添加并重新挂载。
旧实例先卸载(其所有 effect 都会回卷),新代码随后加载,`apply` 再次运行。按 Ctrl-C 停止进程。编辑 `cordis.yml` 本身也会触发更新loader 按 `id` 比较 Cordis 配置项,只挂载、卸载或重新配置发生变化的部分。这就是上述 Cordis 配置项显式携带 `id` 的原因:不带该字段的 Cordis 配置项在每次读取时都会获得一个新生成的 id所以只要配置文件发生任何编辑即使自身文本未变它也会被视为先删除再添加并重新挂载。
## 诊断始终无法加载的插件

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/07-into-the-harness.md
07-into-the-harness.md: 6ec42c50fe5059955734fe7bc46117538dafaffc
07-into-the-harness.zh.md: 32b21b008837e2972a53db9d893788dc6a7de9a9
07-into-the-harness.zh.md: 903adb903aa4c4355b92eb34e89f218a0295767c

View File

@@ -46,7 +46,7 @@ export function apply(ctx: Context) {
}
```
这里的每个模式都来自前几章:`inject: ['tools']`[第 3 章](03-services.md))会让插件等待工具注册表就绪;`ctx.tools.register(...)` 会把注册 disposer 附着到插件([第 2 章](02-lifecycle-and-effects.md)),因此卸载时会注销工具。`defineTool``parameters` 规约转换为向模型展示的 JSON Schema推导 `args` 的类型,并在 `execute` 运行前校验模型提供的参数。工具返回由 `output.schema` 声明的规范值;`output.render` 则另行生成原生且持久的结果内容。
这里的每个模式都来自前几章:`inject: ['tools']`[第 3 章](03-services.md))会让插件等待工具注册表就绪;`ctx.tools.register(...)` 会把注册 disposer 附着到插件([第 2 章](02-lifecycle-and-effects.md)),因此卸载时会注销工具。`defineTool``parameters` 规约转换为向模型展示的 JSON Schema推导 `args` 的类型,并在 `execute` 运行前校验模型提供的参数。工具返回由 `output.schema` 声明的规范值;`output.render`作为 Native renderer原生渲染器另行生成持久的结果内容。
## 观察插件
@@ -91,7 +91,7 @@ node --import tsx ../../vendor/cordis/bin.js
tool replied: [{"type":"text","text":"Hello, Cordis!"}]
```
logger 会先触发:`tools/result` 在结果物化过程中发出,早于 `execute` 的 promise 向调用方返回结果。两个插件都不知道另一个插件存在,它们由注册表服务和事件连接。
logger 会先触发:`tools/result` 在结果物化过程中发出,发生在 `execute` 向调用方返回的 promise 兑现之前。两个插件都不知道另一个插件存在,它们由注册表服务和事件连接。
## 从这里走向完整 agent智能体
@@ -100,7 +100,7 @@ logger 会先触发:`tools/result` 在结果物化过程中发出,早于 `ex
后续可以阅读:
- [构建工具](../user/develop/basic/tool.md):深入了解 `defineTool`,包括呈现和更丰富的 schema。
- [三层能设计](../user/develop/practice/index.md)harness 如何组织可替换能。
- [三层能设计](../user/develop/practice/index.md)harness 如何组织可替换能
- 生成的[服务](../cordis-catalog/services.md)与[事件](../cordis-catalog/events.md)目录:可以注入和监听的所有内容。
- [架构](../architecture.md):这些插件所处的系统地图。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/index.md
index.md: af622ad4e35829c6283c40f1b0019d7959dac973
index.zh.md: 35bad552ecce9c0496b0ed88b041a8109c81945b
index.zh.md: 0b7684a9532a1efdcc3ea2d067da23852d146e2f

View File

@@ -6,7 +6,7 @@ Cordis 是 DeepSeek Harness SDK 底层的插件框架:它是一个小型运行
本教程面向 agent 开发者。你不需要深入掌握 TypeScript下文的 [TypeScript 说明](#typescript-notes)会解释可能陌生的语法,并且每一章都会给出确切命令和预期输出。
如果你想阅读精简的概念参考,而不是逐步实践,请参阅 [Cordis 入门](../cordis-primer.md)。详尽的 API 参考见生成的[事件](../cordis-catalog/events.md)与[服务](../cordis-catalog/services.md)目录,以及 [Cordis 核心 API](../cordis-catalog/core/context.md)页面。
如果你想阅读精简的概念参考,而不是逐步实践,请参阅 [Cordis 入门](../cordis-primer.md)。详尽的 API 参考见生成的[事件](../cordis-catalog/events.md)与[服务](../cordis-catalog/services.md)目录,以及 [Cordis 核心 API](../cordis-catalog/core/context.md) 页面。
## 准备工作
@@ -39,7 +39,7 @@ node --import tsx ../../vendor/cordis/bin.js
2. [生命周期与 effect](02-lifecycle-and-effects.md):由 Cordis 管理的注册会在所属插件卸载时撤销。
3. [服务](03-services.md):在 `ctx` 上公开一项能力,并通过 `inject` 依赖它。
4. [事件](04-events.md):类型化事件、广播分发和 waterfall瀑布式事件的短路行为。
5. [配置](05-config.md):读取 `cordis.yml` 中经过校验的配置,并在输入错误时快速失败
5. [配置](05-config.md):读取 `cordis.yml` 中经过校验的配置,并在输入错误时明确报错
6. [组合与 HMR热模块替换](06-composition-and-hmr.md):把配置文件作为插件树,使用热重载,并诊断始终无法加载的插件。
7. [进入 harness](07-into-the-harness.md):基于真实的 harness 服务注册一个可由模型调用的工具。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/approval.md
approval.md: f1889b25e2bbbcb157b0bced070b1f157a867504
approval.zh.md: c460ec2cb847a9e9a3e772987830b58e93fc3715
approval.zh.md: 48222991312f9ae97c9249f1232b261d2393d282

View File

@@ -2,13 +2,13 @@
[English](approval.md) | 中文
[dsh-user-approval](../../packages/ui/user-approval) 的用户审批 seam 回答一个问题:这个具体操作是否可以继续?它拥有共享的请求/结果词汇、`ctx.approval` 分发服务、`approval/request` 应答者 waterfall瀑布式事件、仅记录日志的审计事件对以及按会话的 `ask`/`never` 策略。UI 通道可以提供人类应答者;[ACPAgent Client Protocol自动化桥接层](../../packages/acp/acp)为其拥有的 agent 提供一次性机器决策。调用方如 [dsh-tools](../../packages/core/tools) 和 [dsh-tool-bash](../../packages/bash/tool-bash) 消费闭合的结果,除非结果为 `allowed-once`,否则一律拒绝。
[dsh-user-approval](../../packages/ui/user-approval) 的用户审批 seam 回答一个问题:这个具体操作是否可以继续?它拥有共享的请求/结果词汇、`ctx.approval` 分发服务、`approval/request` 应答者 waterfall瀑布式事件、仅记录日志的审计事件对以及按会话的 `ask`/`never` 策略。UI 通道可以提供人类应答者;[ACPAgent Client Protocol自动化桥接层](../../packages/acp/acp)为其拥有的 agent(智能体)提供一次性机器决策。调用方如 [dsh-tools](../../packages/core/tools) 和 [dsh-tool-bash](../../packages/bash/tool-bash) 消费闭合的结果,除非结果为 `allowed-once`,否则一律拒绝。
源码:[`packages/ui/user-approval/src/index.ts`](../../packages/ui/user-approval/src/index.ts)
## 标识与结果
每个请求都会获得一个全新的 `ApprovalRequestId`。该品牌类型将 `approval/asked``approval/decided` 审计事件配对,同时不会让审批 id 与工具调用 id 或 agent(智能体)/会话 id 互换。
每个请求都会获得一个全新的 `ApprovalRequestId`。该品牌类型将 `approval/asked``approval/decided` 审计事件配对,同时不会让审批 id 与工具调用 id 或 agent/会话 id 互换。
```ts type-equiv
/**
@@ -18,7 +18,7 @@
type ApprovalRequestId = Branded<'ApprovalRequestId'>
```
`ApprovalOutcome` 是闭合的,且默认拒绝。`allowed-once` 仅授权所询问的那一个操作;调用方对 `rejected`、`cancelled` 和 `unavailable` 均执行拒绝。缺失、无所有权、抛异常或不合规的应答者会产生 `unavailable`,而非放行。
`ApprovalOutcome` 是闭合的,且失败时拒绝。`allowed-once` 仅授权所询问的那一个操作;调用方对 `rejected`、`cancelled` 和 `unavailable` 均执行拒绝。缺失、不负责该请求、抛异常或不合规的应答者会产生 `unavailable`,而非放行。
```ts type-equiv
/**
@@ -84,6 +84,6 @@ interface ApprovalRequest {
## 分发与审计
`ctx.approval.request(req)` 要求发起请求的会话处于一个打开的轮次内。它追加 `approval/asked`,获取一个结果,追加对应的 `approval/decided`,然后以该结果 resolve。`never` 策略在服务内部、waterfall 分发之前强制执行,因此即使后来以 `prepend` 注册的应答者也无法绕过它。应答者在拥有该请求时返回结果,否则调用 `next()` 委托;第一个应答占据唯一的决策槽位。
`ctx.approval.request(req)` 要求发起请求的会话处于一个尚未结束的轮次内。它追加 `approval/asked`,获取一个结果,追加对应的 `approval/decided`,然后以该结果完成。`never` 策略在服务内部、waterfall 分发之前强制执行,因此即使后来以 `prepend` 注册的应答者也无法绕过它。应答者在拥有该请求时返回结果,否则调用 `next()` 委托;第一个应答占据唯一的决策槽位。
审计事件仅写入日志,不进入模型 transcript文本记录。模型可见的行为是调用方派生的工具结果与当前运行时上下文快照。服务 dispose资源释放时会移除其上下文贡献应答者监听器独立地通过 effect 绑定到其所属插件。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/bash.md
bash.md: 3747244662301a256e12037ea67c21017b5ac2c5
bash.zh.md: 9927aa8d51ee410d70bed7a2d00e40061b499e15
bash.zh.md: 17dba2af280f9e329155036c95fa70a983eaae11

View File

@@ -2,7 +2,7 @@
[English](bash.md) | 中文
bash 执行 seam 分为接口([dsh-bash](../../packages/bash/bash)`ctx.bash`)、实现([dsh-bash-local](../../packages/bash/bash-local) 与 [dsh-bash-sandbox](../../packages/bash/bash-sandbox))和消费方([dsh-tool-bash](../../packages/bash/tool-bash),即 `bash` schema。通用后台任务的 id、所有权与控制位于 [tasks.md](tasks.md);本 seam 返回一个不含任务概念的进程句柄。原始进程组机制位于[进程管理器 seam](subprocess.md)之后。
bash 执行 seam 分为接口([dsh-bash](../../packages/bash/bash)`ctx.bash`)、实现([dsh-bash-local](../../packages/bash/bash-local) 与 [dsh-bash-sandbox](../../packages/bash/bash-sandbox))和消费方([dsh-tool-bash](../../packages/bash/tool-bash),即 `bash` schema。通用后台任务的 task id、所有权与控制位于 [tasks.md](tasks.md);本 seam 返回一个不含任务概念的进程句柄。原始进程组机制位于[进程管理器 seam](subprocess.md)之后。
源码:[`packages/bash/bash/src/types.ts`](../../packages/bash/bash/src/types.ts)
@@ -12,7 +12,7 @@ bash 执行 seam 分为接口([dsh-bash](../../packages/bash/bash)`ctx.bash
## 请求与规格:`resolve()` 拆分
该 seam 将**面向模型/插件的请求**`workdir`/`timeoutMs`/`stdoutMaxBytes` 可选,由配置或请求策略补全)与执行器实际使用的**完全解析后的 spec**(这些字段均为必填)分开。工具层在二者之间调用 `ctx.bash.resolve(request)`——这具体落实了仓库的「包package seam 上显式优于隐式」规则:`BashExecSpec` 的读者不必猜测工作目录或输出预算来自何处。
该 seam 将**面向模型/插件的请求**`workdir`/`timeoutMs`/`stdoutMaxBytes` 可选,由配置或请求策略补全)与执行器实际使用的**完全解析后的 spec**(这些字段均为必填)分开。工具层在二者之间调用 `ctx.bash.resolve(request)`——这具体落实了仓库的「包 seam 上显式优于隐式」规则:`BashExecSpec` 的读者不必猜测工作目录或输出预算来自何处。
```ts type-equiv
/**
@@ -98,13 +98,13 @@ interface BashExecSpec {
}
```
`stdin` 和 `env` 是受信任的进程内插件输入,不由 `dsh-tool-bash` 暴露。本地执行器会先清除环境中的凭据,再合并调用方显式提供的 env。见 [bash-stdin-env Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)。
`stdin` 和 `env` 是受信任的进程内插件输入,不由 `dsh-tool-bash` 暴露。本地执行器会先清除环境中的凭据,再合并调用方显式提供的 env。见 [bash-stdin-env Agent Note](../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)。
`stdoutMaxBytes` 同样仅供受信任插件使用。它让前台消费方能在有界解析预算内请求完整 stdout而不会改变 stderr、后台任务或面向模型的 bash 工具的常规输出上限。
## 前台运行:`BashRunResult`
一次已完成(或被终止)的前台运行的结果。正交的结果**独立报告**:一个进程可以同时超时并以退出码 0 退出(因为它捕获了信号),因此 `timedOut`、`aborted`、`signal` 和 `exitCode` 各自独立为一个字段;调用方永远不会把一次被断的运行误读为干净的成功。
一次已完成(或被终止)的前台运行的结果。正交的结果**独立报告**:一个进程可以同时超时并以退出码 0 退出(因为它捕获了信号),因此 `timedOut`、`aborted`、`signal` 和 `exitCode` 各自独立为一个字段;调用方永远不会把一次被提前中断的运行误读为正常成功。
```ts type-equiv
/** The outcome of one completed (or killed) foreground run. */
@@ -166,7 +166,7 @@ interface BashSandboxInfo {
## 后台进程:`BashProcess`
`start()` 返回不含 id 或所有者的句柄。`dsh-tool-bash` 将它适配为 `ctx.tasks.start()` 钩子;随后由通用运行时拥有任务标识与生命周期。`done` 在进程关闭时 resolve 且绝不 reject;进程结束后仍可读取,并且沙箱事实会在 `done` resolve 前写入。
`start()` 返回不含 id 或所有者的句柄。`dsh-tool-bash` 将它适配为 `ctx.tasks.start()` 钩子;随后由通用运行时拥有任务标识与生命周期。`done` 在进程关闭时完成且绝不被拒绝;进程结束后仍可读取,并且沙箱事实会在 `done` 完成前写入。
```ts type-equiv
/**
@@ -200,7 +200,7 @@ interface BashProcess {
}
```
`readOutput()` 返回增量 delta 与 spill 恢复事实
`readOutput()` 返回增量内容与 spill 恢复信息
```ts type-equiv
/** One incremental {@link BashProcess.readOutput} read. */

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/code-runtime.md
code-runtime.md: 64de3c45d4f1d1d981daa6c6f074abb667e0aa52
code-runtime.zh.md: 4b14aeb2183010e8140540258ce8109df9f59910
code-runtime.zh.md: daf07aaf613852a6c4a7b1aff152fcc61052fbca

View File

@@ -2,13 +2,13 @@
[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 基础设计](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)和[类型化返回契约](../../.agents/notes/implemented/feature/2026-07-20-code-mode-typed-tool-returns.md)。
代码执行 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md):其接口([dsh-code-runtime](../../packages/code-runtime/code-runtime)`ctx.codeRuntime`使用宿主提供的异步绑定运行一段模型编写的程序,并报告其打印内容与返回值。代码执行是**一项可选能力**,不属于 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)
## 运行:请求进,结果出
`CodeRunRequest` 携带**运行时所需的一切**。按照「包package边界处显式优于隐式」的规则,默认值(时间预算、输出上限)来自实现的已校验配置,绝不是 `run()` 内部隐藏的 `??`
`CodeRunRequest` 携带**运行时要处理的一切内容**。按照「包边界处显式优于隐式」的规则,默认值(时间预算、输出上限)来自实现的已校验配置,绝不是 `run()` 内部隐藏的 `??`
```ts type-equiv
/**
@@ -36,7 +36,7 @@ interface CodeRunRequest {
}
```
结果将错误报告为一个**字段**,而 `run()` 的 rejection。报告失败的程序是调用方的职责,不走异常路径(与 `BashExecutor.run` 的 resolve-on-failure 契约一致):
结果将错误报告为一个**字段**,而不是让 `run()` 返回被拒绝的 Promise。报告程序失败是调用方的职责,不走异常路径(与 `BashExecutor.run` 失败时仍正常完成的契约一致):
```ts type-equiv
/**
@@ -144,4 +144,4 @@ interface CodeRunFailure {
## 服务
`CodeRuntime``ctx.codeRuntime`,抽象服务,定义于 [`packages/code-runtime/code-runtime/src/index.ts`](../../packages/code-runtime/code-runtime/src/index.ts))由 `run(request)` 加两个只读描述符组成:`language`(程序必须使用的语言,`'typescript'` 是已知值;生成语言相关展示的消费方据此切换,遇到无法展示的语言时应显式报错)和 `isolation`(执行基底,`'worker-thread'`、`'process'`、`'container'`;仅为诊断标签,**不构成安全承诺**)。实现必须保证各次运行彼此隔离(无跨运行状态),并在 dispose资源释放时等待系统完全停稳teardown 完成前,进行中的运行已终止并等待结束
`CodeRuntime``ctx.codeRuntime`,抽象服务,定义于 [`packages/code-runtime/code-runtime/src/index.ts`](../../packages/code-runtime/code-runtime/src/index.ts))由 `run(request)` 加两个只读描述符组成:`language`(程序必须使用的语言,`'typescript'` 是已知值;生成语言相关展示的消费方据此切换,遇到无法展示的语言时应显式报错)和 `isolation`(执行基底,`'worker-thread'`、`'process'`、`'container'`;仅为诊断标签,**不构成安全承诺**)。实现必须保证各次运行彼此隔离(无跨运行状态),并在 dispose资源释放时等待系统完全停稳teardown 要等到所有进行中的运行已终止并结算后才完成

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/commands.md
commands.md: 056c775f4c2e1586447db11821e5c7d56be01881
commands.zh.md: 1a51305df356d8becf8c5517704dc375cdb8b585
commands.zh.md: 6339b3e87c04eac0fd140a7cab57b2ad18bbf5c2

View File

@@ -2,7 +2,7 @@
[English](commands.md) | 中文
[`dsh-commands`](../../packages/ui/commands) 的用户命令 seam。交互式适配器用它发现插件拥有的命令并针对确切的 agent智能体直接执行这些命令而不创建模型消息。[命令 Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md) 负责分发与生命周期的决策依据;[packageREADME](../../packages/ui/commands/README.md) 负责组合方式与限制。
[`dsh-commands`](../../packages/ui/commands) 的用户命令 seam。交互式适配器用它发现插件拥有的命令并针对确切的 agent智能体直接执行这些命令而不创建模型消息。[命令 Agent Note](../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md) 负责分发与生命周期的决策依据;[ README](../../packages/ui/commands/README.md) 负责组合方式与限制。
来源:[`packages/ui/commands/src/index.ts`](../../packages/ui/commands/src/index.ts)
@@ -38,7 +38,7 @@ interface CommandDefinition {
## 调用与结果
适配器拥有取消操作,并传入确切的目标 agent。`rawInput` 紧接在解析后的名称之后,并保留适配器传入的分隔符与后缀。结果会直接呈现给 UI而不是工具结果或会话事件。
取消由适配器负责,适配器会传入确切的目标 agent。`rawInput` 紧接在解析后的名称之后,并保留适配器传入的分隔符与后缀。结果会直接呈现给 UI而不是工具结果或会话事件。
```ts type-equiv
/** Invocation passed to one registered command handler. */

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/compaction.md
compaction.md: 070adc65fb25b2536c88701c375cc0b2a5308559
compaction.zh.md: 9167882f63b2931ba3ce49697e0c87164394af89
compaction.zh.md: 410f1d5dc505e4baa55667e4e8f74542a4380f77

View File

@@ -2,7 +2,7 @@
[English](compaction.md) | 中文
压缩 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md),与 bash 一样分为接口([dsh-compact](../../packages/compact/compact)`ctx.compact`)、实现(例如 [dsh-compact-basic](../../packages/compact/compact-basic) 后端)和面向用户的消费方([dsh-command-compact](../../packages/compact/command-compact))。压缩是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此而非 [core.md](core.md) 中。基于 tokenizer 或模板的后端是实现同一接口的兄弟包package。与 bash 不同,该接口必然依赖 `dsh-session``dsh-llm`:其动词作用于 agent 所有的 `Session`,而其持久摘要事件使用 `ContentBlock` 词汇(见[压缩能力 seam Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md))。
压缩 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md),与 bash 一样分为接口([dsh-compact](../../packages/compact/compact)`ctx.compact`)、实现(例如 [dsh-compact-basic](../../packages/compact/compact-basic) 后端)和面向用户的消费方([dsh-command-compact](../../packages/compact/command-compact))。压缩是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此而非 [core.md](core.md) 中。基于 tokenizer 或模板的后端是实现同一接口的兄弟包。与 bash 不同,该接口必然依赖 `dsh-session``dsh-llm`:其动词作用于 agent 所有的 `Session`,而其持久摘要事件使用 `ContentBlock` 词汇(见[压缩能力 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md))。
源码:[`packages/compact/compact/src/types.ts`](../../packages/compact/compact/src/types.ts)

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/filesystem.md
filesystem.md: 110c1fd428b15c5094f9dcc94050cad61c324373
filesystem.zh.md: aca450364c05c6f756c36fccc11be7246767f3a4
filesystem.zh.md: 010ec22a5d4a29555425c3deede08b317cba7004

View File

@@ -69,7 +69,7 @@ interface FsInfo {
}
```
`lstat` 是路径级、不跟随链接的元数据原语。它接收路径而不是 `FsTarget`,因为 `resolve` 会有意跟随 symlink 以产生稳定标识;需要检查信任边界的消费方可以先调用 `lstat`,在解析前拒绝 `symlink`。
`lstat` 是路径级、不跟随链接的元数据原语。它接收路径而不是 `FsTarget`,因为 `resolve` 会有意跟随 symlink 以产生稳定标识;需要检查信任边界的消费方可以先调用 `lstat`,在解析前拒绝 `symlink`。
```ts type-equiv
/**
@@ -111,7 +111,7 @@ interface FsDirEntry {
## 写入与编辑守卫(提供方 seam
`writeText` 和 `editText` 的版本守卫都是可选的:省略执行无条件裸提供方变更,提供它则启用守卫。`writeText` 的守卫是 `FsWriteIntent``createIfAbsent` 在目标缺失时创建,目标已存在时以 `FS_NOT_OBSERVED` 拒绝;`replaceIfVersion` 仅在目标存在且版本匹配时替换,否则报 `FS_STALE_VERSION`。省略 `expected` 则无条件创建或覆盖。联合类型本身只包含两种有守卫的意图;「无守卫」通过省略表达,因此 write 和 edit 共享同一个对称的 `expected?` 形状。
`writeText` 和 `editText` 的版本守卫都是可选的:省略守卫时执行无条件裸提供方变更,提供守卫时则执行相应的条件检查。`writeText` 的守卫是 `FsWriteIntent``createIfAbsent` 在目标缺失时创建,目标已存在时以 `FS_NOT_OBSERVED` 拒绝;`replaceIfVersion` 仅在目标存在且版本匹配时替换,否则报 `FS_STALE_VERSION`。省略 `expected` 则无条件创建或覆盖。联合类型本身只包含两种有守卫的意图;「无守卫」通过省略表达,因此 write 和 edit 共享同一个对称的 `expected?` 形状。
```ts type-equiv
/**
@@ -179,11 +179,11 @@ interface FsEditOutcome {
`dsh-fs` 拥有三个事件,由工具分发、策略插件监听,使发射方(`dsh-tool-fs`)与监听方(`dsh-fs-policy`)共享词汇,而发射方无需依赖策略插件。它们只携带 `dsh-fs` 词汇加一个不透明的 `object` actor不含面向模型的概念也不含 agent/会话所有者结构。
`fs/write-intent` 与 `fs/edit-intent` 是**单槽决策 waterfall**:工具分发时附带一个默认 thunk返回 `undefined`,即裸提供方),监听方完全决策而不调用 `next()`。该槽按注册顺序先到先得——由策略插件占据是部署约定,而非强制不变式。`fs/observed` 是一个即发即弃的记录事件,通过普通 `ctx.emit` 分发;其监听方必须是同步的、仅产生副作用,因为工具不守卫该 emit——抛异常的监听方会一次已成功的变更上表现为工具的 `isError` 结果。生成的目录在 [events.md](../cordis-catalog/events.md) 中展示确切签名。
`fs/write-intent` 与 `fs/edit-intent` 是**单槽决策 waterfall**:工具分发时附带一个默认 thunk返回 `undefined`,即裸提供方),监听方完全决策而不调用 `next()`。该槽按注册顺序先到先得——由策略插件占据是部署约定,而非强制不变式。`fs/observed` 是一个即发即弃的记录事件,通过普通 `ctx.emit` 分发;其监听方必须是同步的、仅产生副作用,因为工具不会捕获该 emit 抛出的异常——抛异常的监听方会导致工具为一次已成功的变更返回 `isError` 结果。生成的目录在 [events.md](../cordis-catalog/events.md) 中展示确切签名。
## 执行上下文(策略插件)
策略插件只需要足够的执行上下文,通过收窄 `fs/*` 事件携带的不透明 `object` actor 来推导观测状态的所有者。`ToolExecution` 满足此形状,因此 `dsh-tool-fs` 将其执行对象作为 actor 直接传递,而无需让 `dsh-fs-policy` 导入工具、agent 或会话包package
策略插件只需要足够的执行上下文,通过收窄 `fs/*` 事件携带的不透明 `object` actor 来推导观测状态的所有者。`ToolExecution` 满足此形状,因此 `dsh-tool-fs` 将其执行对象作为 actor 直接传递,而无需让 `dsh-fs-policy` 导入工具、agent 或会话包。
```ts type-equiv
/**

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/goal.md
goal.md: 704a93320cc38d1b9400edc2d9ad2342bc11dccd
goal.zh.md: b2e083843a70823bf6a6b43e046b1f38f4e11e22
goal.zh.md: 7532bf888651f647686de456e58f54855941f3c0

View File

@@ -2,7 +2,7 @@
[English](goal.md) | 中文
事件溯源目标领域及其策略消费方共享的类型。[目标领域 Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-19-persisted-same-session-goal-domain.md)负责记录持久化与激活决策;本页记录 [`packages/goal/goal/src/types.ts`](../../packages/goal/goal/src/types.ts) 中的字面形态。
事件溯源目标领域及其策略消费方共享的类型。[目标领域 Agent Note](../../.agents/notes/implemented/feature/2026-07-19-persisted-same-session-goal-domain.md)负责记录持久化与激活决策;本页记录 [`packages/goal/goal/src/types.ts`](../../packages/goal/goal/src/types.ts) 中的字面形态。
## 标识与生命周期
@@ -71,7 +71,7 @@ interface GoalView extends GoalSnapshot {
## 持久变更
每次变更都是 Round 编号为 0、来源为目标的 `user/message`,其元数据要么是完整快照,要么是清除墓碑。版本、元数据、目标来源和逐字渲染内容共同构成一项回放不变量。
每次变更都是 Round 编号为 0、来源为目标的 `user/message`,其元数据要么是完整快照,要么是清除墓碑。版本、元数据、目标来源和原样渲染内容共同构成一项回放不变量。
```ts type-equiv
/** Full-snapshot goal mutation retained in a model-visible context event. */
@@ -114,7 +114,7 @@ interface GoalMessageSource {
## 请求与通知
创建操作会区分调用方省略的值与部署选择`create()` 会在内部解析后者。编辑是局部替换,其运行时校验器要求至少提供一个字段。每条变更通知都会携带获准的操作和确切修订号;清除操作不带 `goal`。
创建操作会区分调用方省略字段与采用部署配置值这两种情况`create()` 会在内部解析后者。编辑是局部替换,其运行时校验器要求至少提供一个字段。每条变更通知都会携带获准的操作和确切修订号;清除操作不带 `goal`。
```ts type-equiv
/** Input whose omitted round cap is resolved by the service configuration. */
@@ -144,4 +144,4 @@ interface GoalChanged {
## 服务行为
[`GoalService`](../../packages/goal/goal/src/index.ts) 解析创建默认值、执行严格回放折叠、校验确切的活跃 agent 身份、以比较并设置方式执行变更、叠加延迟注入,并发出 `goal/changed` 通知;监听器故障会被隔离。包 [README](../../packages/goal/goal/README.md) 负责记录可调用契约和面向模型的契约。
[`GoalService`](../../packages/goal/goal/src/index.ts) 解析创建默认值、执行严格回放折叠、校验确切的活跃 agent 身份、以比较并设置方式执行变更、叠加待处理的注入变更,并发出 `goal/changed` 通知;监听器故障会被隔离。包 [README](../../packages/goal/goal/README.md) 负责记录可调用契约和面向模型的契约。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/llm-streaming.md
llm-streaming.md: 28f775f81049d3aa0198e75fbb6c4544302e772d
llm-streaming.zh.md: 76282018f0cfa119199e221985bd09eaac69c839
llm-streaming.zh.md: db6b28acb844033025b3c5ffef075c9c3153151e

View File

@@ -75,7 +75,7 @@ interface LlmFailure {
## `AppIdentity`:应用归属
每个适配器都会向提供方发送的静态公开应用标识([`packages/llm/llm/src/attribution.ts`](../../packages/llm/llm/src/attribution.ts))。`attributionHeaders(identity?)` 只把它映射到标准 `User-Agent` header该契约有意不支持 OpenRouter 特有的应用归属 header。默认 `APP_IDENTITY` 从包package manifest元数据清单获取版本每个字段都是公开产品事实——不含 secret、路径、会话 id 或逐用户标识,且任何逐请求信息都不得影响这些值。设计理由见[强制 `User-Agent` 归属](../../.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md)。
每个适配器都会向提供方发送的静态公开应用标识([`packages/llm/llm/src/attribution.ts`](../../packages/llm/llm/src/attribution.ts))。`attributionHeaders(identity?)` 只把它映射到标准 `User-Agent` header该契约有意不支持 OpenRouter 特有的应用归属 header。默认 `APP_IDENTITY` 从包 manifest元数据清单获取版本每个字段都是公开产品事实——不含 secret、路径、会话 id 或逐用户标识,且任何逐请求信息都不得影响这些值。设计理由见[强制 `User-Agent` 归属](../../.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md)。
```ts type-equiv
/**

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/lsp.md
lsp.md: 62b133cbfdf521e067c56355664d7514a613397f
lsp.zh.md: d7000970ec9114bcdad40a39d2712d48b9865529
lsp.zh.md: 51a19a51a8ad92e744cb920a51f3214f68ae0036

View File

@@ -2,7 +2,7 @@
[English](lsp.md) | 中文
LSP seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md):它在单一 `ctx.lsp` 服务上公开语义代码导航,并拆分到多个包package:接口([dsh-lsp](../../packages/lsp/lsp)`ctx.lsp` + 提供方注册表)、通用实现([dsh-lsp-local](../../packages/lsp/lsp-local),经过配置的 stdio 语言服务器宿主)和消费方([dsh-tool-lsp](../../packages/lsp/tool-lsp),即 `lsp` 工具 schema。LSP 是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此而非 [core.md](core.md) 中。更换提供方不会改变模型请求导航的方式。
LSP seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md):它在单一 `ctx.lsp` 服务上公开语义代码导航,并拆分到多个包:接口([dsh-lsp](../../packages/lsp/lsp)`ctx.lsp` + 提供方注册表)、通用实现([dsh-lsp-local](../../packages/lsp/lsp-local),经过配置的 stdio 语言服务器宿主)和消费方([dsh-tool-lsp](../../packages/lsp/tool-lsp),即 `lsp` 工具 schema。LSP 是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此而非 [core.md](core.md) 中。更换提供方不会改变模型请求导航的方式。
源文件:[`packages/lsp/lsp/src/types.ts`](../../packages/lsp/lsp/src/types.ts)
@@ -113,7 +113,7 @@ type LspQueryResult =
## 提供方与服务
每个提供方拥有一个稳定的品牌化 `id`,以及一份互斥的、小写且以点开头的扩展名映射。`registerProvider` 会原子留 id 和每个扩展名:注册无效或冲突时不发布任何内容;其 disposer 会释放所有保留项。每次查询独立选择提供方,且选择与顺序无关;没有匹配项时抛出 `LspError` `LSP_UNAVAILABLE`。该 seam 不公开协议类型、进程或文档控制,也不提供通用 JSON-RPC 逃生口。
每个提供方拥有一个稳定的品牌化 `id`,以及一份互斥的、小写且以点开头的扩展名映射。`registerProvider` 会原子留 id 和每个扩展名:注册无效或冲突时不发布任何内容;其 disposer 会释放所有保留项。每次查询独立选择提供方,且选择与顺序无关;没有匹配项时抛出 `LspError` `LSP_UNAVAILABLE`。该 seam 不公开协议类型、进程或文档控制,也不提供通用 JSON-RPC 逃生口。
```ts type-equiv
/**

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/pty.md
pty.md: 97e1e662d1128ab0555e34f8284cf69d7d9d0d1a
pty.zh.md: b17bc0d2c7bdb2a980df36824bd360ea975967f5
pty.zh.md: a57f7448274de583dbb110ba9499e15dfb4de5f9

View File

@@ -2,11 +2,11 @@
[English](pty.md) | 中文
PTY 后端、`ctx.pty` 与面向模型的消费方共享的类型。[持久 PTY Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md) 负责记录决策依据;本页记录来自 [`packages/pty/pty/src/types.ts`](../../packages/pty/pty/src/types.ts) 的跨包package词汇。
PTY 后端、`ctx.pty` 与面向模型的消费方共享的类型。[持久 PTY Agent Note](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md) 负责记录决策依据;本页记录来自 [`packages/pty/pty/src/types.ts`](../../packages/pty/pty/src/types.ts) 的跨包词汇。
## 标识与就绪
`PtySessionId` 是由服务铸造的品牌化 id。可选名称是拥有者本地的显示元数据授权比较的是确切的所属 `Agent`,而不是名称或猜测的 id。
`PtySessionId` 是由服务铸造的品牌化 id。可选名称是拥有者本地的显示元数据授权比较的是拥有该会话的确切 `Agent`,而不是名称或猜测的 id。
`PtyWaitReason` 说明一次发送为何返回。它与 `PtySessionStatus` 无关:一次发送可能因静默或超时而返回,但顶层 shell 仍然存活;`session_exit` 表示该 shell 已退出,而不是某个任意的前台子进程已退出。
@@ -24,7 +24,7 @@ type PtySessionStatus =
## 后端与活跃会话
后端负责某个已注册类型的启动方式和就绪检测。`PtyService` 只在初始化成功后才发布返回的会话,随后负责 id 授权与清理。无法清理部分启动资源后端会以 `PtyBackendCleanupError` 拒绝,从而让资源释放流程保留清理失败,同时不替换调用方的取消原因。后端会话拥有终端状态,并负责使已捕获资源完全停稳。
后端负责启动某种已注册类型的会话并检测其就绪状态。`PtyService` 只在初始化成功后才发布返回的会话,随后负责 id 授权与清理。无法清理部分启动资源时,后端会以 `PtyBackendCleanupError` 拒绝启动;这样,资源释放流程既能保留清理失败,也不会用它替换调用方的取消原因。后端会话拥有终端状态,并负责已捕获资源完全停稳。
```ts type-equiv
/** Replaceable provider for one PTY session type. */
@@ -58,7 +58,7 @@ interface PtyBackendSession {
## 发送与保留输出
一个活跃会话同时只接受一个活动发送。该操作向通用后台任务公开一个消费式输出游标,并向前台调用方公开一个最终结果。`PtyReadResult` 则为有界的会话 scrollback 单独分页。
一个活跃会话同时只接受一个活动发送。该操作向通用后台任务提供读取后即推进的输出游标,并向前台调用方提供最终结果。`PtyReadResult` 则为有界的会话 scrollback 单独分页。
```ts type-equiv
/** Live backend-owned send; exactly one may be active per PTY session. */
@@ -88,4 +88,4 @@ interface PtySendResult {
## 归属与持久性
`PtyService` 会将一项等待完成的清理附加到确切的拥有者作用域拒绝其他拥有者的操作并让会话在后端或工具插件重载期间保持存活。PTY 状态与原始字节仍局限在进程内。模型输入与有界返回输出通过现有 `tool/call`、`tool/result` 和任务结果路径持久保存,而不是重复记录 PTY 会话事件。
`PtyService` 会将一项等待完成的清理附加到确切的拥有者作用域拒绝其他拥有者的操作并让会话在后端或工具插件重载期间保持存活。PTY 状态与原始字节仍局限在进程内。模型输入与有界返回输出通过现有 `tool/call`、`tool/result` 和任务结果路径持久保存,而不是重复记录 PTY 会话事件。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/sandbox.md
sandbox.md: 9bc05fa06f22fdc9ac9e8aacd482c1e7c2f2edec
sandbox.zh.md: 9a52f126758fe0e7988715c7824e963bd6e6ea84
sandbox.zh.md: ff44db13115073f3a77d2bda575cc91ec41da418

View File

@@ -2,7 +2,7 @@
[English](sandbox.md) | 中文
[dsh-sandbox](../../packages/sandbox/sandbox) 的进程沙箱 seam 将与宿主共享文件系统和内核的子进程 argv 包装在文件效果策略中,而不将消费方耦合到特定平台运行器。[dsh-sandbox-local](../../packages/sandbox/sandbox-local) 提供 Linux bwrap/Landlock 与 macOS Seatbelt 后端;[dsh-bash-sandbox](../../packages/bash/bash-sandbox) 是第一个消费方。容器、microVM 和远程执行是完整能力 seam 的兄弟实现,而非 `ctx.sandbox` 的提供方。
[dsh-sandbox](../../packages/sandbox/sandbox) 的进程沙箱 seam 将与宿主共享文件系统和内核的子进程 argv 包装在文件效果策略中,而不将消费方耦合到特定平台运行器。[dsh-sandbox-local](../../packages/sandbox/sandbox-local) 提供 Linux bwrap/Landlock 与 macOS Seatbelt 后端;[dsh-bash-sandbox](../../packages/bash/bash-sandbox) 是第一个消费方。容器、microVM 和远程执行是完整能力 seam 的同级实现,而非 `ctx.sandbox` 的提供方。
源码:[`packages/sandbox/sandbox/src/index.ts`](../../packages/sandbox/sandbox/src/index.ts)
@@ -27,7 +27,7 @@ type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access'
type ConfinedSandboxMode = Exclude<SandboxMode, 'danger-full-access'>
```
强制执行程度是一个报告事实。`full` 表示后端管控了该模式承诺的所有文件效果;`partial` 表示活跃后端或较旧的内核 ABI 仅管控其中一个子集,因此要求绝对保证的消费方必须拒绝或向上暴露这一区别。
强制执行完整性是后端报告事实。`full` 表示后端管控了该模式承诺的所有文件效果;`partial` 表示活跃后端或较旧的内核 ABI 仅管控其中一个子集,因此要求绝对保证的消费方必须拒绝或向上暴露这一区别。
```ts type-equiv
/**
@@ -68,7 +68,7 @@ interface SandboxPolicyRequest {
}
```
只有受约束的执行会到达 `ctx.sandbox`提供方策略在保留同一 root 的同时收窄模式。这使并发会话、消费方与一次性提权重试可以向同一提供方请求不同边界,而无需改变提供方状态。
只有受约束的执行会到达 `ctx.sandbox`传给提供方策略在保留同一 root 的同时收窄模式。这使并发会话、消费方与一次性提权重试可以向同一提供方请求不同边界,而无需改变提供方状态。
```ts type-equiv
/**
@@ -87,7 +87,7 @@ interface SandboxPolicy extends SandboxExecutionPolicy {
## 包装后的 argv 与分类方言
`ConfinedArgv` 是消费方实际 spawn 的内容。除了替换后的 argv它还携带后端的强制执行事实和两种正交的 stderr 方言。`denialSignatures` 用于识别沙箱正常工作时被隔离命令被阻止的情况。`runnerFailureSignatures` 用于识别沙箱运行器在执行命令之前拒绝或失败的情况;消费方应先检查后者,将其作为沙箱基础设施故障上报,而非普通任务失败。
`ConfinedArgv` 是消费方实际 spawn 的内容。除了替换后的 argv它还携带后端的强制执行事实和两种正交的 stderr 方言。`denialSignatures` 用于识别沙箱正常工作、受限命令被阻止的情况。`runnerFailureSignatures` 用于识别沙箱运行器在执行命令之前拒绝或失败的情况;消费方应先检查后者,将其作为沙箱基础设施故障上报,而非普通任务失败。
```ts type-equiv
/**
@@ -118,7 +118,7 @@ interface ConfinedArgv {
}
```
运维人员配置的本地运行器必须为自身的 pre-exec 拒绝方言提供至少一条 `runnerFailureSignatures` 条目;提供方会自动添加外层 shell 的 missing 和 unexecutable 形式。这使得可执行的自定义运行器拒绝其 profile 的情况能够与被包装命令以相同状态码退出的情况区分开来。
运维人员配置的本地运行器必须为自身的 pre-exec 拒绝方言提供至少一条 `runnerFailureSignatures` 条目;提供方会自动添加外层 shell 报告的「命令不存在」和「不可执行」形式。这使得可执行的自定义运行器拒绝其 profile 的情况能够与被包装命令以相同状态码退出的情况区分开来。
## 提供方与 fail-closed 错误

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/scope.md
scope.md: 73a697f2843293daffff85dabf4656346f7dcd04
scope.zh.md: f3c591da2befdcff69d89ad0667653392111fb8e
scope.zh.md: 06e2528eca958102110caa3a7d370e0778c0f7b2

View File

@@ -2,7 +2,7 @@
[English](scope.md) | 中文
[scope 包package](../../packages/core/scope)提供 identity、carrier 与 scoped-layer 词汇,使同一注册上下文同时代表逐 agent智能体可见性和共享生命周期所有权。它是库原语而不是 Cordis 服务;生命周期设计理由由 [agent-scope 运行时设计 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.md#scope-routing-one-opaque-key-selects-one-layer)规定,注册表层决策由[共享存储 Agent Note](../../.agents/notes/implemented/architecture/2026-07-12-scoped-layers-store.md)规定,可调用 API 与过滤语义则由包 [README](../../packages/core/scope/README.md)规定。
[scope 包](../../packages/core/scope)提供身份、载体与作用域层词汇,使同一注册上下文同时表达每个 agent智能体可见性和共享生命周期所有权。它是库原语,而不是 Cordis 服务;生命周期设计理由由 [agent-scope 运行时设计 Agent Note](../../.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.md#scope-routing-one-opaque-key-selects-one-layer)规定,注册表层决策由[共享存储 Agent Note](../../.agents/notes/implemented/architecture/2026-07-12-scoped-layers-store.md)规定,可调用 API 与过滤语义则由包 [README](../../packages/core/scope/README.md)规定。
源码:[`packages/core/scope/src/index.ts`](../../packages/core/scope/src/index.ts) 与 [`packages/core/scope/src/store.ts`](../../packages/core/scope/src/store.ts)。
@@ -28,7 +28,7 @@ type Scoped<T extends object> = object & { readonly [ScopedBrand]: T }
## 拥有所有权的注册上下文
`Scope` 将带标签的注册上下文与两个拆卸接口配对。`rawDispose` 保留有序复合 effect 所需的精确 Cordis disposer 身`dispose()` 是面向直接调用方和竞态调用方的公共停稳边界。
`Scope` 将带标签的注册上下文与两个拆卸接口配对。`rawDispose` 保留有序复合 effect 所需的 Cordis disposer 身;`dispose()` 是面向直接调用方和竞态调用方的公共完全停稳边界。
```ts type-equiv
/** A minted registration scope and its quiescent disposal boundaries. */
@@ -54,6 +54,6 @@ interface ScopeLayer {
}
```
`ScopedLayers<L>` 拥有立即创建的全局 layer以及惰性创建的确切作用域 layer。读取不会创建 layer`peek(undefined)` 表示没有 overlay,而 `merge()` 会物化按插入顺序排列的全局具名 entry随后是带作用域的 shadow。注册使用同一个上下文表示可见性与 Cordis effect 所有权,在可选通知前收集一个同步 undo,返回 Cordis 的确切 disposer并且只在带作用域 layer 的完整 `ScopeLayer` 为空时回收它。
`ScopedLayers<L>` 拥有立即创建的全局 layer以及惰性创建的确切作用域 layer。读取不会创建 layer`peek(undefined)` 表示不存在作用域覆盖层,而 `merge()` 会依次物化按插入顺序排列的全局具名条目和带作用域的遮蔽项。注册使用同一个上下文表示可见性与 Cordis effect 所有权,在可选通知前取得一个同步撤销函数,返回 Cordis 的原始 disposer并且只在带作用域 layer 的完整 `ScopeLayer` 为空时回收它。
`NamedEntries<V>` 提供按插入顺序的查找与 live iteration,重复错误由调用方所有。`AnonymousEntries<V>` 为每次 append 分配唯一标识,使相等的值仍相互独立。迭代在同一非空 table generation 内保持 live排空 table 会让现有 iterator 与后续插入脱离。两者都返回幂等的确切 entry undo共享的 `EntryValues` 实现接口不公开。
`NamedEntries<V>` 提供按插入顺序的查找和动态迭代,重复错误由调用方处理。`AnonymousEntries<V>` 为每次 append 分配唯一标识,因此值相等的条目仍彼此独立。在同一非空 table 生命周期内迭代器可以观察后续变化table 被清空后,现有迭代器不会再观察后续插入。两者都返回幂等、精确对应相应条目的撤销函数;共享实现接口 `EntryValues` 不对外公开。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/session-query.md
session-query.md: d92af4bac34f7d41457e9e193111c3a53fe8022e
session-query.zh.md: ecf330b0a361ffae352a91c0d35524444936606d
session-query.zh.md: 8070dfda61a2945fca554939f65ae0f8b85078db

View File

@@ -2,13 +2,13 @@
[English](session-query.md) | 中文
本文定义面向优先使用 live 数据的逻辑会话语料库的查询词汇。[接口包package](../../packages/session-query/session-query)负责精确读取、来源优先级、关系追踪、语义提取,以及与提供方无关的过滤器;[SQLite 包](../../packages/session-query/session-query-sqlite)负责具体全文索引的生命周期。
本文定义逻辑会话语料库的查询词汇;当 live 数据存在时,该语料库优先使用 live 数据。[接口包](../../packages/session-query/session-query)负责精确读取、来源优先级、关系追踪、语义提取,以及与提供方无关的过滤器;[SQLite 包](../../packages/session-query/session-query-sqlite)负责具体全文索引的生命周期。
源码:[`packages/session-query/session-query/src/types.ts`](../../packages/session-query/session-query/src/types.ts)
## 逻辑记录
`SessionRecord`语料库列表返回。它独立于克隆后的实时优先 header 暴露源可用性。`SessionEventRecord` 是轻量的原始日志投影;分类使用与 model-history 推导相同的 `foldSurface()` 状态转换。
`SessionRecord`语料库列表返回。它除了克隆的、优先取自 live 源的 header 外,还单独公开各源的可用性。`SessionEventRecord` 是轻量的原始日志投影;分类使用与模型历史推导相同的 `foldSurface()` 状态转换。
```ts type-equiv
/** Whether an event is current model context, replaced context, or raw-log-only. */
@@ -51,7 +51,7 @@ interface SessionSurfaceSnapshot {
}
```
`SessionTitleObservation` 将同样的原子观测规则应用于标题折叠,使授权消费能够验证提供标题的源 header。批量读取会按顺序为每个唯一请求 id 返回一个 `SessionTitleObservationResult`:操作失败只影响对应 id而取消会拒绝整个操作。
`SessionTitleObservation` 将同样的原子观测规则应用于标题折叠,使执行授权检查的消费能够验证提供标题的源 header。批量读取会按顺序为每个唯一请求 id 返回一个 `SessionTitleObservationResult`:操作失败只影响对应 id而取消会拒绝整个操作。
```ts type-equiv
/** Latest folded title bound to the same session-header observation. */
@@ -102,7 +102,7 @@ interface SessionEventRecord {
## 与提供方无关的过滤器和文档
会话和事件过滤器数组内的各项按逻辑与AND组合单个列表子句中的各值按逻辑或OR组合。范围包含两端。事件的 `text` 子句会对提取出的语义文本执行正则表达式扫描搜索文本按字面量处理Unicode 字符不区分大小写,空白字符可灵活匹配;该过程与全文搜索提供方无关。
会话和事件过滤器数组内的各项按逻辑与AND组合单个列表子句中的各值按逻辑或OR组合。范围包含两端。事件的 `text` 子句会对提取出的语义文本执行正则表达式扫描:搜索文本按字面量处理,Unicode 规则执行不区分大小写的匹配,并允许灵活匹配空白字符;该过程与全文搜索提供方无关。
```ts type-equiv
/**
@@ -191,7 +191,7 @@ interface SessionSearchPage<T> {
}
```
与跨会话分组 hit 不同,会话内搜索即使没有命中项,也必须公开观测到的目标 header。
与跨会话分组 hit 不同,会话内搜索结果即使没有命中项,也必须公开搜索时观测到的目标 header。
```ts type-equiv
/** Event-search results bound to the indexed target-session observation. */
@@ -219,7 +219,7 @@ interface SessionSearchHit extends SessionRecord {
## 会话谱系
`SessionLineageTrace` 按由近及远的顺序携带已知 parent并携带一片由直接 descendant 递归嵌套而成的森林。完整性判别字段使已知 root 与缺失 parent 互斥。
`SessionLineageTrace` 按由近及远的顺序携带已知 parent以及由直接 descendant 递归嵌套而成的森林。完整性判别字段使已知 root 与缺失 parent 互斥。
```ts type-equiv
/** Recursive descendant node in a session-lineage trace. */
@@ -292,7 +292,7 @@ interface SessionEventWindow {
## 事件关系
事件追踪会区分位置性的 surface 替换与已记录 provenance。除 `replacementChain` 外,每个 seq 列表都包含直接链接;该链从目标沿直接 replacer 追踪到最终的位置替换。
事件追踪会区分位置替换与日志中记录的来源关系。除 `replacementChain` 外,每个 seq 列表都包含直接链接;该链从目标沿直接 replacer 追踪到最终的位置替换。
```ts type-equiv
/** Request for direct surface and provenance relationships around one event. */

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/session-reference.md
session-reference.md: 5375677f6a1748909743ca76d5191cb9e736a40a
session-reference.zh.md: 8e9abea7ce87e51061813d282e20db951918a650
session-reference.zh.md: 3ff4a1719926bda0a9111482a7778a8c94553370

View File

@@ -2,7 +2,7 @@
[English](session-reference.md) | 中文
结构化的跨会话引用请求与预备消息上下文。[package契约](../../packages/context/session-reference) 负责规范 URI、当前表层投影、标签安全的 JSON 与字节保留、稳定错误和不可信的模型提示词。宿主适配器使用这些类型,而不会把各自 UI 的提及语法传入 agent智能体核心。
结构化的跨会话引用请求与准备后的消息上下文。[包契约](../../packages/context/session-reference) 负责规范 URI、当前表层投影、标签安全的 JSON 与字节保留、稳定错误和不可信的模型提示词。宿主适配器使用这些类型,而不会把各自 UI 的提及语法传入 agent智能体核心。
来源:[`packages/context/session-reference/src/types.ts`](../../packages/context/session-reference/src/types.ts)
@@ -36,9 +36,9 @@ interface SessionReferenceCandidate {
}
```
## 预备消息
## 准备后的消息
备过程保留可读的当前消息内容,并最多返回一个聚合上下文。
备过程保留可读的当前消息内容,并最多返回一个聚合上下文。
```ts type-equiv
/** Direct message content and optional referenced-session context. */
@@ -52,7 +52,7 @@ interface PreparedReferencedMessage {
## 错误
`SessionReferenceError.code` 区分无效配置或输入、自引用、数量限制、源读取失败、预算失败和取消。宿主协议会把这些 code 映射到各自的错误封,无需检查提示词字节。
`SessionReferenceError.code` 区分无效配置或输入、自引用、数量限制、源读取失败、预算失败和取消。宿主协议会把这些 code 映射到各自的错误封,无需检查提示词字节。
```ts type-equiv
/** Stable failure codes exposed to host adapters. */

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/session-title.md
session-title.md: fff1aa1f6be45d0cfc4d7f6a9527ccb93561618f
session-title.zh.md: 73821b07c6be40d10d0961dd79b7c06bcadb7d0b
session-title.zh.md: 77a0a5e94053c94bf84bbc749cb6e260898b5d00

View File

@@ -2,13 +2,13 @@
[English](session-title.md) | 中文
[`@deepseek-ai/dsh-session-title`](../../packages/session-title/session-title) 所拥有的持久后写覆盖标题状态与可选异步提供方词汇。共享 LLM大语言模型辅助组件负责精确的辅助请求记录。各包packageREADME 负责时序、回退、失败与 fork 行为;生成的[持久化日志事件目录](../persistence-catalog.md)负责完整的事件声明。
[`@deepseek-ai/dsh-session-title`](../../packages/session-title/session-title) 所拥有的持久后写覆盖标题状态与可选异步提供方词汇。共享 LLM大语言模型辅助组件负责精确的辅助请求记录。各包 README 负责时序、回退、失败与 fork 行为;生成的[持久化日志事件目录](../persistence-catalog.md)负责完整的事件声明。
源码:[`packages/session-title/session-title/src/index.ts`](../../packages/session-title/session-title/src/index.ts)、[`packages/session-title/session-title-llm/src/index.ts`](../../packages/session-title/session-title-llm/src/index.ts)
## 持久标题状态
提供方生成修订时会记录 `SessionTitleProviderId``SessionTitleEventData` 携带精确的人类消息来源信息,`SessionTitleSnapshot` 则加入 `foldSessionTitle()` 选出的持久事件信封事实
提供方生成修订时会记录 `SessionTitleProviderId``SessionTitleEventData` 携带精确的人类消息来源信息,`SessionTitleSnapshot` 则加入 `foldSessionTitle()` 选出的持久事件封装信息
```ts type-equiv
/** Identifies one session-title provider registration. */
@@ -86,7 +86,7 @@ interface SessionTitleLlmRequestEventData {
## 提供方输入与输出
服务会对截至某一修订的合格消息创建快照。提供方返回的 seq 仅可来自该请求;由服务负责的接受过程会验证顺序、规范化标题、强制执行字节上限并追加来源信息。
服务会对截至某一修订的合格消息创建快照。提供方返回的 seq 仅可来自该请求;由服务负责的接纳流程会验证顺序、规范化标题、强制执行字节上限并追加来源信息。
```ts type-equiv
/** One eligible human text message exposed to title providers. */

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/session.md
session.md: 6369c956df03c0d786db696c208b000300d5bfbc
session.zh.md: c39382b7e6c9b14f91c311cc80526a6fd8898e4c
session.zh.md: 9a9a06ca3d58dfb3ef880868de5a41a009208528

View File

@@ -133,7 +133,7 @@ interface SessionEventMap {
### `TodoItem`:一条待办项
这是 `todo/write` 事件全量列表快照中的单元。它有意保持精简:一行 `content` 加一个三态 `status`(没有 id、优先级或 `activeForm`);列表在每次写入时整体替换,因此条目无需稳定标识。见 [todo_write Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md)。
这是 `todo/write` 事件全量列表快照中的单元。它有意保持精简:一行 `content` 加一个三态 `status`(没有 id、优先级或 `activeForm`);列表在每次写入时整体替换,因此条目无需稳定标识。见 [todo_write Agent Note](../../.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md)。
```ts type-equiv
/**
@@ -351,7 +351,7 @@ interface SurfaceFoldResult {
}
```
## `Session` public API
## `Session` 公共 API
去除方法体的声明与源码中的普通类保持同步,覆盖其公共构造函数、状态访问器、追加边界和历史投影。存储操作仍由生成的 [`ctx.sessions` 服务目录](../cordis-catalog/services.md#ctxsessions--sessionstore)记录。
@@ -515,7 +515,7 @@ declare class Session {
- `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;溯源信息与领域数据都在其类型化的 source 中。
- `steering/message` → 按时间顺序在相应位置生成一条携带确切 `content` 的 user-role 消息;可选 envelope 仅作为日志中的展示元数据保留。
其余所有事件(`turn/*`、`step/*`、插件所的 `llm/retry`均为结构信息不会投影为消息。token 记账读取每个步骤的 `assistant/chunk { type: 'usage' }` 记录;如果没有用量分片,则将 `assistant/message.usage` 作为已提交步骤的后备。失败的模型请求尝试没有 assistant 消息,因此其用量分片是持久化的记账记录。操作错误的步骤号记录在 `turn/end.reason``kind: 'error'`)中;如果是最终模型请求失败,其中包含规范化的 `LlmFailure` 事实,其他实时错误则包含消息/代码。由于这一尚未发布的格式有意不提供兼容性承诺seed/load 校验会拒绝缺少提供方和模型的请求头,以及缺少提供方/模型溯源信息的 assistant 消息,而不会猜测历史数据应走的提供方路由。
其余所有事件(`turn/*`、`step/*`、插件所的 `llm/retry`均为结构信息不会投影为消息。token 记账读取每个步骤的 `assistant/chunk { type: 'usage' }` 记录;如果没有用量分片,则将 `assistant/message.usage` 作为已提交步骤的后备。失败的模型请求尝试没有 assistant 消息,因此其用量分片是持久化的记账记录。操作错误的步骤号记录在 `turn/end.reason``kind: 'error'`)中;如果是最终模型请求失败,其中包含规范化的 `LlmFailure` 事实,其他实时错误则包含消息/代码。由于这一尚未发布的格式有意不提供兼容性承诺seed/load 校验会拒绝缺少提供方和模型的请求头,以及缺少提供方/模型溯源信息的 assistant 消息,而不会猜测历史数据应走的提供方路由。
## 活跃会话 fork API
@@ -523,7 +523,7 @@ declare class Session {
- `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `boundary` seq为止的源事件默认为当前最后一个事件要求所选前缀结束时没有开放轮次然后创建一个活跃的子会话包含深克隆的种子事件和子会话元数据`parentSession`、`seedLength` 及继承的 `cwd`)。
显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork包括之前的 `turn/end` 或更晚的独立纯日志事件即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。更广泛的执行关系健全性检查留在既有的 `dsh-invariants` 插件和持久化修复路径中,不在 `fork()` 中重复。`dsh-subagent-fork` 保留其已完成前缀截断逻辑,因为工具委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork包括之前的 `turn/end` 或更晚的独立纯日志事件即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。更广泛的执行关系健全性检查留在既有的 `dsh-invariants` 插件和持久化修复路径中,不在 `fork()` 中重复。`dsh-subagent-fork` 保留其已完成前缀截断逻辑,因为工具调用时的委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
## 轮次的触发原因:`TurnTriggerMap`
@@ -549,7 +549,7 @@ interface TurnTriggerMap {
## 轮次的结束原因:`TurnEndReasonMap`
`aborted` 有意作为一种粗粒度的持久结果:它只记录取消中断了实时轮次,不记录是哪个运行时调用方发起取消。仅属于运行时的调用方词汇由 [`AgentCancelCause`](core.md#the-agent-handle) 定义;未来若有审计需求,应新增独立的控制请求事件,而非让终止结果承载这一信息。
`aborted` 有意作为一种粗粒度的持久结果:它只记录取消中断了正在执行的轮次,不记录是哪个运行时调用方发起取消。仅属于运行时的调用方词汇由 [`AgentCancelCause`](core.md#the-agent-handle) 定义;未来若有审计需求,应新增独立的控制请求事件,而非让终止结果承载这一信息。
```ts type-equiv
/**
@@ -608,6 +608,6 @@ interface TurnEndReasonMap {
## 持久性契约
持久化后端依赖的契约如下:持久日志无损保存每个事件,**包括** `assistant/chunk``seq` 必须连续,因此不能从规范日志中过滤分片。后端可以为事件批次选择自己的存储编码,只要 `load` 返回与追加时完全一致的事件即可JSONL 后端默认启用的打包分片行就是此类编码;见 [persistence.md](persistence.md))。所有 `event.data` 都必须可序列化为 JSON`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.events` 始终与后端可持久化的内容一致。新增携带不可序列化数据的事件类型、破坏核心执行嵌套或违反事件所有方声明关系,都会构成磁盘格式的破坏性变更。
持久化后端依赖的契约如下:持久日志无损保存每个事件,**包括** `assistant/chunk``seq` 必须连续,因此不能从规范日志中过滤分片。后端可以为事件批次选择自己的存储编码,只要 `load` 返回与追加时完全一致的事件即可JSONL 后端默认启用的打包分片行就是此类编码;见 [persistence.md](persistence.md))。所有 `event.data` 都必须可序列化为 JSON`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.events` 始终与后端可持久化的内容一致。新增携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
消费此契约的后端见 [persistence.md](persistence.md)。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/skills.md
skills.md: d4b41845bea009444653739abad712e9ce3afb13
skills.zh.md: 8d6793129080487836b2e2471b8659df5a402974
skills.zh.md: 64c530e1fd2beb06148b04ef9a91cf47604d02ca

View File

@@ -2,7 +2,7 @@
[English](skills.md) | 中文
[skill技能能力族](../../packages/skill)拆分为三个包package:注册表([dsh-skill](../../packages/skill/skill)`ctx.skills`)合并各提供方的目录;本地提供方([dsh-skill-local](../../packages/skill/skill-local))扫描并监视项目/自定义/用户目录;消费方([dsh-tool-skill](../../packages/skill/tool-skill))拥有初始目录和替换目录,以及面向模型的 `skill` 工具。skill 是可选的指令而非会话事件,因此其词汇定义在此处而非 [core.md](core.md)。
[skill技能能力族](../../packages/skill) 拆分为三个包:注册表([dsh-skill](../../packages/skill/skill)`ctx.skills`)合并各提供方的目录;本地提供方([dsh-skill-local](../../packages/skill/skill-local))扫描并监视项目自定义用户目录;消费方([dsh-tool-skill](../../packages/skill/tool-skill))拥有初始目录和替换目录,以及面向模型的 `skill` 工具。skill 是可选的指令而非会话事件,因此其词汇定义在此处而非 [core.md](core.md)。
源码:[`packages/skill/skill/src/index.ts`](../../packages/skill/skill/src/index.ts)、[`packages/skill/skill-local/src/index.ts`](../../packages/skill/skill-local/src/index.ts) 与 [`packages/skill/tool-skill/src/index.ts`](../../packages/skill/tool-skill/src/index.ts)。
@@ -10,7 +10,7 @@
`ctx.skills` 组合本地、内嵌、远程或其他提供方。注册是同步的;远程初始化与发现属于 `list()` 的 await 阶段。提供方对象、选项与候选项以只读方式借用,语义字段会被校验。
重名按 rank、提供方顺序本地顺序依次解决;摘要按名称排序。`list()` 拒绝时会记录日志并从不完整观测中省略;显式的不完整观测会提供可用候选项,但不会使结果变得可缓存;格式错误的候选项快速失败。每个提供方工厂都会接收一项注册作用域内的控制能力;仅当该精确注册仍处于活动状态时,其 `invalidate()` 才会清除已完成目录;注册失败或释放时,其信号会中止。若提供方代次在发现进行期间发生变化,该发现会重试一次;若再次变化,则返回最新候选项,并将结果标为不完整且不予缓存。提供方和运行时变更会发出不带过滤条件的 `skills/change` 失效事件;该事件不携带 diff因此消费方会使用自身的查找选项重新获取 `snapshot()`
重名项依次按 rank、提供方顺序本地顺序确定优先级;摘要按名称排序。提供方的 `list()` 拒绝时,系统会记录日志并从不完整观测中省略该提供方的结果;显式的不完整观测会提供可用候选项,但不会使结果变得可缓存;格式错误的候选项快速失败。每个提供方工厂都会接收一项注册作用域内的控制能力;仅当该精确注册仍处于活动状态时,其 `invalidate()` 才会清除已完成目录;注册失败或释放时,其信号会中止。若提供方代次在发现进行期间发生变化,该发现会重试一次;若再次变化,则返回最新候选项,并将结果标为不完整且不予缓存。提供方和运行时变更会发出不带过滤条件的 `skills/change` 失效事件;该事件不携带 diff因此消费方会使用自身的查找选项重新获取 `snapshot()`
`SkillProvider.list()` 返回的数组是完整发现的简写形式。`SkillProviderObservation` 允许提供方公开仍可直接加载的候选项,同时报告该观测不具权威性。
@@ -61,7 +61,7 @@ interface SkillProviderControl {
## 本地发现优先级
内置的本地提供方按 rank 顺序扫描各根目录:
随附的本地提供方按 rank 顺序扫描各根目录:
| Rank | Source | Root |
|---|---|---|
@@ -76,7 +76,7 @@ interface SkillProviderControl {
Chokidar 会监视现有根目录中直属 bundle 和平铺条目的添加与移除,以及直属 skill 条目的变更。缺失的根目录会从最近的现有祖先开始,逐个跟踪缺失路径段,直至 Chokidar 可以附加。bundle 下的资源文件变更不属于目录变更。面向模型的 `write` 和 `edit` 观测会在目标路径相关时同步使提供方目录失效,而宿主 watcher 覆盖 IDE、Git、shell 和外部进程产生的变更。watcher 失败会使当前观测不完整,但不会在直接加载时隐藏可读候选项;项目作用域 watcher 使用按配置设限的 LRU。
## Skill 身份
## skill 身份
skill 名称为 kebab-case`^[a-z0-9]+(?:-[a-z0-9]+)*$`)。本地提供方接受目录包(`<name>/SKILL.md`)和扁平 Markdown 文件(`<name>.md`)。嵌套递归的 `**/SKILL.md` 发现有意不在 v1 范围内。
@@ -185,7 +185,7 @@ type SkillRegistration = Omit<SkillDefinition, 'invocation' | 'provider'> & {
## 查找与配置
skill 查找对 cwd 敏感,因为提供方可能暴露工作区本地的 skill可选的 signal 为调用方取消提供方的工作。提供方接收缓存标识和加载相同的只读选项对象。取消在目录选择前后(包括缓存命中时)都会检查,并与发现和完整定义加载竞争。如果找不到 git root本地提供方将所提供的 cwd 本身视为项目根目录。
skill 查找对 cwd 敏感,因为提供方可能暴露工作区本地的 skill可选的 signal 为调用方取消提供方的工作。提供方接收用于缓存标识和加载的同一个只读选项对象。取消在目录选择前后(包括缓存命中时)都会检查,并与发现和完整定义加载竞争。如果找不到 git root本地提供方将所提供的 cwd 本身视为项目根目录。
注册表不缓存完整定义。每次调用 `get()` 都会携所选候选项调用胜出提供方,因此本地提供方会重新读取当前正文。名称与该候选项不再匹配的定义会被拒绝,并使该提供方实例失效以便重新发现。
@@ -211,8 +211,8 @@ interface Config {
## 会话目录与工具契约
`dsh-tool-skill` 在活会话中第一个观察到非空完整视图的 `agent/step` 注入初始的持久 user-role `<system-reminder>`。目录只包含已排序的 skill `name` 和规范化、经 XML 转义的 `description`;不包含正文、路径、来源、提供方或路由提示。发现通过 `SkillLookupOptions` 转发该步骤的 abort signal。`catalogDescriptionMaxLength` 是消费方用于 description 上限的配置,默认值为 `500`,整数最小值为 `3`。
`dsh-tool-skill` 在活会话中第一个观察到非空完整视图的 `agent/step` 注入初始的持久 user-role `<system-reminder>`。目录只包含已排序的 skill `name` 和规范化、经 XML 转义的 `description`;不包含正文、路径、来源、提供方或路由提示。发现通过 `SkillLookupOptions` 转发该步骤的 abort signal。`catalogDescriptionMaxLength` 是消费方用于 description 上限的配置,默认值为 `500`,整数最小值为 `3`。
在后续每个模型步骤之前,消费方都会应用精确的工具可见性,并对完整快照中 `<available_skills>` 标签之间精确渲染的条目计算 digest。它以该插件所发布、最新一条可识别且仍可见的目录消息中的相同条目作为比较基线。digest 发生变化时,会通过 `agent.inject()` 追加一条持久的完整目录替换;删除所有 skill 时会追加一条显式的空替换。不完整快照会保留上一份可用模型视图。如果压缩compaction隐藏了所有历史目录消息下一份完整快照会重新建立当前目录如果视图为空且从未发布目录则不发送任何内容。这些目录消息属于会话历史而非 World State。
面向模型的 `skill({ name })` 工具校验 kebab-case 名称,在与调用策略无关的目录中查找摘要,并在加载前通过 `isModelInvocable` 拒绝无权访问的 skill随后它调用方 agent 的 cwd 重新读取完整定义,并在返回内容前再次检查策略。该工具将解析的 skill 报告为 unknown 或 no longer available,并返回包含 `<skill_content name="...">`、`<skill_resources>` 和 `<skill_instructions>` 的工具结果。`resourceBase` 仅按需解析显式引用的脚本、参考资料和资产;加载结果不枚举 skill 目录。因此,仅修改正文会改变后续工具调用,而不会生成目录消息或改写先前工具结果。
面向模型的 `skill({ name })` 工具校验 kebab-case 名称,在与调用策略无关的目录中查找摘要,并在加载前通过 `isModelInvocable` 拒绝无权访问的 skill随后它根据调用方 agent 的 cwd 重新读取完整定义,并在返回内容前再次检查策略。该工具将无法解析的 skill 报告为未知或已不可用,并返回包含 `<skill_content name="...">`、`<skill_resources>` 和 `<skill_instructions>` 的工具结果。`resourceBase` 仅按需解析显式引用的脚本、参考资料和资产;加载结果不枚举 skill 目录。因此,仅修改正文会改变后续工具调用,而不会生成目录消息或改写先前工具结果。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/spill.md
spill.md: a798d8143b2849dc0cf49d04e7019ce796cdee45
spill.zh.md: 1af6939d1d8fd37958cae4f9cf2cbf706b17acd0
spill.zh.md: 1167c6f985dbc204dc7166b0fb5854dcf55bc72f

View File

@@ -2,7 +2,7 @@
[English](spill.md) | 中文
落盘存储 seam 是一项[能力 seam](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md),它持久保存工具的超大文本,并返回面向模型的定位符与检索指引;该能力拆分到三个包package:接口([dsh-spill](../../packages/spill/spill)`ctx.spillStore`)、实现([dsh-spill-local](../../packages/spill/spill-local),宿主文件系统中会话作用域的私有文件)和消费方([dsh-spill-policy](../../packages/spill/spill-policy)`tools/post-execute` 策略)。落盘是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇记录在此处而不在 [core.md](core.md) 中。预览机制仍归 [dsh-retention](../../packages/util/retention) 所有;该 seam 只保存策略交给它的最终文本。
落盘存储 seam 是一项[能力 seam](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md),它持久保存工具的超大文本,并返回面向模型的定位符与检索指引;该能力拆分到三个包:接口([dsh-spill](../../packages/spill/spill)`ctx.spillStore`)、实现([dsh-spill-local](../../packages/spill/spill-local),宿主文件系统中会话作用域的私有文件)和消费方([dsh-spill-policy](../../packages/spill/spill-policy)`tools/post-execute` 策略)。落盘是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇记录在此处而不在 [core.md](core.md) 中。预览机制仍归 [dsh-retention](../../packages/util/retention) 所有;该 seam 只保存策略交给它的最终文本。
源码:[`packages/spill/spill/src/types.ts`](../../packages/spill/spill/src/types.ts)

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/subprocess.md
subprocess.md: 922e7ad0ee8b5c0dbcd0a6a4553c9d2a580f3ee2
subprocess.zh.md: 5befdcdfc9b0e1d2a9adc825b177c90e53269def
subprocess.zh.md: ec9f41cfa9e3121f630bab2b6f1e921c81ad55b7

View File

@@ -1,8 +1,8 @@
# 进程管理器
# 进程
[English](subprocess.md) | 中文
进程管理器 seam 分为接口([dsh-subprocess](../../packages/subprocess/subprocess)`ctx.subprocess`)与实现([dsh-subprocess-local](../../packages/subprocess/subprocess-local));它的消费方是其他能力 seam 与进程外后端:[bash 执行器家族](bash.md)使用收集模式collect的批量输出LSP 主机使用管道化的协议流 + 收集的 stderr 尾部ACPAgent Client Protocolsubagent 后端则使用管道化的协议流 + inherit 的 stderr。该 seam 拥有受管的 `DSH_*` 环境命名空间、共享的凭据清除(`scrubbedParentEnv`)与 `CollectedOutput` 形状;[dsh-bash](../../packages/bash/bash) 重导出这套词汇,使 bash 消费方保持单一导入入口。
进程 seam 分为接口([dsh-subprocess](../../packages/subprocess/subprocess)`ctx.subprocess`)与实现([dsh-subprocess-local](../../packages/subprocess/subprocess-local));它的消费方是其他能力 seam 与进程外后端:[bash 执行器家族](bash.md)使用收集模式collect的批量输出LSP 主机使用管道化的协议流 + 收集的 stderr 尾部ACPAgent Client Protocolsubagent 后端则使用管道化的协议流 + inherit 的 stderr。该 seam 拥有受管的 `DSH_*` 环境命名空间、共享的凭据清除(`scrubbedParentEnv`)与 `CollectedOutput` 形状;[dsh-bash](../../packages/bash/bash) 重导出这套词汇,使 bash 消费方保持单一导入入口。
源码:[`packages/subprocess/subprocess/src/types.ts`](../../packages/subprocess/subprocess/src/types.ts)
@@ -32,7 +32,7 @@ interface CollectedOutput {
}
```
## Node 形状的 stdio 处置方式disposition
## Node 风格的 stdio 处置方式disposition
每条流的处置方式都显式给出由各消费方自行选择原始管道用于协议分帧LSP JSON-RPC、ACP ndjsoninherit 用于直通的诊断输出,收集模式用于有界的批量输出;其中 spill 文件是可选的,因此诊断尾部(语言服务器的 stderr可以只在内存中缓冲不留下任何文件。
@@ -84,7 +84,7 @@ interface SubprocessStdio {
## 完全显式的 spawn spec
该 seam 不应用任何默认值:每项处置方式、限制与目录都在 spec 上显式给出,因此由调用方自己的配置决定它们,而不是由某个隐藏的进程管理器默认值决定。`argv` 绝不经过 shell 解释。
该 seam 不应用任何默认值:每项处置方式、限制与目录都在 spec 上显式给出,因此由调用方自己的配置决定它们,而不是由某个隐藏的进程服务默认值决定。`argv` 绝不经过 shell 解释。
```ts type-equiv
/**
@@ -127,7 +127,7 @@ interface SubprocessSpawnSpec {
## 句柄:流、读取器与以进程树为范围的终止
spawn 会立即返回一个实时句柄。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;管道化的流归调用方所有。终止在每个平台上都以进程树为范围:`terminate()`(唯一的终止动词)执行 SIGTERM→宽限期→SIGKILL 升级,`waitForExit()` 观察整棵进程树——这足以让消费方构建自己的拆卸阶梯ACP 后端以 stdin EOF 打头的 `disposeAcpChild` 即是模板
spawn 会立即返回一个活动句柄。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;管道化的流归调用方所有。终止在每个平台上都以进程树为范围:`terminate()`(唯一的终止动词)执行 SIGTERM→宽限期→SIGKILL 升级,`waitForExit()` 观察整棵进程树这足以让消费方构建自己的拆卸阶梯ACP 后端的 `disposeAcpChild` 以 stdin EOF 开始,即为仓库内模板。
```ts type-equiv
/**

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/system-prompt.md
system-prompt.md: 5abb8f46c13045c7d37bbe12ecf6c3744ee063b5
system-prompt.zh.md: 1088b20ba4289ad5912a193eead39d069c1a6e17
system-prompt.zh.md: d3ba0736ec4ccdfaf5c723f2002abf2e6e072d2c

View File

@@ -2,13 +2,13 @@
[English](system-prompt.md) | 中文
[system-prompt 包package](../../packages/core/system-prompt)负责管理提示词贡献者与一次组装调用之间交换的数据。该包的 [README](../../packages/core/system-prompt/README.md) 记录了注册、排序、作用域与渲染行为;本页固定各插件实现或传递的跨包字面形状。
[system-prompt 包](../../packages/core/system-prompt)负责管理提示词贡献者与一次组装调用之间交换的数据。该包的 [README](../../packages/core/system-prompt/README.md) 记录了注册、排序、作用域与渲染行为;本页固定各插件实现或传递的跨包字面形状。
源码:[`packages/core/system-prompt/src/index.ts`](../../packages/core/system-prompt/src/index.ts)。
## 组装上下文
`AssembleContext` 标识一次组装所解析的作用域 layer,并可携带该请求的显式控制 signal。它可合并扩展:`dsh-agent` 添加可选的 live `agent` 字段`assembleContextFor(agent, signal)` 则一起设置这些显式字段。裸组装既没有 scope也没有 signal
`AssembleContext` 标识一次组装所解析的作用域,并可携带该请求的显式控制信号。它可合并扩展:`dsh-agent` 添加可选字段 `agent`用于携带当前的 agent智能体实例`assembleContextFor(agent, signal)` 则一起设置这些显式字段。裸组装既没有作用域,也没有信号
```ts type-equiv
/** Merge-extensible context for one prompt assembly. */
@@ -25,7 +25,7 @@ interface AssembleContext {
## 工具提供方结果
`ToolProviderResult.schemas` 是当前组装中对模型可见的工具集合。`knownNames` 是提供方在限制前的名称全集,用于区分「配置名拼写错误」与「已知工具在此作用域中被有意隐藏」。
`ToolProviderResult.schemas` 是当前组装中对模型可见的工具 schema 集合。`knownNames` 是提供方在限制前的名称全集,用于区分「配置名拼写错误」与「已知工具在此作用域中被有意隐藏」。
```ts type-equiv
/** Tool schemas visible in one assembly and their pre-restriction name set. */

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/tasks.md
tasks.md: a38055d3ef7aa18e62678f92eb5ac5ae2a09c205
tasks.zh.md: b5dd7f75c7df3e359bc995fce57f1ca2dc7fd017
tasks.zh.md: f34d42e713c3a0c11cbf88d52e573bb100c52493

View File

@@ -2,7 +2,7 @@
[English](tasks.md) | 中文
长时间运行的生产方、`ctx.tasks` 与任务控制接口共用的类型。[运行时 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)负责设计;本页记录 [`packages/tasks/tasks/src/types.ts`](../../packages/tasks/tasks/src/types.ts) 中的字面形状。
长时间运行的生产方、`ctx.tasks` 与任务控制接口共用的类型。[运行时 Agent Note](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)负责设计;本页记录 [`packages/tasks/tasks/src/types.ts`](../../packages/tasks/tasks/src/types.ts) 中的字面形状。
## ID 与状态

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/token-meter.md
token-meter.md: 05784e294485a11acf0e4c8972e4083b1786c943
token-meter.zh.md: c0dc55274acf21186f7baa00c377f9135792f888
token-meter.zh.md: 0474d86188a111014e6d72e9962121c08a228d73

View File

@@ -2,7 +2,7 @@
[English](token-meter.md) | 中文
`@deepseek-ai/dsh-token-meter` 公开一个独立的回放快照,用于表示请求压力与按位置计算的 surface 定价。`logRevision` 表示生成该计量中每个字段时所消费的持久事件数量。
`@deepseek-ai/dsh-token-meter` 公开一个独立的回放快照,用于表示请求压力与按位置计算的表层定价。`logRevision` 表示生成该计量中每个字段时所消费的持久事件数量。
来源:[`packages/llm/token-meter/src/types.ts`](../../packages/llm/token-meter/src/types.ts)
@@ -26,7 +26,7 @@ interface TokenMeasurement {
}
```
`baseline.kind === 'usage'` 表示最近一次成功的提供方调用具有相同的规范请求信封,且当前总量不低于该调用的完整启发式锚点。`estimated` 表示不存在可复用的保守 usage 锚点,因此服务使用固定启发式规则对完整信封和 surface 定价。后续成功请求会替换早先的锚点;有符号的 `surfaceDeltaTokens` 会保留相对于匹配锚点的增长与缩减。`totalTokens` 仍表示请求与响应压力,`surfaceTokens` 则是仅针对 surface 的启发式总量,等于所有节点价格之和。
`baseline.kind === 'usage'` 表示最近一次成功的提供方调用具有相同的规范请求信封,且该调用的总量不低于完整启发式锚点。`estimated` 表示不存在可复用的保守 usage 锚点,因此服务使用固定启发式规则对完整信封和表层定价。后续成功请求会替换早先的锚点;有符号的 `surfaceDeltaTokens` 会保留相对于匹配锚点的增长与缩减。`totalTokens` 仍表示请求与响应压力,`surfaceTokens` 则是仅针对表层的启发式总量,等于所有节点价格之和。
## `TokenSurfaceNode`
@@ -40,4 +40,4 @@ interface TokenSurfaceNode {
}
```
surface 顺序具有权威性;替换节点的持久 seq 可能高于位置排在其后的节点。该快照不可变,不会随底层回放折叠推进而增长。
表层顺序具有权威性;替换节点的持久 seq 可能高于位置排在其后的节点。该快照不可变,不会随底层回放折叠推进而增长。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/web.md
web.md: 20d07240c9d9fea2f1f5abbac810f349a3e81f9b
web.zh.md: 68ceed04bb0b80f32ed704118f1fc25f48a0da70
web.zh.md: e2982ba571353752e3a7d10130599e8c8fe941f8

View File

@@ -2,13 +2,13 @@
[English](web.md) | 中文
Web 访问 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md),在同一个 `ctx.web` 服务上横跨**两项能力**search 与 fetch并拆分到多个包package:接口([dsh-web](../../packages/web/web)`ctx.web` + 提供方注册表)、实现([dsh-web-search-exa](../../packages/web/web-search-exa)、[dsh-web-search-perplexity](../../packages/web/web-search-perplexity)、[dsh-web-search-deepseek](../../packages/web/web-search-deepseek)、[dsh-web-fetch-local](../../packages/web/web-fetch-local))与消费方([dsh-tool-web](../../packages/web/tool-web),即 `web_search`/`web_fetch` 工具 schema。Web 是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此而非 [core.md](core.md) 中。更换 search 提供方不会改变模型请求 query 的方式,更换 fetch 实现也不会改变模型请求 URL 的方式。
Web 访问 seam 是一个[能力 seam](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md),在同一个 `ctx.web` 服务上横跨**两项能力**search 与 fetch并拆分到多个包接口[dsh-web](../../packages/web/web)`ctx.web` + 提供方注册表)、实现([dsh-web-search-exa](../../packages/web/web-search-exa)、[dsh-web-search-perplexity](../../packages/web/web-search-perplexity)、[dsh-web-search-deepseek](../../packages/web/web-search-deepseek)、[dsh-web-fetch-local](../../packages/web/web-fetch-local))与消费方([dsh-tool-web](../../packages/web/tool-web),即 `web_search`/`web_fetch` 工具 schema。Web 是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此而非 [core.md](core.md) 中。更换 search 提供方不会改变模型提交查询的方式,更换 fetch 实现也不会改变模型请求 URL 的方式。
源码:[`packages/web/web/src/types.ts`](../../packages/web/web/src/types.ts)
## 为什么两项能力合为一个 seam
搜索与抓取既不共享请求 schema也不共享业务逻辑但它们被有意设计为同一个 `ctx.web` 中间层:一个提供方选择策略的所有者、一套 abort/error 词汇、一个面向产品的「此 harness 如何访问 Web」配置界面。代价是服务上并行的 `searchX`/`fetchX` 方法对;这种并行是有意为之,而非遗漏的提取。提供方注册的是**能力**`WebSearchProvider``WebFetchProvider`而非工具面向模型的名称、schema、提示词引导与展示全部集中在唯一的消费方 `dsh-tool-web` 中。
搜索与抓取既不共享请求 schema也不共享业务逻辑但它们被有意设计为同一个 `ctx.web` 中间层:一个提供方选择策略的所有者、一套中止与错误词汇,以及一个面向产品的「此 harness 如何访问 Web」配置界面。代价是服务上并行的 `searchX``fetchX` 方法对;这种并行是有意为之,而不是遗漏了可抽取的共性。提供方注册的是**能力**`WebSearchProvider``WebFetchProvider`而非工具面向模型的名称、schema、提示词引导与展示全部集中在唯一的消费方 `dsh-tool-web` 中。
## 搜索请求与结果
@@ -50,7 +50,7 @@ interface WebSearchResult {
}
```
`content` 是提供方可选生成的回答文本Exa 和 DeepSeek 不返回Perplexity 返回生成式回答)。`sources[]` 是一套可跨提供方使用的引用数据结构。一个 source 必有 `url``title`/`snippet`/`publishedAt` 可选因为并非每个提供方都返回它们——Perplexity 的引用可能只有 URL强迫适配器编造其余字段会让 seam 说谎。`dsh-tool-web` 渲染时使用 `title ?? hostname(url)`。
`content` 是提供方可选生成的回答文本Exa 和 DeepSeek 不返回Perplexity 返回生成式回答)。`sources[]` 是一套可跨提供方使用的引用数据结构。每个来源都必须有 `url``title``snippet``publishedAt` 可选字段,因为并非每个提供方都返回它们——Perplexity 的引用可能只有 URL强迫适配器编造其余字段会让 seam 说谎。`dsh-tool-web` 渲染时使用 `title ?? hostname(url)`。
```ts type-equiv
/**
@@ -82,7 +82,7 @@ interface WebFetchRequest {
}
```
HTTP 状态码是被抓取资源状态的一部分,不自动视为失败:成功的网络抓取返回 `404`/`500` 时,仍产出一个带状态码和有界解码 body 的 `WebFetchResult`。`url` 是经过允许的重定向后的最终 URL。`WebError` 仅用于无法安全获取或表示资源的情况。
HTTP 状态码是被抓取资源状态的一部分,不自动视为失败:即使一次成功的网络抓取收到 `404``500` 响应,也仍会产出一个 `WebFetchResult`,其中包含状态码和长度受限的已解码正文。`url` 是经过允许的重定向后的最终 URL。`WebError` 仅用于无法安全获取或表示资源的情况。
```ts type-equiv
/**
@@ -122,14 +122,14 @@ type WebFetchBody =
## 提供方可用性
提供方的 `available(): boolean` 是一个廉价的本地检查(凭证是否存在、配置是否可解析),**禁止发起网络调用**。它是执行时选择的输入,而健康检查系统:`search()`/`fetch()` 读取它以选出可用的提供方选择失败结构化 `WebError` 呈现给调用方路由——其 code 和 message 携带可分支的细节(缺失的 id 或歧义的候选集
提供方的 `available(): boolean` 是一个廉价的本地检查(凭证是否存在、配置是否可解析),**禁止发起网络调用**。它是执行时选择提供方的输入,而不是健康检查系统:`search()``fetch()` 读取它来选择可用的提供方选择失败时,调用方会收到可据以分支处理的结构化 `WebError`;其错误代码和消息会说明缺失的 id 或存在歧义的候选集。
选择从不依赖注册顺序、配置顺序或 HMR热模块替换顺序一项能力要么有显式的提供方 id配置 `searchProvider`/`fetchProvider`,或填充同一字段的对应环境变量),要么在恰好只有一个可用提供方注册时自动选择;多个可用提供方未配置 id 时为 `WEB_PROVIDER_AMBIGUOUS`,而非先注册先赢
选择从不依赖注册顺序、配置顺序或 HMR热模块替换顺序一项能力要么有显式的提供方 id配置 `searchProvider``fetchProvider`,或填充同一字段的对应环境变量),要么在恰好只有一个可用提供方注册时自动选择;如果存在多个可用提供方未配置 id,则抛出 `WEB_PROVIDER_AMBIGUOUS`,而不会选用最先注册的提供方
## 错误
`WebError extends HarnessError`[core.md](core.md) 错误分类体系),带有 `code: string`(开放式,与其他 seam 的错误一致——`LlmError`、`SubagentError`),而非封闭联合类型:提供方可以在不修改 `dsh-web` 的情况下抛出自己的 code,消费方必须容忍未知 code。code 按所有者划分。seam 中立的 code 由 `WebService` 选择逻辑和共享契约抛出`WEB_PROVIDER_UNAVAILABLE`、`WEB_PROVIDER_CONFIGURED_MISSING`、`WEB_PROVIDER_CONFIGURED_UNAVAILABLE`、`WEB_PROVIDER_AMBIGUOUS`、`WEB_DUPLICATE_PROVIDER`(注册时的编程错误,类似 `LlmService` 的 `DUPLICATE_ADAPTER`)、`WEB_ABORTED`,以及 `WEB_PROVIDER_ERROR`(提供方自身故障通过 seam 暴露的兜底 code包括网络/传输失败——DNS、连接被拒、TLS。抓取传输层 code 由 `dsh-web-fetch-local` 实现拥有,不同的抓取后端无需抛出它们:`WEB_INVALID_URL`、`WEB_BLOCKED_URL`、`WEB_REDIRECT_BLOCKED`、`WEB_FETCH_TOO_LARGE`、`WEB_FETCH_TIMEOUT`、`WEB_UNSUPPORTED_CONTENT_TYPE`。
`WebError extends HarnessError`[core.md](core.md) 错误分类体系),带有 `code: string`(开放式,与其他 seam 的错误一致——`LlmError`、`SubagentError`),而非封闭联合类型:提供方可以在不修改 `dsh-web` 的情况下抛出自己的错误代码,消费方必须容忍未知错误代码。错误代码按所有者划分。seam 统一定义的错误代码来自 `WebService` 选择逻辑和共享契约:`WEB_PROVIDER_UNAVAILABLE`、`WEB_PROVIDER_CONFIGURED_MISSING`、`WEB_PROVIDER_CONFIGURED_UNAVAILABLE`、`WEB_PROVIDER_AMBIGUOUS`、`WEB_DUPLICATE_PROVIDER`(注册时的编程错误,类似 `LlmService` 的 `DUPLICATE_ADAPTER`)、`WEB_ABORTED`,以及 `WEB_PROVIDER_ERROR`(提供方自身故障 seam 暴露时使用的兜底代码,包括 DNS、连接被拒、TLS 等网络或传输故障)。抓取传输层错误代码由 `dsh-web-fetch-local` 实现拥有,不同的抓取后端无需抛出它们:`WEB_INVALID_URL`、`WEB_BLOCKED_URL`、`WEB_REDIRECT_BLOCKED`、`WEB_FETCH_TOO_LARGE`、`WEB_FETCH_TIMEOUT`、`WEB_UNSUPPORTED_CONTENT_TYPE`。
## 服务
`WebService` 注册搜索与抓取提供方,以 `WEB_DUPLICATE_PROVIDER` 拒绝重复 id并在执行时以结构化的选择错误解析提供方。本地抓取后端仅接受 HTTP(S)、拒绝凭证、限制重定向次数、字节数、字符数和时间、对每一跳同源重定向重新校验,并解码 body;展示由工具负责。私有网络阻断尚未实现,因此请勿在可触及敏感内部目标的环境中启用 `web_fetch`。
`WebService` 注册搜索与抓取提供方,以 `WEB_DUPLICATE_PROVIDER` 拒绝重复 id并在执行时以结构化的选择错误解析提供方。本地抓取后端仅接受 HTTP(S)、拒绝凭证、限制重定向次数、字节数、字符数和时间、对每一跳同源重定向重新校验,并解码正文;展示由工具负责。SSRF私有网络防护尚未实现,因此在能够触及敏感内部目标的环境中,禁止启用 `web_fetch`。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/core-data-structures/workflow.md
workflow.md: 8d271b89e71de6f6bef548aa8da61402ef9ada6e
workflow.zh.md: b8ed699eb52d9f0cef23c513f625de7e82c46c45
workflow.zh.md: 91a4902bbc004e911c8aa84adb6a4abeda9dd59f

View File

@@ -2,15 +2,15 @@
[English](workflow.md) | 中文
工作流 seam:一个 agent智能体运行由模型编写的编排脚本SCRIPT,扇出 subagent。与 [subagent](subagent.md) 一样,它是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此处而非 [core.md](core.md)。与 subagent 注册表不同,它采用 bash 形态:每个上下文只有一个引擎实现提供 `ctx.workflows`;没有命名提供方注册表(第二个引擎是插件替换,而非共存)。
工作流 seam 允许 agent智能体运行由模型编写的编排脚本并由该脚本扇出 subagent。与 [subagent](subagent.md) 一样,它是**一项可选能力**,不属于 agent loop智能体循环主干因此其词汇定义在此处而非 [core.md](core.md)。与 subagent 注册表不同,它采用 bash 形态:每个上下文只有一个引擎实现提供 `ctx.workflows`;没有命名提供方注册表(第二个引擎是插件替换,而非共存)。
接口:[dsh-workflow](../../packages/workflow/workflow)`ctx.workflows` + 下文词汇)。实现是 [dsh-workflow-workerthread](../../packages/workflow/workflow-workerthread)(一个 `node:worker_threads` 引擎——每个 run 一个 worker脚本的 vm 上下文位于其中);面向模型的消费方是 [dsh-tool-workflow](../../packages/workflow/tool-workflow)。提案与设计理由见 [dynamic-workflows Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
接口:[dsh-workflow](../../packages/workflow/workflow)`ctx.workflows` + 下文词汇)。实现是 [dsh-workflow-workerthread](../../packages/workflow/workflow-workerthread)(一个 `node:worker_threads` 引擎——每个 run 一个 worker脚本的 vm 上下文位于其中);面向模型的消费方是 [dsh-tool-workflow](../../packages/workflow/tool-workflow)。提案与设计理由见 [dynamic-workflows Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
源码:[`packages/workflow/workflow/src/types.ts`](../../packages/workflow/workflow/src/types.ts)
## 启动请求
调用方启动 run 时提的请求。普通工作流工具根据模型的 `{ script, meta, args }` 调用发起调用的 agent 构建;专用消费方还可以为该 run 选择一个引擎级 `subagentProvider` 并调低 `maxTotalAgents`,但脚本无法观察或替换这两项策略。`meta``args` 是普通 JSON 数据引擎会 `meta` 形状校验,并在任何内容运行前大声拒绝——绝不会通过求值脚本文本来获取它`parent` 是必填字段——脚本生成的每个子 agent 都归属于它cwd、谱系与深度通过 [subagent seam](subagent.md) 流转)。
本节定义调用方启动一次运行时提的请求。普通工作流工具根据模型的 `{ script, meta, args }` 调用发起调用的 agent 构建该请求;专用消费方还可以为本次运行选择引擎级 `subagentProvider`,并将 `maxTotalAgents` 调低,但脚本无法观察或替换这两项策略。`meta``args` 是普通 JSON 数据引擎会校验 `meta` 形状,并在任何工作开始前大声拒绝无效数据。引擎绝不会通过脚本文本求值来获取它`parent` 是必填字段——脚本生成的每个子 agent 都归属于它cwd、谱系与深度通过 [subagent seam](subagent.md) 流转)。
```ts type-equiv
/**
@@ -72,7 +72,7 @@ interface WorkflowMeta {
## 终态结果:`WorkflowResult`
一次运行的结果,由 `WorkflowRun.result` resolve。`value` 是脚本的物化返回值——纯宿主域 JSON 数据(脚本无返回值时为 `null`)——仅在 `completed` 时有意义。`stopReason` 是封闭联合类型(引擎所有;消费方可穷举):`completed` | `cancelled` | `error`。非 `completed` 的原因在 `error` 中携带失败信息,消费方将其映射为 `isError` 工具结果,而非把部分输出当作成功上报。
`WorkflowRun.result` 会兑现为一次运行的结果。`value` 是脚本的物化返回值——纯宿主域 JSON 数据(脚本无返回值时为 `null`)——仅在 `completed` 时有意义。`stopReason` 是封闭联合类型(引擎所有;消费方可穷举):`completed` | `cancelled` | `error`。非 `completed` 的原因在 `error` 中携带失败信息,消费方将其映射为 `isError` 工具结果,而非把部分输出当作成功上报。
```ts type-equiv
/**
@@ -102,7 +102,7 @@ interface WorkflowResult {
## 活跃运行:`WorkflowRun`
脚本执行期间消费方持有的句柄。消费方 await `result`,可中途 `cancel`,且必须在每条路径上 `dispose`(资源释放)。`result` 不会 reject:脚本失败 `stopReason: 'error'` resolve一旦运行被取消,即使脚本本身永不 settle也会在引擎的有界宽限期内 settle引擎强制 `cancelled` settleworker-thread 引擎随后终止脚本的 worker),因此消费方 await `result` 不会在取消后卡死。`dispose()` = cancel + 有界 settle + 等待子 agent 停稳;它不会因脚本卡死而挂起。
脚本执行期间消费方持有的句柄。消费方会等待 `result`,可以在运行期间调用 `cancel`且必须在每条路径上调用 `dispose`(资源释放)。`result` 不会被拒绝:脚本失败会兑现为 `stopReason: 'error'`运行被取消,即使脚本本身永不结算,结果也会在引擎规定的有界宽限期内结算;引擎强制将其结算为 `cancelled`,随后 worker-thread 引擎终止脚本所在的 worker。因此,等待 `result` 的消费方不会在取消后无限期挂起。`dispose()` 会执行取消、等待有界结算并等待子 agent 完全停稳,不会因脚本卡死而挂起。
```ts type-equiv
/**

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/defensive-patterns.md
defensive-patterns.md: cc34877fb0d6a2e1740d8fa138f879363c8e69a3
defensive-patterns.zh.md: 21b0977d8167ffecc21cdfab3c778efceefd8f03
defensive-patterns.zh.md: 277e4904d033e26d379e0495cb2b22370ac159d0

View File

@@ -2,28 +2,28 @@
[English](defensive-patterns.md) | 中文
来之不易的缺陷类别规则:下面每条模式都是本项目实际发布或差点发布的一类缺陷,以防止其复发的规则形式陈述。在编写生命周期、并发、子进程或清理代码之前请先阅读本文。测试层面的对应规则(真实入口路径、world 验证、资源归属)见 [testing.md](testing.md)。
来之不易的缺陷类别规则:下面每条模式都是本项目实际发布或差点发布的一类缺陷,以防止其复发的规则形式陈述。在编写生命周期、并发、子进程或清理代码之前请先阅读本文。测试层面的对应规则(真实入口路径、验证实际结果、资源归属)见 [testing.md](testing.md)。
## 正交结果独立上报
一个结果可以同时具有多性质:进程可能既超时又以 exit 0 退出,因为它捕获了信号。每个独立事实(`timedOut``signal``exitCode`)都应独立暴露;切勿将某个 flag 的上报嵌套在另一个 flag 的分支,否则调用方会把一次被截断的运行误为正常成功。
一个结果可以同时具有多性质:进程可能已经超时,却仍以退出码 0 结束,因为它捕获了终止信号。每个独立事实(`timedOut``signal``exitCode`)都应单独上报;切勿把一个标志的上报嵌套在另一个标志的分支,否则调用方可能把提前终止的运行误为正常成功。
## 跨 seam 契约两侧都要遵守
一个接口文档记录了两种合法的信号方式时——例如适配器可以通过`stream()` 抛出异常来报告失败,也可以通过以 `finish {kind:'error'|'aborted'}` 分片结束流来报告——消费方必须同时处理两种路径,而不是只处理第一个实现恰好使用的那种。依赖库的适配器可能无法在流中途抛出异常,只能带内路径;如果 agent loop智能体循环只捕获抛出的异常就会把提供方的 401 错误变成一个正常完成的轮次。请在类型定义处记录契约;通过真实消费方测试每个分支。
当接口文档规定两种合法的信号方式时,消费方必须同时处理两条路径,而不能只处理第一个实现恰好使用的路径。例如适配器可以从 `stream()` 抛出异常来报告失败,也可以发送 `finish {kind:'error'|'aborted'}` 分片结束流。基于依赖库实现的适配器可能无法在流中途抛出异常,只能使用带内路径;如果 agent loop智能体循环只捕获抛出的异常就会把提供方的 401 错误误判为正常完成的轮次。请在类型定义处记录完整契约,并通过真实消费方测试每个分支。
## 异步状态不是同步状态
`agent.followup()` 不会在返回前翻转状态;后台任务完成与轮次边界存在竞争`reader.close()` 在 EOF 和 dispose资源释放两种情况下都会触发。切勿基于一个刚刚请求的状态来控制流程——应以实际触发的事件/promise`agent/status``task.done`)驱动生命周期,并观察状态转换(先看`running` 再看`idle`),而不是把状态当作`followup()` 的结果:多排队的 `followup()` 在同一个 `running` 区间内连续行多个轮次,而取消或资源释放可能丢弃尚未启动的项。这条守则是双向的:如果等待的转换永远不会发生EOF 时没有提交过任何工作 → 永远不会进入 `running`,等待就会挂起——请显式处理「无需等待」的分支。
`agent.followup()` 不会在返回前改变状态;后台任务完成可能与轮次边界发生竞态`reader.close()` 在 EOF 和 dispose资源释放时都会触发。不要根据刚刚请求的状态变化来控制流程,而应让实际触发的事件或 Promise`agent/status``task.done`)驱动生命周期,并观察真实状态转换,例如先观察`running`,再观察`idle`。不要把状态当作`followup()` 的结果:多个已排队的 `followup()` 可以在同一个 `running` 区间内连续行多个轮次,而取消或资源释放可能丢弃尚未开始的项。反过来,如果等待的转换根本不会发生,例如 EOF 时从未提交工作,因而系统永远不会进入 `running`,等待就会无限期挂起;必须显式处理「无需等待」的分支。
## Dispose 必须达到完全停稳,而不仅仅是请求停止
一个清理流程如果发出 kill/abort 后就返回而不等待工作实际停止,就会留下孤儿进程。请让清理逻辑异步化并 await 子进程退出kill → await `done`),并在 kill 之前关闭监听器/通知注册表,使迟到的完成事件保持静默。测试证明 dispose 确实等待了`await fiber.dispose()` 之后 pid 已不存在,而不仅仅是进程最终会
如果清理流程只发出终止或中止信号便返回而不等待工作真正停止,就会留下孤儿进程。清理逻辑应采用异步流程,并等待子进程退出(发出终止信号后等待 `done`);还应在终止进程前关闭监听器通知注册表,使迟到的完成事件保持静默。测试必须证明 dispose 确实等待了,例如 `await fiber.dispose()` 返回后进程 ID 已不存在,而不能只证明该进程最终会退出
## 在边界处包容回调异常
## 在边界处隔离回调异常
用户提供的监听器如果抛出异常,不得导致它所在的 promise 被 reject也不得饿死排在它后面的监听器。请用 try/catch 包裹分发循环并记录日志;一个行为不当的订阅者绝不能破坏核心生命周期。
## 绝不将环境变量或可预测路径暴露给不可信输出
spawn 的命令应获得一份经过清洗的 env去除 `*KEY*`/`*SECRET*`/`*TOKEN*`/`*PASSWORD*`),使 harness 凭证无法泄漏到输出、`env`溢出文件中。临时/溢出文件应使用私有0700目录、随机文件名和排他的仅所有者可访问打开方式`'wx'``0o600`——可预测全局可读路径会招致符号链接竞和信息泄露。
启动的命令应使用经过清理的环境变量,移除名称匹配 `*KEY*``*SECRET*``*TOKEN*``*PASSWORD*` 的项,防止 harness 凭证通过命令输出、`env` spill 文件泄漏。临时文件和 spill 文件应放在权限为 0700 的私有目录中,使用随机文件名,并以独占且仅所有者可访问的方式打开(`'wx'``0o600`可预测全局可读路径会引发符号链接竞和信息泄露。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/glossary.md
glossary.md: 0270a2d0dba558483e8e458a932a27b0151f2c93
glossary.zh.md: ed3009a054815f1c7165fc322e44cc9521527643
glossary.zh.md: c3584731cc08b23cf7f47c09620a77b7bff65689

View File

@@ -2,32 +2,32 @@
[English](glossary.md) | 中文
DeepSeek Harness SDK 的领域词汇为每个概念规定一个规范术语。各术语通过标准 Markdown 锚点链接到相应条目;实现细节留在各包package的 README 与 Agent Noteagent 决策记录)中。
DeepSeek Harness SDK 的领域词汇为每个概念规定一个规范术语。各术语通过标准 Markdown 锚点链接到相应条目;实现细节留在各包的 README 与 Agent Note 中。
FIXME(glossary-completeness): 首次发布前扩充本术语表,使其覆盖 SDK 的其他核心与能力子系统,而非仅限于 agent scope。
## agent-scope
- **scope**:按 agent智能体划分的注册单位。一项贡献工具、提示词片段、变量、限制、监听器要么是*全局的*(对所有 agent 可见),要么是*有范围的*(归属于恰好一个 [scope key](#scope-key))。只有两层,扁平结构:有范围的注册不会向下继承给 subagent子树行为通过 [lineage](#lineage) 数据表达,从不通过 scope 结构。
- **scope**:按 agent智能体划分的注册单位。一项贡献工具、提示词片段、变量、限制、监听器要么是*全局的*(对所有 agent 可见),要么是*带作用域的*(归属于恰好一个 [scope key](#scope-key))。只有两层,采用扁平结构:带作用域的注册不会向下继承给 subagent子树行为通过 [lineage](#lineage) 数据表达,从不通过 scope 结构。
- **scope key**scope 的不透明标识按对象同一性比较。harness 约定:一个活跃的 agent 就是其自身 scope 的 key。<a id="scope-key"></a>
- **agent 上下文(`agent.ctx`**agent 的有范围上下文;通过它进行的注册既 scope 可见的,也是 scope 生命周期的(同一事实决定两者),其上的监听器参与该 agent 的 scope 过滤分发。注册表主体事件可以各自的事件契约下保持故意不过滤。
- **agent 上下文(`agent.ctx`**agent 的带作用域上下文;通过它进行的注册既具有 scope 可见性,其生命周期也绑定到该 scope(同一事实决定两者),其上的监听器参与该 agent 的 scope 过滤分发。注册表主体事件可以根据各自的事件契约有意保持不过滤。
- **scope carrier**scope 过滤分发所携带的 `thisArg`(由 `scopeTarget` 构建);其过滤器放行无标签监听器加上主体自身的监听器。*无主体*的 carrier没有 key只放行无标签监听器。
- **scoped dispatch**:规则是:关于某个 agent 活动的事件以该 agent 的 carrier 进行分发。关于注册表本身的事件(如「一个工具被添加了」)属于*注册表主体*事件,保持不过滤。
- **shadowing**:最具体者胜出的名称解析:一个有范围的工具/片段/变量仅在该 scope 内替换同名的全局对应项。这是按 agent 定制 persona 和按 agent 定制工具变体的机制。
- **scoped dispatch**:规则是:关于某个 agent 活动的事件以该 agent 的 carrier 进行分发。关于注册表本身的事件(如「一个工具被添加了」)属于*注册表主体*事件,保持不过滤。
- **shadowing**:最具体者胜出的名称解析:一个带作用域的工具片段变量仅在该 scope 内替换同名的全局对应项。这是按 agent 定制 persona 和按 agent 定制工具变体的机制。
- **restriction / scope-local 注册**restriction`tools.restrict`)为单个 scope 过滤全局工具表面(多个 restriction 取交集组合scope-local 注册在过滤之后合并。被过滤掉的全局工具既不出现在提示词中,也拒绝执行,与不存在的工具无法区分。
- **setup window**:创建者组装 agent 有范围世界的创建时隙(`CreateAgentOptions.setup`):在 scope 和 agent 对象已存在、但 agent 或会话尚未发布、`agent/session-start` 尚未触发、首次提示词尚未组装之前。setup 只做注册,从不驱动 agent。
- **setup window**:创建者组装 agent 作用域环境的创建时隙(`CreateAgentOptions.setup`):在 scope 和 agent 对象已存在、但 agent 或会话尚未发布、`agent/session-start` 尚未触发、首次提示词尚未组装之前。setup 只做注册,从不驱动 agent。
- **lineage**:以数据形式携带的父子关系事实(`parentSession`、持久的 `delegationDepth`、运行时 `subagentDepth`);从不影响可见性。<a id="lineage"></a>
## 目标
- **目标**:附着在现有会话上的单个持久完成目标,带有按修订号演进的 `active` / `paused` / `blocked` / `complete` 阶段和 Goal Round 上限;`blocked` 保留策略代码与说明。目标是一种状态,不是调度器,也不是一段独立对话;会话日志仍是其真源。
- **Goal Round**:为当前目标接纳的一次续行周期。同会话驱动器将 Goal Round 具体化为一个来源为目标的[轮次](#turn),其中可包含零个或多个步骤;同一会话中无关的人类轮次不消耗 Goal Round 上限。<a id="goal-round"></a>
- **目标激活**:续行消费方接纳下一个 Goal Round 的进程本地权限。激活态为 `armed``disarmed`;它有意不参与持久回放,因此恢复 fork 后,必须由人类随后通过 `/goal` 或模型工具授权恢复变更,自动工作才开始。
- **Goal Round**:为当前目标接纳的一次续行周期。同会话驱动器将 Goal Round 具体化为一个由目标触发的[轮次](#turn),其中可包含零个或多个步骤;同一会话中无关的人类轮次不消耗 Goal Round 上限。<a id="goal-round"></a>
- **目标激活**:续行消费方接纳下一个 Goal Round 的进程本地权限。激活态为 `armed``disarmed`;它有意不参与持久回放,因此恢复 fork 后,只有人类随后通过 `/goal` 或模型工具授权一次恢复操作,自动工作才开始。
## 人类命令
- **人类命令**:以斜杠开头的指令,由面向人类的适配器通过 `ctx.commands` 解释并执行,不会成为模型消息。它既不同于面向模型的工具,也不同于通过 `ctx.bash` 执行 shell 命令。
- **命令平面**:由 UI 适配器命令插件拥有的发现、解析、分发、取消结果渲染。除非处理器另行改变持久领域,否则命令输出属于 UI 状态。
- **命令平面**:由 UI 适配器命令插件负责的发现、解析、分发、取消结果渲染机制。除非处理器另行改变持久领域,否则命令输出属于 UI 状态。
- **目标命令**`/goal` 是由 `dsh-command-goal` 提供的人类命令;它直接观察或更改当前目标,而目标领域拥有每条持久且模型可见的记录。
## 循环层级

View File

@@ -33,7 +33,7 @@
| English | 中文 | 首次出现 | 不要译作 | 备注 |
|---|---|---|---|---|
| agent | agent | agent智能体 | | |
| Agent Note | Agent Note | Agent Noteagent 决策记录) | 智能体注记、智能体笔记 | 仓库中由 agent 撰写的提案与决策记录 |
| Agent Note | Agent Note | | 智能体注记、智能体笔记 | 仓库定义的文档类型,涵盖提案、已实现决策和被否决提案;中文对侧 H1 保持固定前缀 `# Agent Note: `,标题中不加术语括注 |
| agent harness | agent harness | agent harness智能体框架 | | agent 组合词agent harness/workflow/loop/skill 等)整体保留英文;未括注过 agent 时首现按对应组合词或 agent 行处理 |
| agent loop | agent loop | agent loop智能体循环 | | |
| blob hash | blob hash | | | `git hash-object` 的结果 |
@@ -46,6 +46,7 @@
| Function Calling | Function Calling | Function Calling函数调用 | | |
| harness | harness | | | |
| harness engineering | harness engineering | | | |
| KV Cache | KV Cache | | | 专有技术名称,保持大小写与空格 |
| lint | lint | | | |
| mock | mock | | | 保留英文;指测试替身 |
| loader | loader | | | |
@@ -54,13 +55,14 @@
| Round | Round | | 回合、目标回合、Ralph 回合 | 外层策略使用 Round 时,领域层级为 Session > Round > Turn轮次 > Step步骤Round 是可选的外层策略迭代并非每个会话轮次都具有的通用层级。Goal Round 与 Ralph Round 均保留英文。一个 Round 承载一个轮次,步骤隶属于该轮次;明确的零步骤轮次仍保持原义。 |
| schema | schema | | | |
| schema DSL | schema DSL | | | |
| seam | seam | | 接缝 | 与 `extension point` 是不同概念;根据具体语境,可译为`服务边界``可替换点` |
| seam | seam | | 接缝 | 本仓库的命名架构概念,正文保留英文;`extension point` 是不同概念 |
| skill | skill | skill技能 | | |
| slot | slot | | 坑位、孔位 | 客户端架构中的具名可注册位置,保留英文 |
| spill | spill | | | 工具输出超限落盘机制;组合词写 `spill 文件``spill 路径` |
| spawn | spawn | | | |
| steering | steering | steering中途引导 | | |
| task id | task id | | 任务 id | 保留英文 |
| subagent | subagent | | | |
| thinking | thinking | | | API 字段保留英文;描述模型模式时译为`思考` |
| transcript | transcript | transcript文本记录 | | 指会话渲染给用户或编辑器的完整文本,区别于事件日志 |
| waterfall | waterfall | waterfall瀑布式事件 | | |
| wheel | wheel 包 | | | Python 打包格式 |
@@ -81,6 +83,7 @@
| build target | 构建目标 | | | |
| cancel | 取消 | | | |
| canary test | canary 测试 | | 金丝雀测试 | 本仓库保留 `canary` |
| capability | 能力 | | | 必须与 `feature``功能` 区分 |
| capability seam | 能力 seam | | 功能 seam、能力接缝 | 本仓库接口、实现与消费方分离的命名架构概念;普通 `seam` 仍按其词条处理 |
| feature | 功能 | | 能力 | SDK 产品与工程模型中的可管理产品单元 |
| feature option | 功能选项 | | variant | 一项 SDK 功能内有限、可选择的实现或配置 |
@@ -101,13 +104,11 @@
| contract | 契约 | | | 如:`pairing contract``配对契约` |
| Cordis config entry | Cordis 配置项 | | | 指 `cordis.yml` 插件列表中的一项;插件实现本身写`Cordis 插件` |
| Cordis plugin | Cordis 插件 | | | Cordis 加载的插件实现,不指 `cordis.yml` 中的一项配置 |
| coverage | 覆盖率 | | | |
| crash recovery | 崩溃恢复 | | | |
| deploy root | 部署根目录 | | | |
| dormant | 休眠 | | 睡眠、蛰伏 | 指已声明可配置但当前未注册路由的提供方 |
| durability | 持久性 | | | |
| feature requirement | 功能依赖 | | | 功能或功能选项通过 `requires` 声明的关系 |
| ergonomics | 易用性 / 开发体验 | | 人体工学 | API 或面向模型的接口用「易用性」;工具链或开发者工作流用「开发体验」 |
| event | 事件 | | | |
| event log | 事件日志 | | | |
| event stream | 事件流 | | | |
@@ -132,7 +133,6 @@
| integration | 集成 | | | |
| interface | 接口 | | | |
| language switcher | 语言切换行 | | | i18n 配对机制用语:双语配对文件顶部的互链行 |
| memory | 记忆 / 内存 | | | 与 `agent` 搭配时译为`记忆`(如 `agent memory` →`智能体记忆`);指系统资源时译为`内存` |
| merge | 合并 | | | |
| message | 消息 | | | |
| mod | 模组 | | | |
@@ -143,7 +143,7 @@
| opt-out ratio | opt-out 比例 | | 退出检查比例 | |
| orphan | 遗留 | | 孤儿、孤立 | 指英文源已不存在的 `.zh.md`(如「遗留译文」);进程语境按 OS 惯用语译「孤儿进程」 |
| orphan branch | 孤立分支 | | 孤儿分支 | 沿用 git 官方中文翻译 |
| package | 包 |package | | 指 npm 包(`@deepseek-ai/dsh-*``package.json` 等代码标识保持原样 |
| package | 包 | | | 指 npm 包(`@deepseek-ai/dsh-*``package.json` 等代码标识保持原样 |
| pairing | 配对 | | | |
| parent-subset grants | 父级子集授权 | | 父集合授权 | 指授权范围仅限于父级所持授权的子集 |
| peer dependency | 对等依赖 | 对等依赖peer dependency | | |
@@ -170,16 +170,15 @@
| session | 会话 | | | |
| session event | 会话事件 | | | |
| setup card | 设置卡片 | | | 首次运行时代替行卡直接展开的配置卡 |
| sidecar file | 伴随文件 | | | 指与文档同目录的普通伴随文件 |
| sidecar record | 伴随记录 | | 旁挂记录 | 指与文档同目录的伴随记录文件 |
| smoke test | 冒烟测试 | | | |
| snapshot | 快照 | | | |
| source of truth | 真源 | | 事实来源、唯一来源 | |
| spine | 主干 | | | |
| staged | 暂存 | | | 沿用 git 官方中文翻译 |
| stale | 陈旧 | | 过期 | 与 `fresh``新鲜`)成对;门禁输出中保留英文 `stale` 不翻译;`expired` 才译为`过期` |
| step | 步骤 | | | |
| stream | 流 | | | |
| streaming | 流式输出 | | | |
| structural signature | 结构签名 | | | i18n 配对机制用语:门禁比对两侧文件时提取的有序结构序列(标题层级、代码块、列表等) |
| Summary | 概述 | | | 事故复盘标题用语 |
| system prompt | 系统提示词 | | | |

View File

@@ -1,6 +1,6 @@
# Translation prompt (pipeline asset)
本文件是自动翻译流水线的 prompt 模板;从 `# Translation Prompt` 开始的正文会逐字进入模型请求,因此本文件不参与双语配对(见 [README.md](README.md) 排除清单)。模板正文与内嵌 few-shot 正误例由 jingtingxiang 基于对存量译文的质量评审撰写,是流水线行为的拍板基线。渲染时把 [terminology.md](terminology.md) 整表填入 `{{terminology}}`除此之外不注入任何其他仓库文件translation-rules.md 约束人和 agent 的翻译工作,不注入本模板)。[style-samples.md](style-samples.md) 定义文体,模板中的 Examples 只用于说明典型问题,两者冲突时以文体样例为准。[提示词 v4 契约 Agent Note](../../.agents/notes/implemented/process/2026-07-23-translation-prompt-v4-contract.md) 记录该协议的决策与取舍;修改本文件会改变翻译行为,需正常经过 PR 评审。
本文件是自动翻译流水线的 prompt 模板;从 `# Translation Prompt` 开始的正文会逐字进入模型请求,因此本文件不参与双语配对(见 [README.md](README.md) 排除清单)。模板正文与内嵌 few-shot 正误例由 jingtingxiang 基于对存量译文的质量评审撰写,是流水线行为的拍板基线。渲染时把 [terminology.md](terminology.md) 整表填入 `{{terminology}}`除此之外不注入任何其他仓库文件translation-rules.md 约束人和 agent 的翻译工作,不注入本模板)。[style-samples.md](style-samples.md) 定义文体,模板中的 Examples 只用于说明典型问题,两者冲突时以文体样例为准。[提示词 v4 契约 Agent Note](../../.agents/notes/implemented/process/2026-07-23-translation-prompt-v4-contract.md) 记录兼容协议v7 保留该协议并选择性吸收经评估的生成质量改进。修改本文件会改变翻译行为,需正常经过 PR 评审。
## 占位符契约
@@ -33,128 +33,174 @@
````text
# Translation Prompt
You are a senior technical translator specializing in LLM and agent development documentation. Your task is to translate the given source document from {{source_lang}} to {{target_lang}}, producing natural, professional technical prose.
You are a senior technical translator specializing in LLM and agent development documentation. Your task is to translate the complete source document from {{source_lang}} to {{target_lang}}, producing natural, professional technical prose.
Read each complete semantic unit, understand it, and restate it as a native technical author would write it in the target language. Do not mechanically preserve source-language syntax. Then verify the translation against the source clause by clause: preserve every proposition and add none. Fluency never justifies losing or altering meaning, and completeness never justifies unnatural word-for-word prose.
## Priority
Apply these authorities in order:
1. Preserve the source meaning and the required document structure, protected content, and formatting.
2. Follow the injected terminology table exactly.
3. Use the injected whole-document gold pairs to calibrate target-language voice and phrasing.
4. Apply the general writing guidance and illustrative examples in this prompt.
A lower-priority rule may refine but never override a higher-priority requirement. Gold pairs calibrate voice; they are not a translation memory. No style preference, gold-pair phrasing, or embedded example may override source meaning, required structure, protected content, or the terminology table.
## Quality Requirements
### Structure and Format Preservation
- Output a complete translated document that maintains exactly the same structure as the source: heading hierarchy, list shape, table columns, link targets, and code blocks.
- Fenced code blocks must be byte-identical to the source, including ALL comments inside them. Do NOT translate comments inside code blocks. This is a hard rule with no exceptions.
- Inline code spans (commands, flags, paths, API names, version numbers) must be kept verbatim. Never translate or reformat them.
- Every relative link must point to the same target as in the source. Link text is translated; link targets are not.
- Language switcher line: when translating into Chinese, write `[English](source-filename.md) | 中文`. When translating into English, write `English | [中文](source-filename.zh.md)`. Do NOT copy the switcher line from the source file unchanged — you must flip the link direction.
- After a closing bold marker `**`, insert a space before the next character when that character is a Latin letter, digit, or CJK ideograph. Never insert a space before any punctuation (full-width or half-width).
- Output a complete translated document that maintains the same document frame as the source: heading hierarchy and order, list kinds and item counts, ordered-list starts, table rows and columns, link targets, and code blocks.
- Paragraph boundaries may change within the same structural unit when the target language needs different semantic grouping. Do not merge or move content across headings, list items, table cells, or other independent structural units.
- Keep each prose paragraph on one physical line. Use paragraph breaks, not hard-wrapped lines inside a paragraph.
- Fenced code blocks must be byte-identical to the source, including info strings, whitespace, and ALL comments inside them. Do NOT translate or reformat any content inside code blocks. This is a hard rule with no exceptions.
- Inline code spans must be kept verbatim. This includes commands, flags, paths, identifiers, API and event names, config keys, protocol values, version numbers, and other machine-readable tokens. Never translate or reformat them.
- Every relative link must point to the same target as in the source. Translate link text; do not change link targets.
- Language switcher line: when an English source contains `English | [中文](source-filename.zh.md)`, write `[English](source-filename.md) | 中文`. When a Chinese source contains `[English](source-filename.md) | 中文`, write `English | [中文](source-filename.zh.md)`. Do NOT copy the source switcher unchanged. If the source has no switcher, do not invent a filename or switcher; the pipeline inserts the canonical target switcher after parsing `<final>`.
- Preserve emphasis marker types and the semantic spans they cover. Do not add, remove, move, or change bold and italic markers.
### Faithfulness
- Preserve every proposition in the source and add none. Every sentence, list item, note, FIXME, warning, example, caveat, prerequisite, and guarantee must have an equivalent in the translation. Count list items on both sides.
- Preserve actors, objects, conditions, exceptions, negation, modality, causal relationships, and distinctions between concepts.
- Preserve the exact strength and orientation of contracts. Completion and lifecycle conditions, failure behavior, directions and data flow, normal and exceptional result channels, ownership changes, and quantitative bounds must not be weakened, strengthened, reversed, or merged.
- Translate ideas rather than source-language idioms, but never use fluency as a reason to omit or alter meaning.
### Tone and Style
- The translation must read as if originally written in the target language by a native speaker. If an expression sounds like a word-for-word rendering from the source language, rephrase it.
- The translation must read as if originally written in the target language by a native technical author. If an expression sounds like a word-for-word rendering from the source language, rephrase it.
- Write in a professional, formal tone appropriate for developer documentation. Never use colloquial or casual expressions.
- Use polite imperative forms where the text instructs the reader to do something.
- Name an actor when the target language would otherwise obscure an actor that the source states or unambiguously implies. Never invent responsibility merely to avoid a passive construction.
- Prefer established target-language engineering idiom over literal renderings, and localize metaphors instead of transplanting them.
- Use polite imperative forms where the text instructs the reader to do something. In Chinese, address the reader as ``, not ``.
- Keep the author's register: concise stays concise, detailed stays detailed.
### Sentence Structure
- Break long sentences with commas or semicolons. Avoid run-on sentences.
- Prefer active voice. Convert passive constructions to active if it reads more naturally.
- Translate meaning, not words. Restructure sentences where the target language grammar requires it.
- Do not invent words or expressions that do not exist in natural technical writing of the target language.
- Break long sentences where the target language needs a pause. Avoid run-on sentences.
- Use active voice when it improves clarity without changing or inventing the actor. Retain passive voice when the actor is unknown, irrelevant, or intentionally omitted.
- Restructure source-language syntax into clear target-language syntax. Preserve the logical scope of conditions, concessions, negation, coordination, and modifiers.
- Split or combine clauses when needed for readability, provided every source relationship remains explicit.
- Translate meaning, not words. Do not invent words or expressions that a native technical author would not use.
### Word Choice
- Prefer precise, formal vocabulary over casual or colloquial alternatives.
- When multiple synonyms exist, choose the one most commonly used in professional technical documentation of the target language.
- Translate ordinary prose when an established target-language expression is clear. Preserve proper nouns, canonical product names, code identifiers, APIs, paths, package names, and terms that the terminology table requires to remain in the source language.
- Use context to resolve polysemous words. A familiar word does not have one fixed rendering in every technical domain.
- Avoid slang, internal jargon, or overly literal translations that would not be recognized by the general developer audience.
- Do not use the same word to translate two different source-language terms that carry distinct meanings.
- Avoid repeating the same verb in close proximity; vary word choice for readability.
- Do not use the same word to translate distinct source-language concepts when their distinction matters.
- Avoid repeating the same ordinary verb in close proximity when a natural equivalent preserves the exact meaning. Never vary a terminology-table form, defined concept, or contract verb merely for stylistic variety.
#### When translating into Chinese
- When a number modifies a noun, always include a Chinese classifier or measure word (量词). For example: "three-package seam" → "由三个包构成的 seam", not "三包 seam".
- When a number modifies a noun, include a natural Chinese classifier or measure word when Chinese grammar requires one. For example: "three-package seam" → "由三个包构成的 seam", not "三包 seam". Do not add classifiers to code, identifiers, versions, units, or fixed names.
### Punctuation
#### When translating into Chinese
- Use full-width Chinese punctuation in prose: `,。:;?!()「」`.
- Strongly prefer replacing all em-dashes (——) with colons, periods, commas, or parentheses. Keep an em-dash only if no other punctuation works at all.
- Use enumeration commas (、) between parallel items, not regular commas.
- List item endings: use semicolons or no punctuation. Do not end list items with commas.
- Put one half-width space between Chinese text and Latin words/numbers.
- For RFC 2119 keywords (MUST, MUST NOT, SHOULD, MAY), translate to the corresponding Chinese term (必须、禁止、应当、可以) and keep the SOURCE emphasis marker: plain source stays plain (必须), italic source stays italic (*必须*), and bold source stays bold (**必须**).
- Use full-width Chinese punctuation in Chinese prose: `,。:;?!()「」`. Keep half-width punctuation inside code spans, numbers, and complete verbatim English text.
- Prefer colons, periods, commas, or parentheses over em dashes when they make the sentence clearer or more natural. Keep an em dash when it is the clearest natural punctuation.
- Use enumeration commas (、) between parallel Chinese items, not regular commas.
- Keep list-item endings consistent with their grammar. Complete sentences may end with periods or other grammatically required punctuation; do not end list items with commas.
- Put one half-width space between Chinese text and Latin words or numerals. Do not add a space next to full-width punctuation, and do not leave a meaningless half-width space between two Chinese characters.
- Markdown emphasis markers do not create a word boundary. Determine spacing from the rendered adjacent characters: Chinese next to Chinese takes no space, while Chinese next to a Latin word or numeral takes one half-width space.
- Use half-width digits and Latin letters, never full-width forms.
- For RFC 2119 keywords (MUST, MUST NOT, SHOULD, MAY), translate to the corresponding Chinese term (必须、禁止、应当、可以), preserve the SOURCE emphasis span exactly, and do not weaken its normative strength: plain source stays plain (必须), italic source stays italic (*必须*), and bold source stays bold (**必须**).
#### When translating into English
(To be added.)
- Use half-width English punctuation and standard English spacing. Preserve full-width punctuation only in verbatim Chinese text.
- Convert enumeration commas (、) to English commas and Chinese prose quotation marks to English double quotes.
- Convert Chinese topic-comment sentences and omitted-subject constructions into clear English subjects when the actor is stated or unambiguously implied. Do not invent an actor.
- Use concise professional developer prose and established English technical terms. Do not transliterate Chinese engineering idioms literally.
- Use the terminology table's English column exactly and do not carry Chinese first-occurrence glosses into English prose.
## Terminology
A terminology table is provided below. Follow it strictly:
- Render every listed term exactly as specified.
- When the target language is Chinese, use the "中文" column. On first occurrence, write the "首次出现" value with its parenthetical gloss; on subsequent occurrences, write only the part before the parentheses.
- When the target language is Chinese, use the "中文" column. On the document's first prose occurrence, write the "首次出现" value when one is specified; on later occurrences, write only the part before the parenthetical gloss.
- When the target language is English, use the "English" column without a Chinese gloss; do not copy the "中文" or "首次出现" value into English prose.
- If a term has already been glossed as part of a compound term, do not gloss it again when it appears alone later.
- NEVER use translations listed in the "不要译作" column.
- For technical terms not in the table, follow the target language: for a Chinese target, use an established Chinese rendering from a major Chinese-language OSS or vendor source, or keep the source term and flag it as pending when no such precedent exists; for an English target, use the established English technical term, or preserve an ambiguous source term with a short English gloss and flag it as pending. Do not invent a translation. This rule applies to terminology only; for general prose, freely restructure and paraphrase for natural expression.
- Code spans and other protected tokens remain verbatim even when their text resembles a listed term.
- For an unlisted technical term, use an established target-language technical term when its meaning is unambiguous in context. For a Chinese target, use an established Chinese rendering from a major Chinese-language OSS or vendor source; if you cannot reliably determine such a rendering, preserve the source term and record `[Terminology: pending]` in `<review>` with a tentative rendering for human review. For an English target, use the established English technical term; if the source term has no unambiguous established equivalent, preserve it with the shortest English gloss needed to make it intelligible and record `[Terminology: pending]` in `<review>`. A tentative rendering may appear in `<review>` but must not be silently adopted in `<translation>` or `<final>`, and you must not invent or claim a specific external precedent. This rule applies to terminology only; for general prose, freely restructure and paraphrase for natural expression.
{{terminology}}
## Output Format
Produce your output in three XML sections:
Return exactly three raw XML sections in the order shown below. Do not wrap the response in a Markdown code fence and do not add analysis or text before, between, or after the sections. The fence below only displays the required shape; do not reproduce the fence.
The outer section tags are framing. If Markdown inside any section body contains a line consisting only of `<translation>`, `</translation>`, `<review>`, `</review>`, `<final>`, or `</final>`, prefix that line with `\`. If the original line already has one or more backslashes immediately before the tag, add one more. The parser removes exactly one framing escape; tags mentioned inline need no escaping.
```xml
<translation>
(Complete translation of the source document)
(First pass: the complete translation, written as natural target-language technical prose)
</translation>
<review>
(Self-review notes, one correction per line with category tag, e.g.)
(Second pass: actual corrections only, one correction per line with a category tag, e.g.)
- [Tone] "旁挂记录" → "伴随记录"(生造词)
- [Sentence] 第 3 段补充逗号断句
- [Punctuation] 两处破折号替换为冒号
- [Terminology: pending] source term → tentative rendering
- 无修正
</review>
<final>
(Final translation after corrections)
(Complete final translation after corrections)
</final>
```
## Self-Review Instructions
After writing `<translation>`, re-read it in the target language only, without looking at the source. Check by category:
After writing `<translation>`, verify it in two directions. First re-read it in the target language only, without looking at the source; awkward phrasing is easier to notice without source-language anchoring. Then compare it against the source clause by clause for completeness and exact meaning. Resolve doubts before writing `<review>`; do not include reasoning transcripts, checks that passed, tentative suggestions, retractions, or no-op corrections.
**Structure**
- Is the heading hierarchy, list shape, and code block content identical to the source?
- Are ALL comments inside code blocks left untranslated (byte-identical to source)?
- Is the language switcher line correctly flipped (not copied from source)?
- Are link targets preserved, and are spaces after bold markers present only before Latin letters, digits, or CJK ideographs?
- Is the heading hierarchy and order, list shape and count, ordered-list start, table shape, and code block content identical to the source?
- Are ALL comments and info strings inside code blocks left untranslated and byte-identical to the source?
- Are inline code spans and machine-readable tokens verbatim?
- Is an existing language switcher correctly flipped, and is no switcher or filename invented when the source lacks one?
- Are link targets and emphasis spans preserved?
- Does spacing across emphasis boundaries follow the same Chinese/Latin/numeral rule as ordinary prose?
- Are wrapper-tag lines inside section bodies escaped with one additional backslash?
**Faithfulness**
- Clause by clause, is anything added, dropped, weakened, strengthened, reversed, merged, or re-bounded? Are list item counts identical on both sides?
- Do actors, objects, conditions, exceptions, negation, modality, causal relationships, guarantees, contract directions, result channels, ownership changes, and quantities survive exactly?
**Tone & Style**
- Does every sentence read as if originally written by a native speaker?
- Is there any colloquial, casual, or overly informal phrasing?
- Does every sentence read as if originally written by a native technical author?
- Is there any colloquial, casual, overly informal, promotional, or transplanted metaphorical phrasing?
- Are actors explicit where the target language needs them, without inventing responsibility?
**Sentence Structure**
- Are there run-on sentences that need breaking?
- Are there stiff passive constructions that should be converted to active voice?
- Are there stiff passive constructions that can safely become active, or active constructions that invent an actor?
- Are conditions, concessions, negation, coordination, and modifiers scoped clearly?
**Word Choice**
- Are there overly literal translations that sound unnatural?
- Is the same target-language word used to translate two distinct source concepts?
- Are ordinary prose words left untranslated despite an established target-language expression?
- Does each polysemous word fit its local context?
- Is the same target-language word used for distinct source concepts, or is a defined term varied merely to avoid repetition?
- Is any slang or internal jargon present?
**Terminology**
- For a Chinese target, are first-occurrence glosses correctly applied (not missing, not repeated)? For an English target, are Chinese glosses absent?
- For a Chinese target, are first-occurrence glosses correctly applied to the true first prose occurrence, neither missing nor repeated? For an English target, are Chinese glosses absent?
- Are any "不要译作" forbidden translations present?
- For unlisted terms, does a Chinese target use established Chinese precedent or retain the source term as pending, and does an English target use established English terminology or preserve only an ambiguous source term with a short English gloss?
- Do protected tokens remain untouched even when they resemble terminology entries?
- For an unlisted term, does a Chinese target use an established Chinese rendering or preserve the source term as pending when no reliable rendering is known, and does an English target use the established English technical term or preserve only an ambiguous source term with the shortest necessary gloss and a pending notice?
**Punctuation** (when target is Chinese)
- Are there em-dashes that should be replaced with colons, periods, or commas?
- Are list items ending with commas instead of semicolons?
- Do RFC 2119 keywords preserve the source emphasis exactly?
- Are punctuation, mixed-script spacing, quotation marks, Latin letters, and digits in their required forms?
- Are there em dashes that make the sentence less clear and should be replaced, while natural em dashes remain intact?
- Are list-item endings grammatically consistent, with none ending in commas?
- Do RFC 2119 keywords preserve the source emphasis span and normative strength exactly?
Record corrections in `<review>` with category tags. Then output the corrected version in `<final>`. If no corrections are needed, write "无修正" in `<review>` and copy the translation unchanged into `<final>`.
Record actual corrections in `<review>`, then output the corrected complete document in `<final>`. If no correction or pending terminology notice is needed, write exactly `- 无修正` in `<review>` and copy `<translation>` unchanged into `<final>`. If `<review>` contains only pending terminology notices, copy `<translation>` unchanged into `<final>`.
## Examples
Below are representative examples of common problems and their corrections. Follow the "Good" versions.
Below are representative examples of common problems and their corrections. Follow the "Good" versions within the rule each example illustrates; examples do not override source context or higher-priority requirements.
### Colloquial verb → Professional verb
- Source: `The repo pins pnpm@11.7.0 in package.json`

View File

@@ -38,9 +38,12 @@ flowchart TD
end
subgraph group_bash["packages/bash"]
pkg_bash["bash"]
pkg_bash_env["bash-env"]
pkg_bash_local["bash-local"]
pkg_bash_sandbox["bash-sandbox"]
pkg_pwsh_local["pwsh-local"]
pkg_tool_bash["tool-bash"]
pkg_tool_pwsh["tool-pwsh"]
end
subgraph group_fs["packages/fs"]
pkg_fs["fs"]
@@ -503,6 +506,10 @@ flowchart TD
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_subprocess
pkg_bash_local --> pkg_timeout
pkg_pwsh_local --> pkg_bash
pkg_pwsh_local --> pkg_invariants
pkg_pwsh_local --> pkg_subprocess
pkg_pwsh_local --> pkg_timeout
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
pkg_fs_policy --> pkg_fs
@@ -690,18 +697,11 @@ flowchart TD
pkg_tool_goal --> pkg_session
pkg_tool_goal --> pkg_system_prompt
pkg_tool_goal --> pkg_tools
pkg_tool_bash --> pkg_agent
pkg_tool_bash --> pkg_bash
pkg_tool_bash --> pkg_invariants
pkg_tool_bash --> pkg_llm
pkg_tool_bash --> pkg_paths
pkg_tool_bash --> pkg_sandbox
pkg_tool_bash --> pkg_sandbox_policy
pkg_tool_bash --> pkg_session_persistence
pkg_tool_bash --> pkg_system_prompt
pkg_tool_bash --> pkg_tasks
pkg_tool_bash --> pkg_tools
pkg_tool_bash --> pkg_user_approval
pkg_bash_env --> pkg_bash
pkg_bash_env --> pkg_invariants
pkg_bash_env --> pkg_paths
pkg_bash_env --> pkg_session_persistence
pkg_bash_env --> pkg_tools
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_invariants
pkg_tool_fs --> pkg_llm
@@ -885,6 +885,25 @@ flowchart TD
pkg_tool_workflow --> pkg_system_prompt
pkg_tool_workflow --> pkg_tools
pkg_tool_workflow --> pkg_workflow
pkg_tool_bash --> pkg_agent
pkg_tool_bash --> pkg_bash
pkg_tool_bash --> pkg_bash_env
pkg_tool_bash --> pkg_invariants
pkg_tool_bash --> pkg_llm
pkg_tool_bash --> pkg_sandbox
pkg_tool_bash --> pkg_sandbox_policy
pkg_tool_bash --> pkg_system_prompt
pkg_tool_bash --> pkg_tasks
pkg_tool_bash --> pkg_tools
pkg_tool_bash --> pkg_user_approval
pkg_tool_pwsh --> pkg_agent
pkg_tool_pwsh --> pkg_bash
pkg_tool_pwsh --> pkg_bash_env
pkg_tool_pwsh --> pkg_invariants
pkg_tool_pwsh --> pkg_llm
pkg_tool_pwsh --> pkg_system_prompt
pkg_tool_pwsh --> pkg_tasks
pkg_tool_pwsh --> pkg_tools
pkg_subagent_acp --> pkg_agent
pkg_subagent_acp --> pkg_invariants
pkg_subagent_acp --> pkg_llm
@@ -964,27 +983,6 @@ flowchart TD
pkg_client_ui_subagent --> pkg_invariants
pkg_client_ui_subagent --> pkg_subagent
pkg_client_ui_subagent --> pkg_token_meter
pkg_agent_spine_demo --> pkg_agent
pkg_agent_spine_demo --> pkg_agent_loop
pkg_agent_spine_demo --> pkg_goal
pkg_agent_spine_demo --> pkg_goal_session
pkg_agent_spine_demo --> pkg_invariants
pkg_agent_spine_demo --> pkg_llm
pkg_agent_spine_demo --> pkg_llm_retry
pkg_agent_spine_demo --> pkg_paths
pkg_agent_spine_demo --> pkg_scope
pkg_agent_spine_demo --> pkg_session
pkg_agent_spine_demo --> pkg_session_title
pkg_agent_spine_demo --> pkg_skill
pkg_agent_spine_demo --> pkg_skill_local
pkg_agent_spine_demo --> pkg_system_prompt
pkg_agent_spine_demo --> pkg_tasks_local
pkg_agent_spine_demo --> pkg_tool_bash
pkg_agent_spine_demo --> pkg_tool_goal
pkg_agent_spine_demo --> pkg_tool_skill
pkg_agent_spine_demo --> pkg_tool_tasks
pkg_agent_spine_demo --> pkg_tools
pkg_agent_spine_demo --> pkg_workspace_context
pkg_sdk_protocol --> pkg_invariants
pkg_sdk_protocol --> pkg_llm
pkg_sdk_protocol --> pkg_session
@@ -1020,6 +1018,39 @@ flowchart TD
pkg_jsonrpc --> pkg_sdk_protocol
pkg_jsonrpc --> pkg_session
pkg_jsonrpc --> pkg_subagent
pkg_agent_spine_demo --> pkg_agent
pkg_agent_spine_demo --> pkg_agent_loop
pkg_agent_spine_demo --> pkg_bash_env
pkg_agent_spine_demo --> pkg_goal
pkg_agent_spine_demo --> pkg_goal_session
pkg_agent_spine_demo --> pkg_invariants
pkg_agent_spine_demo --> pkg_llm
pkg_agent_spine_demo --> pkg_llm_retry
pkg_agent_spine_demo --> pkg_paths
pkg_agent_spine_demo --> pkg_scope
pkg_agent_spine_demo --> pkg_session
pkg_agent_spine_demo --> pkg_session_title
pkg_agent_spine_demo --> pkg_skill
pkg_agent_spine_demo --> pkg_skill_local
pkg_agent_spine_demo --> pkg_system_prompt
pkg_agent_spine_demo --> pkg_tasks_local
pkg_agent_spine_demo --> pkg_tool_bash
pkg_agent_spine_demo --> pkg_tool_goal
pkg_agent_spine_demo --> pkg_tool_skill
pkg_agent_spine_demo --> pkg_tool_tasks
pkg_agent_spine_demo --> pkg_tools
pkg_agent_spine_demo --> pkg_workspace_context
pkg_sdk_client --> pkg_invariants
pkg_sdk_client --> pkg_llm
pkg_sdk_client --> pkg_sdk_protocol
pkg_sdk_client --> pkg_session
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
pkg_acp_demo --> pkg_acp
pkg_acp_demo --> pkg_agent_spine_demo
pkg_acp_demo --> pkg_app_boot
@@ -1040,17 +1071,6 @@ flowchart TD
pkg_cli_demo --> pkg_session_persistence_jsonl
pkg_cli_demo --> pkg_tools
pkg_cli_demo --> pkg_workspace_context
pkg_sdk_client --> pkg_invariants
pkg_sdk_client --> pkg_llm
pkg_sdk_client --> pkg_sdk_protocol
pkg_sdk_client --> pkg_session
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
```
| Package | Group | Depends on |
@@ -1139,6 +1159,7 @@ flowchart TD
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection) |
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`pwsh-local`](../packages/bash/pwsh-local) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`skill`](../packages/skill/skill) |
@@ -1179,7 +1200,7 @@ flowchart TD
| [`session-telemetry-otel`](../packages/telemetry/session-telemetry-otel) | `telemetry` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`session`](../packages/core/session), [`session-telemetry`](../packages/telemetry/session-telemetry) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`bash-env`](../packages/bash/bash-env) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
@@ -1210,6 +1231,8 @@ flowchart TD
| [`tool-pty`](../packages/pty/tool-pty) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`pty`](../packages/pty/pty), [`retention`](../packages/util/retention), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`tool-tasks`](../packages/tasks/tool-tasks) | `tasks` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`bash-env`](../packages/bash/bash-env), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-pwsh`](../packages/bash/tool-pwsh) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`bash-env`](../packages/bash/bash-env), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
@@ -1221,14 +1244,14 @@ flowchart TD
| [`client-ui-permission`](../packages/client/ui-permission) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-command`](../packages/client/ui-command), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`permission`](../packages/ui/permission) |
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`plan-mode`](../packages/plan/plan-mode) |
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`paths`](../packages/util/paths), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks-local`](../packages/tasks/tasks-local), [`tool-bash`](../packages/bash/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`sdk-protocol`](../packages/sdk/sdk-protocol) | `sdk` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/sdk-protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/acp/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`cli-demo`](../packages/examples/cli-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`bash-env`](../packages/bash/bash-env), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`paths`](../packages/util/paths), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks-local`](../packages/tasks/tasks-local), [`tool-bash`](../packages/bash/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`sdk-client`](../packages/sdk/sdk-client) | `sdk` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/sdk-protocol), [`session`](../packages/core/session) |
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/sdk-client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/acp/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`cli-demo`](../packages/examples/cli-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/postmortem/0001-acp-default-export-drops-inject.md
0001-acp-default-export-drops-inject.md: 2d36f24fa54814e39345d7fe68792023c2cf0194
0001-acp-default-export-drops-inject.zh.md: c528f8be04013803274e80e51970754e92a935ae
0001-acp-default-export-drops-inject.zh.md: 6ae7d45f58e09f205a5653f7c6d306014d4d393e

View File

@@ -6,7 +6,7 @@ Status: resolved (fix in PRPull Request #41 `feat/acp-2-bridge`)
## 摘要
两个集成错误在单元测试全覆盖的情况下仍然导致 ACP 崩溃:一个 default export 使 Loader 丢弃了 `inject`,一个经 traceable 代理的可选服务查找在 shadow 边界上失败。手动挂载的测试绕过了这两条路径。修复方案增加了无需 API key 的真实 Loader 覆盖率,并为插件导出和可选服务访问制定了包package级规则。
两个集成错误在单元测试全覆盖的情况下仍然导致 ACP 崩溃:一个默认导出使 Loader 丢弃了 `inject`,一个经可追踪代理的可选服务查找在 shadow 边界上失败。手动挂载的测试绕过了这两条路径。修复方案增加了无需 API key 的真实 Loader 覆盖率,并为插件导出和可选服务访问制定了包级规则。
## 概述
@@ -20,7 +20,7 @@ ACP 服务器无法创建或加载任何一个会话——而这正是编辑器
- bridgeRFC 010落地时附带完整的单元测试套件codec、内存传输、基于属性的协议形状测试、失败路径、HMR热模块替换、一个需要 key 的真实 API e2e 测试,以及一个无需 key 的 stdout 纯净性 e2e 测试。全部绿色100% 覆盖率。
- 真实 Zed 会话在 `session/new` 上立即失败,报错 `cannot get property "agents" without inject`
- 调查最初追踪了一个 Cordis「traceable/shadow」理论看似合理且该机制确实存在——见 Bug #2),随后在 vendor 的 `reflect.ts` 对实际 fiber 遍历做了插桩,并运行了真实子进程。trace 显示 throw 发生`apply()` 第 179 行、*插件加载时*,位于 ROOT fiber 且没有 shadow——推翻了 shadow 理论对 `session/new` 的解释。
- 调查最初追踪了一个 Cordis「traceable/shadow」理论看似合理且该机制确实存在——见 Bug #2),随后在 vendor 目录中`reflect.ts` 对实际 fiber 遍历做了插桩,并运行了真实子进程。跟踪结果显示,异常`apply()` 第 179 行、*插件加载时*抛出,位于 ROOT fiber 且没有 shadow——推翻了 shadow 理论对 `session/new` 的解释。
- 找到根因 #1:一行多余的 `export default apply`。删除后 `session/new` 修复。
- 删除后暴露了 Bug #2`session/load` 仍然在 `sessionPersistence` 上抛错——这是一个真正不同的机制shadow 遍历),通过隔离修复并重新运行真实子进程得到确认。
@@ -47,19 +47,19 @@ unwrapExports(exports: any) {
}
```
存在 default export 时,`exports.default ?? exports` 解析为**裸 `apply` 函数**。裸函数没有 `inject`、没有 `name`、没有 `Config` 属性——这些作为*兄弟*命名导出存在于模块命名空间上,而 unwrap 到 `.default` 把整个命名空间丢弃了。Loader 随后基于空的 `inject` 构建了插件的 fiber。
存在默认导出时,`exports.default ?? exports` 解析为**裸 `apply` 函数**。裸函数没有 `inject`、没有 `name`、没有 `Config` 属性——这些作为*兄弟*命名导出存在于模块命名空间上,而 unwrap 到 `.default` 把整个命名空间丢弃了。Loader 随后基于空的 `inject` 构建了插件的 fiber。
因此 `apply` 在一个**没有注入任何服务**的 fiber 中运行。第一行 `const agents = ctx.agents` 遍历 fiber 树ROOT → Include → Loader → ROOT在所有 fiber 的 store 中都找不到 `agents`,到达根 fiber`runtime === null`)后抛出 `cannot get property "agents" without inject`。崩溃发生在*加载时*,而非后续的请求处理器中——请求只是恰好触发了加载。
**修复:** 删除 `export default apply`。Loader 随后使用模块命名空间,正确识别 `inject`/`name`/`Config``apply` 在一个真正授予了声明服务的 fiber 中运行。
## 根因 #2——可选服务读取通过 traceable shadow 触发 inject 守卫(导致 `session/load` 崩溃)
## 根因 #2——可选服务读取通过可追踪 shadow 触发 inject 守卫(导致 `session/load` 崩溃)
修复 #1 后,`session/new` 正常工作,但 `session/load` 仍然抛出 `cannot get property "sessionPersistence" without inject`。这个问题*确实* Cordis 的 traceable/shadow 机制,值得精确理解。
修复 #1 后,`session/new` 正常工作,但 `session/load` 仍然抛出 `cannot get property "sessionPersistence" without inject`。这个问题*确实*源于 Cordis 的可追踪代理/shadow 机制,值得精确理解。
`session/load` 调用 `agents.resume(...)`,后者委托给 `AgentLoop.resume()`,其中读取了 `this.ctx.sessionPersistence`。`AgentLoop` 的 `static inject` 故意不包含 `sessionPersistence`——注入它会导致非持久化的演示永远挂起,等待一个永远不会加载的后端。该服务由一个独立的兄弟插件/fiber 提供,以机会性方式读取。
Cordis 中的服务访问通过上下文代理(`vendor/cordis/src/reflect.ts`)进行。当通过从外部 fiber 获取的 *traceable 代理*调用服务方法时此处bridge fiber 调用 `ctx.agents.resume`,注册表返回 `this.factory`——即 `AgentLoop`——重新包装为绑定到调用方的新 traceable 代理),`createShadowMethod``vendor/cordis/src/utils.ts`)将 `this` 重新绑定到一个 *shadow* 对象,其 `ctx` 携带 `[symbols.shadow]` 指向 `AgentLoop` 自身的构造上下文。在 `resume` 内部,`this.ctx.sessionPersistence` 的解析从 shadow 的 fiber 开始遍历:
Cordis 中的服务访问通过上下文代理(`vendor/cordis/src/reflect.ts`)进行。当通过从另一条 fiber 获取的*可追踪代理*调用服务方法时此处bridge fiber 调用 `ctx.agents.resume`,注册表返回 `this.factory`——即 `AgentLoop`——重新包装为绑定到调用方的新 traceable 代理),`createShadowMethod``vendor/cordis/src/utils.ts`)将 `this` 重新绑定到一个 *shadow* 对象,其 `ctx` 携带 `[symbols.shadow]` 指向 `AgentLoop` 自身的构造上下文。在 `resume` 内部,`this.ctx.sessionPersistence` 的解析从 shadow 的 fiber 开始遍历:
```ts ignore-check
// reflect.ts get handler
@@ -88,7 +88,7 @@ if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false) // ← direct glob
## 为什么所有测试都没有捕获(真正的失败)
两个 bug 共享同一个流程缺口:**没有任何测试通过插件的真实加载路径或真实调用拓扑来驱动它。**
两个 bug 都源于同一个根本流程缺口:**没有任何测试通过插件的真实加载路径或真实调用拓扑来驱动它。**
- 内存 harness 通过手动构建插件对象来挂载 bridge`ctx.plugin({ name, inject, apply })`。这手动提供了 `inject`,因此永远无法复现 Bug #1——`unwrapExports` 只被 *Loader* 调用,`ctx.plugin` 从不调用它。即使 `ctx.plugin(NamespaceImport)` 也无法捕获。
- 同一个 harness 将所有内容平铺挂载在一个根上下文上,因此从中触达的 `AgentLoop` 恢复要么运行在顶层(`!runtime` 绕过),要么通过一个 origin 仍然解析在 root 上的 shadow——掩盖了 Bug #2 的祖先遍历失败。
@@ -101,13 +101,13 @@ if (!ctx.fiber.runtime) return ctx.reflect.get(prop, false) // ← direct glob
- **删除 `export default apply`**`packages/acp/acp/src/index.ts`——Bug #1 的修复。
- **`AgentLoop.resume` 使用 `this.ctx.get('sessionPersistence')`**`packages/core/agent-loop/src/index.ts`——Bug #2 的修复,附注释说明 shadow 遍历陷阱。
- **无需 key 的 `session/new` e2e通过真实 stdio 运行**`examples/acp-agent/tests/acp.e2e.ts`):以子进程方式通过真实 Loader 启动示例,并断言 `session/new` 正常返回。无需 API key 即可 Bug #1 上大声失败。已验证恢复 `export default apply` 时测试失败。
- **无需 key 的 `session/new` e2e通过真实 stdio 运行**`examples/acp-agent/tests/acp.e2e.ts`):以子进程方式通过真实 Loader 启动示例,并断言 `session/new` 正常返回。无需 API key 即可明确暴露 Bug #1。已验证恢复 `export default apply` 时测试失败。
- **e2e spawn 中设置 `TSX_TSCONFIG_PATH`**:子进程从临时 cwd 运行tsx 无法通过向上搜索找到仓库根的 tsconfig `paths` 映射——因此 dsh-* 的 import 静默回退到已构建的 `lib/`。将 tsx 指向仓库 tsconfig 使解析不依赖 cwd确保测试运行的是*源码*而非可能陈旧的构建产物。
- **[docs/testing.md](../testing.md) 规则**:「测试真实入口路径」,行覆盖率不等于行为覆盖率——将这一教训编纂为所有未来插件的规则。
## 经验教训
- 命名空间插件与 default export 在 Cordis Loader 下互斥。选择命名空间形式(`name`/`inject`/`Config`/`apply`),不要添加 `export default`——`unwrapExports` 会丢弃命名空间。
- 对于插件机会性读取但未在 `static inject` 中声明的服务,使用 `ctx.get(name)`,绝不使用 `ctx.<name>`。属性代理通过仅向祖先方向的 fiber 遍历解析,经由外部 shadow 时会失败;`ctx.get(name)` 是拓扑无关的查找(且默认严格——非活跃后端读取为 `undefined`而非在 teardown 过程中被交出)。
- 对于插件机会性读取但未在 `static inject` 中声明的服务,使用 `ctx.get(name)`,绝不使用 `ctx.<name>`。属性代理通过仅向祖先方向的 fiber 遍历解析,经由外部 shadow 时会失败;`ctx.get(name)` 是拓扑无关的查找(且默认采用严格模式——非活跃后端读取为 `undefined`不会在 teardown 期间仍将该后端返回给调用方)。
- 手动构建插件的测试无法验证插件的加载方式。至少一个测试必须端到端地驱动真实的 Loader/export 路径。当核心操作不调用模型时,该测试无需 API key——因此它属于 CI而非 key 门控之后。
- 相信 trace,不要信理论。优雅的 shadow 解释是真实的,但它是*第二个* bug*第一个*是一行导出错误,在数小时看似合理但实际错误的推理之后,一个 fiber 遍历的 `console.error` 在几分钟内就找到了它。
- 相信跟踪结果,不要信理论。优雅的 shadow 解释是真实的,但它是*第二个* bug*第一个*是一行导出错误,在数小时看似合理但实际错误的推理之后,一个 fiber 遍历的 `console.error` 在几分钟内就找到了它。

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/postmortem/0002-js-expression-disabled-filesystem-tools.md
0002-js-expression-disabled-filesystem-tools.md: 30ff9d920821a8d55c4bea5f120f1aeeca6634b3
0002-js-expression-disabled-filesystem-tools.zh.md: b103ec6de5d6d6406ba48ec34f6ebb479e472352
0002-js-expression-disabled-filesystem-tools.zh.md: 3c18a48d3b7e925a6e75c2d3edb3ec642e72e1b3

View File

@@ -12,13 +12,13 @@ ACPAgent Client Protocol示例试图通过 `disabled: !!js ...` 有条件
默认的 ACP 组合有意只启用 bash因为其沙箱无法约束进程内的文件系统提供方。文件系统快照场景仍然需要 `read``write``edit`,因此这些插件被放在默认的 `cordis.yml` 中,并附带一个 `disabled` 表达式,意图仅在全权限启动和快照模式下启用它们。
Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader 递归地对插件的 `config` 进行插值,但直接消费 `disabled`入口元数据。因此每个文件系统入口看到的都是一个 truthy 对象,在所有模式下均保持禁用。
Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader 递归地对插件的 `config` 进行插值,但直接读取 `disabled`配置项元数据。因此每个文件系统配置项看到的都是一个 truthy 对象,在所有模式下均保持禁用。
## 影响
七个文件系统场景和一个混合工作区编辑场景调用了注册表中不存在的工具。其结构化会话日志携带 `ToolNotFoundError`code 为 `UNKNOWN_TOOL`stdout 渲染出通用的失败工具卡片。快照套件通过了,因为结构化会话日志和 stdout 渲染出的通用失败工具卡片均与刷新后的 fixture测试前置数据匹配它证明的是回归的确定性回放而非文件系统行为的正确性。
实际运行的受限默认模式并未获得意外的文件系统访问权限。一个简单的插值修复反而会制造该风险:权限预设在运行时更新 bash 沙箱和审批状态,但无法挂载、卸载或约束文件系统栈。
实际运行的受限默认模式并未获得意外的文件系统访问权限。草率地直接修复插值反而会带来这一风险:权限预设在运行时更新 bash 沙箱和审批状态,但无法挂载、卸载或约束文件系统栈。
## 时间线
@@ -29,7 +29,7 @@ Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader
## 根因
实现时假设 `!!js` 适用于整个 Loader 入口。其实际边界更窄:`Entry._resolveConfig()` 仅对 `entry.options.config` 进行插值;`Entry.disabled` 直接测试 `entry.options.disabled`不经过插值。YAML 标签在语法上合法,因此加载过程不产生任何诊断信息。
实现时假设 `!!js` 适用于整个 Loader 配置项。其实际边界更窄:`Entry._resolveConfig()` 仅对 `entry.options.config` 进行插值;`Entry.disabled` 直接测试 `entry.options.disabled`不经过插值。YAML 标签在语法上合法,因此加载过程不产生任何诊断信息。
快照框架将任何确定性的 transcript文本记录视为有效行为。Header pin 验证了组合后的工具 schema但文件系统场景共享来自默认组合的 pin因此未独立证明其所需工具已注册。刷新在任何语义断言拒绝缺失工具之前就已重写了预期的 stdout 和会话日志。
@@ -37,8 +37,8 @@ Cordis Include 将每个 `!!js` 标量解析为一个表达式对象。Loader
- 文件系统场景启动 `fs.cordis.yml`:一个显式的固定全权限 overlay配有对应的回放配置和独立的 request-header 类。
- [`AGENTS.md`](../../AGENTS.md) 与 [Cordis 入门](../cordis-primer.md#loader-configuration)明确说明 `!!js` 仅在插件 `config` 内有效,条件式组合应使用 overlay。
- `verify-cordis-config` 解析仓库中的 Cordis YAML拒绝 Loader 入口元数据中的表达式节点(包括 include patch 和插入的入口)。
- `dsh-acp-snapshot` 在新运行和已提交的会话 fixture 中拒绝结构化的 `UNKNOWN_TOOL` 结果,防止其被提交为预期输出。
- `verify-cordis-config` 解析仓库中的 Cordis YAML拒绝 Loader 配置项元数据中的表达式节点(包括 include patch 和插入的配置项)。
- `dsh-acp-snapshot`新运行和已提交的会话 fixture 中拒绝结构化的 `UNKNOWN_TOOL` 结果,防止其被提交为预期输出。
## 教训

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/postmortem/README.md
README.md: 4858f8841e92a895f2d1a840b59b42758e83d952
README.zh.md: 127eb19422f1eb6738c1d246791064096f4382f9
README.zh.md: 45fe23275e6236d5210ba32622b51457d170aaeb

View File

@@ -2,16 +2,16 @@
[English](README.md) | 中文
事故复盘记录的是:一个 bug 流入了不该流入的环节(真实用户、已合并的 PRPull Request、已发布的版本值得关注的是*为什么我们的流程放过了它*,而不仅仅是那一行修复。
事故复盘记录的是:一个 bug 出现在了不该出现的地方(真实用户、已合并的 PRPull Request、已发布的版本值得关注的是*为什么我们的流程放过了它*,而不仅仅是那一行修复。
事故复盘不是 [Agent Noteagent 决策记录)](../../.agents/notes/README.md)Agent Note 记录一个经过深思熟虑的设计决策及其被否决的替代方案,或提出未来工作)。它是一份回顾性的失败记录:什么坏了、机制是什么、为什么每道安全网都没拦住、以及为此新增了哪些具体防护措施,以确保同类 bug 下次出现时会明确报错。
事故复盘不是 [Agent Note](../../.agents/notes/README.md)Agent Note 记录一个经过深思熟虑的设计决策及其被否决的替代方案,或提出未来工作)。它是一份回顾性的失败记录:什么坏了、机制是什么、为什么每道安全网都没拦住、以及为此新增了哪些具体防护措施,以确保同类 bug 下次出现时会明确报错。
当一个 bug 满足以下条件时,请撰写事故复盘:**隐蔽**(机制不显而易见,即使是细心的工程师也得费力重新推导)、**系统性**(逃逸的原因是测试/工具/约定的缺口,而非一次性的笔误)、**重新发现的代价高**它消耗了真实的调试时间且下次还会如此。请链接该事故复盘所推动建立的防护措施测试、AGENTS.md 规则、ADR
当一个 bug 满足以下条件时,请撰写事故复盘:**隐蔽**(机制不显而易见,即使是细心的工程师也得费力重新推导)、**系统性**(逃逸的原因是测试工具约定的缺口,而非一次性的笔误)、**重新发现的代价高**它消耗了真实的调试时间且下次还会如此。请链接该事故复盘所推动建立的防护措施测试、AGENTS.md 规则、ADR
每篇事故复盘以一段**摘要**开头:一个简短段落,让忙碌的读者在三十秒内吸收要点——什么坏了、用直白的话说根因是什么、为什么逃逸了、可长期沿用的教训是什么——然后才是后续的详细「概述 / 时间线 / 根因 / 防护措施」各节。
每篇事故复盘以一段**执行摘要**开头:一个简短段落,让忙碌的读者在三十秒内吸收要点——什么坏了、用直白的话说根因是什么、为什么逃逸了、可长期沿用的教训是什么——然后才是后续的详细「概述、时间线、根因、防护措施」各节。
| # | 标题 |
|---|---|
| [0001](0001-acp-default-export-drops-inject.md) | ACPAgent Client Protocol服务器在连接时崩溃`export default` 丢失了插件的 `inject` |
| [0002](0002-js-expression-disabled-filesystem-tools.md) | 文件系统快照工具被一个字面量 `!!js` 对象永久禁用 |
| [0003](0003-web-agent-gui-feedback-loop.md) | Web agent 验证了替代服务器,而非承载其会话的 GUI |
| [0003](0003-web-agent-gui-feedback-loop.md) | Web agent(智能体)验证了替代服务器,而非承载其会话的 GUI |

View File

@@ -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 docs/testing.md
testing.md: 89d495e05c7521becea052ad67ac602ba28c22ef
testing.zh.md: 8e19734d09d5b0bdbeffe9426bed7c12f23fbc41
testing.md: 728c65f1911cbaa098f653af9436a92336fbe068
testing.zh.md: 72b7c3bcfa69f4c65fc128dbcbb19c080fb0fbc4

View File

@@ -7,9 +7,9 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning
## Tiers
- **Unit** (`pnpm run test`): vitest over package and example specs under their `tests/**` directories plus repository script specs under `scripts/**/*.spec.ts`; tests stay with the code area they exercise. Every registry gets an HMR-safety test (dispose the contributing fiber, assert cleanup). Prefer edge cases, error paths, event ordering, concurrency races, and permanent contract regressions (see `packages/core/agent-loop/tests/contract-regressions.spec.ts`).
- **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate is correctly flagging for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped.
- **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate is correctly flagging for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped. Per-file 100% on `packages/bash/pwsh-local/src` needs a real `pwsh`: without one its executor suites self-skip and `vitest.config.ts` exempts the file so pwsh-less hosts stay green, while CI runners ship pwsh and enforce the full bar.
- **Real-API e2e** (`pnpm run test:e2e`): with-key tests against live provider APIs — the DeepSeek model plus provider-specific smokes that gate on their own keys (`EXA_API_KEY`, `PERPLEXITY_API_KEY`, …); each suite self-skips without its key so keyless CI stays green ([real-API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md)).
- **Snapshot** (`pnpm run test:snapshot`): keyless expected outputs cover external behavior — transport contracts and presentation, while persisted logs pin assembled backend behavior. ACP boots the real automation-server example, replays a recorded session, and diffs normalized JSON-RPC plus the re-persisted log ([ACP snapshot Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)); headless pins `stream-json` through its real one-shot process. Use `pnpm run test:snapshot:record` when a model transcript changes and `pnpm run test:snapshot:refresh` when replay input remains valid; review every JSONL and expected-output diff. One ACP scenario (`text-turn`) pins full system-prompt/tool-schema content; other fixtures tokenize it so an edit churns one line ([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)).
- **Snapshot** (`pnpm run test:snapshot`): keyless expected outputs cover external behavior — transport contracts and presentation, while persisted logs pin assembled backend behavior. ACP boots the real automation-server example, replays a recorded session, and diffs normalized JSON-RPC plus the re-persisted log ([ACP snapshot Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)); headless pins `stream-json` through its real one-shot process. TUI journeys replay primary/child JSONL through the real loop and tools, then project ANSI into semantic terminal-state outputs; package snapshots retain transient states and a real PTY covers the process boundary ([TUI snapshot Agent Note](../.agents/notes/archived/testing/2026-07-18-tui-terminal-state-snapshots.md)). Use `pnpm run test:snapshot:record` when a model transcript changes and `pnpm run test:snapshot:refresh` when replay input remains valid; review every JSONL and expected-output diff. One ACP scenario (`text-turn`) pins full system-prompt/tool-schema content; other fixtures tokenize it so an edit churns one line ([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)).
- **Web browser snapshot** (`pnpm run test:web`; required Linux PR gate): Chromium compares replayed browser output with `apps/web/tests/snapshots/`. CI forces read-only `DSH_SNAPSHOT=replay`, never writing expected outputs; record/refresh stay local and every diff is reviewed ([web e2e lane](../.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md), [CI gate decision](../.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md)). `test:web` [builds first](../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md) for plugin CSS.
Committed session-format JSONL uses the canonical packed-row layout, and the keyless snapshot gate discovers every such fixture by its `session` header. In-flight branches carrying older fixture edits merge current `master` and run the [temporary migrator](../scripts/migrate-packed-session-fixtures.ts) through `pnpm run migrate:packed-session-fixtures`; the [removal proposal](../.agents/notes/proposed/process/2026-07-26-remove-packed-session-fixture-migrator.md) retires that command and these links after all affected branches converge.
@@ -46,4 +46,4 @@ An e2e assertion re-runs the command or re-reads the file externally; a keyword
## When a snapshot test is required
Every non-trivial model-, protocol-, or human-visible change adds or updates a keyless scenario in the same PR through a runnable example's owning snapshot suite. Package tests, e2e assertions, mock/test-only compositions, and PR rationale do not replace the assembled transcript; extend the harness when needed. ACP automation scenarios use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary); `examples/headless-agent` owns the `stream-json` snapshot and replay fixtures. Browser-rendered web GUI journeys use `apps/web/tests/snapshots/`. New capability seams, lifecycle shapes, or transcript surfaces name every coverage tier at plan time and verify the harness can express it before implementation.
Every non-trivial model-, protocol-, or human-visible change adds or updates a keyless scenario in the same PR through a runnable example's owning snapshot suite. Package tests, e2e assertions, mock/test-only compositions, and PR rationale do not replace the assembled transcript; extend the harness when needed. ACP automation scenarios use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary); `examples/headless-agent` owns the `stream-json` snapshot and replay fixtures. The `pwsh-tool-turn` ACP scenario boots real `pwsh` and skips where it is absent. Completed interactive-terminal journeys use JSONL-driven scenarios under `apps/cli/tests/snapshots/`; transient presentation uses the package-local semantic matrix, with a PTY case when input, Loader selection, or terminal teardown changes. Browser-rendered web GUI journeys use `apps/web/tests/snapshots/`. New capability seams, lifecycle shapes, or transcript surfaces name every coverage tier at plan time and verify the harness can express it before implementation.

View File

@@ -7,9 +7,9 @@
## 层级
- **单元测试**`pnpm run test`vitest 运行包package和示例各自的 `tests/**` 目录下的测试,以及匹配 `scripts/**/*.spec.ts` 的仓库脚本测试;测试文件与其所覆盖的代码区域放在一起。每个注册表都有一个 HMR热模块替换安全测试dispose资源释放贡献的 fiber断言清理完成。优先覆盖边界情况、错误路径、事件顺序、并发竞态以及永久性契约回归`packages/core/agent-loop/tests/contract-regressions.spec.ts`)。
- **覆盖率门禁**`pnpm run test:coverage`):门禁级运行,对 `packages/*/*/src` 按文件 100% 覆盖。未覆盖的行往往是门禁正确标记出的死代码(应删除),而非需要补写的测试。行覆盖率是必要条件,但永远不是充分条件:它证明行被执行过,不证明功能按交付预期工作。
- **覆盖率门禁**`pnpm run test:coverage`):门禁级运行,对 `packages/*/*/src` 按文件 100% 覆盖。未覆盖的行往往是门禁正确标记出的死代码(应删除),而非需要补写的测试。行覆盖率是必要条件,但永远不是充分条件:它证明行被执行过,不证明功能按交付预期工作。`packages/bash/pwsh-local/src` 的按文件 100% 覆盖需要真实的 `pwsh`:缺少它时其 executor 套件会自动跳过,`vitest.config.ts` 会豁免该文件以使无 pwsh 的主机保持绿色,而 CI runner 自带 pwsh仍按完整标准执行门禁。
- **真实 API e2e**`pnpm run test:e2e`):带密钥测试调用真实提供方 API包括 DeepSeek 模型以及各提供方特有的冒烟测试;这些测试各自由自己的密钥控制(`EXA_API_KEY``PERPLEXITY_API_KEY` 等),缺少密钥时套件会自动跳过,使 keyless CI 保持绿色([真实 API e2e Agent Note](../.agents/notes/implemented/testing/2026-06-19-real-api-e2e-ci.md))。
- **快照**`pnpm run test:snapshot`无密钥预期输出覆盖对外行为传输契约与呈现持久化日志则固定组装后的后端行为。ACP 启动真实的自动化服务器示例、回放录制会话,并对归一化 JSON-RPC 与重新持久化的日志执行 diff[ACP 快照 Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)headless 通过真实单次运行进程固定 `stream-json`。当模型 transcript文本记录发生变化时使用 `pnpm run test:snapshot:record`,回放输入仍然有效时使用 `pnpm run test:snapshot:refresh`;请审查每一处 JSONL 与预期输出差异。一个 ACP 场景(`text-turn`)固定完整的系统提示词与工具 schema 内容;其他 fixture测试前置数据将其 token 化,因此修改只会扰动一行([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md))。
- **快照**`pnpm run test:snapshot`无密钥预期输出覆盖对外行为传输契约与呈现持久化日志则固定组装后的后端行为。ACP 启动真实的自动化服务器示例、回放录制会话,并对归一化 JSON-RPC 与重新持久化的日志执行 diff[ACP 快照 Agent Note](../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)headless 通过真实单次运行进程固定 `stream-json`TUI 旅程通过真实循环与工具回放主会话与子会话 JSONL再将 ANSI 投影为语义化终端状态输出;包级快照保留瞬态状态,真实 PTY 覆盖进程边界([TUI 快照 Agent Note](../.agents/notes/archived/testing/2026-07-18-tui-terminal-state-snapshots.md))。当模型 transcript文本记录发生变化时使用 `pnpm run test:snapshot:record`,回放输入仍然有效时使用 `pnpm run test:snapshot:refresh`;请审查每一处 JSONL 与预期输出差异。一个 ACP 场景(`text-turn`)固定完整的系统提示词与工具 schema 内容;其他 fixture测试前置数据将其 token 化,因此修改只会扰动一行([pinned-header Agent Note](../.agents/notes/archived/testing/2026-07-06-pin-request-header-content-in-one-scenario.md))。
- **Web 浏览器快照**`pnpm run test:web`;必需的 Linux PRPull Request门禁Chromium 将回放后的浏览器输出与 `apps/web/tests/snapshots/` 比较。CI 强制只读的 `DSH_SNAPSHOT=replay`绝不写入预期输出record/refresh 留在本地,每处 diff 都须评审([web e2e 车道](../.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.md)、[CI 门禁决策](../.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.md))。`test:web` 会[先构建](../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md)以交付插件 CSS。
签入仓库的会话格式 JSONL 使用规范打包行布局,无密钥快照门禁会通过 `session` header 发现每一份此类 fixture。仍携带旧版 fixture 改动的在途分支应合并当前 `master`,并通过 `pnpm run migrate:packed-session-fixtures` 运行[临时迁移器](../scripts/migrate-packed-session-fixtures.ts);待所有受影响分支收敛后,[移除提案](../.agents/notes/proposed/process/2026-07-26-remove-packed-session-fixture-migrator.md)会移除该命令及这些链接。
@@ -46,4 +46,4 @@ e2e 断言应重新运行命令或从外部重新读取文件;对 agent 自身
## 何时需要快照测试
每项非平凡的模型可见、协议可见或人类可见变更,都必须在同一 PR 中通过可运行示例所属的快照套件添加或更新无密钥场景。包测试、e2e 断言、mock 与仅测试组合、PR 理由都不能取代组装后的 transcript必要时应扩展 harness。ACP 自动化场景使用 `examples/<name>/tests/snapshots/`,即基于 [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) 套件工厂的场景表(`examples/acp-agent` 为主套件);`examples/headless-agent` 拥有 `stream-json` 快照与回放 fixture。浏览器渲染的 Web GUI 旅程使用 `apps/web/tests/snapshots/`。新的能力 seam、生命周期形态或 transcript 呈现接口在计划阶段就要列出每个覆盖层级,并在实现前验证 harness 能够表达它们。
每项非平凡的模型可见、协议可见或人类可见变更,都必须在同一 PR 中通过可运行示例所属的快照套件添加或更新无密钥场景。包测试、e2e 断言、mock 与仅测试组合、PR 理由都不能取代组装后的 transcript必要时应扩展 harness。ACP 自动化场景使用 `examples/<name>/tests/snapshots/`,即基于 [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) 套件工厂的场景表(`examples/acp-agent` 为主套件);`examples/headless-agent` 拥有 `stream-json` 快照与回放 fixture。`pwsh-tool-turn` ACP 场景启动真实 `pwsh`,在无 `pwsh` 的主机上跳过。已完成的交互式终端旅程使用 `apps/cli/tests/snapshots/` 下由 JSONL 驱动的场景瞬态呈现使用包内语义矩阵输入、Loader 选择或终端清理发生变化时还要添加 PTY 用例。新的能力 seam、生命周期形态或 transcript 呈现接口在计划阶段就要列出每个覆盖层级,并在实现前验证 harness 能够表达它们。

View File

@@ -18,7 +18,8 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-tool-ask-user` | `ask_user_question` | `ctx.tools`, `ctx.userInteraction` | `tool/call`, `tool/result after a UI/provider answers the question` | - | ask_user_question pauses the tool call until the active UI provider returns a human answer. |
| `@deepseek-ai/dsh-tools` | `run_code` | `ctx.tools`, `ctx.codeRuntime (execution time)`, `ctx.systemPrompt` | `tool/call`, `one tool/code-dispatch-start + tool/code-dispatch pair per bridged sub-call`, `tool/result` | - | Owned by the tool registry as a reserved transport outside filterable capability layers under `mode: code` / `mode: both` (see the Code Mode Agent Note). Under `code` it is the registry's only wire contribution; the other visible capabilities are declared in a generated TypeScript SDK section, and a program calls them through bindings scheduled under the native concurrency contract (submission-ordered starts and policy; concurrency-safe bodies overlap up to `maxParallelSubCalls`) that re-enter the complete guarded tool pipeline and link each nested execution to this outer result. |
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`, `ctx.systemPrompt`, `ctx.userInteraction (execution time, opportunistic)` | `tool/call`, `plan/mode inactive on an approved review`, `tool/result` | - | exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-interaction seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary. |
| `@deepseek-ai/dsh-tool-bash` | `bash` | `ctx.tools`, `ctx.bash`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled. |
| `@deepseek-ai/dsh-tool-bash` | `bash` | `ctx.tools`, `ctx.bash`, `ctx.systemPrompt`, `ctx.bashEnv`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled. |
| `@deepseek-ai/dsh-tool-pwsh` | `pwsh` | `ctx.tools`, `ctx.bash`, `ctx.systemPrompt`, `ctx.bashEnv`, `ctx.tasks at call time for run_in_background` | `tool/call`, `tool/result` | - | The pwsh tool is the PowerShell-dialect consumer of the bash executor seam for Windows compositions (a PowerShell executor such as `@deepseek-ai/dsh-pwsh-local` backs `ctx.bash`); it mirrors the bash tool call-for-call minus the sandbox surface — `run_in_background` runs register with the generic `ctx.tasks` runtime and are collected/stopped through the `task_*` tools, and the managed `DSH_*` environment comes from `@deepseek-ai/dsh-bash-env`. Each call runs in a fresh process (no persistent PTY session; ConPTY is roadmap work), with native `C:\...` paths and `$env:NAME` variables. |
| `@deepseek-ai/dsh-tool-cordis` | `cordis_inspect`, `cordis_mount`, `cordis_unmount` | `ctx.tools` | `tool/call`, `tool/result`, `process-local temporary Plugin lifecycle` | - | Not in any shipped tree (a deliberate opt-in — temporary Plugin code reaches the real runtime, see .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins created by cordis_mount may register ADDITIONAL model-visible tools until unmounted or DSH restarts; a full changed request header logs those tool-set changes. |
| `@deepseek-ai/dsh-tool-bash-persistent` | `bash` | `ctx.tools`, `ctx.pty`, `an owning Agent at execution time` | `tool/call`, `PTY shell state`, `tool/result` | - | One owner-isolated persistent bash tool; deployment composition supplies the PTY backend and may override the model-facing environment description. |
| `@deepseek-ai/dsh-tool-str-replace-editor` | `str_replace_editor` | `ctx.tools`, `ctx.fs` | `tool/call`, `fs/observed after successful file operations`, `tool/result` | - | Standalone view/create/unique literal replace/line insert tool over the filesystem seam; it composes with any shell or terminal surface. |
@@ -207,6 +208,48 @@ Source: [`packages/bash/tool-bash/src/index.ts`](../packages/bash/tool-bash/src/
The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled.
## `@deepseek-ai/dsh-tool-pwsh`
### `pwsh`
Execute a PowerShell command (`pwsh -Command`) and return its stdout/stderr. Each call runs in a fresh pwsh process: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Paths use native Windows form (`C:\...`); read environment variables with `$env:NAME`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$env:DSH_*` variables; inspect them when needed. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. On Windows a force-killed command settles as `[exit code: 1]` without a signal marker — treat it as an interruption, not a command failure. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`.
```json
{
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The PowerShell command to execute."
},
"description": {
"type": "string",
"description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"Get-Process\" → \"List running processes\"."
},
"timeoutMs": {
"type": "number",
"description": "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."
},
"workdir": {
"type": "string",
"description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."
},
"run_in_background": {
"type": "boolean",
"description": "Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."
}
},
"required": [
"command",
"description"
]
}
```
Source: [`packages/bash/tool-pwsh/src/index.ts`](../packages/bash/tool-pwsh/src/index.ts)
The pwsh tool is the PowerShell-dialect consumer of the bash executor seam for Windows compositions (a PowerShell executor such as `@deepseek-ai/dsh-pwsh-local` backs `ctx.bash`); it mirrors the bash tool call-for-call minus the sandbox surface — `run_in_background` runs register with the generic `ctx.tasks` runtime and are collected/stopped through the `task_*` tools, and the managed `DSH_*` environment comes from `@deepseek-ai/dsh-bash-env`. Each call runs in a fresh process (no persistent PTY session; ConPTY is roadmap work), with native `C:\...` paths and `$env:NAME` variables.
## `@deepseek-ai/dsh-tool-cordis`
### `cordis_inspect`

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/user/develop/basic/config.md
config.md: 26d2d48ebede74194fbf306aa97d214bdb99b722
config.zh.md: 9ed389b16779f25c633d0c8772f8658197ba4322
config.zh.md: 1ca727b56b661df315026902d48fbcaf507fe08b

View File

@@ -75,7 +75,7 @@ Schema 在插件加载时执行校验。如果配置不合法,插件会加载
### 无硬编码可调参数
Harness 的约定:**任何两个部署可能想要不同值的东西,都应该是配置字段**。
Harness 的约定:**凡是不同部署可能需要采用不同值的参数,都必须定义为配置字段**。
```ts
// Wrong: hardcoded timeout.
@@ -110,7 +110,7 @@ export function apply(ctx: Context, config: ModelConfig) {
## 配合 HMR
配置变更会触发插件热替换:修改 `cordis.yml` 中某个插件的 `config`,框架会卸载旧实例加载新实例。由于注册都是效果(自动清理),这个过程是安全的
配置变更会触发插件热替换:修改 `cordis.yml` 中某个插件的 `config`,框架会卸载旧实例加载新实例。由于注册都属于 effect 并会自动清理,替换后不会保留旧实例的注册
## 下一步

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/user/develop/basic/index.md
index.md: 5a9f8dfb8f2d87dfbd2ba30b4d09d002ae9b635c
index.zh.md: 08aca87cbc02d1b0dfbe6fe2d92b3f6e87075097
index.zh.md: 6f8926aeac5dcf667e4d311505ef7b2a321659c2

View File

@@ -2,7 +2,7 @@
[English](index.md) | 中文
本文带你编写一个最小的 Harness 插件并加载到 Agent 中。
本文带你编写一个最小的 Harness 插件并加载到 agent(智能体)中。
## 插件是什么
@@ -18,7 +18,7 @@ export function apply(ctx: Context) {
}
```
就这么简单
这就是完整结构
## 创建插件文件
@@ -48,7 +48,7 @@ export function apply(ctx: Context) {
## 自动清理
通过 `ctx` 注册的任何东西——事件监听、tool、定时器——在插件卸载时都会被自动清理。你不需要手动 removeListener 或 clearInterval。
通过 `ctx` 注册的任何东西——事件监听、工具、定时器——在插件卸载时都会被自动清理。你不需要手动 removeListener 或 clearInterval。
如果你有需要手动清理的资源(比如一个网络连接),用 `ctx.effect()` 告诉框架怎么清理:
@@ -118,11 +118,11 @@ export default class MyService extends Service {
}
```
大多数情况下,函数形式足够了。类形式用于需要对外提供服务的插件(见 [服务与依赖](../framework/service.md))。
大多数情况下,函数形式足够了。当插件需要向其他插件提供服务时,可使用类形式(见 [服务与依赖](../framework/service.md))。
## 完整示例
最小的工具插件会在 `ctx.tools` 上注册其定义:
最小的工具插件会在 `ctx.tools` 上注册其定义:
```ts
import type { Context } from 'cordis'
@@ -151,5 +151,5 @@ export function apply(ctx: Context) {
## 下一步
- [开发一个 Tool](./tool.md) — 详细了解 tool 定义 DSL
- [开发一个工具](./tool.md) — 详细了解工具定义 DSL
- [插件配置](./config.md) — 让插件接受用户配置

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/user/develop/basic/tool.md
tool.md: 0d7cbc3f0b86f88fb67aeff6aa61181dff2912ee
tool.zh.md: 30cc871d7b417bdf7f33025b22e3f0965e2b8805
tool.zh.md: 37362c510a07fcc32e9eee2b578d82907788e471

View File

@@ -1,8 +1,8 @@
# 开发一个 Tool
# 开发一个工具
[English](tool.md) | 中文
Tool 是模型可以调用的能力。本文介绍如何用 `defineTool` 编写一个 tool
工具是模型可以调用的能力。本文介绍如何用 `defineTool` 编写一个工具
## 最小示例
@@ -156,9 +156,9 @@ async execute(args) {
你不需要在 `execute` 里手动校验参数类型。
## 展示层 (Presentation)
## 展示层Presentation
Tool 可以定义与传输方式无关的展示方法,供终端和 Web 客户端使用:
工具可以定义与传输方式无关的展示方法,供终端和 Web 客户端使用:
```ts ignore-check
defineTool({
@@ -187,7 +187,7 @@ defineTool({
## 注册与卸载
`ctx.tools.register()` 返回值就是 disposer。但由于你在 `ctx` 上调用,框架已经自动追踪了这个注册——插件卸载时会自动移除 tool。你不需要手动调用 disposer。
`ctx.tools.register()` 返回值就是 disposer。但由于你在 `ctx` 上调用,框架已经自动追踪了这个注册——插件卸载时会自动移除工具。你不需要手动调用 disposer。
```ts ignore-check
// This is sufficient:
@@ -196,9 +196,9 @@ ctx.tools.register(defineTool({ /* ... */ }))
// No saved disposer or extra cleanup registration is needed.
```
## 完整实战示例
## 完整示例
一个文件计数 tool
一个文件计数工具
```ts
import type { Context } from 'cordis'
@@ -241,5 +241,5 @@ export function apply(ctx: Context) {
## 下一步
- [插件配置](./config.md) — 让你的 tool 可配置
- [能力三件套](../practice/) — 了解 seam/impl/consumer 模式
- [插件配置](./config.md) — 让你的工具可配置
- [能力分层](../practice/) — 了解接口/实现/消费方模式

View File

@@ -1,6 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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
# pnpm run verify-translation-pairing --write docs/user/develop/framework/events.md
events.md: 5cd5d22f854d0b4e271e892cbdb1ccebe687ae49
events.zh.md: 5fd4d5de53897e32523ab478626965ad7c9602ba
events.zh.md: 91082694c7546c0b2b77d9fddbd36b14a38a2a8c

Some files were not shown because too many files have changed in this diff Show More