docs(i18n): proofread README translations 141-160

This commit is contained in:
j-xiang
2026-07-29 15:29:38 +08:00
parent 57b01e68d3
commit a37097846b
20 changed files with 146 additions and 146 deletions

View File

@@ -1,12 +1,12 @@
# subprocess/:进程管理能力家族
# subprocess/进程能力家族
[English](README.md) | 中文
spawn 受管子进程树的共用归属位置:完全显式的 spawn spec stdio 处置方式disposition为 Node 形状、按流划分(原始管道、inherit、附带 spill 文件的有界尾部保留收集harness 中所有 spawn 调用方共用的那一份凭据清除;基于偏移量的增量读取;以进程树为范围、带 SIGTERM→宽限期→SIGKILL 升级的信号发送;以及协作式 dispose资源释放阶梯。命令默认值补全、shell 语义、deadline、协议分帧与呈现留在消费方:[bash 执行器](../bash/README.md)、[LSP 主机](../lsp/README.md)与 [ACPAgent Client Protocolsubagent 后端](../subagent/README.md)。参见[进程管理器 seam Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md)。
这里集中提供受管子进程树的 spawn 能力:完整指定的 spawn spec采用 Node 风格、按流划分的 stdio 处置方式disposition,包括原始管道、inherit、附带 spill 文件的有界尾部保留收集harness 中所有 spawn 调用方共用的凭据清除机制;基于偏移量的增量读取;以进程树为范围、带 SIGTERM→宽限期→SIGKILL 升级的信号发送;以及协作式 dispose资源释放阶梯。命令默认值补全、shell 语义、时限、协议分帧与呈现留在消费方:[bash 执行器](../bash/README.md)、[LSP 主机](../lsp/README.md)与 [ACPAgent Client Protocolsubagent 后端](../subagent/README.md)。参见[subprocess seam Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md)。
| 包package | ctx 键 | 角色 |
|---|---|---|
| [`subprocess`](subprocess/README.md)`@deepseek-ai/dsh-subprocess` | `ctx.subprocess` | seam 本体:抽象的 `SubprocessService.spawn(spec)`、完全显式且带按流划分 stdio 处置方式的 `SubprocessSpawnSpec``SubprocessHandle`流、基于偏移量的读取器、terminate/waitForExit/dispose以及共享的凭据清除 + `DSH_*`/`CollectedOutput` 词汇 |
| [`subprocess-local`](subprocess-local/README.md)`@deepseek-ai/dsh-subprocess-local` | 无 | 本地实现detached 进程树、按处置方式接线的流、附带有界私有 spill 文件的尾部保留截断、`DSH_*` 合并次序、带升级的进程树信号发送、dispose 阶梯,以及先终止再等待退出的 dispose |
服务拥有跨消费方重载进程存续期;消费方拥有一个进程的含义(一条 bash 命令、未来的非 shell 运行器)以及塑造它的每一项默认值。
即使消费方重载进程生命周期仍由服务负责管理;消费方负责定义进程的含义(一条 bash 命令、未来的非 shell 运行器)以及决定塑造该进程的每一项默认值。

View File

@@ -2,15 +2,15 @@
[English](README.md) | 中文
[`@deepseek-ai/dsh-subprocess`](../subprocess/README.md) seam 的本地实现:`LocalSubprocessService` 每个 spec 的 argv 为 detached 进程树 spawn,依照 spec 中按流划分的 stdio 处置方式disposition完成接线原始管道、inherit、附带可选 spill 文件的有界尾部保留收集),并以进程树为范围按 SIGTERM→SIGKILL 升级发送信号。该实现没有任何配置:每项处置方式、限制与目录都随 spawn spec 到达,因此随部署变化的旋钮留在各调用方 seam 的配置里([`dsh-bash-local`](../../bash/bash-local/README.md)、[`dsh-lsp-local`](../../lsp/lsp-local/README.md)、[`dsh-subagent-acp`](../../subagent/subagent-acp/README.md))。
[`@deepseek-ai/dsh-subprocess`](../subprocess/README.md) seam 的本地实现:`LocalSubprocessService` 每个 spec 的 argv spawn 为 detached 进程树,依照 spec 中按流划分的 stdio 处置方式disposition完成接线原始管道、inherit、附带可选 spill 文件的有界尾部保留收集),并以进程树为范围发送信号,按 SIGTERM→SIGKILL 逐级升级。该实现没有任何配置:每项处置方式、限制与目录都随 spawn spec 传入,因此随部署变化的可调参数留在各调用方 seam 的配置里([`dsh-bash-local`](../../bash/bash-local/README.md)、[`dsh-lsp-local`](../../lsp/lsp-local/README.md)、[`dsh-subagent-acp`](../../subagent/subagent-acp/README.md))。
## 行为(以及设计来源)
- **带平台正确信号发送的 detached 进程树**POSIX 子进程使用 `detached` spawn拥有独立进程组信号以负 pgid 发送并以直接子进程作为回退Windows 通过 `taskkill /PID <pid> /T /F` 终止进程树(可为测试注入)。`terminate()`(句柄唯一的终止动词)先发送 SIGTERM经过 spec 的宽限期后再发送 SIGKILL沿用 OpenCode 的升级策略;管道与子 shell 会随父进程一起结束),进程树消亡后为空操作;`waitForExit()` 轮询整棵进程树的存活状态,使消费方的拆卸能确认真正的完全停稳。组长进程退出后,仍然打开的管道也只获得同样有界的排空宽限期,因此存活的后代进程无法无限期地拖住结果不结算。系统会容忍 ESRCH脱离该组重新挂载的 daemon 仍可能存活,这与调研工具的局限相同。
- **按流划分的处置方式**`'pipe'` 把原始流原样交给调用方(协议分帧仍归消费方所有);`'inherit'` 直通父进程的描述符收集模式collect在输出超过上限后于内存中保留尾部错误与结果通常聚集在末尾沿用 pi/OpenCode 的理由),并在配置了 spill 上限时把完整流追加到一个私有临时文件;省略 `spill` 则只保留尾部,即诊断尾部的形状。某条流大于 spill 上限时,会丢弃已不完整的 spill仅返回带截断标记的尾部spill 文件描述符在结算时封存最终关闭失败时则不公布路径以免声称存在不完整的文件。spill 文件权限为 `0600`、名称随机,位于按需延迟创建的 `0700` 每进程目录之下。
- **以适合平台的方式发送信号的 detached 进程树**POSIX 子进程使用 `detached` spawn拥有独立进程组信号以负 pgid 发送并以直接子进程作为回退Windows 通过 `taskkill /PID <pid> /T /F` 终止进程树(可为测试注入)。`terminate()`(句柄唯一的终止操作)先发送 SIGTERM经过 spec 的宽限期后再发送 SIGKILL沿用 OpenCode 的升级策略;流水线与子 shell 会随父进程一起结束),进程树消亡后为空操作;`waitForExit()` 轮询整棵进程树的存活状态,使消费方的拆卸能确认真正的完全停稳。组长进程退出后,仍然打开的管道也只获得同样有界的排空宽限期,因此存活的后代进程无法无限期地拖住结果不结算。系统会容忍 ESRCH重新指定父进程并脱离该组的 daemon 仍可能存活,这与调研工具的局限相同。
- **按流划分的处置方式**`'pipe'` 把原始流原样交给调用方(协议分帧仍归消费方所有);`'inherit'` 直通父进程的描述符收集模式collect在输出超过上限后于内存中保留尾部错误与结果通常聚集在末尾沿用 pi/OpenCode 的理由),并在配置了 spill 上限时把完整流追加到一个私有临时文件;省略 `spill` 则只保留用于诊断尾部。某条流大于 spill 上限时,会丢弃已不完整的 spill仅返回带截断标记的尾部spill 文件描述符在结算时封存最终关闭失败时则不公布路径以免声称存在不完整的文件。spill 文件权限为 `0600`、名称随机,位于按需创建、权限为 `0700` 每进程目录之下。
- **凭据清除 + 显式合并**:以 `process.env` 为基础,移除形似凭据的变量(`*KEY*``*SECRET*``*TOKEN*`)和所有环境中已有的 `DSH_*` 名称spec 的显式 `env` 在该清除之后合并且不做命名空间校验,因此有意提供的凭据或当前 `DSH_*` 事实会胜出,而陈旧的嵌套 harness 身份无法从环境中隐式漏入。提供的 stdin 会被写入后关闭;否则 fd 0 指向 `/dev/null`。参见 [stdin/env Agent Noteagent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)与[受管环境 Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md)。
- **基于偏移量的读取**:收集模式的读取器以全流字节坐标返回增量服务自身从不持有游标因此消费方自有的游标bash 的后台读取路径)与完整流重读可以共存,结算前后皆然。
- **先终止再等待退出的 dispose**:服务保留存活句柄,只为让自身的 dispose 能对每个仍在运行的进程树执行升级并等待其退出;已结算与 spawn 失败的句柄在结算时即离开存活集合。
- **基于偏移量的读取**:收集模式的读取器按完整流的字节坐标返回增量服务自身从不持有游标因此消费方自有的游标bash 的后台读取路径)与完整流重读可以共存,结算前后皆然。
- **先终止再等待退出的 dispose(资源释放)**:服务保留存活句柄,只为让自身的 dispose 能对每个仍在运行的进程树执行升级并等待其退出;已结算与 spawn 失败的句柄在结算时即离开存活集合。
## 模型体验
@@ -18,7 +18,7 @@
#### KV Cache 影响
不会直接失效;请求前缀变更由具名消费方负责。
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项

View File

