Merge remote-tracking branch 'origin/master' into xtr/agent-loop-message-machine

# Conflicts:
#	packages/context/workspace-context/README.md
#	packages/llm/llm-retry/README.md
#	packages/session-persistence/session-checkpoint-policy/README.md
#	scripts/type-equiv.manifest.json
This commit is contained in:
_Kerman
2026-07-26 16:45:27 +08:00
699 changed files with 15263 additions and 1394 deletions

View File

@@ -0,0 +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
README.md: f08157d411a018141cdc21c487f81ae198f4de56
README.zh.md: d321ee9a181eb27ccecb143d3733ee46e417b27f

View File

@@ -1,5 +1,7 @@
# ui/ — human and SDK-client integration surfaces
English | [中文](README.zh.md)
Human-facing channels and the out-of-process SDK server. These are **product** packages: real interfaces that a person or SDK client drives.
| Package | Role | ctx key |

22
packages/ui/README.zh.md Normal file
View File

@@ -0,0 +1,22 @@
# ui/:面向用户和 SDK 客户端的集成接口
[English](README.md) | 中文
面向用户的交互通道和进程外 SDK 服务器。这些是**产品** 包package由用户或 SDK 客户端直接操作的真实接口。
| 包 | 职责 | ctx 键 |
|---|---|---|
| `commands/` | 用户命令注册表:共享发现元数据、作用域遮蔽、取消以及 UI 直接分派 | `ctx.commands` |
| `user-approval/` | 一次性用户审批机制、封闭的结果词汇、审计事件和逐会话审批策略 | `ctx.approval` |
| `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 使用的库) |
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 包提供具体实现。
基于 [`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` 则提供后端与可选工具。

View File

@@ -0,0 +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
README.md: 59fc4ed46f047ee8f72aa237ec3a47b21091358f
README.zh.md: 596392a8eb11469a9f3afb34046c2c37521a4ed6

View File

@@ -1,5 +1,7 @@
# `@deepseek-ai/dsh-app-boot`
English | [中文](README.zh.md)
Shared boot glue for the app bins ([`dsh`](../../../apps/cli/README.md), [`dsh-cli-demo`](../../examples/cli-demo/README.md), [`dsh-acp-demo`](../../examples/acp-demo/README.md)): each bin is a thin self-executing composition over these helpers, parameterized by its diagnostic prefix, so the loader-failure lore lives once — under the per-file coverage gate — instead of drifting between published artifacts.
| Export | Role |

View File

@@ -0,0 +1,48 @@
# `@deepseek-ai/dsh-app-boot`
[English](README.md) | 中文
供 app bin[`dsh`](../../../apps/cli/README.md)、[`dsh-cli-demo`](../../examples/cli-demo/README.md)、[`dsh-acp-demo`](../../examples/acp-demo/README.md))共用的启动粘合层:每个 bin 都是在这些 helper 上构建的精简自执行组合并以自身诊断前缀参数化。这样Loader 故障处理知识只需维护一处并接受逐文件覆盖率门禁,不会在已发布产物之间逐渐分化。
| 导出 | 职责 |
|---|---|
| `resolveConfigPath(path, snapshotMode, cwd?)` | 生成绝对配置路径;当 `snapshotMode === 'replay'` 时,把 basename 为 `cordis.yml`/`.yaml` 的文件替换为同级 `cordis.snapshot.yml` |
| `loadEnv(binName, dir?, warn?)` | 加载已被 git 忽略的 `.env`Node `process.loadEnvFile`);文件不存在不影响启动,文件无法加载时输出一行带标签的警告(默认写入 stderr |
| `installFailLoud(binName, proc?)` | 将 `boot()` 之后未处理的 Loader rejection 转换为一行带标签的 stderr 消息并执行 `exit(1)`;返回卸载函数(供测试使用) |
| `assertEntriesLoaded(ctx, binName)` | 树结算后,如果其中存在已启用但没有 fiber 的条目(即导入失败的插件模块),则抛出异常 |
| `loadPersonalPatches(binName, dir?)` | 解析 Harness home 中可选的 `config.yaml`(默认使用 [`resolveDshHome()`](../../util/paths/README.md):先取 `$DSH_HOME`,否则取 `~/.dsh`):其顶层是一个 YAML 数组,内容为 include 的 `PatchOptions`(按 id 定位的配置覆盖、`insert` 列表,允许 `!!js`);文件不存在时返回 `undefined`,文件不可读、不可解析或内容不是数组时抛出异常 |
| `boot(binName, absoluteConfigPath, patches?, prepare?)` | 创建根上下文,在插件挂载前执行可选的宿主准备操作(例如 `ctx.provide(RESUME_SESSION_ID_KEY, id)`),再挂载 Loader/include 树并等待其结算,断言所有条目均已加载,最后返回根上下文 |
| `RESUME_SESSION_ID_KEY` | bin 通过 `boot``prepare` 钩子设置的上下文键,用于把要恢复的会话 id 交给已启动配置;配置以裸标识符 `resumeSessionId``!!js` 表达式中读取它,因此恢复操作无需环境变量 |
| `addHarnessSourceSection(ctx, sourceRoot)` | 添加全局 `harness:source` 提示词段落(顺序紧随 harness 身份、位于 persona 之前),告知 agent智能体自身源代码 checkout 的磁盘路径;如果已启动树没有此项服务,则不执行操作并返回 `undefined`。这里的服务是 `systemPrompt`;该段落注册到它的 fiber因此开发环境 HMR热模块替换重新加载系统提示词后它会消失直至下次启动 |
| `HARNESS_SOURCE_SECTION` | `'harness:source'` 段落名称,供 `addHarnessSourceSection` 注册使用 |
这些保护处理两类故障。`loader.await()` 会吞掉初始化 rejection`Promise.allSettled`Node 仍会因随后产生的未处理 rejection 以非零状态退出,而 `installFailLoud` 会把冗长转储替换为一行带标签的消息,并确保执行 `exit(1)`。插件导入失败则只会由 Loader 记录日志(否则,即使配置存在拼写错误,进程也会以代码 0 退出),并留下没有 fiber 的条目;`assertEntriesLoaded` 会将其转换为 `boot()` rejection。
配置中的裸插件 specifier`@deepseek-ai/dsh-*`、npm 包package通过 Cordis Loader 的内部模块 loader 解析。仓库 bin 会安装 Loader 的可选 peer `node-addon-require-builtin`;外部调用方必须提供该组件,或者把插件安装到普通 Node import 解析可以找到的位置。相对 specifier 无需原生 helper并以配置目录为基准解析。bin 的子进程冒烟测试覆盖内部 loader 路径,而本包的单元测试套件会在进程内使用相对 specifier 配置驱动 `boot()`
此包不包含 loader 钩子,也不提供开发模式接口:`dsh-scripts` launcher[`sdk/scripts`](../../sdk/scripts/README.md),共享项目模型见 [`sdk/helper`](../../sdk/helper/README.md)持有进程启动、tsx 注册和本地插件源代码解析,并在自身的启动序列中使用这些 helper。
## 个人配置
开发者的机器本地偏好位于所有仓库之外的 Harness home 中(默认 `~/.dsh`,可由 `$DSH_HOME` 覆盖;统一由根级 [`resolveDshHome`](../../util/paths/README.md) 解析),并由 `dsh` CLI命令行界面的 TUI 界面([`apps/cli`](../../../apps/cli/README.md)使用demo bin 会原样启动仓库中提交的树。这里有两个可选文件:
- **`.env`**:在调用目录的 `.env` 之后加载;`process.loadEnvFile` 从不覆盖已有值,因此优先级为环境中的值 > 项目 `.env` > 个人 `.env`
- **`config.yaml`**:在发布的默认配置上应用 Loader overlay patch语义与 include 条目的 `patches` 相同(以仓库提交的 Code Mode overlay 为模板):按 id 定位的 patch 会替换对应条目的整个 `config`(未改字段也要重述),`insert` 会添加条目,`!!js` 表达式则在挂载时插值,因此个人 `apiKey` 可以引用个人 `.env`。如果 patch 指定的条目 id 不在已启动树中Loader 会发出警告并跳过。空文件或仅含注释的文件会抛出异常(其解析结果为空,而不是列表);如需禁用 overlay请使用 `[]` 或删除该文件。
子进程测试 launcher 会把 `DSH_HOME` 指向逐测试隔离的目录,确保开发者的个人 overlay 不会泄漏到 fixture测试前置数据中。
## 模型体验
模型通过此包加载的插件树间接受到影响该树决定最终应用中的提示词、schema、消息和模型适配器。唯一贡献模型可见文本的导出 `addHarnessSourceSection`,也只有在消费方启动后调用它时才会产生影响。
#### KV Cache 影响
`boot()` 不会直接使缓存失效;消费方调用 `addHarnessSourceSection` 时,会在系统提示词靠前位置、逐请求内容之前添加一行短文本,因此不会使跨轮次缓存失效。请求前缀的其他任何变化均由相应的具名消费方持有。
## 已知限制与延期工作
- **裸包 specifier 依赖 Loader 内部机制**:生产 bin 需要 Loader 的可选原生 helper没有该 helper 的进程内调用方必须使用可解析的相对file specifier或使用 tsx 路径映射。
- **快照回放替换仅识别特定 basename**:只有以 `cordis.yml``cordis.yaml` 结尾的配置会映射到同级 `cordis.snapshot.yml`;自定义配置名称需要调用方自行选择。
- **环境加载局限于 cwd 且为可选操作**helper 只加载一个 `.env` 文件,并在失败时发出警告;它不会搜索父目录、合并 profile 或验证必需变量。
- **个人配置采用 patch 形式**:按 id 定位的 patch 会替换条目的整个 `config`,而不是深度合并,因此个人覆盖必须重述需要保留的基础字段。
- **个人 patch 只能看到已启动文件自身的条目**:如果 overlay 叶子通过嵌套 include 条目访问其基础配置(例如 Code Mode 配置),个人 patch id 只会在 overlay 的顶层条目中解析,不会进入被 include 的子树。

View File

@@ -0,0 +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
README.md: 8fd49723c4b0534eebd2e590c647caadd63136a7
README.zh.md: e2ad8ad80d002d769cf6a2c9f4f09c37ce960935

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-commands
English | [中文](README.zh.md)
Plugin-owned human-command registry consumed by interactive UI adapters. The [plugin command registration Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md) owns the boundary and dispatch contract.
## Service contract

View File

@@ -0,0 +1,41 @@
# @deepseek-ai/dsh-commands
[English](README.md) | 中文
由插件拥有、供交互式 UI 适配器使用的面向用户命令注册表。[插件命令注册 Agent Noteagent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-19-plugin-command-registration.md)定义了其边界与分发契约。
## 服务契约
`ctx.commands.register(definition)` 注册一个小写命令名称、描述、可选的非结构化输入提示,以及可中止的处理器。每个已注册命令都可供所有已组合的命令适配器使用;与某项部署不兼容的插件不会在此注册。普通上下文中的注册全局生效。在 `agent.ctx` 下挂载的命令生产插件会声明自身的 `commands` 注入,并创建精确限定到该 agent 的定义;该定义会遮蔽同名的全局定义。这种子级注入形态保留了 agent 作用域,同时不会让核心 agent loop 依赖 UI 服务。同一层中的名称重复会在注册时失败。每个 disposer 都是 Cordis effect 返回的确切 disposer注册或移除命令时系统会通知每个 `commands/change` 观察者,使实时适配器能够刷新发现结果。观察者失败会写入日志,既不能否决注册表变更,也不能阻止后续观察者运行。
`list(agent)` 在应用作用域遮蔽后,返回按名称排序的不可变描述符。`find(agent, name)` 返回相应定义。`execute(agent, line, signal)` 使用 `parseCommand()`,且只运行已知命令;语法无效或名称未知时返回 `undefined`
`parseCommand()` 识别位于字节零位置的斜杠、由小写字母、数字、`_``-` 构成的名称,以及名称后紧接输入末尾或空白的形式。它将名称后的每个字节作为 `rawInput` 返回,其中包括分隔空白;消费方拥有各命令专用的语法,只能执行该语法允许的规范化。
处理器返回 `success``error`,并可附带 UI 文本。适配器直接渲染结果,结果绝不进入模型历史。注册表绝不会隐式地把 `rawInput` 提交给 agent命令生产方可以通过接收命令的 `Agent` 显式安排模型可见工作,此时该生产方拥有由此产生的消息契约。注册表会让处理器完成与所提供的中止信号竞速,但不协作的处理器可能在调用方停止等待后继续产生自身的外部副作用。
## 组合
终端应用组合包会将此服务与 `dsh-tui` 一起挂载;无 UI 的 agent 主干和 ACPAgent Client Protocol自动化应用不会挂载它。自定义交互式组合与命令生产方会显式挂载 `@deepseek-ai/dsh-commands`
## 模型体验
### 直接面向用户的命令
#### 模型看到的内容
注册表自身不会提交任何内容。已知斜杠命令在 UI 命令平面执行,其 `CommandResult` 文本不会作为用户消息提交。已交付的适配器会拒绝未知斜杠命令输入,而不是将其变成模型提示词。命令生产方可以显式使用接收命令的 `Agent`;例如,[`dsh-plan-mode`](../../plan/plan-mode/README.md#model-and-human-surfaces)在选择 plan mode 后,会提交 `/plan [message]` 中的可选消息。
#### Token 影响
命令发现、执行和 UI 输出不会增加模型 token。命令生产方显式安排的 agent 工作与相应 agent 输入具有相同的 token 影响。
#### KV Cache 影响
注册表元数据、命令输入和直接输出绝不会进入模型请求,也不会影响其缓存。被命令变更的领域拥有之后产生的所有缓存影响。
## 已知限制与延期工作
- **仅支持非结构化文本输入**:表单、补全 schema 和类型化参数仍由各命令自行解析。
- **不持久化命令输出**:适配器会实时显示结果,但通用注册表不会将结果加入会话日志,也不会在重新连接后重建结果。
- **副作用采用协作式取消**:中止后,分发会停止等待;处理器必须遵循信号,才能停止已经进入外部系统的工作。

View File

@@ -0,0 +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
README.md: 9297147b7a53739c46871b527ce51868eac1e244
README.zh.md: 62596197b95729215408dfc1496c129ace6cbad4

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-jsonrpc
English | [中文](README.zh.md)
The `jsonrpc` plugin serves newline-delimited JSON-RPC over stdio so out-of-process SDK clients can drive harness agents. [`HarnessSdkServer`](src/server.ts) owns the protocol methods and notifications; [`jsonrpc-demo`](../../examples/jsonrpc-demo/README.md) supplies the surrounding `cordis.yml` application.
## Wiring

View File

@@ -0,0 +1,47 @@
# @deepseek-ai/dsh-jsonrpc
[English](README.md) | 中文
`jsonrpc` 插件通过 stdio 提供以换行符分隔的 JSON-RPC使进程外 SDK 客户端能够驱动 harness agent智能体。[`HarnessSdkServer`](src/server.ts) 持有协议方法和通知;[`jsonrpc-demo`](../../examples/jsonrpc-demo/README.md) 提供外围的 `cordis.yml` 应用。
## 组装
`inject: ['agents']`。服务器按 `sessionId` 获取或创建一个 agent。只有服务建立快照时的生命周期 `local` 标志为 true服务器才会转发 subagent 完成事件;提供方名称、子级 id 和持久化谱系均不能证明本地性。已注册的适配器优先;未被持有的 `deepseek` 路由会挂载 `dsh-llm-deepseek`,任何其他未被持有的提供方都会导致初始化失败。其他功能由外围 `cordis.yml` 提供。
## 配置
`maxTokensAsSuccess` 默认为 `false`。对于需要区分「因 token 上限而结束但可接受的 agent 结果」与「基础设施故障」的评测宿主,请将其设为 `true``JsonRpcConfig.input``output``exit` 是仅供运行时使用的传输 seam生产环境使用进程 stdio 和 `process.exit`
## stdout 即协议
Stdout 只承载 JSON-RPC 帧。部署不得组合 stdout logger诊断应写入 stderr。
## 关闭与退出语义
插件响应 `shutdown`,将 SDK 持有的 agent 和订阅 dispose资源释放至完全停稳关闭传输层然后以代码 0 退出。EOF 和信号退出由 app bin 处理,后者会 dispose 根上下文。仅卸载此插件会停止服务,但不会退出进程。
## 协议说明
`initialize.serverInfo.name` 的协议稳定值为 `deepseek-harness-sdk-runtime`。一个会话只接受一个进行中的提示词;重叠请求会立即失败,其他会话保持独立,当前请求结算后该会话可再次使用。`session.finished` 报告由该提示词消息触发的轮次结果;后续注入或插件持有的零步骤轮次仍会作为 `session.event` 通知流式发出,但不能替换该提示词的状态。持久化根目录和 persona 由 `cordis.yml` 提供。
## 模型体验
### SDK 用户消息
#### 模型看到的内容
对于每个已接受的 `session/prompt`,对话模型会将调用方提供的 `contentBlocks` 原样接收为该 SDK 会话中的一条用户消息。此包package不会添加系统提示词文本或工具 schema这些内容来自外围 `cordis.yml` 中的插件。
#### Token 影响
依数据而定的用户消息 token 会进入保留的会话历史并在后续轮次中重复发送直至另一个包将其压缩compaction。JSON-RPC 帧、会话通知和服务器内部记录不会增加模型上下文 token。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
## 已知限制与延期工作
- **协议没有逐会话关闭或提示词取消方法**SDK 创建的 agent 会一直存活到进程关闭;一条已接受的提示词必须运行到 agent 空闲,该会话才能接受下一条。
- **stdout 纯净性由部署保证**:外围配置仍可能加载 stdout logger 并破坏 JSON-RPC 通道;此插件不会检查或否决同级 logger。
- **自动挂载适配器仅支持 DeepSeek**`initialize` 可以复用任何预先注册的模型适配器,但唯一的回退行为是挂载 `dsh-llm-deepseek`

View File

@@ -0,0 +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
README.md: 6a59ad9425bf5bfeb89e9798304a2eb90ee55bfa
README.zh.md: 0e7db1bd41a15ac4be18d33db7b9011a5bc24e7e

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-permission
English | [中文](README.zh.md)
User-facing permission presets through `ctx.permission` ([`PermissionService`](src/index.ts)). Each configured name bundles `sandbox/mode` with `approval/policy`; the defaults are `workspace-write` (`workspace-write` + `ask`) and `danger-full-access` (`danger-full-access` + `never`). UI adapters may expose the table as one selector, while sandbox execution and approval continue to consume their own knobs.
`set(session, name)` records a changed selection in a log-only `permission/preset` event, then calls each knob's setter only when its effective value changes. The selection event precedes the knob events and preserves user intent when presets share a bundle; a net-zero selection appends nothing. `current(events)` prefers a still-matching recorded selection, then the first matching table entry, and otherwise returns `custom`. Clients may display `custom` as the current value, but cannot select it.

View File

@@ -0,0 +1,24 @@
# @deepseek-ai/dsh-permission
[English](README.md) | 中文
通过 `ctx.permission`[`PermissionService`](src/index.ts))提供面向用户的权限 preset。每个配置名称都会将 `sandbox/mode``approval/policy` 组成一组;默认项为 `workspace-write``workspace-write` + `ask`)和 `danger-full-access``danger-full-access` + `never`。UI 适配器可以将该表作为单个选择器公开,而沙箱执行与审批仍分别消费各自的调节项。
`set(session, name)` 会先在仅写日志的 `permission/preset` 事件中记录已变更的选择,再仅对实际值发生变化的调节项调用 setter。选择事件先于调节项事件并在多个 preset 共享同一组取值时保留用户意图;净变化为零的选择不会追加任何内容。`current(events)` 优先返回仍与当前调节项匹配的已记录选择,其次返回表中第一个匹配项,否则返回 `custom`。客户端可以把 `custom` 显示为当前值,但不能选择它。
该服务要求存在具有约束能力的 `ctx.bash` 执行器和 `ctx.approval`。表中名为 `custom` 的条目会在加载时抛出异常;如果组合在表外指定默认值,则零事件会话会推导出 `custom`。详见[沙箱切换设计](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)。
## 模型体验
间接地,通过 `dsh-user-approval``dsh-tool-bash`:二者会渲染由此服务的调节项事件所选择的审批策略提示词、切换通知和沙箱工具结果;`permission/preset` 本身只写入日志。
#### KV Cache 影响
不会直接使缓存失效;具名消费方拥有所有请求前缀变更。
## 已知限制与延期工作
- **当前没有已交付的组合挂载此服务**:在 [ACP 变为仅用于自动化](../../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md)之前ACP 桥接层是唯一的选择器preset 表为下一个公开运行时策略切换的交互式入口保留。
- **只组合两个机制调节项**preset 选择沙箱模式和审批策略agent智能体profile 选择尚未纳入 `PresetSpec`
- **`custom` 只能推导得出**:调用方可以从不匹配的调节项组合切换出去,但无法通过此服务选中或持久化一个具名 custom preset。
- **preset 表位于进程级别**:配置在插件生命周期内固定;更改可用 preset 必须重新加载插件。

View File

@@ -0,0 +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
README.md: 8e779f4025c20cd200344efb7cb8cd6bc09ba64d
README.zh.md: fe1dc5559882532c4f44e705cc6daa2c7f4f8905

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-tool-ask-user
English | [中文](README.zh.md)
Model-facing `ask_user_question` tool over `ctx.userInteraction`. It lets the model ask the human a concise question when it needs confirmation, a choice, or missing information before continuing.
## Tool

View File

@@ -0,0 +1,57 @@
# @deepseek-ai/dsh-tool-ask-user
[English](README.md) | 中文
模型侧 `ask_user_question` 工具,基于 `ctx.userInteraction` 实现。当模型需要确认、选择或缺失信息才能继续时,它可以借此向用户提出简明问题。
## 工具
`ask_user_question` 接受以下参数:
- `questions`:必填的非空问题对象数组。
- `id`:每个问题必填的稳定 id会原样包含在回答中。
- `question`:每个问题必填的问题文本。
- `header`:可选的简短标题。
- `options`:可选选项,包含 `label``description`。如需推荐某个选项,请将其置于首位,并在该标签末尾追加 `(Recommended)`
- `multi_select`:该问题是否可以返回多个选中的选项。
工具调用 `ctx.userInteraction.ask()`,并返回规范的 `{ answers: [{ id, selected, custom? }] }``selected` 包含选项标签;仅当用户自由填写回答时才会出现 `custom`并覆盖选中的选项。Native renderer 会保留紧凑的 JSON 文本形式 `{ "answers": [{ "id": "...", "selected": ["..."], "custom": "..." }] }`
## 职责
此包package是用户交互 seam 的消费方。它不渲染 UI也不了解输入的收集方式它只将模型参数转换为 `AskUserQuestionRequest`,并把用户回答返回给 agent loop智能体循环
## 模型体验
### 工具 schema
#### 模型看到的内容
模型会看到生成的 [`ask_user_question` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-ask-user),其中包含问题 id、提示语、标题、选项和多选标志。
#### Token 影响
工具可见的每个请求都会产生固定的 schema 开销。
#### KV Cache 影响
只要定义和可见性保持不变,前缀即可稳定复用。插件生命周期变化或作用域限制可能从此 schema 开始使复用失效。
### 工具调用历史与结果
#### 模型看到的内容
模型提出的完整问题保留在 assistant 工具调用参数中。用户回答后,下一步骤会看到精确采用 `{"answers":[{"id":"<id>","selected":["<label>"],"custom":"<text>"}]}` 形式的紧凑 JSON不使用 `custom` 时会省略该字段,`selected` 可以包含零个、一个或多个标签。调用等待期间的 UI 交互不属于模型上下文。
#### Token 影响
参数和回答 JSON 是依数据而定的保留 token等待用户时不会产生 token 开销。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
## 已知限制与延期工作
- **待处理问题会阻塞工具调用,直至用户作答**:该工具未声明 `timeout-policy` 预算;取消仅沿用当前轮次的 `exec.signal`
- **Native 回答渲染为 JSON 文本**:规范值仍为结构化数据,但模型侧结果使用紧凑 JSON而非更丰富的内容块词汇。

View File

@@ -0,0 +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
README.md: 3d64de9f0703838cad10f8e04665ec4b985d00dc
README.zh.md: 5cf41dd76c7c28cd2d605466c7c10cfe1c1dd958

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-tui
English | [中文](README.zh.md)
The interactive terminal front door for DeepSeek Harness agents, built on [`@earendil-works/pi-tui`](https://www.npmjs.com/package/@earendil-works/pi-tui). It requires stdin and stdout TTYs; scripts and Loader pipes should use the one-shot [`@deepseek-ai/dsh-cli-demo`](../../examples/cli-demo/README.md) app instead.
The implemented [TUI feature Agent Note](../../../.agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.md) owns the front-door decision; the [file-reference autocomplete Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-tui-file-reference-autocomplete.md) owns path-only `@file` behavior; the [terminal-state snapshot Agent Note](../../../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md) owns its verification strategy.

View File

@@ -0,0 +1,165 @@
# @deepseek-ai/dsh-tui
[English](README.md) | 中文
DeepSeek Harness agent智能体的交互式终端入口基于 [`@earendil-works/pi-tui`](https://www.npmjs.com/package/@earendil-works/pi-tui) 构建。它要求 stdin 和 stdout 均为 TTY脚本和 Loader pipe 应改用单次执行的 [`@deepseek-ai/dsh-cli-demo`](../../examples/cli-demo/README.md) app。
已实现的 [TUI 功能 Agent Noteagent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.md)持有终端入口决策;[文件引用自动补全 Agent Note](../../../.agents/notes/implemented/feature/2026-07-23-tui-file-reference-autocomplete.md)持有仅路径的 `@file` 行为;[终端状态快照 Agent Note](../../../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md)持有其验证策略。
支持 macOS、Linux 和 Windows 上的交互式终端。Windows 使用 pi-tui 原生控制台 VT 输入处理;[Windows 支持 Agent Note](../../../.agents/notes/implemented/feature/2026-07-20-windows-tui-support.md)持有平台决策与 ConPTY 进程验证。
本包package只持有交互式终端展示和输入。它注入 `agents`、[`commands`](../commands/README.md)、`llm``systemPrompt``tokenMeter``tools``userInteraction`,可选读取 `skills` 服务(仅在已挂载时存在),然后驱动由 app 或开发者代码创建或恢复的 agent。Agent 生命周期、持久化与模型侧 [`ask_user_question`](../tool-ask-user/README.md) 工具仍是独立组合项。
终端成功启动后,本包会提供终端本地的 `ctx.tui` 扩展服务。注入该服务的插件可以使用组件工厂和受限布局选项调用 `openOverlay()`;宿主会公开 viewport、语义化主题、显示文本转义、重绘、关闭和生命周期信号但不公开 pi-tui 树、终端、焦点控制器或 overlay 句柄。插件 overlay、模型选择器和用户问题共用一个 FIFO 模态队列。每个请求都是调用方插件 fiber 的 effect因此卸载会移除排队工作或在清理结算前关闭可见工作终端关闭会先卸载依赖项再停止 pi-tui。Overlay 状态不会记录或回放。组件代码受信任,可以渲染 ANSI 样式,但必须通过 `host.display()` 处理不受信任文本。[交互式扩展 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-22-tui-interactive-extension-service.md)持有该边界和未采用的替代方案。
TUI 从活跃会话表层重建已恢复历史,渲染 Markdown 响应与 reasoning将每个工具的 `presentCall` / `presentResult` 意图应用到终端、diff 或通用卡片,把最新的 `todo/write` 计划保留在编辑器上方,并在左下方宽键盘面板中展示 `ctx.userInteraction` 问题,包含进度、编号选项和对齐说明。最新记录的会话标题成为 header 副标题;标题不存在时使用 `welcome`,终端窗口标题则变为 `<session title> — <configured title>`。持久 `llm/retry` 事件会撤回失败步骤的实时 chunk并在 transcript文本记录中渲染计划重试次数、延迟和失败成功、耗尽与取消随后通过普通会话事件结算。Footer 会对每个已记录模型步骤的用量只计一次,包括失败尝试;对于没有用量 chunk 的日志,以已提交消息的用量回退。其空闲视图会比较 token-meter 压力与当前路由的 `ctx.llm.resolveModelContext()`;适配器没有容量元数据时显示 `context unknown`,并显示工具卡片模式、当前模型和 reasoning 状态。Agent 运行时,这些摘要会替换为已经过工作时间指示器和 `esc interrupt`。表层替换事件会重建 transcript使经过压缩compaction的历史不会再次出现。
如果逻辑工作区标签与会话宿主目录不同,嵌入方可以提供 `TuiRuntime.formatCwd`。该覆盖只改变 footer 标签;工具仍使用会话 `cwd`
在模型输出、会话事件、工具 presenter、问题、配置或诊断到达 pi-tui 的 ANSI 感知 renderer 或终端标题前TUI 会把换行之外的 C0 和 C1 控制字符渲染为可见 `\xNN` 文本。这些来源无法添加终端控制序列;终端渲染与样式仍由 TUI 和 pi-tui 持有。
在 token 边界输入 `@` 会搜索会话工作目录下的文件和目录。没有路径的模糊查询使用可复用的有界工作区索引;包含 `/` 的查询直接列出该目录,选择文件夹后会保持补全开启以继续深入。含空白的路径会插入为 `@"path with spaces"`。选择文件只会插入其路径和一个尾随空格TUI 不会读取文件、附加隐藏上下文,也不会把路径替换为引用对象。注册模型侧 `read` 工具后TUI 会添加一条固定系统提示词指令,要求模型在需要显式路径内容时读取该路径。
挂载可选的 `ctx.sessionReferences` 后,同一个 `@` 菜单还会提供仅含元数据的会话候选项,插入 `@[label](dsh-session:<payload>)`并在分派前准备所选快照。会话引用保持结构化因为模型没有类似文件系统的工具可在稍后检索会话快照。准备期间会禁止重复提交并在失败时恢复编辑器输入。TUI 会在异步准备后根据状态选择 `agent.steer()``agent.followup()`,因此空闲 followup 仍会分派 `agent/prompt-submit`,而轮次中的 steering 会在检查点加入且不触发该 hook。
Agent 运行时,普通编辑器提交会调用 `agent.steer()`;其他时候调用 `agent.followup()`。提交行以斜杠开头时会改为进入 `ctx.commands`:已知命令直接执行,未知命令产生警告,两条路径都不会自动到达模型。命令生产方可以显式调度 agent 工作;[`dsh-plan-mode`](../../plan/plan-mode/README.md#model-and-human-surfaces) 使用该契约实现 `/plan [message]`。TUI 将 `/help``/model``/clear``/reasoning``/tools``/redraw``/reload``/resume``/status``/exit` 注册为 agent 作用域定义;其他所有有效命令都会动态加入自动补全与 `/help``/skill:` 补全也相同。编辑器上方的状态行会报告 TUI 从会话事件派生的轮次阶段,包括等待首个 token、思考、响应或执行工具它显示该阶段已经过时间和运行中的步骤总数每秒刷新并以 `Enter sends steering, Esc cancels` 提示结尾。Steering 消息等待到达模型期间,会在提示前插入 `N queued ·` 徽标每条消息排空后随即清除。Ctrl+C 或 Escape 会取消运行中的轮次。工具卡片把长主体折叠为可配置的头尾预览Ctrl+O 在预览与完整输出之间切换所有卡片。Ctrl+R 切换 reasoningCtrl+L 重绘Ctrl+D 在空闲时退出。
`/model` 将建议性的 `ctx.llm` catalog 打开为键盘选择器Up/Down 移动Enter 选择Escape 关闭。`/model <model>` 仍可直接选择无歧义的模型 id`/model <provider>/<model>` 则选择精确目标。已配置目标或最新记录的请求 header 会初始化选择器;由于 catalog 仅提供建议,未列出的当前模型仍会显示。选择仅对本 TUI 会话有效。提示词组装会为一个步骤建立目标快照,替换 `{{provider}}``{{model}}`,并通过 `agent/request` 应用同一组值;因此组装期间的切换会从后续步骤开始生效。请求 header 会持久记录真正到达模型的目标,未使用的选择则只存在于进程本地。
`/reload`(实验性,仅开发环境)会重新读取所有基于文件的 loader 配置树,并把 diff 应用到运行中 app它手动调用 HMR热模块替换watcher 的配置路径;上下文中必须有 cordis Loader否则退化为警告。它只在 agent 空闲时运行,并拒绝 reload 进行期间的再次进入。模块源代码热重载仍由 watcher 持有。挂载 `skills` 服务后,`/skill:<name> [instructions]` 会把该 skill 的指令作为一个 user 轮次加载到会话中;自动补全列出模型可调用的 skill任何 skill包括模型禁用的 skill都可通过精确名称加载。
Footer 将会话报告的用量汇总为 `↑<uncached input> ↓<output>`;任何输入计费后,后面会显示 `cache <rate>%`,表示提供方缓存服务的已计费提示词 token 占比(未缓存输入加缓存读写),并四舍五入为百分比。它还会比较 token-meter 压力与当前路由的 `ctx.llm.resolveModelContext()`适配器没有容量元数据时省略上下文占比并显示当前模型和工具卡片模式footer 过窄时,右侧会优先裁剪。
`/status` 会向 transcript 添加一张时间点诊断卡片,并在 agent 运行时保持可用。它报告会话 id、标题、工作目录、所选提供方模型、reasoning 块可见性、agent 状态、事件/轮次/步骤/工具调用计数、精确输入/输出/缓存 token bucket、KV-cache 命中率、token-meter 上下文用量与容量、创建时间和最新事件时间。缺失标题、模型、缓存输入或上下文容量时会明确标记,而非推断。该卡片只存在于终端,不会重复紧凑 footer。
`/resume` 会针对当前工作区打开全 viewport 键盘选择器,而非居中对话框。获得焦点的搜索字段紧跟搜索 glyph 开始,并发出 pi-tui 的 cursor marker使终端 IME 组合保持锚定在字段内。候选项按最近记录的活动排序,可按日志支持的标题或会话 id 搜索;每行报告 current/live/persisted 状态、上一轮次结果、近期提供方模型以及存在时的持久目标阶段。Up/Down 与 Page Up/Page Down 导航Enter 恢复Escape 会先清除非空搜索再次按下才取消Ctrl+C 则直接取消。当前会话、已在本运行时中活跃的会话、不可读日志、cwd 不匹配或日志所记提供方没有当前适配器的会话仍会显示,但不可选择。选择时会重复这些检查,并要求当前 agent 空闲,随后 flush 当前会话。TUI 接着停止终端 UI并调用由宿主持有的可选 `TuiRuntime.handoffResume`;存在 `process.execve` 时,发布的 `dsh` 宿主会对 app 执行 dispose资源释放并替换自身进程。恢复操作保留相同的 `SessionId`、transcript、标题、todo 和持久目标目标激活仍保持解除TUI 会要求用户确认或执行 `/goal resume`
`resumeCommand` 仍是部署持有的回退行为:只有当前会话已持久化后,退出才会打印它;不支持原地 handoff 的宿主会显示所选会话的命令。`{session}` 展开为会话 id。TUI 代码绝不会执行模板或任意 shell 文本。
## 配置
| 键 | 默认值 | 含义 |
|---|---|---|
| `welcome` | 未设置 | 会话出现已记录标题前使用的 banner 副标题行未设置时banner 进入时没有副标题 |
| `sessionId` | `main` | 由终端驱动的精确共享 agent会话身份 |
| `showReasoning` | `true` | 渲染 reasoning 块 |
| `maxToolOutputLines` | `6` | 折叠工具卡片的头尾预览所保留的输出行数 |
| `maxQuestionOptions` | `8` | 问题面板中可见的选项数 |
| `maxModelOptions` | `8` | 模型选择器中可见的模型数 |
| `maxResumeOptions` | `8` | 恢复选择器中可见的会话数 |
| `questionDialogWidth` | `200` | 问题面板宽度(列数),以终端宽度为上限 |
| `questionDialogMaxHeight` | `20` | 问题面板最大行数 |
| `modelDialogWidth` | `72` | 模型选择器宽度(列数) |
| `modelDialogMaxHeight` | `20` | 模型选择器最大行数 |
| `fileSearchMaxResults` | `20` | 一次 `@` 查询显示的最大文件和目录候选数 |
| `fileSearchMaxEntries` | `10000` | 无路径模糊查询使用的有界工作区索引最多保留的路径数 |
| `fileSearchExcludedDirectories` | `['.git', 'node_modules']` | 遍历和直接补全时忽略的目录 basename |
| `showHardwareCursor` | `false` | 在 pi-tui 的 IME marker 处显示硬件 cursor |
| `color` | `true` | 应用内置 ANSI palette参见[颜色](#color) |
| `title` | `DeepSeek Harness` | 终端窗口标题的产品后缀。 |
| `resumeCommand` | 未设置 | 供退出提示和不支持原地 handoff 的宿主使用的 shell 命令模板,其中 `{session}` 会展开为会话 id |
```yaml
- id: terminal
name: '@deepseek-ai/dsh-tui'
config:
welcome: 'Coding agent ready.'
sessionId: main-session-123
showReasoning: true
maxToolOutputLines: 6
fileSearchExcludedDirectories: ['.git', 'node_modules', 'dist']
```
任一进程流不是 TTY 时,启动会在挂载前失败。组合 app 必须先挂载 TUI再挂载由配置创建的 agent使入口能够观察 `agent-loop/config-start-failed`;完全匹配会话的失败会在全屏模式启动前写出并以状态 1 退出而不是留下空白终端。dispose 会停止接收扩展请求,卸载 `ctx.tui` 提供方及其依赖插件,中止运行中的命令,移除 TUI 定义,停止 loader拒绝待处理问题排空终端输入恢复终端状态注销事件 listener 和用户交互提供方,并且绝不会在 HMR 期间退出替换进程。
## 颜色
Palette 使用标准 16 色 ANSI 前景色和 SGR 属性,每个终端都会将其重新映射到当前配色方案,因此浅色与深色背景下都保持可读。正文使用终端默认前景色,而非固定色调。成组区域(用户提示词、工具卡片)使用彩色左侧 gutter bar而非填充背景块问题面板使用粗体强调色文本突出活跃行选择器则使用反色。所有效果都只作用于前景色因此不会与终端背景冲突。设置 `color: false` 可移除所有样式。
## 模型体验
### 交互式提示词输入
#### 模型看到的内容
每次非空普通编辑器提交都会成为一个文本块;目标 agent 空闲时通过 `agent.followup()` 发送,运行时通过 `agent.steer()` 发送。会话 mention 会变为可读的 `@label` 文本,加上由 [`dsh-session-reference`](../../context/session-reference/README.md) 定义的持久不受信任上下文;其完整 JSON 隐藏在紧凑引用卡片之后。斜杠命令和按键绑定仅用于 TUI命令结果仍是终端通知。命令生产方可以调度单独的 agent 输入,例如 `/plan [message]` 接受的可选消息。
#### Token 影响
提交的文本会按 agent loop 的普通会话历史与压缩规则保留。Header、已记录标题、卡片、Markdown 渲染、状态行、计划和帮助文本不会增加 token。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
### 文件引用自动补全
#### 模型看到的内容
所选文件仍是普通 user 文本,例如 `@src/index.ts``@"docs/design notes.md"`;自动补全不会添加内容块、持久上下文或特殊引用 payload。注册 `read` 后,此 TUI agent 的每个请求还会包含下方固定系统提示词段落。模型会判断任务是否需要文件内容,并在需要时通过普通工具循环调用 `read`;只有路径不能证明文件已经过检查。
##### 精确系统提示词文本
```markdown
Paths prefixed with @ are files explicitly referenced by the user. Use the read tool when their contents are needed; do not claim to have inspected a file before reading it.
```
#### Token 影响
自动补全本身不增加 token。所选路径只贡献普通 user 文本 token`read` 可用时,固定指令会贡献系统提示词 token。只有模型选择的 `read` 调用返回文件内容后,这些内容才会占用上下文。
#### KV Cache 影响
固定指令属于稳定系统提示词前缀,可以跨轮次复用。每个所选路径都是仅追加 user 文本;后续 `read` 结果通过普通工具 transcript 追加所请求内容。
### 会话模型选择
#### 模型看到的内容
`/model` 命令文本和键盘选择器输入均不会记录或发送。新步骤会在提示词变量和请求路由中同时收到所选提供方/模型对。
#### Token 影响
选择器不会添加消息。更改目标可能改变插值后的系统提示词文本,并把后续请求发送给所选模型。
#### KV Cache 影响
更改提供方或模型会进入该目标的缓存域;不假定不同目标间可以复用缓存。
### 手动调用 skill
#### 模型看到的内容
提交 `/skill:<name> [instructions]` 会加载具名 skill并交付一个文本块`<skill name="…">` 元素包装 skill 指令;提供方公开资源基准时,会先添加一行定位 skill 相对资源;最后附上用户输入的尾随指令。交付遵循普通输入同样的空闲时 followup、运行时 steer 规则。选择 skill 的是命令而非模型;模型禁用的 skill 不出现在自动补全中,但仍可按精确名称加载。
#### Token 影响
渲染后的 skill 块与尾随指令会作为一个 user 轮次保留,并遵循 agent loop 的普通会话历史和压缩规则;重复调用会再次追加正文。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
### 交互式用户问题回答
#### 模型看到的内容
消费方调用 `ctx.userInteraction.ask()` 时,此提供方会按顺序显示各个问题,并返回选中选项标签或 `custom` 文本。中止、取消或 UI dispose 会变为 `Error: ask_user_question was interrupted before the user answered`;该转换由 `dsh-tool-ask-user` 完成。
#### Token 影响
等待和终端 overlay 不增加 token已解析回答或错误只会通过调用工具或插件的结果对模型可见。
#### KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
## 已知限制与延期工作
- **恢复功能没有跨进程会话锁**:选择器会拒绝本运行时中已知处于活跃状态的会话,但另一个进程可以在 handoff 之前或期间恢复同一持久 id。能够运行并发宿主的部署必须在 TUI 外协调所有权。
- **一个已配置会话持有 transcript 和编辑器**:其他 agent 的问题仍可使用共享 overlay 提供方,但会话渲染与提示词输入仍绑定到 `sessionId`
- **工具卡片是文本终端展示**终端、diff 与通用卡片使用工具持有的标题/内容,但会话内容目前没有用于内联图像渲染的图像块。
- **有意不支持非 TTY 运行**:需要自动化的 app bundle 必须组合单次执行或服务器入口(`dsh-cli-demo``dsh-acp`),而不能依赖内部回退。
- **手动 `/skill:` 调用总会重新加载完整 skill 正文**TUI 不会检测会话中是否已存在某项 skill因此重复调用会再次追加其指令。
- **文件发现只发现宿主工作区**:自动补全读取 TUI 进程的会话 `cwd`,所选文本随后由已配置 `read` 工具解释。挂载远程或虚拟文件系统的部署必须对齐这些 namespace或提供其他补全接口。
- **文件搜索使用显式目录排除项,而非 ignore 文件**:默认排除 `.git``node_modules`,部署还可以配置更多 basename但不会解释 `.gitignore``.ignore`。目录 symlink 不会遍历。

View File

@@ -0,0 +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
README.md: 38bcfbfe81c3ff5f16d1835259bd4c35a06dcb64
README.zh.md: 2a3a6d08d66c70a22b3a23a2341efc0452b8a782

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-user-approval
English | [中文](README.zh.md)
Channel-neutral one-shot approval seam. `ctx.approval.request(req)` returns `allowed-once`, `rejected`, `cancelled`, or `unavailable`; missing or failing answerers fail closed, and a grant applies only to the requested action. Exact event signatures live in the generated [Cordis catalog](../../../docs/cordis-catalog/events.md).
Each request must belong to an open agent turn. The service appends a paired `approval/asked` and `approval/decided` audit record, while the model sees only the resulting logged tool outcome. An aborted request resolves `cancelled`; an audit append that fails before commit rejects rather than returning an unlogged decision.

View File

@@ -0,0 +1,63 @@
# @deepseek-ai/dsh-user-approval
[English](README.md) | 中文
与通道无关的一次性审批 seam。`ctx.approval.request(req)` 返回 `allowed-once``rejected``cancelled``unavailable`;应答者缺失或失败时会以拒绝方式关闭,授权也只适用于所请求的操作。确切事件签名见生成的 [Cordis 目录](../../../docs/cordis-catalog/events.md)。
每个请求都必须属于一个打开的 agent智能体轮次。服务会追加一对 `approval/asked``approval/decided` 审计记录,而模型只会看到由此产生且已写入日志的工具结果。已中止的请求会 resolve 为 `cancelled`;如果审计追加在提交前失败,请求会被拒绝,而不会返回一项未记录的决定。
应答者是 `approval/request` waterfall瀑布式事件监听器。要回答所拥有 agent 的请求,请返回一个结果;否则调用 `next()` 委托。限定到 agent 的监听器只接收该 agent 的请求每项部署应当组合一个终端应答者因为同级监听器的顺序不是策略优先级机制。ACPAgent Client Protocol自动化桥接层为其拥有的会话提供一次性机器决定。
`ApprovalPolicy``'ask'``'never'`。实际值取最后一条 `approval/policy` 事件,并回退到配置;`setApprovalPolicy()` 是写入路径。`'never'` 会在交互式分发之前拒绝请求,也是提示词中唯一声明的策略。切换最多产生一条合并通知:如果覆盖发生在最后一个 `request/header` 之后,则归因于用户;否则归因于操作方/配置。
工具流水线通过此 seam 路由 `ask` 决定,并在该 seam 缺失时以拒绝方式关闭;沙箱 bash 工具也会将它用于升权重试。ACP 自动化桥接层根据客户端的机器策略,回答其自有 agent 的调用。审计事件仍只写入日志,因此模型只会看到发起请求的消费方所返回的结果。详见[审批 seam Agent Noteagent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-06-approval-seam.md)和[沙箱 Agent Note](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)。
## 模型体验
### 系统提示词与策略通知
#### 模型看到的内容
`ask` 下,每个 agent 请求都会携带下方的 ask 策略提示词段。在 `never` 下,请求会携带下方的 never 策略提示词段。策略切换会在下一步骤前精确注入 `The approval policy changed from "<old>" to "<new>" (changed by the user).``The approval policy changed from "<old>" to "<new>" (changed by the operator/config).`
##### Ask 策略提示词段
```markdown
<!-- dsh-user-approval-policy:ask -->
```
##### Never 策略提示词段
```markdown
Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).
<!-- dsh-user-approval-policy:never -->
```
#### Token 影响
每个请求有少量固定成本,`never` 下的成本更高;变更通知按条件出现,并保留在历史中。
#### KV Cache 影响
审批策略不变时,前缀保持稳定。`ask``never` 切换会改变系统提示词段,并从首个变化的 token 开始使复用失效;随附通知只会追加。
### 工具结果
#### 模型看到的内容
`approval/asked``approval/decided` 只写入日志。模型只会看到发起请求的消费方最终给出的允许、拒绝、取消或不可用工具结果;面向人类的权限 UI 不属于上下文。
#### Token 影响
不会产生重复的审计 token。拒绝可能以一条少量且保留的错误替换正常工具结果而允许会保留消费方的普通结果。
#### KV Cache 影响
仅追加;新出现的可见内容位于可复用请求前缀之后,不会使现有 KV-cache 条目失效。
## 已知限制与延期工作
- **请求只在打开的轮次内有效**:空闲时或轮次之间的调用方会在审计前抛出异常;持久化的轮次外审批工作流仍属延期事项。
- **仅存在一次性授权**:结果词汇包含 `allowed-once`,但不含 `allow-always`、记忆规则、撤销或授权存储;会话策略只有 `ask``never`
- **请求不携带工具参数**:应答者会看到工具名称、原因和可选调用 idACP 机器通道要求调用 id并会委托不含 id 的请求。
- **没有内置应答者**:无头或组合不完整的部署会 resolve 为 `unavailable` 并以拒绝方式关闭;服务自身绝不会提示人类。

View File

@@ -0,0 +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
README.md: d234d6677bdd772f1bbd2c979c0d41f90aef5c32
README.zh.md: b70a61d6491e0bb0e52215cdeaeea3d728f7f153

View File

@@ -1,5 +1,7 @@
# @deepseek-ai/dsh-user-interaction
English | [中文](README.zh.md)
Abstract user-interaction seam. It owns `ctx.userInteraction`, the service a model-facing tool or permission plugin uses when it needs to pause work and ask the human for a decision.
## Service: `UserInteractionService` (ctx key: `userInteraction`)

View File

@@ -0,0 +1,39 @@
# @deepseek-ai/dsh-user-interaction
[English](README.md) | 中文
抽象用户交互 seam。它拥有 `ctx.userInteraction`:当面向模型的工具或权限插件需要暂停工作并询问人类决定时所使用的服务。
## 服务:`UserInteractionService`ctx 键:`userInteraction`
### 公开 API
- `ctx.userInteraction.registerProvider(provider): () => void` 注册 UI 侧提供方。同一上下文中只能有一个活跃提供方dispose资源释放会将其注销。
- `ctx.userInteraction.ask(request): Promise<AskUserQuestionAnswer>` 向活跃提供方提问并等待回答。
### 关键类型
- `AskUserQuestionRequest``{ questions: [{ id, question, detail?, header?, options?, multiSelect? }], agent?, signal? }``detail` 提供辅助文本,提供方会将其随问题一起渲染,而不会将其变成选项标签。
- `AskUserQuestionOption``{ label, description? }`
- `AskUserQuestionAnswer``{ answers: [{ id, selected, custom? }] }`
- `UserInteractionProvider`:包含 `ask(request)` 的 UI 实现。
- `UserInteractionError``HarnessError` 的子类,包含 `EMPTY_QUESTIONS``NO_PROVIDER``DUPLICATE_PROVIDER``ASK_ABORTED` 等代码。
当回答包含 `custom` 时,`selected` 为空自定义文本会覆盖所选选项而不是补充它们。UI 可以把跳过的条目保留为 `{ id, selected: [] }`,既维持现有回答形态,也保留该批次中的其他回答。
## 职责
这是接口包package`@deepseek-ai/dsh-tool-ask-user` 等面向模型的消费方依赖此 seam`dsh-tui` 和宿主运行时提供交互式实现。循环保持不变:工具调用等待 Promise工具结果随后恢复正常的 agent loop智能体循环
## 模型体验
间接地,通过 `dsh-tool-ask-user`:它会将成功的提供方回答保留为紧凑 JSON或返回以下失败之一`Error: ask_user_question was aborted before the user answered``Error: ask_user_question requires at least one question``Error: no user-interaction provider is registered``Error: <message>`。等待人类回答不会增加 token。
#### KV Cache 影响
不会直接使缓存失效;具名消费方拥有所有请求前缀变更。
## 已知限制与延期工作
- **每个上下文只能有一个提供方**:不支持路由或扇出到多个 UI第二次注册会抛出 `DUPLICATE_PROVIDER`,未注册任何提供方时,`ask()` 会抛出 `NO_PROVIDER`,而不会降级。
- **词汇仅包含问题表单形态**可选选项加可选自定义文本更丰富的交互形态文件选择器、diff 预览确认)尚无 seam 词汇。