@@ -2,28 +2,28 @@
[English](README.md) | 中文
进程管理器 seam`ctx.subprocess`)。抽象的 `SubprocessService` 只暴露一个方法:`spawn(spec): SubprocessHandle`,外加所有消费方共享的词汇:完全显式的 `SubprocessSpawnSpec`、携带基于偏移量的非消费式输出读取器的 `SubprocessHandle``SubprocessOutcome``CollectedOutput`,以及受管的 `DSH_*` 环境命名空间(`DSH_ENV_PREFIX``DshEnvironment`)。本地实现位于 [`dsh-subprocess-local`](../subprocess-local/README.md)。
进程 seam`ctx.subprocess`)。抽象的 `SubprocessService` 只暴露一个方法:`spawn(spec): SubprocessHandle`,外加所有消费方共享的词汇:完全显式的 `SubprocessSpawnSpec`、携带基于偏移量的非消费式输出读取器的 `SubprocessHandle``SubprocessOutcome``CollectedOutput`,以及受管的 `DSH_*` 环境命名空间(`DSH_ENV_PREFIX``DshEnvironment`)。本地实现位于 [`dsh-subprocess-local`](../subprocess-local/README.md)。
## 契约
- `spawn(spec)` 立即返回一个实时句柄;`done` 在进程关闭时以退出事实 resolve`SubprocessOutcome` 不携带输出,也不携带原因分类),仅在 spawn 层面失败时 reject。
- spec 完全显式argv、cwd、按流划分的 stdio 处置方式disposition、宽限期因为随部署变化的默认值属于调用方 seam 的配置,而不属于某个隐藏的进程管理器默认值(`dsh-bash` 的 request/spec 拆分是这条规则的所属模板)。`argv` 绝不经过 shell 解释;需要 shell 的消费方自行传入 `['bash', '-c', command]`
- stdio 按流采用 Node 形状`'pipe'` 把原始流交给调用方做自己的协议分帧LSP 的 JSON-RPC、ACPAgent Client Protocol的 ndjson`'inherit'` 直通父进程描述符以承载诊断输出收集模式collect`{ maxBytes, spill? }` 则缓冲一段有界尾部,外加可选的完整流 spill 文件。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;偏移量滑出内存尾部窗口的读取标记为 `lossy`,并在 spill 文件存在时指向它。收集到的输出在结算后仍可读取。
- `spawn(spec)` 立即返回一个活动句柄;`done` 在进程关闭时以退出事实 resolve`SubprocessOutcome` 不携带输出,也不携带原因分类),仅在 spawn 层面失败时 reject。
- spec 完全显式argv、cwd、按流划分的 stdio 处置方式disposition、宽限期因为随部署变化的默认值属于调用方 seam 的配置,而不属于某个隐藏的进程默认值(`dsh-bash` 的 request/spec 拆分是这条规则的所属模板)。`argv` 绝不经过 shell 解释;需要 shell 的消费方自行传入 `['bash', '-c', command]`
- stdio 按流采用 Node 风格`'pipe'` 把原始流交给调用方做自己的协议分帧LSP 的 JSON-RPC、ACPAgent Client Protocol的 ndjson`'inherit'` 直通父进程描述符以承载诊断输出收集模式collect`{ maxBytes, spill? }` 则缓冲一段有界尾部,外加可选的完整流 spill 文件。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;偏移量滑出内存尾部窗口的读取标记为 `lossy`,并在 spill 文件存在时指向它。收集到的输出在结算后仍可读取。
- 终止在每个平台上都以进程树为范围POSIX 用 detached 进程组并以直接子进程回退Windows 用 `taskkill /T``terminate()`(唯一的终止动词)执行 SIGTERM→宽限期→SIGKILL 升级(幂等,也由 spec 的 abort 信号驱动,进程树消亡后为空操作);`waitForExit(signal?)` 观察整棵进程树的存活状态使消费方自有的拆卸阶梯能在真正完全停稳后才进入下一层。管理器只响应中止但绝不判定原因deadline、拆卸阶梯与原因分类归调用方所有
- `scrubbedParentEnv()` / `SENSITIVE_ENV_PATTERN` 是唯一一份共享的凭据清除定义:环境中形似凭据的名称与 `DSH_*` 名称都会被丢弃spec 的显式 `env` 在清除之后合并且不做命名空间校验——有意转发的凭据或当前 `DSH_*` 事实之所以能保留下来,正因为它是调用方的显式选择,而陈旧的同名环境值永远到不了子进程。无法把 spawn 路由到该服务的调用点node-pty 后端、由 SDK 管理的传输层)改为导入凭据清除函数。
- `scrubbedParentEnv()` / `SENSITIVE_ENV_PATTERN` 是唯一一份共享的环境清理定义:环境中形似凭据的名称与 `DSH_*` 名称都会被丢弃spec 的显式 `env` 在清除之后合并且不做命名空间校验——有意转发的凭据或当前 `DSH_*` 事实之所以能保留下来,正因为它是调用方的显式选择,而陈旧的同名环境值永远到不了子进程。无法把 spawn 路由到该服务的进程启动方node-pty 后端、由 SDK 管理的传输层)改为导入环境清理函数。
- 服务自身的 dispose资源释放会终止所有仍在运行的受管进程并等待其退出。
参见[进程管理器数据结构目录](../../../docs/core-data-structures/subprocess.md)与 [seam Agent Noteagent 决策记录)](../../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md)。
参见[进程数据结构目录](../../../docs/core-data-structures/subprocess.md)与[seam Agent Noteagent 决策记录)](../../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md)。
## 模型体验
通过消费方 seam 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出生命周期面向模型的全部渲染归消费方所有
通过消费方 seam 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出生命周期的全部面向模型渲染均由消费方负责
#### KV Cache 影响
不会直接失效;请求前缀变更由具名消费方负责。
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项
- **node-pty 与由 SDK 管理的 spawn 只共享凭据清除**PTY 后端的终端 fork 与 MCP SDK 自己的 stdio 传输层无法把 spawn 路由到这道 seamfork/spawn 调用归库所有);它们改为导入 `scrubbedParentEnv`,使环境策略保持单一来源。
- **拆卸阶梯归消费方所有**:该 seam 只提供信号动词与进程树存活等待,不提供现成的停稳序列;每个进程外消费方自行编码其子进程的配合形状ACP 后端以 stdin EOF 打头的阶梯是仓库内模板)。
- **node-pty 与由 SDK 管理的 spawn 只共享环境清理**PTY 后端的终端 fork 与 MCP SDK 自己的 stdio 传输层无法把 spawn 路由到这道 seamfork/spawn 调用归库所有);它们改为导入 `scrubbedParentEnv`,使环境策略保持单一来源。
- **拆卸阶梯归消费方所有**:该 seam 只提供信号动词与进程树存活等待,不提供现成的停稳序列;每个进程外消费方自行编码其子进程的配合方式ACP 后端以 stdin EOF 打头的阶梯是仓库内模板)。

View File

@@ -2,15 +2,15 @@
[English](README.md) | 中文
这些包用于开发、测试和示例,而非作为产品 API 发布。它们是实工作区包(类型、经过测试受覆盖率门禁约束),但具有**较低的兼容性预期**:当其背后的开发需求变化时,它们可以改变或被移除,无需像产品包那样谨慎执行弃用流程。
这些包package用于开发、测试和示例,而非作为产品 API 发布。它们是实际的工作区包(具备类型、经过测试,并受覆盖率门禁约束),但具有**较低的兼容性预期**:当其背后的开发需求变化时,它们可以改变或被移除,无需像产品包那样谨慎执行弃用流程。
| 包 | 职责 | ctx 键 |
|---|---|---|
| `acp-snapshot/` | ACP 测试工具包:共享子进程/客户端启动器、快照 harness、规范化器和套件工厂 | (库:由 ACP e2e 和 `*.snapshot.ts` 套件导入) |
| `agent-loop-testkit/` | 为测试具体 agent loop 的测试挂载共享先决条件 | (库:由 AgentLoop 集成测试导入) |
| `acp-snapshot/` | ACPAgent Client Protocol测试工具包:共享子进程/客户端启动器、快照 harness、规范化器和套件工厂 | (库:由 ACP e2e 和 `*.snapshot.ts` 套件导入) |
| `agent-loop-testkit/` | 为验证具体 agent loop(智能体循环)的测试挂载共享先决条件 | (库:由 AgentLoop 集成测试导入) |
| `invariants/` | 用于开发诊断的运行时事件契约断言 | (监听 `session/*``agent/*` |
| `loader-smoke/` | 共享的真实 Loader 子进程 harness用于无密钥示例冒烟测试 | (库:由示例 e2e 套件导入) |
| `llm-mock-server/` | 可编脚本的 OpenAI 兼容 HTTP/SSE 故障服务器与 CLI用于 LLM 恢复测试 | (独立服务器和测试库) |
| `llm-replay/` | 录制/回放适配器:已记录的会话 JSONL 短路 `llm/stream`(无密钥快照测试) | (监听 `llm/stream` |
| `llm-mock-server/` | 可编的 OpenAI 兼容 HTTP/SSEServer-Sent Events故障服务器与 CLI命令行界面用于 LLM大语言模型恢复测试 | (独立服务器和测试库) |
| `llm-replay/` | 录制/回放适配器:通过已记录的会话 JSONL `llm/stream` 进行短路处理(无密钥快照测试) | (监听 `llm/stream` |
`invariants` 是开发支持,但没有环境保护:无论在何处注册,它都会运行;默认 `dsh-agent-spine-demo` bundle 无条件挂载它。`agent-loop-testkit` 为手工构建的 AgentLoop 测试集中管理必需服务主干,而不负责其 loop 或场景。`llm-replay` 支撑演示和受文件覆盖率门禁约束的快照测试层,`llm-mock-server` 则通过确定性 HTTP/SSE 故障驱动真实提供方适配器。`acp-snapshot` 包含 ACP 子进程/客户端边界以及快照 harness、规范化器和套件机制`loader-smoke` 负责无密钥示例 e2e 套件使用的并列真实 Loader 启动边界。只有当某个包获得已记录的产品消费方时,它才会从 `support/` 升级到产品分组。
`invariants` 是开发支持,但没有环境条件限制:无论在何处注册,它都会运行;默认 `dsh-agent-spine-demo` bundle 无条件挂载它。`agent-loop-testkit` 为手工构建的 AgentLoop 测试集中管理必需服务主干,而不负责其 agent loop 或场景。`llm-replay` 支撑演示和受文件覆盖率门禁约束的快照测试层,`llm-mock-server` 则通过确定性 HTTP/SSE 故障驱动真实提供方适配器。`acp-snapshot` 包含 ACP 子进程/客户端边界以及快照 harness、规范化器和套件机制`loader-smoke` 负责无密钥示例 e2e 套件使用的并列真实 Loader 启动边界。只有当某个包获得文档记载的产品消费方时,它才会从 `support/` 转入产品分组。

View File

@@ -2,9 +2,9 @@
[English](README.md) | 中文
测试具体 `AgentLoop` 的测试挂载共享先决条件`mountAgentLoopTestDependencies(ctx, options?)` 按依赖顺序安装 LLM、会话、系统提示词、工具和 agent 服务,然后在 loop 挂载前返回。
运行具体 `AgentLoop` 的测试共享挂载先决依赖`mountAgentLoopTestDependencies(ctx, options?)` 按依赖顺序安装 LLM(大语言模型)、会话、系统提示词、工具和 agent(智能体)服务,然后在 agent loop 挂载前返回。
调用方注册适配器和可选插件,使用待测配置挂载 `AgentLoop`,并 dispose 自己的 Context。系统提示词和工具注册表配置可通过 `options` 转发;辅助工具不提供超出服务自有默认值的测试默认值。插件加载失败会拒绝辅助工具调用,而顺序中较早激活的服务仍归调用方的 Context 所有。
调用方注册适配器和可选插件,使用待测配置挂载 `AgentLoop`,并 dispose(资源释放)自己的 Context。系统提示词和工具注册表配置可通过 `options` 转发;辅助函数不提供超出服务自有默认值的测试默认值。插件加载失败会使辅助函数调用被拒绝,而顺序中较早激活的服务仍归调用方的 Context 所有。
```ts
import { Context } from 'cordis'
@@ -18,16 +18,16 @@ await mountAgentLoopTestDependencies(ctx)
await ctx.plugin(AgentLoop, { agents: [] })
```
针对注入失败、部分拓扑、服务加载顺序或服务拆卸的测试会直接挂载其依赖,而不使用此辅助工具
针对注入失败、部分拓扑、服务加载顺序或服务清理的测试会直接挂载其依赖,而不使用此辅助函数
## 模型体验
无。该测试专用组合辅助工具既不驱动也不修改模型请求。
#### KV 缓存影响
#### KV Cache 影响
无;该包既不组装也不发送提供方请求。
## 已知限制与待完成工作
## 已知限制与暂缓事项
- **只共享必需的先决主干**:适配器、可选插件、`AgentLoop`、agent 和 Context 拆卸仍由调用方负责,以使场景专用顺序保持可见。
- **只共享必需的先决主干**:适配器、可选插件、`AgentLoop`、agent 和 Context 清理仍由调用方负责,以使特定场景的挂载顺序清晰可见。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
用于包自有运行时不变量检查的可配置注册表服务。根插件注册 `ctx.invariants`;它不包含产品检查或产品包导入。每个工作区包都发布一个 `./invariant` 配套入口,用于注册其精确 NPM 包名。
用于包package自有运行时不变量检查的可配置注册表服务。根插件注册 `ctx.invariants`;它不包含产品检查或产品包导入。每个工作区包都发布一个 `./invariant` 配套入口,用于注册其精确 NPM 包名。
## 服务:`InvariantService``ctx.invariants`
@@ -16,19 +16,19 @@ interface Config {
默认值为 `enabled: true``package_allowlist: []``package_blocklist: []`。只有在服务启用、allowlist 为空或至少一个 allowlist pattern 匹配完整 NPM 名称,且没有 blocklist pattern 匹配时包才被选中。因此blocklist 匹配优先于 allowlist 匹配。
每个条目都是区分大小写的 JavaScript 正则表达式源,使用 `new RegExp(pattern)` 编译。除非源提供 `^``$`,否则匹配不锚定;不解析 `/pattern/flags` 语法。同一列表中的空白、带前后空白、无效或重复条目会使服务启动失败。有效 pattern 可以不匹配任何当前已加载包,以使后续加载和 HMR 保持确定性。
每个条目都是区分大小写的 JavaScript 正则表达式源,使用 `new RegExp(pattern)` 编译。除非源提供 `^``$`,否则匹配不锚定;不解析 `/pattern/flags` 语法。同一列表中的空白、带前后空白、无效或重复条目会使服务启动失败。有效 pattern 可以不匹配任何当前已加载包,以使后续加载和 HMR(热模块替换)保持确定性。
`ctx.invariants.register(packageName, installer)` 为完整 NPM 包名保留一个活动注册,即使过滤器使其 installer 保持非活动,并返回 disposer。已启用贡献在专用子 Cordis fiber 中运行。Installer 可以通过 `installer.inject` 声明所需服务接口,并收到 `fail(message)`;后者抛出绑定到注册包的 `InvariantError`。在注册成功前,系统会等待同步或异步 installer 完成;失败会 dispose 子级,并原子释放归属。
`ctx.invariants.register(packageName, installer)` 为完整 NPM 包名保留一个活动注册,即使过滤器使其 installer 保持非活动,并返回 disposer。已启用贡献在专用子 Cordis fiber 中运行。installer 可以通过 `installer.inject` 声明所需服务接口,并收到 `fail(message)`;后者抛出绑定到注册包的 `InvariantError`。在注册成功前,系统会等待同步或异步 installer 完成;失败会原子地 dispose(资源释放)子级并释放归属。
服务拥有每个注册 fiber返回的 disposer 同时属于配套 fiber。卸载任一侧都会移除监听器、跟踪状态和保留。因此配套入口可以重新加载并注册同一包名而不保留旧状态。由会话支撑的配套入口从持久事件重建 baseline仅实时配套入口观察重新加载后开始的操作。
`InvariantError` 扩展 `Error`,携带稳定 `code: 'INVARIANT'`,并公开所属 `packageName`,而不向服务添加产品依赖。
在每个组合中Session 自身负责不可变且接口有效的日志存储:它对每个候选项制作一份无损 JSON 快照,验证完整来源和位置替换,将 `tool/result` 替换限制为一个当前结果的 `content`,深度冻结已接受记录,并通过不可变数组快照公开日志。`dsh-session` 不变量配套入口检查 Session 不负责的其余跨记录规则。
在每个组合中Session 自身负责不可变且通过对外接口校验的日志存储:它对每个候选项制作一份无损 JSON 快照,验证完整来源和位置替换,将 `tool/result` 替换限制为一个当前结果的 `content`,深度冻结已接受记录,并通过不可变数组快照公开日志。`dsh-session` 不变量配套入口检查 Session 不负责的其余跨记录规则。
## 包配套入口
发布和注册覆盖全部包;运行时断言刻意不使用合成内容。只有当包拥有可观察事件关系或相关可变数据关系时配套入口才安装检查。确认必需方法、插件名称、注入、effect 或固定纯函数结果属于类型、加载或单元测试关注点,而非运行时不变量。
发布和注册覆盖全部包;但不会为了覆盖全部包而人为编造运行时断言。只有当包拥有可观察事件关系或相关可变数据关系时配套入口才安装检查。确认必需方法、插件名称、注入、effect 或固定纯函数结果属于类型、加载或单元测试关注点,而非运行时不变量。
如果不存在合理的运行时关系,配套入口使用空 installer并以包专用的前置 `No runtime invariant:` 注释说明原因。纯工具、行为已通过 seam 观察的薄实现、仅组合包、二进制程序、契约需要崩溃/往返测试的持久化适配器和测试支持包通常属于此类。当 owner 获得可变状态或事件协议时,必须重新审视该说明。
@@ -36,18 +36,18 @@ interface Config {
| 配套入口 | 检查 |
|---|---|
| `dsh-session`, `dsh-agent`, `dsh-scope`, `dsh-agent-loop` | 会话包含关系和调用/结果跟踪、agent 状态转换、inbox FIFO 守恒、作用域 subject 和模型请求重建。 |
| `dsh-session`, `dsh-agent`, `dsh-scope`, `dsh-agent-loop` | 会话包含关系和调用/结果跟踪、agent(智能体)状态转换、inbox FIFO 守恒、作用域 subject 和模型请求重建。 |
| `dsh-llm`, `dsh-llm-retry`, `dsh-tools`, `dsh-system-prompt` | 流语法、持久重试位置和边界、工具流水线阶段与冻结结果,以及权威提示词组装数据。 |
| `dsh-compact`, `dsh-hook-protocol`, `dsh-sandbox-policy` | 持久压缩与钩子配对、压缩元数据和沙箱 mode 词汇。 |
| `dsh-compact`, `dsh-hook-protocol`, `dsh-sandbox-policy` | 持久压缩compaction与钩子配对、压缩元数据和沙箱 mode 词汇。 |
| `dsh-fs`, `dsh-subagent`, `dsh-workflow` | 文件系统事件身份、提供方/子级配对和工作流/agent 生命周期身份。 |
| `dsh-goal`, `dsh-goal-session` | 持久 goal 来源/内容一致性、修订和生命周期转换、时间戳、顺序接纳 Round 和重建的继续提示词。 |
| `dsh-goal`, `dsh-goal-session` | 持久 goal 来源/内容一致性、修订和生命周期转换、时间戳、依次获准的 Round 和重建的继续提示词。 |
| `dsh-permission`, `dsh-user-approval` | 活动 preset 引用和审批询问/决定审计配对。 |
| `dsh-tasks`, `dsh-tool-todo` | 任务快照生命周期/归属字段和持久整表 todo 结构。 |
| `dsh-time-context` | 持久时钟读数与会话开放轮次、下一步骤位置已用 baseline 一致;渲染时间可解析,且不晚于其事件。 |
| `dsh-time-context` | 持久时钟读数与会话中正在进行的轮次、下一步骤开始前的位置已用时间 baseline 一致;渲染时间可解析,且不晚于其事件。 |
每个 owner 的根入口仍与诊断独立。单独加载服务不会安装产品检查;在没有服务时加载配套入口,会等待其声明的 `invariants` 注入。
每个 owner 的根入口仍独立于诊断。单独加载服务不会安装产品检查;在没有服务时加载配套入口,会等待其声明的 `invariants` 注入。
`pnpm run verify-package-invariants` 发现全部工作区包。它拒绝生成标记、未说明的空 installer、省略或忽略 reporter 的非空 installer、错误注册名称以及不完整的导出、发布、依赖、TypeScript 引用或 bundle 接线。该源规则是最低归属检查;聚焦测试证明每个可执行配套入口的语义。
`pnpm run verify-package-invariants` 发现全部工作区包。它拒绝生成标记、未说明的空 installer、省略或忽略 reporter 的非空 installer、错误注册名称以及不完整的导出、发布、依赖、TypeScript 引用或 bundle 接线。该源规则是最低归属检查;聚焦测试证明每个可执行配套入口的语义。
## 组合
@@ -68,18 +68,18 @@ ctx.plugin(SessionInvariant)
标准 agent 主干挂载服务和 4 个核心有状态配套入口。自定义组合为希望检查其契约的其他已加载包显式添加配套入口;过滤器可以在不改变包入口的情况下禁用或选择注册。
每个普通 Vitest 拓扑都挂载显式启用的服务和当前测试包的配套入口。聚焦套件覆盖可执行配套入口的有效和无效观察,一个穷尽拓扑则挂载全部配套入口,以证明注册和 dispose 接线。
每个普通 Vitest 拓扑都挂载显式启用的服务和当前测试包的配套入口。聚焦套件覆盖可执行配套入口的合法与违规观测,一个穷尽拓扑则挂载全部配套入口,以证明注册和 dispose 接线。
## 模型体验
无。服务和配套入口观察运行时事件和可变快照不会更改提示词、消息、schema、流或工具结果。
#### KV 缓存影响
#### KV Cache 影响
无;不变量检查不组装或发送提供方请求。
## 已知限制与待完成工作
## 已知限制与暂缓事项
- 请求重建覆盖 loop 在冻结前显式标记的请求;直接一次性 LLM 调用即使由调用方冻结或附加会话 id仍不在该标记契约内。
- 请求重建覆盖 loop 在冻结前显式标记的请求;直接一次性 LLM(大语言模型)调用即使由调用方冻结或附加会话 id仍不在该标记契约内。
- 仅实时生命周期配套入口无法重建自身重新加载前开始的操作。标准组合和测试组合会在相应操作开始前挂载它们。
- 正则表达式过滤器在服务生命周期内固定;更改它们需要执行普通 Cordis 插件重新加载。

View File

@@ -2,21 +2,21 @@
[English](README.md) | 中文
用于无密钥快照测试的回放 LLM 插件。它已记录的**会话 JSONL** fixture 重建模型流,使测试可以在无 API 密钥的情况下使用固定模型 transcript 启动真实 agent。配置 `providers` 后,它会注册仅回放适配器,其目录可供测试模型发现的场景使用;没有 `providers` 时,它会安装不需要发现的测试所用 catch-all `llm/stream` waterfall。
用于无密钥快照测试的大语言模型LLM回放插件。它根据已记录的**会话 JSONL** fixture(测试前置数据)重建模型流,使测试无 API 密钥即可针对固定模型 transcript(文本记录)启动真实 agent(智能体)。配置 `providers` 后,它会注册仅用于回放适配器,其模型目录可供测试模型发现功能的场景使用;未配置 `providers` 时,它会安装无需模型发现功能的测试所用 catch-all `llm/stream` waterfall(瀑布式事件)
其消费方 ACP、headless `stream-json` 和 TUI 快照套件,以及 web 浏览器 e2e lane。Loader 驱动套件使用此插件替真实 LLM 适配器;web lane 直接安装它,以保留拆卸消费句柄。将派生和回放逻辑保留在此处,可使其受 `packages/*/src` 上每文件 100% 覆盖率门禁约束。
其消费方包括 ACPAgent Client Protocol、headless `stream-json` 和 TUI 快照套件,以及 Web 浏览器 e2e 流水线。Loader 驱动套件使用此插件替真实 LLM 适配器;Web 流水线直接安装它,以保留清理阶段的消费检查句柄。将派生和回放逻辑在此处,可使其受 `packages/*/src` 的逐文件 100% 覆盖率门禁约束。
## Fixture 的工作方式
## fixture 的工作方式
Fixture 就是持久化会话日志(`<scenario>/session.jsonl`)。其 `assistant/chunk` 事件携带每个 `StreamChunk`,因此按 `(turn, step)` 对其分组可重建每次 `stream()` 调用的分片序列(每个 loop 步骤一次模型调用)。因此,录制操作是「运行一次真实 agent 并收集 `.jsonl`,由快照 harness 完成;该插件不执行录制。Fixture 的 `request/header` 内容可能被 token 化为 `{{system}}`/`{{tools}}`harness 在一个场景中固定该内容,并除其余场景);回放对此并不关心,因为派生只读取 `assistant/chunk` 事件和第 0 行会话 header。
fixture 就是持久化会话日志(`<scenario>/session.jsonl`)。其 `assistant/chunk` 事件包含每个 `StreamChunk`,因此按 `(turn, step)` 分组可重建每次 `stream()` 调用的分片序列(每个循环步骤调用一次模型)。因此,录制就是“运行一次真实 agent 并收集 `.jsonl`,由快照 harness 完成;该插件本身不录制。fixture 的 `request/header` 内容可能被标记化为 `{{system}}`/`{{tools}}`harness 在一个场景中固定该内容,并除其余场景中的内容);回放不受影响,因为派生过程只读取 `assistant/chunk` 事件和第 0 行会话 header。
有两种失败 mode 无法仅 `assistant/chunk` 重建:在任何分片前纯抛出(例如 HTTP 401日志只包含 `turn/end {error}` 而没有分片),以及 cancel/hang时序,而非分片内容)。需要这些的场景提供可选 sidecar`<scenario>/replay.override.json`,它要么替换派生脚本(裸 `ReplayEntry[]`要么增补派生脚本(`{ patches: [{ at, entry }] }`:保留全部由 JSONL 派生的调用,仅在点名的调用索引处换入,索引从 0 计;`at` 等于派生长度时为追加,正是注入瞬态抛出之后那次重试尝试所占的槽位。Patch 索引必须互不重复。覆写文档、每个 patch 与每个条目,以及每个分片的判别字段都会在文件加载时接受校验`hang` 条目可以指定 `readyFile`在其前缀分片到达 loop 后、等待取消前,回放会写入空标记,使外部驱动器可以在不观察展示更新的情况下确定性取消。
有两种失败模式无法仅根据 `assistant/chunk` 重建:在产生任何分片前直接抛出异常(例如 HTTP 401此时日志只 `turn/end {error}` 而没有分片),以及取消或挂起(差异在时序,而非分片内容)。需要这些行为的场景提供伴随文件`<scenario>/replay.override.json`:它可以替换派生脚本(裸 `ReplayEntry[]`也可以增补派生脚本(`{ patches: [{ at, entry }] }`:保留所有从 JSONL 派生的调用,只替换指定的从 0 开始计数的调用索引;当 `at` 等于派生长度时,则在注入瞬态异常后的重试位置追加一次调用)。补丁索引不得重复。文件加载时会校验覆写文档、每个补丁和条目,以及每个分片的判别标签`hang` 条目可以指定 `readyFile`前缀分片到达循环后、开始等待取消前,回放会写入这个空标记,使外部驱动程序无需观察展示更新即可确定性取消。
## 嵌套 agent每会话键控
父 agent 委托给进程内 subagent 的场景会记录多个日志:父级(`session.jsonl`)和每个子级各一个`session.1.jsonl`等)。每个 agent 在同一上下文中作为自己`Session` 运行,因此回放必须为每个 agent 提供自的脚本。
父 agent 委托给进程内 subagent(子 agent的场景会记录多个日志:父会话使用 `session.jsonl`每个子会话各使用一个日志`session.1.jsonl` 等)。每个 agent 在同一上下文中作为独立`Session` 运行,因此回放必须为每个 agent 提供自的脚本。
回放调用会话 id 为每次调用建键(由 agent loop 标记的 `GenerateOptions.sessionId`)。实时会话 id 每次运行中都是新的随机值,绝不等于记录,因此实时会话通过**首次调用顺序** 绑定到已记录脚本:脚本按 header `createdAt` 排序(父在前,因为它必须先进行流式输出才能委托);第一个进行任何调用的实时会话取第一个脚本,下一个新会话取下一个,以此类推。然后,每个会话推进自己的游标。没有 `sessionId` 的调用绑定主脚本的单一匿名会话,因此单会话场景与以前完全相同。实时会话超过已记录脚本数时快速失败
回放根据发起调用会话 id 为每次调用建键(`GenerateOptions.sessionId` 由 agent loop 写入)。实时会话 id 每次运行时都会重新随机生成,绝不等于记录中的 id,因此实时会话**首次调用顺序**绑定到已记录脚本:脚本按 header 中的 `createdAt` 排序(父会话在前,因为它必须先开始流式输出才能委托);第一个发起调用的实时会话取第一个脚本,下一个新会话取下一个脚本,以此类推。此后每个会话分别推进自己的游标。没有 `sessionId` 的调用视为一个绑定主脚本的匿名会话,因此单会话场景的行为与以前完全相同。不同实时会话的数量超过已记录脚本数时会明确报错
## 配置
@@ -24,9 +24,9 @@ Fixture 就是持久化会话日志(`<scenario>/session.jsonl`)。其 `assis
|---|---|---|---|
| `file` | string | `$DSH_SNAPSHOT_FILE` | 主(父)`session.jsonl` fixture 的路径。必需(配置或 env。 |
| `overrideFile` | string | `$DSH_SNAPSHOT_OVERRIDE` | 主会话的可选 `ReplayOverrideDoc` sidecar`ReplayEntry[]` 替换其派生脚本,`{ patches }` 则按调用索引增补该脚本。 |
| `childFiles` | string[] | `$DSH_SNAPSHOT_CHILD_FILES` (path-delimited) | 嵌套场景中已记录的 subagent 子会话日志;单会话场景为空。 |
| `childFiles` | string[] | `$DSH_SNAPSHOT_CHILD_FILES`(以路径分隔符分隔) | 嵌套场景中已记录的 subagent 子会话日志;单会话场景为空。 |
| `providers` | `ReplayProviderConfig[]` | 无 | 可选的仅回放提供方和模型目录。每个提供方可以设置 `retryPolicy`,每个模型可以发布 `contextWindow`;已配置路由通过回放适配器分派,绝不执行提供方 I/O。 |
| `paceMs` | number | 无(突发) | 可选的每分片毫秒延迟,使下游传输(例如真实浏览器观察的 web SSE mux看到真正的增量传递。它只是仿真开关测试不得依赖它保证正确性。值必须是非负整数pace 等待期间中止会迅速取消流。 |
| `paceMs` | number | 无(突发) | 可选的每分片毫秒延迟,使下游传输(例如真实浏览器观察Web SSEServer-Sent Events多路复用器看到真正的增量传递。它只是仿真开关测试不得依赖它保证正确性。值必须是非负整数pace 等待期间中止会迅速取消流。 |
```yaml
- id: llm-replay
@@ -52,10 +52,10 @@ Fixture 就是持久化会话日志(`<scenario>/session.jsonl`)。其 `assis
## 导出项
- `installLlmReplay(ctx, config)`:安装已配置回放适配器或 catch-all `llm/stream` 监听器;返回 `ReplayHandle`(包含用于 HMR 安全的 `dispose()`,以及 `assertConsumed()` 拆卸检查;后者确保每个已记录脚本都绑定到实时会话,且每个已绑定游标都已耗尽,从而将场景静默驱动的模型调用少于记录数转换为明确诊断)。在测试中使用它,可以不通过 Loader 或 env var 驱动回放。
- `loadSessionScripts(config)`:解析场景的有序 `SessionScript[]` (主 + 子),准备按首次调用顺序绑定到实时会话。
- `loadReplayScript(config)`:只解析主会话的 `ReplayEntry[]` (如果存在则使用经校验的 sidecar 替换或 patch否则从 JSONL 派生fixture 缺失时快速失败)。
- `deriveReplayScript(events)` / `parseSessionLog(text)` / `parseSessionHeader(text)`:将已记录会话日志转换为脚本并读取其 header `id`/`createdAt` 的纯辅助工具。派生分组必须以 `finish` 分片结束;没有该分片的分组是已抛出 `stream()` 的指纹,必须改用 override sidecar 表达。
- `installLlmReplay(ctx, config)`:安装已配置回放适配器或 catch-all `llm/stream` 监听器;返回 `ReplayHandle`(包含用于保证 HMR(热模块替换)安全的 `dispose()`,以及清理阶段执行的 `assertConsumed()` 检查;后者确保每个已记录脚本都绑定到实时会话,且每个已绑定游标都已耗尽,从而将场景静默驱动的模型调用少于记录数转换为明确诊断)。在测试中使用它,可以不通过 Loader 或 env var 驱动回放。
- `loadSessionScripts(config)`:解析场景的有序 `SessionScript[]`(主会话 + 子会话),准备按首次调用顺序绑定到实时会话。
- `loadReplayScript(config)`:只解析主会话的 `ReplayEntry[]`(如果伴随文件存在则使用经校验的替换或补丁;否则从 JSONL 派生fixture 缺失时明确报错)。
- `deriveReplayScript(events)` / `parseSessionLog(text)` / `parseSessionHeader(text)`:将已记录会话日志转换为脚本并读取其 header `id`/`createdAt` 的纯辅助工具。派生分组必须以 `finish` 分片结束;没有该分片的分组是 `stream()` 抛出异常的指纹,必须改用 override sidecar 表达。
- 类型 `ReplayEntry` / `ReplayOverrideDoc` / `ReplayOverridePatch` / `SessionScript` / `ReplayConfig` / `ReplayProviderConfig` / `ReplayModelConfig` / `ReplayHandle` / `Config`
## 插件导出形态
@@ -66,11 +66,11 @@ Fixture 就是持久化会话日志(`<scenario>/session.jsonl`)。其 `assis
无。该无密钥测试适配器不向提供方模型发送请求,只将已记录 assistant 分片回放到测试 loop 中。
#### KV 缓存影响
#### KV Cache 影响
无;该包既不组装也不发送提供方请求。
无;该包package既不组装也不发送提供方请求。
## 已知限制与待完成工作
## 已知限制与暂缓事项
- **首次调用顺序脚本绑定假设串行委托**:并发运行同级 subagent 的 cut或运行中落地的压缩摘要调用)会非确定性地将实时会话绑定到已记录脚本;在这种场景出现前暂不实现更强的键控(`XXX(concurrent-subagents)`)。
- **只有生分片的调用派生**纯分片前抛出或 cancel/hang 场景需要 `replay.override.json` sidecar。替换和 patch 两种形式都只影响主会话;子会话脚本仍从各自日志派生。
- **首次调用顺序脚本绑定假设串行委托**:并发运行同级 subagent 的 cut或运行中发生的上下文压缩context compaction摘要调用)会非确定性地将实时会话绑定到已记录脚本;在这种场景出现前暂不实现更强的键控(`XXX(concurrent-subagents)`)。
- **只有会产生分片的调用才能派生**在产生分片前直接抛出异常或取消/挂起的场景需要 `replay.override.json` 伴随文件。替换和补丁两种形式都只影响主会话;子会话脚本仍从各自日志派生。

View File

@@ -4,20 +4,20 @@
用于测试通过 Cordis Loader 启动应用和 `cordis.yml` 的共享子进程 harness。`resolveExampleLaunch` 选择本地 `src` modetsx 和根 tsconfig 路径)或 CI `lib` mode普通 Node 和包导出);选择依据为显式 mode 或 `DSH_EXAMPLE_MODE`
`runLoaderSmoke` 接受 bin 和配置路径、可选的完整 bin 参数、环境覆盖、stdin、运行前设置和清理前检查。它负责隔离 cwd、DSH 主目录、诊断、deadline、终止、EOF 和清理;在零退出后返回两个流,失败时拒绝并带两个流。
`runLoaderSmoke` 接受可执行文件路径和配置路径、可选的完整可执行文件参数、环境变量覆盖、标准输入、运行前准备和清理前检查。它负责隔离工作目录、DSH 主目录、诊断、截止时间、终止、EOF 和清理;进程以零状态退出后返回两个流,失败时则返回拒绝并带两个流。
这是支持层测试基础设施,而非产品 API。
## 模型体验
无。该测试专用 harness 启动示例进程并检查它们的流,不会改变组装模型请求。
无。该测试专用 harness 启动示例进程并检查它们的流,不会改变组装后的模型请求。
#### KV 缓存影响
#### KV Cache 影响
无;该包既不组装也不发送提供方请求。
## 已知限制与待完成工作
## 已知限制与暂缓事项
- **构建 mode 需要事先构建**:配置还必须能够通过 `examples/node_modules` 向上解析每个命名包。
- **构建模式需要事先构建**:配置还必须能够通过 `examples/node_modules` 向上解析每个命名包。
- **捕获的 stdout 和 stderr 仅受 execa 默认 100 MB `maxBuffer` 约束**:失控子进程会在该上限处被终止,而不是在冒烟测试自选的预算处。
- **超时只终止直接子进程**:故障 fixture 生成的进程树可比冒烟测试存活更久,需要外部清理。
- **超时只终止直接子进程**故障 fixture测试前置数据spawn 的进程树可比冒烟测试存活更久,需要外部清理。

View File

@@ -1,13 +1,13 @@
# tasks/:后台任务能力
# tasks/:后台任务能力
[English](README.md) | 中文
后台 task id、有者隔离、读取、取消、等待和完成通知的共用归属位置。Bash、subagent 及未来的长时间运行工具共用一套面向模型的协议。参见[后台任务运行时 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和[任务注册表 seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)。
这是后台任务 id、有者隔离、读取、取消、等待和完成通知的共用归属位置。Bash、subagent(子 agent及未来的长时间运行工具共用一套面向模型的协议。参见[后台任务运行时 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和[任务注册表 seam Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)。
| 包package | ctx 键 | 角色 |
|---|---|---|
| [`tasks`](tasks/README.md)`@deepseek-ai/dsh-tasks` | `ctx.tasks` | 注册表 seam品牌 `<kind>-N` id、按有者设防的 readkillwaitlist 契约、快照词汇、防止 `attachSurface` 配置错误防线,以及快照不变式配套插件 |
| [`tasks-local`](tasks-local/README.md)`@deepseek-ai/dsh-tasks-local` | 无 | 进程局部的注册表实现:内存记录、首次结果优先的结算簿记,以及等待完成的拥有者清理与拆卸路径 |
| [`tasks`](tasks/README.md)`@deepseek-ai/dsh-tasks` | `ctx.tasks` | 注册表 seam品牌类型的 `<kind>-N` id、按有者隔离的读取/终止/等待/列出契约、快照词汇、`attachSurface` 配置错误防线,以及快照不变式配套 |
| [`tasks-local`](tasks-local/README.md)`@deepseek-ai/dsh-tasks-local` | 无 | 进程注册表实现:内存记录、首次结算为准的簿记,以及等待执行完毕的所有者清理路径和销毁路径 |
| [`tool-tasks`](tool-tasks/README.md)`@deepseek-ai/dsh-tool-tasks` | 无 | 面向模型的控制接口:`task_output``task_list``task_kill`、完成通知注入和后台工作习惯提示词段落 |
注册表拥有跨生产方或接口重载的状态;工具包拥有呈现。生产方通过 `ctx.tasks.start` 注册执行钩子,并自行决定其配置是否公开 `run_in_background`
生产方或接口重载时,状态仍由注册表持有;工具包负责呈现。生产方通过 `ctx.tasks.start` 注册执行钩子,并自行决定其配置是否公开 `run_in_background`

View File

@@ -2,25 +2,25 @@
[English](README.md) | 中文
[`@deepseek-ai/dsh-tasks`](../tasks/README.md) 注册表 seam 的进程局部实现:`LocalTaskService` 把每条记录保存在内存中,按 kind 签发 `<kind>-N` id并且只交出全新快照从不交出实时状态。它没有配置作为插件加载后即注册为 `ctx.tasks`
[`@deepseek-ai/dsh-tasks`](../tasks/README.md) 注册表 seam 的进程本地实现:`LocalTaskService` 把每条记录保存在内存中,按 kind 签发 `<kind>-N` id并且只交出全新快照从不交出实时状态。它没有配置作为插件加载后即注册为 `ctx.tasks`
## 生命周期
任务属于其 owner 和后端,而不是生产方工具 fiber因此重载生产方或表层不会停止任务。某个 owner 的第一个任务会把一个等待的 effect 附加到精确的 `Agent` scope。owner 释放会取消该对象的任务,等待生产方完全停稳,并移除其快照;复用 agent 或 Session id 无法重定向旧清理。
任务属于其所有者和后端,而不是生产方工具 fiber因此重载生产方或接口不会停止任务。某个所有者的第一个任务会把一个会被等待的 effect 附加到对应 `Agent` 对象的 scope 上。所有者的 dispose资源释放会取消该对象的任务,等待生产方完全停稳,并移除其快照;复用 agent智能体id 或会话 id 无法重定向旧清理操作
服务释放会关闭监听器、取消所有存活任务、等待其记录,并从仍存活的 owner scope 分离 effect。如果拆卸取消抛出异常,服务会强制记录标为失败,并警告工作可能遗留,而不会死锁。取消已返回但始终不终止 `done` 时,系统无法将其与缓慢停止区分开,拆卸可能因此停滞。
服务 dispose 会关闭监听器、取消所有存活任务、等待其记录完成,并从仍存活的所有者 scope 分离 effect。如果销毁期间的取消操作抛出异常,服务会强制记录标为失败,并警告工作可能成为孤立工作,而不会死锁。取消操作已返回但 `done` 始终未结算时,系统无法将其与缓慢停止区分开,销毁过程可能因此停滞。
结算遵循首次结优先:最早出现的终止结果(生产方结算、被隔离`failed``done` 拒绝,或拆卸强制失败)只记录一次,只通知监听器一次并对每个监听器单独隔离故障,然后释放等待方。挂起的等待会在监听器运行前把任务标记为已报告,因此呈现完成情况的表层不会重复发出通知。
结算遵循首次结优先原则:最早出现的终止结果(生产方结算、`failed` 隔离处理`done` 拒绝,或销毁时的强制失败)只记录一次,只通知监听器一次;各监听器的故障会单独隔离,随后释放等待方。挂起的等待会在监听器运行前把任务标记为已报告,因此呈现完成情况的表层不会重复发出通知。
## 模型体验
通过生产方插件和 [`dsh-tool-tasks`](../tool-tasks/README.md) 间接影响;它们会渲染 task id、输出、状态、取消和完成通知。
通过生产方插件和 [`dsh-tool-tasks`](../tool-tasks/README.md) 间接影响;它们会呈现任务 id、输出、状态、取消和完成通知。
#### KV Cache 影响
不会直接失效;请求前缀变更由命名消费方负责。
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项
- **任务只存在于进程本地**:记录随 harness 进程一起消亡;持久或跨重启执行需要一个单独实现该 seam 的后端。
- **静默无效的取消可能使拆卸停滞**:只有显式抛出异常才能安全地强制标为失败。
- **任务只存在于进程本地**:记录随 harness 进程终止而消失;持久或跨重启执行需要一个单独实现该 seam 的后端。
- **静默无效的取消可能使销毁过程停滞**:只有显式抛出异常才能安全地强制标为失败。

View File

@@ -6,21 +6,21 @@
## 服务契约
- `start(spec): TaskId` 验证控制表层、spec、精确的存活 owner以及可选的 `outputLimitBytes`,然后只调用生产方的 `run()` 一次。启动方抛出异常时不注册任何内容;成功返回会直接提交,不再执行其他可能失败的步骤。
- `start(spec): TaskId` 验证控制表层、spec、确切且仍存活 owner以及可选的 `outputLimitBytes`(如提供则须为正数),然后只调用生产方的 `run()` 一次。启动方抛出异常时不注册任何内容;成功返回会直接提交,不再执行其他可能失败的步骤。
- `get(id, caller?)``list(caller?)` 返回非消费式快照。列表只包含调用方拥有及无 owner 的任务。
- `read(id, caller?)` 消费流任务的唯一游标;对于最终输出任务,则以幂等方式读取终止输出。
- `kill(id, caller?, reason?)` 在更改状态前调用生产方取消。取消抛出异常时任务保持运行;成功则把状态改为 `stopping`,并将终止交付标记为已报告。
- `wait(id, timeoutMs, caller?, signal?)` 返回终止快照,或在超时时返回存活快照。中止只会停止等待;一旦终止交付已向该等待方提交,终止结果优先。
- `onTaskDone(listener)` 观察每条终止记录及其精确 owner。监听器抛出异常拒绝会被封装;系统不会等待监听器工作。
- `onTaskDone(listener)` 观察每条终止记录及其精确 owner。监听器抛出异常和产生的拒绝会被隔离;系统不会等待监听器工作。
- `attachSurface(name)` 在其 effect 生命周期内声明控制表层。如果没有附加任何表层,`start()` 会在生产方执行前失败。
有 owner 的访问会比较任务的 `SessionId` 与调用方。`bash-1` 等 id 可预测,因此这道隔离是安全边界。无 owner 的任务向调用方开放,并持续到服务释放。
`outputLimitBytes` 是生产方拥有的模型呈现策略,会原样携带到快照中。控制表层在添加状态或通知元数据后应用它;注册表不会重写生产方输出,也不会为省略此字段的生产方虚构默认值。
实现还必须兑现契约的生命周期语义:注册的存续期长于生产方与控制表层 fiberowner 释放和服务释放会取消存活工作并等待守约的生产方,结算遵循首次结果优先(一条终止记录、一轮故障隔离的监听器通知,然后释放等待方)。
实现还必须兑现契约的生命周期语义:注册的存续期长于生产方 fiber 与控制表层 fiberowner 释放和服务释放会取消仍在运行的工作并等待守约的生产方,结算遵循首次结果优先(一条终止记录、一轮异常受到隔离的监听器通知,然后释放等待方)。
参见[任务类型目录](../../../docs/core-data-structures/tasks.md)、[运行时 Agent Note](../../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)。
参见[任务类型目录](../../../docs/core-data-structures/tasks.md)、[运行时 Agent Noteagent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)。
## 模型体验
@@ -28,10 +28,10 @@
#### KV Cache 影响
不会直接失效;请求前缀变更由命名消费方负责。
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
## 已知限制与暂缓事项
- **流输出只有一个消费游标**:独立观察者需要游标或快照 API。
- **前台工作无法提升**:生产方在启动前选择前台或后台。
- **契约是进程内的**`TaskStart.run()` 传入回调和确切的 `Agent` 对象;持久或跨进程后端必须先重塑身份、重启、所有权与观察语义,才能实现此 seam。
- **前台工作无法转为后台**:生产方在启动前选择前台或后台。
- **契约是进程内的**`TaskStart.run()` 传入回调和确切的 `Agent` 对象;持久或跨进程后端必须先重塑身份、重启、所有权与观察语义,才能实现此 seam。

View File

@@ -14,11 +14,11 @@
它们的规范值依次为 `{ text, task }``PublicTaskSnapshot[]``{ outcome: 'cancellation-requested' | 'already-finished', task }`。公共快照携带 id、kind、label、status/detail 及开始/结束时间;它有意省略 `ownerSession` 和内部 `reported` 通知位。原生 renderer 保留上述状态与确认文本。
当生产方提供 `outputLimitBytes` 时,`task_output`终止 `task_kill` 和完成通知会在添加状态或通知文本后,对完整的原生 UTF-8 结果施加上限。只要能够容纳,读取就会保留输出尾部与控制后缀;有界完成通知则先为 `background task <id>``task_output` 收集指令预留空间,再把剩余字节用于可变的 kind、label、status、detail 与截断标记。一个前置 pre-execute 监听器会在策略运行前捕获调用方可见任务;每个任务控制定义的 final-content 回调会把其生产方上限应用到单文本拒绝、短路、规范化工具或流水线失败、替换和阻止;结构化多块策略结果保持自身形状。已有的生产方截断标记会复用,不会重复添加。省略该字段的生产方保留现有的无界控制表层行为。
当生产方提供 `outputLimitBytes` 时,`task_output`针对已终止任务的 `task_kill` 和完成通知会在添加状态或通知文本后,对完整的原生 UTF-8 结果施加上限。只要能够容纳,读取就会保留输出尾部与控制后缀;有界完成通知则先为 `background task <id>``task_output` 收集指令预留空间,再把剩余字节用于可变的 kind、label、status、detail 与截断标记。一个前置 pre-execute 监听器会在策略运行前捕获调用方可见任务;每个任务控制定义的 final-content 回调会把其生产方上限应用到单文本拒绝、短路、规范化工具或流水线失败、替换和阻止;结构化多块策略结果保持自身形状。已有的生产方截断标记会复用,不会重复添加。省略该字段的生产方保留现有的无界控制表层行为。
## 完成通知
一项尚未报告的完成会向精确 owner 的会话注入 `background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.`。应用上限时, PTY 支持的 64 字节下限,稳定 id 前缀和收集命令的优先级高于可变 label/detail因此通知仍可操作。注入是下一次请求使用的持久上下文并非唤醒。kill 或终止性 read/wait 会把交付标为已报告并抑制重复通知。owner 释放竞态无需特殊处理循环没有终结状态teardown 期间注入的通知作为空闲上下文追加——会话仍挂接时随之持久化以供恢复,脱离后随无引用日志一并丢弃。
一项尚未报告的完成会向精确 owner 的会话注入 `background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.`。应用上限时,即使采用 PTY 支持的 64 字节下限,稳定 id 前缀和收集命令的优先级高于可变 label/detail因此通知仍可操作。注入是下一次请求使用的持久上下文并非唤醒。kill 或针对已终止任务的 read/wait 会把交付标为已报告并抑制重复通知。owner 释放竞态无需特殊处理循环没有终结状态teardown 期间注入的通知作为空闲上下文追加——会话仍挂接时随之持久化以供恢复,之后随脱离挂接的日志一并丢弃。
## 配置
@@ -35,7 +35,7 @@
#### 模型看到的内容
该插件注册 scope 中的每次请求都包含以下指引。按 agent scope 过滤工具时,可能会隐藏工具,却不会移除独立注册的提示词区段。
该插件注册 scope 中的每次请求都包含以下指引。按 agent(智能体)scope 过滤工具时,可能会隐藏工具,却不会移除独立注册的提示词区段。
##### 后台任务指引
@@ -45,7 +45,7 @@ Track every background task id you start. You are notified in-session when a tas
#### Token 影响
激活期间,每次请求承担少量固定输入成本
激活期间,每次请求都会产生少量固定输入 token 开销
#### KV Cache 影响
@@ -59,7 +59,7 @@ Track every background task id you start. You are notified in-session when a tas
#### Token 影响
工具可见每次请求承担固定 schema 成本
工具可见时,每次请求都会产生固定 schema token 开销
#### KV Cache 影响
@@ -69,15 +69,15 @@ Track every background task id you start. You are notified in-session when a tas
#### 模型看到的内容
读取会返回输出或 `(no new output)`,随后是 `[status: <status>]` 和可选 detail。空列表返回 `(no background tasks)`。kill 返回 `requested cancellation of task <id>` 或现有终止状态。尚未报告且有 owner 的完成使用上述通知。
读取会返回输出或 `(no new output)`,随后是 `[status: <status>]` 和可选 detail。空列表返回 `(no background tasks)`。kill 返回 `requested cancellation of task <id>` 或现有终止状态。尚未报告且有 owner 的任务完成使用上述通知。
#### Token 影响
结果与通知在压缩前保留于父级历史。流读取不会重复已消费的输出;生产方提供的 `outputLimitBytes` 会限制每次完整读取或通知。
结果与通知在压缩compaction前保留于父级历史。流读取不会重复已消费的输出;生产方提供的 `outputLimitBytes` 会限制每次完整读取或通知。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 配置项失效。
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与暂缓事项

View File

@@ -2,9 +2,9 @@
[English](README.md) | 中文
面向外部的会话上报遥测telemetryseam 及其 OpenTelemetry 后端。整套设计归档于[复活 Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)边界公理harness 的职责止于 `emit()`,投递由上报 SDK 负责)、`telemetry/record` waterfall瀑布式事件脱敏规则由部署方挂载seam 自身不带任何规则、固定分片投影、handoff 游标,以及运维记录通道。
面向外部的会话上报遥测telemetryseam 及其 OpenTelemetry 后端。整套设计固定在[复活 Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)边界公理harness 的职责止于 `emit()`,投递由上报 SDK 负责)、`telemetry/record` waterfall瀑布式事件脱敏规则由部署方挂载seam 自身不带任何规则、固定分片投影、handoff 游标,以及运维记录通道。
| 包 | 职责 |
| 包package | 职责 |
|---|---|
| [`@deepseek-ai/dsh-session-telemetry`](session-telemetry/) | seam 本体捕获点、投影、脱敏、handoff 游标、运维信号,以及最小后端契约(`emit`/`flush?`/`shutdown`)。 |
| [`@deepseek-ai/dsh-session-telemetry-otel`](session-telemetry-otel/) | 部署方要加载的后端OTel JS SDK 的日志流水线(`LoggerProvider` + `BatchLogRecordProcessor` + OTLP/HTTP 导出器经透传passthrough原样配置。 |

View File

@@ -17,7 +17,7 @@
processor: {} # optional; passed verbatim to BatchLogRecordProcessor
```
`exporter.url` 是本包唯一自行校验的字段:必填、无默认值、必须能解析为 `http(s)`,因此缺失端点会在插件加载时失败(`processor.maxExportBatchSize` 不是正整数时同样如此SDK 会接受该值,随后却在关闭时因它挂起)。其余全部是 SDK 自己的选项形态,由 SDK 拥有并在 SDK 文档中说明两个配置块都整体透传passthrough`OTLPExporterNodeConfigBase` 的每个字段(`headers``timeoutMillis``compression``keepAlive` 等)都会到达导出器;批处理、导出节奏(`scheduledDelayMillis`)、重试、队列上限,以及持续失败下的丢失策略,都是 SDK 的文档化行为,经 `processor` 透传调优。该后端刻意不实现 `flush()`:批处理器是进程内唯一执行 flush 的组件,`shutdown()` 的排空正因如此才是完整的。从 `cordis.yml` 中删除该配置块即为退出方式:无残留状态,也没有 `enabled` 开关。
`exporter.url` 是本包package唯一自行校验的字段:必填、无默认值、必须能解析为 `http(s)`,因此缺失端点会在插件加载时失败(`processor.maxExportBatchSize` 不是正整数时同样如此SDK 会接受该值,随后却在关闭时因它挂起)。其余全部是 SDK 自己的选项形态,由 SDK 拥有并在 SDK 文档中说明两个配置块都整体透传passthrough`OTLPExporterNodeConfigBase` 的每个字段(`headers``timeoutMillis``compression``keepAlive` 等)都会到达导出器;批处理、导出节奏(`scheduledDelayMillis`)、重试、队列上限,以及持续失败下的丢失策略,都是 SDK 的文档化行为,经 `processor` 透传调优。该后端刻意不实现 `flush()`:批处理器是进程内唯一执行 flush 的组件,`shutdown()` 的排空正因如此才是完整的。从 `cordis.yml` 中删除该配置块即为退出方式:无残留状态,也没有 `enabled` 开关。
## 哪些数据会离开本机
@@ -25,7 +25,7 @@
## 字段映射
seam 记录 → SDK 日志记录:`time``timestamp`/`observedTimestamp``severity``severityNumber`/`severityText`INFO 9 / WARN 13 / ERROR 17`body` → 结构化日志 body`attributes` 原样照搬。接收端基于 `(session.id, event.seq)` 去重、按严重级别告警,并通过 `shutdown` 记录的缺失检测崩溃(一个曾有活动、没有 `shutdown` 运维记录、且已然陈旧的会话,就是未干净结束的会话)。该标记的含义是遥测干净地停止了对该会话的观察:它在会话自身 dispose资源释放时发出对于届时仍在运行的会话则在应用拆卸时发出标记之后又出现该会话的更多事件说明发生的是遥测重载而不是会话重启。跨谱系lineage的流并不自足恢复的会话在其自身 id 的流上从上一个进程停止之处继续fork 出的会话,其流从继承边界开始,前缀位于父会话的流中,由接收端基于 `session.parent_id` + `session.seed_length` 拼接。继续而非回放的一个后果:流中一个开启后再未关闭的轮次,标志着上一个进程死在了该轮次之内。恢复时本地日志会以合成的关闭事件修复,但这些修复绝不导出:导出的流忠实于崩溃进程实际发出的内容,其后干净的 `shutdown` 标记也只证明恢复后进程自身的退出。
seam 记录 → SDK 日志记录:`time``timestamp`/`observedTimestamp``severity``severityNumber`/`severityText`INFO 9 / WARN 13 / ERROR 17`body` → 结构化日志 body`attributes` 原样照搬。接收端基于 `(session.id, event.seq)` 去重、按严重级别告警,并通过 `shutdown` 记录的缺失检测崩溃(一个曾有活动、没有 `shutdown` 运维记录、且已然陈旧的会话,就是未干净结束的会话)。该标记的含义是遥测干净地停止了对该会话的观察:它在会话自身 dispose资源释放时发出对于届时仍在运行的会话则在应用关闭时发出标记之后又出现该会话的更多事件说明发生的是遥测重载而不是会话重启。跨谱系lineage的流并不自足恢复的会话在其自身 id 的流上从上一个进程停止之处继续fork 出的会话,其流从继承边界开始,前缀位于父会话的流中,由接收端基于 `session.parent_id` + `session.seed_length` 拼接。继续而非回放的一个后果:流中一个开启后再未关闭的轮次,标志着上一个进程死在了该轮次之内。恢复时本地日志会以合成的关闭事件修复,但这些修复绝不导出:导出的流忠实于崩溃进程实际发出的内容,其后干净的 `shutdown` 标记也只证明恢复后进程自身的退出。
## 模型体验
@@ -35,7 +35,7 @@ seam 记录 → SDK 日志记录:`time` → `timestamp`/`observedTimestamp`
无;本包既不组装也不发送提供方请求。
## 已知限制与延期工作
## 已知限制与暂缓事项
- **上游实验性源码树**`@opentelemetry/sdk-logs` 仍从上游实验性experimental源码树发布SDK API 的变动只会落在本包也仅落在本包seam 契约不动。
- **无真实 collector 覆盖**:所有测试都导出到本地 mock collector无密钥的 Loader 组合 e2e`tests/loader-composition.e2e.ts`在每次运行中都覆盖协议格式wire format形态而面对真实 OTLP 部署的行为认证、TLS、限流属于 SDK 导出器文档的职责范围。

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
遥测telemetryseam会话事件上报的捕获侧隔在一个后端契约之后任何上报 SDK 都无需变形即可满足该契约。塑造本包一切设计的边界公理:**本包的职责止于 `emit()`**。批处理、重试、排队与丢失策略都属于后端自身的 SDK本包既不为其立规也不做包装。设计依据与被否决的替代方案见[复活 Agent Noteagent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)。
遥测telemetryseam会话事件上报的捕获侧隔在一个后端契约之后任何上报 SDK 都无需变形即可满足该契约。塑造本包package一切设计的边界公理:**本包的职责止于 `emit()`**。批处理、重试、排队与丢失策略都属于后端自身的 SDK本包既不为其立规也不做包装。设计依据与被否决的替代方案见[复活 Agent Noteagent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)。
## 后端契约
@@ -10,11 +10,11 @@
## 捕获点
协调器的全部注册都经由组合方 fiber 的 effect 完成:`session/created`(收养:记录 header并经投影从构造边界起回读日志来自 fork 或恢复的构造函数种子绝不会在 firehose 上再次发出,也绝不会再次导出)、`session/event`(投影、深拷贝、脱敏、交接;零 I/O`session/flush`(转发可选的 `flush()` 提示并返回 void循环所等待的并行任务绝不能等待遥测`session/disposed`(在会话自身的终止边缘发出该会话的 `shutdown` 运维记录,接收端正是在这个边缘锚定崩溃检测;随后将该会话退役,因此长生命周期的后端既不会保留已关闭的会话,也不会在卸载时再次标记它们)、`agent/error`(唯一的实时总线转发;会话事件词汇有意不包含运错误记录)、一个 dispose effect拆卸时先标记每个仍存活的会话再等待后端的 `shutdown()`;失败只发出警告而不抛出),以及对 `ctx.sessions.list()` 的收养扫描(热重载不会重放 `session/created`)。
协调器的全部注册都经由组合方 fiber 的 effect 完成:`session/created`(收养:记录 header并经投影从构造边界起回读日志来自 fork 或恢复的构造函数种子绝不会在 firehose 上再次发出,也绝不会再次导出)、`session/event`(投影、深拷贝、脱敏、交接;零 I/O`session/flush`(转发可选的 `flush()` 提示并返回 void循环所等待的并行任务绝不能等待遥测`session/disposed`(在会话自身的终止边缘发出该会话的 `shutdown` 运维记录,接收端正是在这个边缘锚定崩溃检测;随后将该会话退役,因此长生命周期的后端既不会保留已关闭的会话,也不会在卸载时再次标记它们)、`agent/error`(唯一的实时总线转发;会话事件词汇有意不包含运错误记录)、一个 dispose effect拆卸时先标记每个仍存活的会话再等待后端的 `shutdown()`;失败只发出警告而不抛出),以及对 `ctx.sessions.list()` 的收养扫描(热重载不会重放 `session/created`)。
## 脱敏 waterfall
## 脱敏 waterfall(瀑布式事件)
每条记录在投影与 `emit()` 之间都要经过 `telemetry/record` waterfall(瀑布式事件),这是该 seam 的擦除扩展点。seam 自身不带任何规则:最内层的 `next()` 原样透传记录,因此未挂载监听器时,记录以捕获时的原样到达后端;导出数据能干净到什么程度,恰恰取决于部署方挂载了什么规则。监听器通过变换 `next()` 的返回值来堆叠;不调用 `next()` 就返回,即替换其下方的全部逻辑;抛出异常的监听器会在协调器的隔离范围内以 fail-closed 方式下这一条记录。脱敏只作用于导出副本;权威会话日志永不改写。
每条记录在投影与 `emit()` 之间都要经过 `telemetry/record` waterfall这是该 seam 的脱敏扩展点。seam 自身不带任何规则:最内层的 `next()` 原样透传记录,因此未挂载监听器时,记录以捕获时的原样到达后端;导出数据能干净到什么程度,恰恰取决于部署方挂载了什么规则。监听器通过变换 `next()` 的返回值来堆叠;不调用 `next()` 就返回,即替换其下方的全部逻辑;抛出异常的监听器会在协调器的隔离范围内以 fail-closed 方式下这一条记录。脱敏只作用于导出副本;权威会话日志永不改写。
## handoff 游标
@@ -36,7 +36,7 @@
无;本包既不组装也不发送提供方请求。
## 已知限制与延期工作
## 已知限制与暂缓事项
- **尽力而为的投递**:游标标记的是已交接而非已投递;在重载窗口内被拆除的会话无法重新收养;崩溃时留在后端队列中的内容会丢失。持久 outboxspool、每 sink 游标、at-least-once推迟到有部署方提出明确的崩溃丢失要求时再实现见[复活 Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)。
- **尽力而为的投递**:游标标记的是已交接而非已投递;在重载窗口内被拆除的会话无法重新收养;崩溃时留在后端队列中的内容会丢失。持久 outboxspool、每 sink 游标、at-least-once推迟到有部署方提出明确的崩溃丢失要求时再实现见[复活 Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)。
- **不内置脱敏规则**:未挂载 `telemetry/record` 监听器时,记录以捕获时的原样离开进程,包括文件内容或命令输出中内嵌的任何凭据;向共享 collector 导出的部署方自行负责其规则集。

View File

@@ -2,10 +2,10 @@
[English](README.md) | 中文
工具调用超时策略插件。它是单一 **产品** package它是 `tools/execute` 环绕分发 seam由 [`dsh-tools`](../core/tools) 拥有)和纯 [`dsh-timeout`](../util/timeout) 库的部署策略消费方,而非带接口/实现拆分的可替换能力,因此无需 seam 三包组合。
工具调用超时策略插件。它是单一 **产品**package它是 `tools/execute` 环绕分发 seam由 [`dsh-tools`](../core/tools) 拥有)和纯 [`dsh-timeout`](../util/timeout) 库的部署策略消费方,而非带接口/实现拆分的可替换能力,因此无需 seam 三包组合。
| 包 | 职责 | ctx 键 |
|---|---|---|
| `timeout-policy/` | `tools/execute` 包装层:对每个已配置工具,它都在 `exec.signal`启动单次调用截止时间,并在截止时间先到时返回结构化 `TOOL_TIMEOUT` 结果 | (注册 `tools/execute` 监听器;不注入任何内容) |
| `timeout-policy/` | `tools/execute` 包装层:对每个已配置工具,它都在 `exec.signal`设置单次调用截止时间,并在截止时间先到时返回结构化 `TOOL_TIMEOUT` 结果 | (注册 `tools/execute` 监听器;不注入任何内容) |
超时被拆分为三层:[`dsh-timeout`](../util/timeout) 拥有纯计时/分类原语(`deadline`/`timeoutOf`每种能力拥有终止操作bash 终止其进程组fetch 提供方关闭其 socket本包则拥有 *作为部署策略的面向模型工具调用预算*:没有面向模型的超时参数,也没有全局默认值。它是[超时库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) 所预见的中间件。`bash` 和钩子命令执行保留各自的 `BASH_TIMEOUT` 后端超时,不经过此策略。
超时被拆分为三层:[`dsh-timeout`](../util/timeout) 拥有纯计时/分类原语(`deadline`/`timeoutOf`每种能力拥有终止操作bash 终止其进程组fetch 提供方关闭其 socket本包则拥有 *作为部署策略的面向模型工具调用预算*:没有面向模型的超时参数,也没有全局默认值。它是[超时库 Agent Noteagent 决策记录)](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md) 所预见的中间件。`bash` 和钩子命令执行保留各自的 `BASH_TIMEOUT` 后端超时,不经过此策略。

View File

@@ -2,11 +2,11 @@
[English](README.md) | 中文
工具调用超时强制执行器:`tools/execute` 环绕分发监听器。它会在 `exec.signal`启动单次调用的协作式截止时间;适用条件是工具声明了 `timeoutMs`且声明位于其 `ToolDefinition`截止时间先到时,它返回结构化 `TOOL_TIMEOUT` 结果。预算从工具自身的声明中读取(`ToolDefinition.timeoutMs`,由拥有该工具的插件设置),因此此插件是 **零配置** 的。它是 `tools/execute` 包装层的参考实现,也是面向模型工具调用预算的强制执行归属地(超时库 Agent Note 所预见的中间件)。
工具调用超时强制执行器:`tools/execute` 环绕分发监听器会在 `exec.signal`设置单次调用的协作式截止时间;适用声明了 `timeoutMs` 且声明位于其 `ToolDefinition`的工具。该截止时间先到时,它返回结构化 `TOOL_TIMEOUT` 结果。预算从工具自身的声明中读取(`ToolDefinition.timeoutMs`,由拥有该工具的插件设置),因此此插件是**零配置**的。它是 `tools/execute` 包装层的参考实现,也是面向模型工具调用预算的强制执行归属地(超时库 Agent Noteagent 决策记录)所预见的中间件)。
## 插件(命名空间:`timeout-policy`
它是函数/命名空间插件(`name`/`inject`/`apply`),而非服务。它不注册工具,也不接受配置;它消费 `ctx.tools``tools/execute` waterfall`dsh-tools` 注册表始终提供),并读取每个已分发工具声明的 `timeoutMs`;该声明来自注册表(`ctx.tools.get(exec.name)`)。
它是函数/命名空间插件(`name`/`inject`/`apply`),而非服务。它不注册工具,也不接受配置;它消费 `ctx.tools``tools/execute` waterfall瀑布式事件)(`dsh-tools` 注册表始终提供),并读取每个已分发工具声明的 `timeoutMs`;该声明来自注册表(`ctx.tools.get(exec.name)`)。
```yaml
- id: timeout-policy
@@ -19,21 +19,21 @@
**声明了 `timeoutMs` 的工具**,监听器会:
1. 从注册表中的工具自身声明(`ctx.tools.get(exec.name)?.timeoutMs`)读取预算,并启动 `deadline(exec.signal, timeoutMs, 'TOOL_TIMEOUT')`:一个将调用方中止与此插件计时器融合的信号(`@deepseek-ai/dsh-timeout`)。
2. 将该派生信号替换到 `exec` 上用于下游分发,然后恢复调用方自身的信号(cordis `next()` 忽略传入的参数,因此包装层会原地修改共享 `exec`;恢复可使 `tools/post-execute` 看到调用方的信号)。
3. 分发后,如果 `timeoutOf(d.signal, 'TOOL_TIMEOUT')` 匹配,即此插件自身的计时器触发,则将结果替换为结构化 `TOOL_TIMEOUT` 工具结果:`{ isError: true, error: { message, info: { name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' } }, content: 'Error: tool call timed out after <ms>ms' }`
1. 从注册表中的工具自身声明(`ctx.tools.get(exec.name)?.timeoutMs`)读取预算,并设置 `deadline(exec.signal, timeoutMs, 'TOOL_TIMEOUT')`:一个将调用方中止与此插件计时器融合的信号(`@deepseek-ai/dsh-timeout`)。
2. 将该派生信号替换到 `exec` 上用于下游分发,然后恢复调用方自身的信号(Cordis `next()` 忽略传入的参数,因此包装层会原地修改共享 `exec`;恢复可使 `tools/post-execute` 看到调用方的信号)。
3. 分发后,如果 `timeoutOf(d.signal, 'TOOL_TIMEOUT')` 检测到此插件自身的计时器触发,则将结果替换为结构化 `TOOL_TIMEOUT` 工具结果:`{ isError: true, error: { message, info: { name: 'ToolTimeoutError', code: 'TOOL_TIMEOUT' } }, content: 'Error: tool call timed out after <ms>ms' }`
**未声明预算的工具** 会原样委托(不启动截止时间)。
基础 `next()``tools/execute` 在注册表中带规范化的分发 thunk因此当超时信号到达抛出自身上游中止错误的提供方时分发会先将其转换为普通错误结果再由此包装层替换为 `TOOL_TIMEOUT`。这一顺序就是替换依据信号(`timeoutOf`)而非已分发结果形状的原因。
基础 `next()`注册表为 `tools/execute` 提供的、带规范化处理的分发 thunk因此当超时信号到达抛出自身上游中止错误的提供方时分发会先将其转换为普通错误结果再由此包装层替换为 `TOOL_TIMEOUT`。这一顺序就是替换依据信号(`timeoutOf`)而非已分发结果形状的原因。
### 协作式,而非硬终止
派生信号只会 **通知**;终止仍于工具以及它`exec.signal` 转发的能力(`dsh-timeout`不拥有 kill)。**因此,声明 `timeoutMs` 意味着「与 `exec.signal` 协作」**:忽略该信号的工具不会在超时时停止。只有转发信号的工具才应声明该字段;已交付的 `web_fetch`/`web_search`(通过 `ctx.web` 转发给提供方)是参考实现。`TOOL_TIMEOUT` 无需会话事件以满足可重建性:它是最终面向模型的 `tool/result`,已由循环记录。
派生信号只会 **通知**是否终止仍取决于工具及其`exec.signal` 转发的能力(`dsh-timeout`本身不负责硬终止)。**因此,声明 `timeoutMs` 意味着「与 `exec.signal` 协作」**:忽略该信号的工具不会在超时时停止。只有转发信号的工具才应声明该字段;已交付的 `web_fetch`/`web_search`(通过 `ctx.web` 转发给提供方)是参考实现。`TOOL_TIMEOUT` 无需会话事件以满足可重建性:它是最终面向模型的 `tool/result`,已由循环记录。
### 与其他 `tools/execute` 包装层组合
多个 `tools/execute` 监听器按 cordis 注册顺序组合。与未来的重试/沙箱/指标包装层一起使用时,注册顺序决定语义:「超时覆盖整个重试操作」(超时注册在外层),或「超时覆盖每次尝试」(超时注册在内层)。
多个 `tools/execute` 监听器按 Cordis 注册顺序组合。与未来的重试/沙箱/指标包装层一起使用时,注册顺序决定语义:「超时覆盖整个重试操作」(超时注册在外层),或「超时覆盖每次尝试」(超时注册在内层)。
## 模型体验
@@ -45,13 +45,13 @@
#### Token 影响
未超时调用为零 token。超时会添加一条短小且保留的错误结果,并可防止体积更大的延迟提供方结果进入上下文。
未超时调用不会增加 token。超时会添加一条会被保留的简短错误结果,并可防止体积更大、较晚返回的提供方结果进入上下文。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与延后工作
## 已知限制与暂缓事项
- **协作式,绝不是硬终止**:截止时间只通过 `exec.signal` 通知;忽略该信号的工具不会在超时时停止(参见「协作式,而非硬终止」一节)。
- **没有统一预算**:只有声明 `timeoutMs` 并将其放在 `ToolDefinition` 上的工具才会获得截止时间;未声明工具没有注册表级默认值(已交付的 `bash`/`read`/`write`/`edit` 有意不声明)。

View File

@@ -1,11 +1,11 @@
# todo/todo规划能力系列
# todo/todo规划能力家族
[English](README.md) | 中文
面向模型的 todo 工具。它是单一 **产品** package这里没有接口实现 seam因为该列表是由单一所有者管理的会话状态每个 agent智能体会话拥有自己的列表而非可替换能力。
面向模型的 todo 工具。它是单一 **产品**package这里没有接口实现 seam因为该列表是由单一所有者管理的会话状态每个 agent智能体会话拥有自己的列表而非可替换能力。
| 包 | 职责 | ctx 键 |
|---|---|---|
| `tool-todo/` | 面向模型的 `todo_write` 工具;将完整列表写入会话日志(`todo/write` | (注册到 `ctx.tools` |
列表存在于事件溯源会话日志中(`SessionEventMap['todo/write']`,由 [`dsh-session`](../core/session) 拥有);本包是追加快照的轻量消费方。[TUI 应用](../examples/tui-demo)等 UI 以及宿主/客户端运行时会根据会话事件渲染该持久列表。
列表存在于事件溯源会话日志中(`SessionEventMap['todo/write']`,由 [`dsh-session`](../core/session) 拥有);本包是追加快照的轻量消费方。[TUI 应用](../examples/tui-demo)等 UI 以及宿主/客户端运行时会根据会话事件渲染该持久列表。

View File

@@ -6,29 +6,29 @@
## 功能
注册一个工具 `todo_write(todos: [{ content, status }])``ctx.tools`。模型每次调用都会发送完整列表,不存在部分更新或单项编辑。每次调用都会向调用 agent 的会话日志追加 `todo/write` 事件(完整列表快照),具体调用 `agent.session.append('todo/write', { todos })`;当前列表是最新的该类事件(回放时后写者胜)。
注册一个工具 `todo_write(todos: [{ content, status }])``ctx.tools`。模型每次调用都会发送完整列表,不存在部分更新或单项编辑。每次调用都会向调用 agent 的会话日志追加 `todo/write` 事件(完整列表快照),具体调用 `agent.session.append('todo/write', { todos })`;当前列表是最新的该类事件(回放时后写覆盖先写)。
`status``pending``in_progress``completed` 之一。
## 单一所有者
该列表属于调用工具的唯一 agent 会话。不存在 subagent共享swarm scope非 agent 调用方(没有 `exec.agent`)无处写入列表,因此会被拒绝。这是有意设置的 scope 限制,详见 Agent Note。
该列表属于调用工具的唯一 agent 会话。不存在 subagent共享swarm scope非 agent 调用方(没有 `exec.agent`)无处写入列表,因此会被拒绝。这是有意设置的 scope 限制,详见 Agent Noteagent 决策记录)
## 验证
除 schema 的类型/必填/枚举检查外,`execute` 还会拒绝空或重复的 `content`、同时存在多个 `in_progress` 任务的情况(连贯计划最多只有一个活跃任务),以及 `content`/`status` 之外的任何条目键——扩展条目形状id、嵌套响亮失败而不是被静默压平,保证落日志的快照与模型自认为写入的内容一致。顺序与保持列表最新的纪律由模型根据工具描述负责。
除 schema 的类型/必填/枚举检查外,`execute` 还会拒绝空或重复的 `content`、同时存在多个 `in_progress` 任务的情况(连贯计划最多只有一个活跃任务),以及 `content`/`status` 之外的任何条目键——扩展条目形状id、嵌套明确报错而不是被静默压平,保证落日志的快照与模型自认为写入的内容一致。列表的顺序及及时更新由模型依照工具描述负责。
## 渲染
规范结果为 `{ todos, counts: { pending, inProgress, completed } }`;其 Native 渲染器返回精简的更新确认。工具还会写入完整 `todo/write` 会话事件。UI 订阅事件流,并自行渲染该持久列表:[TUI 应用](../../examples/tui-demo)与 [web 客户端](../../client/ui-conversation)基于站立计划(其后没有更晚 `turn/start` 的最近一次 `todo/write`显示计划条web 另有专属工具行)([展示](../../../.agents/notes/implemented/feature/2026-07-23-web-todo-display.md)、[生命周期](../../../.agents/notes/implemented/feature/2026-07-28-todo-plan-clears-on-next-turn.md))。
规范结果为 `{ todos, counts: { pending, inProgress, completed } }`;其 Native 渲染器返回精简的更新确认。工具还会写入完整 `todo/write` 会话事件。UI 订阅事件流,并自行渲染该持久列表:[TUI 应用](../../examples/tui-demo)与 [web 客户端](../../client/ui-conversation)基于当前有效计划(其后没有更晚 `turn/start` 的最近一次 `todo/write`显示计划条web 另有专属工具行)([展示](../../../.agents/notes/implemented/feature/2026-07-23-web-todo-display.md)、[生命周期](../../../.agents/notes/implemented/feature/2026-07-28-todo-plan-clears-on-next-turn.md))。
## 会话投影
当组合挂载了 `ctx.sessionProjections`[`@deepseek-ai/dsh-session-projection`](../../session-projection/session-projection/README.md))时,本包在一个注入子插件注册 `todos` 投影单元:`init` = `null`(尚无写入)、`apply` = 从每个 `todo/write` 取整表,并在每个 `turn/start` 清为 `null`站立计划;`turn/end` 保留刚完成的清单;其余事件都返回同一个状态引用)、`view` = 恒等、`stateVersion` = 2。key 在本包合并进 `SessionProjectionMap`(经接口包的 `/types` 出口);框架驱动该单元,载体历史尾页与 `session/projection` 推送帧上供给该值。未注册表的组合不受影响。生命周期理由见 [下一轮清空 todo 计划](../../../.agents/notes/implemented/feature/2026-07-28-todo-plan-clears-on-next-turn.md)。
当组合挂载了 `ctx.sessionProjections`[`@deepseek-ai/dsh-session-projection`](../../session-projection/session-projection/README.md))时,本包在一个注入子插件注册 `todos` 投影单元:`init` = `null`(尚无写入)、`apply` = 从每个 `todo/write` 取整表,并在每个 `turn/start` 清为 `null`当前有效计划;`turn/end` 保留刚完成的清单;其余事件都返回同一个状态引用)、`view` = 恒等、`stateVersion` = 2。该键在本包合并进 `SessionProjectionMap`(经接口包的 `/types` 出口);框架驱动该单元,载体通过历史尾页与 `session/projection` 推送帧提供该值。未挂载注册表的组合不受影响。生命周期理由见 [下一轮清空 todo 计划](../../../.agents/notes/implemented/feature/2026-07-28-todo-plan-clears-on-next-turn.md)。
## 导出形状
函数/命名空间插件:导出 `name`/`inject`/`apply`,不提供默认导出。意外的 `export default`通过 Loader 的 `unwrapExports` 折叠模块并丢弃 `inject`(参见 [docs/postmortem/0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md))。
函数/命名空间插件:导出 `name`/`inject`/`apply`,不提供默认导出。意外的 `export default` Loader 的 `unwrapExports` 折叠为默认导出,并导致 `inject` 丢失(参见 [docs/postmortem/0001](../../../docs/postmortem/0001-acp-default-export-drops-inject.md))。
## 模型体验
@@ -40,28 +40,28 @@
#### Token 影响
工具可见的每个请求都有固定 schema 成本
工具可见的每个请求都有固定 schema token 开销
#### KV Cache 影响
只要定义和可见性不变,前缀就保持稳定。插件生命周期或 scope 限制可能会使此 schema 之后的复用失效。
只要定义和可见性不变,前缀就保持稳定。插件生命周期或 scope 限制可能会使此 schema 起的缓存复用失效。
### 工具调用历史与结果
#### 模型所见内容
每个 assistant 工具调用都会在参数中保留整个替换列表。成功时精确返回 `Updated todo list: <pending> pending, <inProgress> in progress, <completed> completed.`。稳定失败文本为 ``Error: invalid todo: `content` must be a non-empty string``、`Error: invalid todos: duplicate content "<content>"`、`Error: invalid todos: at most one task may be in_progress, got <count>` 和 `Error: todo_write requires an owning agent session`。完整 `todo/write` 会话事件是 UI 与回放状态,而非第二条模型消息。
每个 assistant 工具调用都会在参数中保留整个替换列表。成功时原样返回 `Updated todo list: <pending> pending, <inProgress> in progress, <completed> completed.`。稳定失败文本为 ``Error: invalid todo: `content` must be a non-empty string``、`Error: invalid todos: duplicate content "<content>"`、`Error: invalid todos: at most one task may be in_progress, got <count>` 和 `Error: todo_write requires an owning agent session`。完整 `todo/write` 会话事件是 UI 与回放状态,而非第二条模型消息。
#### Token 影响
Token 增长与模型每次提交的完整列表成比例且这些调用参数会保留到压缩compaction。结果本身很小且形状固定。
token 用量会随模型每次提交的完整列表增长且这些调用参数会保留到压缩compaction。结果本身很小且形状固定。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
## 已知限制与延后工作
## 已知限制与暂缓事项
- **仅单一所有者 scope**:列表属于唯一调用 agent 会话subagent共享swarm scope 是有意裁减(参见「单一所有者」一节),非 agent 调用方会被拒绝。
- **目形状有意保持最小**`content` 加三态 `status`;整表替换不需要稳定 id、优先级或 active-form 字段。
- **仅单一所有者 scope**:列表属于唯一调用 agent 会话subagent共享swarm scope 是有意设置的限制(参见「单一所有者」一节),非 agent 调用方会被拒绝。
- **目形状有意保持最小**`content` 加三态 `status`;整表替换不需要稳定 id、优先级或 active-form 字段。
- **整表替换是唯一操作**:没有部分更新,也没有回读工具;模型每次调用都必须重新发送完整列表。

View File

@@ -2,21 +2,21 @@
[English](README.md) | 中文
面向用户的交互通道和进程外 SDK 服务器。这些是**产品** package由用户或 SDK 客户端直接操作的真实接口。
面向用户的交互通道和进程外 SDK 服务器。这些是**产品**包package由用户或 SDK 客户端直接操作的真实接口。
| 包 | 职责 | ctx 键 |
|---|---|---|
| `commands/` | 用户命令注册表:共享发现元数据、作用域遮蔽、取消以及 UI 直接分派 | `ctx.commands` |
| `user-approval/` | 一次性用户审批机制、封闭的结果词汇、审计事件和逐会话审批策略 | `ctx.approval` |
| `permission/` | 面向用户的权限预设(`workspace-write`/`danger-full-access`用一个产品级选择组合沙箱模式与审批策略两个调节项,并写入各自的会话事件 | `ctx.permission` |
| `permission/` | 面向用户的权限预设(`workspace-write`/`danger-full-access`通过一项产品级选择组合沙箱模式与审批策略两个可调参数,并写入各自的会话事件 | `ctx.permission` |
| `user-interaction/` | UI 支持的确认工具所使用的抽象用户问答 seam | `ctx.userInteraction` |
| `tool-ask-user/` | 模型侧 `ask_user_question` 工具,基于 `ctx.userInteraction` 实现 | (注册到 `ctx.tools` |
| `tui/` | 交互式 pi-tui 终端通道:渲染会话标题、事件和工具意图,响应 `ctx.userInteraction`,并托管由 effect 持有的插件浮层 | `ctx.tui`(驱动 `ctx.agents` |
| `jsonrpc/` | 面向进程外 SDK 客户端的 stdio JSON-RPC 服务器 | (驱动 `ctx.agents` |
| `app-boot/` | app bin 的共享启动粘合层:加载 `.env`、Loader 快速失败保护、感知快照的配置解析,以及等待整棵树停稳的启动序列 | (供各 bin 使用的库) |
| `app-boot/` | app bin 的共享启动粘合层:加载 `.env`会明确报错的 Loader 保护机制、感知快照的配置解析,以及等待整棵树停稳的启动序列 | (供各 bin 使用的库) |
UI 集成属于客户端驱动插件,而非对循环的修改:它使用现有的 `agent/*` 事件分类和 `dsh-agent` 工厂。[`tui`](tui/README.md) 是交互式终端入口,并提供终端本地的 `ctx.tui` 扩展服务;[`jsonrpc`](jsonrpc/README.md) 为进程外 SDK 客户端提供服务,非交互式单次任务使用 `cli-demo`。[`commands`](commands/README.md) 是 TUI 使用的用户发现与分派通道;命令输入和输出不会成为模型消息。
UI 集成属于客户端驱动插件,而非对循环的修改:它使用现有的 `agent/*` 事件分类和 `dsh-agent` 工厂。[`tui`](tui/README.md) 是交互式终端入口,并提供终端本地的 `ctx.tui` 扩展服务;[`jsonrpc`](jsonrpc/README.md) 为进程外 SDK 客户端提供服务,非交互式的一次性任务使用 `cli-demo`。[`commands`](commands/README.md) 是 TUI 使用的仅面向用户发现与分派通道;命令输入和输出不会成为模型消息。
`user-approval``user-interaction``tool-ask-user` 位于此处,因为向用户提问是由 UI 支持的产品功能,并不属于提供方无关的核心主干。`user-approval` 持有一次性的 `ctx.approval` 决策机制及其策略层级;应答方仍归拥有 agent智能体的通道或自动化传输层所有`user-interaction` 保持提供方无关(`ctx.userInteraction``tool-ask-user` 是其模型侧消费方,而交互式 app 包提供具体实现
`user-approval``user-interaction``tool-ask-user` 位于此处,因为向用户提问是由 UI 支持的产品功能,并不属于提供方的核心主干。`user-approval` 负责一次性的 `ctx.approval` 决策机制及其策略层级;应答逻辑仍由负责 agent智能体的通道或自动化传输层提供`user-interaction` 保持提供方无关(`ctx.userInteraction``tool-ask-user` 是其模型侧消费方,而交互式 app 包提供具体的提供方
基于 [`agent-spine-demo`](../examples/agent-spine-demo/README.md) 组合的可运行 app bundle 位于 [`examples/`](../examples/README.md)`tui-demo``acp-demo``jsonrpc-demo`)。`acp-demo``jsonrpc-demo` 持有启动 bin`tui-demo` bundle 则由产品 [`dsh`](../../apps/cli/README.md) CLI 启动。`ui/` 保留可复用的用户SDK 通道插件和共享 `app-boot` 粘合层;仅供自动化使用的 ACP 传输层位于 [`acp/`](../acp/README.md)。每个入口都持有自己的 stdout 策略,叶子 `cordis.yml` 则提供后端与可选工具。
基于 [`agent-spine-demo`](../examples/agent-spine-demo/README.md) 组合的可运行 app bundle 位于 [`examples/`](../examples/README.md)`tui-demo``acp-demo``jsonrpc-demo`)。`acp-demo``jsonrpc-demo` 各自提供启动 bin`tui-demo` bundle 则由产品 [`dsh`](../../apps/cli/README.md) CLI(命令行界面)启动。`ui/` 保留可复用的用户SDK 通道插件和共享 `app-boot` 粘合层;仅供自动化使用的 ACPAgent Client Protocol传输层位于 [`acp/`](../acp/README.md)。每个入口都负责自己的 stdout 策略,叶子 `cordis.yml` 则提供后端与可选工具。