diff --git a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml index 0d2231b449..ff01e8a69a 100644 --- a/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml +++ b/.agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-04-doc-tiers-and-budgets.md 2026-07-04-doc-tiers-and-budgets.md: e7b3421d09a1ae5ab9a9373e8040832c1b0d4b97 -2026-07-04-doc-tiers-and-budgets.zh.md: 3e372f975943e8e4868aa6a386361ba324ef7109 +2026-07-04-doc-tiers-and-budgets.zh.md: 18bf98777c87512e6161a4529db66030ced47bfd diff --git a/README.i18n.yaml b/README.i18n.yaml index f5eb3be571..8b5cea2324 100644 --- a/README.i18n.yaml +++ b/README.i18n.yaml @@ -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 README.md -README.md: 7777c35a2785856e553cb963920288ff8021dc27 -README.zh.md: 85ee977063e4d822d71e1e780ab4a461260aaf53 +README.md: 7f393077201a974335f9f72f5924d8b1fec585c3 +README.zh.md: ecdb2057fafc4678220b8ccedfe39cb7f387b36a diff --git a/README.md b/README.md index 7777c35a27..7f39307720 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,9 @@ It uses an architecture where **everything is a plugin**. ## Internal testing notice -感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 +DeepSeek Harness is under internal testing. Features and interfaces may change. -“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。 - -为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log;如需关闭,可以设置环境变量 `DSH_TELEMETRY_DISABLED=1`。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。 +The internal build uploads all Session Logs by default to help diagnose reported problems. Set `DSH_TELEMETRY_DISABLED=1` to disable telemetry. Send feedback through the internal WeChat group. ## Install @@ -26,7 +24,7 @@ scripts/install.sh The installer requires `git` and Node `^22.19 || >=24`, offers to install `pnpm` when it is missing, prompts for a DeepSeek API key, builds the required repository artifacts, and launches the Web UI. -The installer keeps every checkout under `~/.dsh/source`: the master clone at `~/.dsh/source/master` and each install's staging checkout as a git worktree `~/.dsh/source/staging-`. The stable symlink `~/.dsh/source/current` points at the active staging worktree, and `dsh` in `~/.local/bin` links to `current/bin/dsh`, so an upgrade repoints one symlink and the `dsh` on PATH never moves. Re-running the command adds a fresh staging worktree from an updated master and repoints `current` at it. See [`scripts/install.sh`](scripts/install.sh) for alternate install locations and other options. +The default active checkout is `~/.dsh/source/current`, and the launcher is linked into `~/.local/bin`. Re-run the installer to update. [`scripts/install.sh`](scripts/install.sh) owns alternate locations, update mechanics, and recovery options. ## Use DeepSeek Harness diff --git a/README.zh.md b/README.zh.md index 85ee977063..ecdb2057fa 100644 --- a/README.zh.md +++ b/README.zh.md @@ -8,11 +8,9 @@ DeepSeek Harness(`dsh`)是一款基于 DeepSeek Harness SDK 构建的开源 ## 内测声明 -感谢您愿意拨冗试用 DeepSeek Harness。当前版本仍处于内部测试阶段,功能仍待完善,体验难免有些粗糙。 +DeepSeek Harness 正处于内部测试阶段,功能和接口可能发生变化。 -“如切如磋,如琢如磨。” 产品的成长,离不开一次次真实的碰撞与坦诚的反馈。您在真实使用中发现的问题,也可能促使我们重新审视,甚至推翻已有的设计。 - -为了帮助我们更准确地还原您真实使用中的问题,内测版本默认会上传所有 Session Log;如需关闭,可以设置环境变量 `DSH_TELEMETRY_DISABLED=1`。另外,如果您有任何反馈与建议,请在企业微信群中留言告诉我们。每一条反馈,都会帮助我们把它打磨得更好。 +为帮助诊断上报的问题,内测版本默认上传所有会话日志。设置 `DSH_TELEMETRY_DISABLED=1` 可关闭遥测。请通过内部企业微信群反馈问题和建议。 ## 安装 @@ -26,7 +24,7 @@ scripts/install.sh 安装器要求系统已安装 `git` 和 Node `^22.19 || >=24`,缺少 `pnpm` 时可代为安装,并会提示输入 DeepSeek API 密钥,然后构建所需的仓库产物并启动 Web UI。 -安装器会把所有检出都放在 `~/.dsh/source` 下:master 克隆位于 `~/.dsh/source/master`,每次安装的 staging 检出是一个 git worktree `~/.dsh/source/staging-<时间戳>`。稳定符号链接 `~/.dsh/source/current` 指向当前生效的 staging worktree,`~/.local/bin` 中的 `dsh` 链接到 `current/bin/dsh`,因此升级只需重指一个符号链接,PATH 上的 `dsh` 从不移动。再次运行该命令会基于更新后的 master 新增一个 staging worktree,并把 `current` 重指到它。其他安装位置和选项见 [`scripts/install.sh`](scripts/install.sh)。 +默认生效的检出位于 `~/.dsh/source/current`,启动器链接到 `~/.local/bin`。再次运行安装器即可更新。其他位置、更新机制和恢复选项由 [`scripts/install.sh`](scripts/install.sh) 负责。 ## 使用 DeepSeek Harness diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 8b04f25504..be03addc50 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -27,7 +27,7 @@ The Cordis framework and its foundation libraries are source-vendored into this ## Runtime npm dependencies -External packages that a workspace package resolves at runtime. `scripts/install.sh` installs this repository itself, so the tier covers every plugin a user can mount from `cordis.yml` — not only what the `dsh` CLI/TUI, the Web UI, and the Python SDK runtime load by default. +External packages that a workspace package resolves at runtime. `scripts/install.sh` installs this repository itself, so the tier covers every plugin a user can mount from `cordis.yml` — not only what the `dsh` CLI, Web UI, and Python SDK runtime load by default. | Package | License | | --- | --- | diff --git a/apps/cli/README.i18n.yaml b/apps/cli/README.i18n.yaml index fd317e355d..e115e43e64 100644 --- a/apps/cli/README.i18n.yaml +++ b/apps/cli/README.i18n.yaml @@ -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 apps/cli/README.md -README.md: 6fdca68eed11dffe46bf2fbde9a7899359690dca -README.zh.md: d8d7122729df1dd8aaed8207ddfb0a0470778b01 +README.md: ce7af5a299e45d6f107686aff043246914dce8ed +README.zh.md: e97fec9d6bb726cb1e419a1ca2fa1871d4d203ca diff --git a/apps/cli/README.md b/apps/cli/README.md index 6fdca68eed..ce7af5a299 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -2,75 +2,24 @@ English | [中文](README.zh.md) -The `dsh` command has three entry modes: a required raw config overlay, a one-shot headless prompt, and the Web UI. [`src/args.ts`](src/args.ts) owns the Commander grammar, and [`src/bin.ts`](src/bin.ts) dynamically imports only the selected runner. Unknown commands and leaked options fail with a nonzero exit code. +The `dsh` command is the product launcher for raw Cordis configurations, the Web UI, and one-shot headless tasks. [`src/args.ts`](src/args.ts) owns the command grammar, and [`src/bin.ts`](src/bin.ts) loads only the selected runner. Invalid commands, options from another mode, configuration errors, and boot failures exit nonzero. + +## Entry modes + +| Command | Purpose | +|---|---| +| `dsh --config ./app.cordis.yml` | Run an explicit patch-list configuration over the shipped base. | +| `dsh web` | Start the browser UI with the shipped Web composition and optional personal configuration. | +| `dsh -p "task"` | Run one fresh persisted session, print the final answer, and exit. | + +The invoking directory is the default workspace root. Web and headless share the shipped provider, persistence, policy, tool, repository Plugin, and telemetry composition; raw config selects its own deployment-specific front door. ## Raw config -Raw `dsh` requires an explicit patch-list config: +Raw `dsh` requires `--config`. The named patch list is applied directly over [`config/base.cordis.yml`](config/base.cordis.yml); it is not a complete replacement tree and does not add a surface overlay or personal `$DSH_HOME/config.yaml`. Use `--dump-default-config` and `--dump-config` to inspect the resulting tree without booting it. -```sh -dsh --config ./app.cordis.yml -``` +The [CLI behavior reference](reference/README.md) owns exact overlay precedence, flags, shutdown behavior, deployment defaults, and the source launcher. -The named file is applied directly over [`config/base.cordis.yml`](config/base.cordis.yml) through the Include plugin's patch algorithm. It is not a complete replacement tree, and neither the personal `$DSH_HOME/config.yaml` nor another surface overlay is added. The base deliberately contains no startup agent or interaction front door; the required overlay selects those deployment details. Relative config paths resolve from the invoking directory. A parse, schema, resolution, or plugin boot failure is reported and exits nonzero. SIGINT and SIGTERM dispose the mounted root before exit. +## Development -A patch targets a base row by `id` and replaces that row's complete `config` value rather than deep-merging keys. Patch lists may also insert new rows whose plugin modules the shipped Loader can resolve: - -```yaml -- id: agent-loop - config: - agents: - - id: main - provider: deepseek-official - model: deepseek-v4-flash -``` - -Inspect the effective tree without booting it: - -```sh -dsh --dump-default-config -dsh --config ./app.cordis.yml --dump-config -``` - -`--dump-default-config` prints only the shipped base. `--dump-config` requires `--config` and prints base plus overlay with provenance comments. Composition uses `applyEntryPatches` and `entryListSchema` from `@cordisjs/plugin-include`; `!!js` expressions remain unevaluated, and unmatched patch targets are reported on stderr. - -## Web and headless - -`dsh web` boots `base.cordis.yml` plus [`config/web.cordis.yml`](config/web.cordis.yml), followed by `$DSH_HOME/config.yaml` when present. `dsh web --config ` replaces that personal layer with the explicit patch list. `--host`, `--port`, `--workspace-root`, and repeatable `--trusted-host` values become Web host patches; their owning plugin schemas validate them at boot. `--dev` mounts the client-plugin HMR receiver and expects a separate `pnpm run dev:web` watcher for no-refresh client bundle updates. - -```sh -dsh web -dsh web --config ./web-profile.cordis.yml -dsh web --dump-default-config -dsh web --dump-config -``` - -The production Web runner needs built package and frontend artifacts (`pnpm run build`). It serves `http://127.0.0.1:3080` by default. Binding all interfaces also trusts the machine's discovered LAN IP literals; `--trusted-host` adds named authorities accepted by the `/api` browser-trust fence. - -`dsh -p "task"` uses the same base and Web composition with the startup personal config, starts its Web host on an OS-assigned port, runs one fresh persisted session, prints the final answer, and exits. It accepts neither `--config` nor raw config-dump flags. - -Web and headless process shutdown gives the plugin tree up to five seconds to dispose. The first `SIGINT`/`SIGTERM` starts that graceful drain; a second signal forces immediate exit. If headless normal completion is already stuck in disposal, the first `Ctrl+C` is the escalation and exits immediately instead of being swallowed. - -Both modes treat the invoking directory as the default workspace root, load applicable `AGENTS.md` or `CLAUDE.md` instructions with a 65,536-byte render budget, and use an in-memory SQLite session content index. Web watches valid personal config edits; headless reads the file once at startup. The [app-boot personal-config contract](../../packages/ui/app-boot/README.md#personal-config) owns layer precedence, credential storage, live-update failure behavior, and `$DSH_HOME` resolution. - -New sessions default to the `workspace-write` permission preset. Bash and filesystem mutations are restricted to the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. `DSH_PERMISSION_MODE` changes the process fallback. Stored General-settings permissions affect later Web sessions, not an already-open one. - -`DSH_TOOLS_MODE` selects `native`, `code`, or `both` for the Web/headless process; another value fails at boot. [`config/core-web.cordis.yml`](config/core-web.cordis.yml) is an optional Web overlay that reduces the native model surface to persistent `bash` and `str_replace_editor` while retaining the shipped host, browser, workspace, persistence, and permission composition. - -## Shared deployment behavior - -The base mounts the native DeepSeek adapter, settings and credential providers, stable `web_search`, repository Plugin support, and session telemetry. Provider credentials live in `$DSH_HOME/.env` or the ambient environment and remain rotatable because the launcher never hoists the credential file into `process.env`. Search uses `DEEPSEEK_API_KEY` and accepts `DEEPSEEK_SEARCH_BASE_URL`; `web_fetch` is disabled unless an overlay inserts a provider and enables it. - -Session events stream as OTLP/HTTP logs by default. `DSH_TELEMETRY_OTLP_URL` selects another collector. Any non-empty `DSH_TELEMETRY_DISABLED` disables the telemetry row before boot. The shipped base has no telemetry redaction rule, so exported records can contain message text, tool arguments and results, and workspace paths; the [telemetry Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md) owns that deployment decision. - -The empty `repository-plugins` row lets Web/headless personal config and raw overlays mount prepared immutable repository Plugin generations. See the [repository Plugin contract](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration). The CLI also ships `@deepseek-ai/dsh-mcp-client` as a dependency for overlays, but no MCP server is enabled by default because each server command is trusted executable code outside the agent sandbox. - -## Source launcher - -Link the source-running launcher onto PATH: - -```sh -ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh -``` - -It resolves the checkout through its real path and launches `apps/cli/src/bin.ts` with `node --import tsx/esm`. `TSX_TSCONFIG_PATH` is pinned to the checkout root, so workspace package resolution is independent of the invoking directory. `pnpm run dsh` uses the same entry and forwards arguments. The built form is `apps/cli/lib/bin.js` after `pnpm run build`. +Production Web and headless runs require built package and frontend artifacts. From a checkout, `pnpm run dsh` runs the TypeScript entry and forwards arguments; the [source-launcher reference](reference/README.md#source-launcher) describes the PATH symlink and module-resolution contract. diff --git a/apps/cli/README.zh.md b/apps/cli/README.zh.md index d8d7122729..e97fec9d6b 100644 --- a/apps/cli/README.zh.md +++ b/apps/cli/README.zh.md @@ -2,75 +2,24 @@ [English](README.md) | 中文 -`dsh` 命令有三种入口模式:必需的原始配置 overlay、一次性 headless 提示词,以及 Web UI。[`src/args.ts`](src/args.ts) 拥有 Commander 命令行语法,[`src/bin.ts`](src/bin.ts) 只会动态导入选中模式的运行器。未知命令和误传入其他模式的选项都会以非零代码退出。 +`dsh` 命令是原始 Cordis 配置、Web UI 和一次性无头任务的产品启动器。[`src/args.ts`](src/args.ts) 负责命令语法,[`src/bin.ts`](src/bin.ts) 只加载选中的运行器。无效命令、来自其他模式的选项、配置错误和启动失败都会以非零状态退出。 + +## 入口模式 + +| 命令 | 用途 | +|---|---| +| `dsh --config ./app.cordis.yml` | 在随附基础配置之上运行显式 patch 列表配置。 | +| `dsh web` | 使用随附 Web 组合和可选个人配置启动浏览器 UI。 | +| `dsh -p "task"` | 运行一个新的持久化会话,打印最终答案并退出。 | + +调用目录是默认 workspace 根目录。Web 与无头模式共享随附的提供方、持久化、策略、工具、repository Plugin 和遥测组合;原始配置自行选择部署专用前端入口。 ## 原始配置 -原始 `dsh` 要求显式传入一份 patch 列表配置: +原始 `dsh` 必须提供 `--config`。指定的 patch 列表直接应用到 [`config/base.cordis.yml`](config/base.cordis.yml) 之上;它不是完整替代树,也不会添加 surface overlay 或个人 `$DSH_HOME/config.yaml`。使用 `--dump-default-config` 和 `--dump-config` 可在不启动的情况下检查生成的配置树。 -```sh -dsh --config ./app.cordis.yml -``` +[CLI(命令行界面)行为参考](reference/README.md)负责确切的 overlay 优先级、flag、关闭行为、部署默认值和源码启动器。 -指定文件会通过 Include 插件的 patch 算法,直接应用在 [`config/base.cordis.yml`](config/base.cordis.yml) 之上。它不是完整替换树,系统也不会添加个人 `$DSH_HOME/config.yaml` 或其他 surface overlay。base 有意不包含启动 agent(智能体)或交互入口;必需的 overlay 负责选择这些部署细节。相对配置路径以调用目录为基准解析。配置解析、schema 校验、模块解析或插件启动失败都会被报告,并以非零代码退出。SIGINT 和 SIGTERM 会在退出前 dispose(资源释放)已挂载的根上下文。 +## 开发 -patch 通过 `id` 定位 base 配置项,并替换该配置项的完整 `config` 值,而不是深度合并各个键。它也可以插入新配置项: - -```yaml -- id: agent-loop - config: - agents: - - id: main - provider: deepseek-official - model: deepseek-v4-flash -``` - -可以在不启动应用的情况下检查有效配置树: - -```sh -dsh --dump-default-config -dsh --config ./app.cordis.yml --dump-config -``` - -`--dump-default-config` 只打印随附 base。`--dump-config` 要求提供 `--config`,并打印带来源注释的 base 与 overlay。组合过程使用 `@cordisjs/plugin-include` 的 `applyEntryPatches` 和 `entryListSchema`;`!!js` 表达式保持未求值状态,未匹配的 patch 目标会报告到 stderr。 - -## Web 与 headless - -`dsh web` 会启动 `base.cordis.yml` 加 [`config/web.cordis.yml`](config/web.cordis.yml),并在 `$DSH_HOME/config.yaml` 存在时继续应用该文件。`dsh web --config ` 会以显式 patch 列表替换个人层。`--host`、`--port`、`--workspace-root` 和可重复的 `--trusted-host` 值会转为 Web 宿主 patch;各自所属插件的 schema 会在启动时校验它们。`--dev` 会挂载客户端插件 HMR(热模块替换)接收器,要实现无需刷新的客户端 bundle 更新,还需单独运行 `pnpm run dev:web` watcher。 - -```sh -dsh web -dsh web --config ./web-profile.cordis.yml -dsh web --dump-default-config -dsh web --dump-config -``` - -生产 Web 运行器需要已构建的包(package)与前端产物(`pnpm run build`)。它默认通过 `http://127.0.0.1:3080` 提供服务。绑定所有网络接口时,系统也会信任本机探测到的 LAN IP 字面量;`--trusted-host` 可添加 `/api` 浏览器信任边界所接受的具名权威。 - -`dsh -p "task"` 使用相同的 base 与 Web 组合及启动时个人配置,在由操作系统分配的端口上启动 Web 宿主,运行一个全新的持久会话,打印最终答案后退出。它不接受 `--config` 或原始配置输出标志。 - -Web 与 headless 的进程关闭流程最多给插件树 5 秒执行 dispose。第一次 `SIGINT`/`SIGTERM` 会启动这次优雅排空;第二次信号会立即强制退出。如果 headless 的正常完成流程已经卡在 dispose 中,第一次 `Ctrl+C` 就会触发强制退出:进程立即结束,该信号不再被吞掉。 - -两种模式都以调用目录作为默认 workspace 根目录,加载适用的 `AGENTS.md` 或 `CLAUDE.md` 指令,渲染预算为 65,536 字节,并使用内存 SQLite 会话内容索引。Web 会持续应用有效的个人配置编辑;headless 只在启动时读取该文件一次。层次优先级、凭据存储、实时更新失败行为与 `$DSH_HOME` 解析均由 [app-boot 个人配置契约](../../packages/ui/app-boot/README.md#personal-config) 统一定义。 - -新会话默认使用 `workspace-write` 权限 preset。Bash 和文件系统写操作受限于会话 workspace 与平台临时根目录;读取、网络访问与进程可见性不受限制。`DSH_PERMISSION_MODE` 会改变进程回退值。已存储的常规设置权限会影响之后的 Web 会话,不会更改已打开的会话。 - -`DSH_TOOLS_MODE` 为 Web/headless 进程选择 `native`、`code` 或 `both`;其他值会在启动时失败。[`config/core-web.cordis.yml`](config/core-web.cordis.yml) 是可选的 Web overlay,它在保留随附宿主、浏览器、workspace、持久化与权限组合的同时,将面向原生模型的工具缩减为持久 `bash` 和 `str_replace_editor`。 - -## 共享部署行为 - -base 会挂载原生 DeepSeek 适配器、设置与凭据提供方、稳定的 `web_search`、仓库插件支持与会话遥测。提供方凭据位于 `$DSH_HOME/.env` 或环境中,且仍可轮换,因为启动器绝不会把凭据文件提升进 `process.env`。搜索使用 `DEEPSEEK_API_KEY` 并接受 `DEEPSEEK_SEARCH_BASE_URL`;除非 overlay 插入提供方并启用 `web_fetch`,否则后者处于禁用状态。 - -会话事件默认以 OTLP/HTTP 日志的形式流式发送。`DSH_TELEMETRY_OTLP_URL` 用于选择其他 collector。`DSH_TELEMETRY_DISABLED` 的任何非空值都会在启动前禁用遥测配置项。随附 base 没有遥测脱敏规则,因此导出记录可能包含消息文本、工具参数与结果,以及 workspace 路径;该部署决策由[遥测 Agent Note](../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md) 统一定义。 - -空的 `repository-plugins` 配置项允许 Web/headless 个人配置与原始 overlay 挂载已准备的不可变仓库插件 generation。详见[仓库插件契约](../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)。CLI(命令行界面)还将 `@deepseek-ai/dsh-mcp-client` 作为 overlay 依赖发布,但默认不启用任何 MCP 服务器,因为每条服务器命令都是 agent 沙箱之外的受信任可执行代码。 - -## 源码启动器 - -将以源码运行的启动器链接到 PATH: - -```sh -ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh -``` - -它会通过自身实际路径解析该检出,并使用 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`。`TSX_TSCONFIG_PATH` 固定指向检出根目录,因此 workspace 包解析不受调用目录影响。`pnpm run dsh` 使用同一入口并转发参数。构建后的形式是执行 `pnpm run build` 后的 `apps/cli/lib/bin.js`。 +生产环境的 Web 和无头运行需要已构建的包与前端产物。在 checkout 中,`pnpm run dsh` 会运行 TypeScript 入口并转发参数;[源码启动器参考](reference/README.md#source-launcher)说明 PATH 符号链接和模块解析契约。 diff --git a/apps/cli/reference/README.i18n.yaml b/apps/cli/reference/README.i18n.yaml index ff9cab7b06..7e5b8e5c58 100644 --- a/apps/cli/reference/README.i18n.yaml +++ b/apps/cli/reference/README.i18n.yaml @@ -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 apps/cli/reference/README.md -README.md: 6c83dd8312cf797614b03e8632b72fd1133577d3 -README.zh.md: 00016ebc2f2cffa489072695cdc280ed2d875145 +README.md: b37ec9ed61ea4e9899a51316065d4188f30997ad +README.zh.md: ca29808a6c8e670f0d0b82c59b1a2c1fa0e13565 diff --git a/apps/cli/reference/README.md b/apps/cli/reference/README.md index 6c83dd8312..b37ec9ed61 100644 --- a/apps/cli/reference/README.md +++ b/apps/cli/reference/README.md @@ -2,81 +2,75 @@ English | [中文](README.zh.md) -Argv is parsed once through a [Commander](https://github.com/tj/commander.js) adapter ([`src/args.ts`](../src/args.ts)): one program whose default (no subcommand) is the TUI/headless surface (`--config`, `-p`/`--prompt`, `--resume`, `--dump-config`, `--dump-default-config`), whose `meta` subcommand is the same TUI over this checkout, whose `upgrade` subcommand is a guided-session entry, and whose `web` subcommand is the browser UI. `meta` and `upgrade` are experimental: each runs only with its `--experimental` flag or with `DSH_EXPERIMENTAL=1` in the environment, and fails loud (stderr, exit 1) otherwise. `src/bin.ts` switches on the resolved mode and dynamic-imports only that mode's module. `dsh --help` lists every mode and `dsh web --help` renders the web usage, `dsh --version` prints this app's version, and an unknown option or a mistyped `--resume` fails loud (stderr, exit 1) instead of misrouting. Every subcommand that shares no option with the default surface — `upgrade`, `web`, `meta` — rejects a leaked `--config`/`-p`/`--resume`/dump flag rather than running and dropping it. `dsh web`'s `--host`/`--port` are unvalidated pass-through overrides: the `dsh-host-webserver` schema is the single source of both the default (the shipped Web overlay value when a flag is absent) and validity, and rejects a bad value at boot. `--trusted-host` appends named authorities for the /api browser-trust fence; an all-interfaces bind additionally derives the machine's LAN IP literals itself ([`src/app-cli-entry.ts`](../src/app-cli-entry.ts)), so the printed LAN URL works without flags. +This reference defines the raw-config, Web, and headless command modes. Argv is parsed once through [`src/args.ts`](../src/args.ts), and [`src/bin.ts`](../src/bin.ts) dynamically imports only the selected runner. -The TUI surface: +## Raw config -- boots `base.cordis.yml` plus `tui.cordis.yml` through [`dsh-app-boot`](../../../packages/ui/app-boot/README.md); `--config ` applies a patch-list overlay instead of the personal overlay, while `--config-replace ` boots that file as the complete tree; -- resumes a persisted session with `dsh --resume ` and, when the Node host exposes `process.execve`, supplies the TUI's in-place handoff host: after selector preflight and current-session flush, the host disposes the app and replaces the process with a normalized resume invocation; runtimes without process replacement leave the session running and say so. This CLI owns session identity and the exit line rather than the config: it mints or selects the `main` session id and provides it, plus the exact command that reproduces this invocation, on the boot context ([`MAIN_SESSION_ID_KEY`](../../../packages/ui/tui/README.md) and `TUI_GOODBYE_MESSAGE_KEY`). No `cordis.yml` key can drop resume, and a missing or unreadable id fails loud instead of creating a fresh session; -- treats the **invoking directory** as the workspace — sessions, relative paths, and workspace instructions resolve from the cwd (`dsh meta` is the sole exception, below); -- tells the agent where its own source lives: after boot it adds a prompt section naming this harness checkout, resolved from the launcher's real path so it holds under a PATH symlink and an arbitrary cwd, so the self-referential `cordis` toolset can read and modify it; -- applies the personal overlay from `~/.dsh` (see [app-boot's Personal config](../../../packages/ui/app-boot/README.md#personal-config)): `config.yaml` patches the booted tree, while `.env` there is the credential provider's own store (never hoisted into the environment, so keys stay rotatable). Environment precedence is ambient > project `.env`. The shipped tree's Cordis HMR keeps `config.yaml` live; an explicit `--config` tree replaces that overlay, and a tree without HMR reads it at startup only. -- presents the [versioned first-run welcome](../../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md) through the mounted TUI overlay service when its immutable marker is absent under `DSH_HOME`; only Enter creates that version's marker, while Escape, disposal, or process exit leaves it eligible. The official DeepSeek icon, responsive terminal rasters, all-locale Chinese copy, and notice version are static local owners; the overlay never writes a session event or model context. -- registers bare `/compact`: while the agent is idle, it summarizes useful older history even below automatic pressure, rejects arguments, and reports success only after the standalone replacement bracket is durable. A prompt submitted during compaction keeps its queue identity and starts after that checkpoint; injected context remains visible. - -`dsh meta` is that same TUI with this harness checkout as the workspace, so working on dsh itself needs no `cd`. It chdirs to the checkout root — resolved from the launcher's real path, the same root the source-path prompt section names — after the environment is settled, so precedence is unchanged while the session cwd and HMR watch root move together. Meta always starts a fresh session and accepts no default-surface options; use ordinary `dsh --resume ` to resume a persisted session. - -`dsh upgrade` is a guided fresh-session entry over the default TUI surface: it mints a fresh session in the invoking directory and seeds its first turn with the bundled `dsh-upgrade` skill, exactly as if the user typed `/skill:`. The launcher passes the skill name on the boot context ([`INITIAL_SKILL_KEY`](../../../packages/ui/tui/README.md)), which the TUI auto-invokes once the chat is live. The command takes no options beyond the experimental gate — `--config`, `-p`, and `--resume` fail loud — and seeds only on this first launch, so a later `dsh --resume ` of the session is an ordinary TUI session with no re-injection. - -`dsh --dump-config` and `dsh web --dump-config` print the composed config tree — the shipped base, the surface overlay, and the `--config` or personal overlay, exactly the layers that surface would boot — as YAML on stdout and exit without booting; `--dump-default-config` stops at the surface overlay, so diffing the two shows precisely what the user layer changes. Each run of rows is preceded by a `# ==` comment naming the file it comes from and the layers that patched it (e.g. `# == base.cordis.yml, patched by tui.cordis.yml`), so the output shows provenance while staying one loadable document. Composition runs through the include's own patch algorithm and YAML dialect (`applyEntryPatches`/`entryListSchema` from `@cordisjs/plugin-include`), so the dump cannot drift from what boots; `!!js` expressions print verbatim and unevaluated, and a patch whose target row is absent is reported on stderr with its layer, mirroring the Loader's boot-time warning. Launcher-owned boot-context values (session identity, CLI-flag patches) are per-invocation facts outside the config tree and do not appear. The dump flags reject boot-only flags (`-p`, `--resume`, `--config-replace`) rather than silently ignoring them, and `--dump-default-config` takes no `--config`. - -The Web and headless surfaces boot `base.cordis.yml` plus `web.cordis.yml`, then apply `$DSH_HOME/config.yaml`; an explicit `--config ` replaces that personal overlay. Both surfaces otherwise share the same composition: both tell the coding agent its resolved model and session working directory, treat the invoking directory as the default project and Workspace root, create named Workspaces beneath that root unless `--workspace-root ` overrides it, load applicable `AGENTS.md`/`CLAUDE.md` instructions into each agent-loop request prefix with a 65,536-byte render budget, opt into first-message model titles, use the same bounded transient model-request retry policy as the TUI, and mount a disposable in-memory SQLite content-index service. Web additionally names the DeepSeek Harness Web GUI as the interaction surface, this checkout as its own source location, and the process's canonical local URL and mode in both the prompt and managed `$DSH_WEB_URL`/`$DSH_WEB_MODE`; references such as “this page” therefore identify the GUI without claiming access to implicit DOM, route, or screenshot state. In production mode the host reads rebuilt frontend dist and client bundles on the next request, so refreshing the existing URL updates that GUI without replacing its process. `dsh web --dev` mounts the client-plugin HMR receiver, but no-refresh updates additionally require `pnpm run dev:web` in the same checkout to watch and rebuild plugin bundles; shell and ordinary package changes still require a rebuild and page refresh. Bare `apps/web` Vite serving fails before listening because it cannot inject `window.__DSH_BOOT__`. The index service is ACTIVE at boot, while its `node:sqlite` module and database handle open only on the first content search. This keeps Node 22 startup output free of SQLite's experimental warning before search is used; the first actual search may still emit the runtime warning. Each service instance owns its database, so parallel invocations neither share unsupported SQLite state nor leave derived index files behind, and the first search lazily reconciles live and persisted logs. Headless differs only in listening on an OS-assigned port (parallel `dsh -p` runs never collide; the stderr-printed URL opens the live session in a browser). Both need the frontend dist and client bundles built (`pnpm run build && pnpm run build:web`). - -The shared composition defaults new TUI, Web, and headless sessions to the `workspace-write` permission preset (`workspace-write` file mode plus `ask` approval policy). Sandbox-enforced bash and filesystem mutations may write only under the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. The browser answers one-shot approval requests and exposes the Access picker; the TUI exposes `/permission`, but has no approval-request answerer, so an automatic wider retry there fails closed until the user deliberately changes the session preset. `DSH_PERMISSION_MODE` changes the process fallback, while a stored General-settings Permission value applies to later sessions without changing an open one. - -All three surfaces consume `$DSH_HOME/config.yaml`; the TUI and Web apply valid edits live, while one-shot headless runs read it at startup. The shipped trees include an empty `repository-plugins` row, so a standalone user can add prepared GitHub Plugins without an SDK project or install command: - -```yaml -- id: repository-plugins - name: '@deepseek-ai/dsh-repository-plugin' - config: - repositories: - - 'github:PolyArch/humanize#' -``` - -The repository must contain a prepared `.dsh-plugin` package; the [repository Plugin contract](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration) documents authoring, nested Plugin paths, the immutable cache, trust boundary, and failure semantics. A failed live edit keeps the last good tree and emits Cordis's `hmr/config-update-failed` event. - -The shipped TUI and Web compositions register the native DeepSeek adapter plus pi-ai OpenAI and Anthropic profiles. Credentials and endpoint overrides come from the provider-standard `DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`, `OPENAI_API_KEY` / `OPENAI_BASE_URL`, and `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL` pairs in the boot's layered environment. - -Every surface also registers `web_search` and only `web_search`. Search uses DeepSeek's Anthropic-compatible Messages endpoint, resolves the same `DEEPSEEK_API_KEY` reference for every call, and accepts the separate `DEEPSEEK_SEARCH_BASE_URL` endpoint override; each search is an auxiliary model request with its own latency and token cost. `web_fetch` remains disabled and the composition mounts no default fetch provider, so deployments that need arbitrary page retrieval must opt in through an overlay. The deployment decision and its security boundary live in the [default Web search Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md). - -`DSH_TOOLS_MODE` selects the tool presentation mode for the whole Web/headless process: `native` (the schema default when unset), `code` (the `run_code`-only Code Mode wire), or `both`; any other value fails loud at boot through the `dsh-tools` config schema. The setting is process-wide because Loader composition is static. The TUI surface ignores it and pins `native`. - -[`core-web.cordis.yml`](../config/core-web.cordis.yml) is an opt-in `dsh web --config` overlay that keeps the shipped Web host, browser, Workspace, persistence, and permission composition while reducing the default native model surface to owner-scoped persistent `bash` and `str_replace_editor`. The PTY backend and editor consume the existing Web sandbox and filesystem providers. An open persistent shell prevents changing that session's permission mode until the shell closes, so a shell created under wider access cannot survive a downgrade. `DSH_TOOLS_MODE` still controls native/Code Mode presentation for the resulting two-tool registry. - -From a source checkout, start this minimal Web profile with: +Raw `dsh` requires an explicit patch-list config: ```sh -pnpm run dsh web --config apps/cli/config/core-web.cordis.yml +dsh --config ./app.cordis.yml ``` -Every `dsh` surface — TUI, Web, and headless — reports session telemetry by default (the row lives in the shared `base.cordis.yml`): every session-log event streams as OTLP/HTTP log records to `https://harness-telemetry.deepseeksvc.com/v1/logs` on a 10-second batch cadence. `DSH_TELEMETRY_OTLP_URL` points the exporter at a different collector; setting `DSH_TELEMETRY_DISABLED` to ANY non-empty value — including `0` or `false` — disables the row before it loads (a privacy switch prefers off-by-mistake over on-by-mistake). No redaction rule is mounted in this composition yet: exported records are the raw captured copy, including message text, tool arguments and results, and the session's working-directory path. The deployment rulings live in the [web-telemetry-default-mount Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md). +The named file is applied directly over [`config/base.cordis.yml`](../config/base.cordis.yml) through the Include plugin's patch algorithm. It is not a complete replacement tree, and neither the personal `$DSH_HOME/config.yaml` nor another surface overlay is added. The base deliberately contains no startup agent or interaction front door; the required overlay selects those deployment details. Relative config paths resolve from the invoking directory. A parse, schema, resolution, or plugin boot failure is reported and exits nonzero. SIGINT and SIGTERM dispose the mounted root before exit. -MCP servers are not a shipped default, because a default would have to name one: `@deepseek-ai/dsh-mcp-client` mounts exactly one server per row and spawns it as a child process, outside `ctx.bash` and so outside the sandbox policy. The package is a runtime dependency of this CLI, so an installed `dsh` can mount your own servers from `$DSH_HOME/config.yaml` or a `--config` overlay without a source checkout: +A patch targets a base row by `id` and replaces that row's complete `config` value rather than deep-merging keys. Patch lists may also insert new rows whose plugin modules the shipped Loader can resolve: ```yaml -- insert: - - id: mcp-github - name: '@deepseek-ai/dsh-mcp-client' - config: - serverName: github - transport: stdio - command: npx - args: ['-y', '@modelcontextprotocol/server-github'] - env: - GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN +- id: agent-loop + config: + agents: + - id: main + provider: deepseek-official + model: deepseek-v4-flash ``` -The model then sees `mcp__github__*`. See the [mcp-client README](../../../packages/mcp/mcp-client/README.md) for the Streamable HTTP transport and the full field table. +Inspect the effective tree without booting it: -## Install (developer machine) +```sh +dsh --dump-default-config +dsh --config ./app.cordis.yml --dump-config +``` -Symlink the source-running launcher onto your PATH; it resolves the checkout through its own real path, so code changes apply on the next launch with no build step: +`--dump-default-config` prints only the shipped base. `--dump-config` requires `--config` and prints base plus overlay with provenance comments. Composition uses `applyEntryPatches` and `entryListSchema` from `@cordisjs/plugin-include`; `!!js` expressions remain unevaluated, and unmatched patch targets are reported on stderr. + +## Web and headless + +`dsh web` boots `base.cordis.yml` plus [`config/web.cordis.yml`](../config/web.cordis.yml), followed by `$DSH_HOME/config.yaml` when present. `dsh web --config ` replaces that personal layer with the explicit patch list. `--host`, `--port`, `--workspace-root`, and repeatable `--trusted-host` values become Web host patches; their owning plugin schemas validate them at boot. `--dev` mounts the client-plugin HMR receiver and expects a separate `pnpm run dev:web` watcher for no-refresh client bundle updates. + +```sh +dsh web +dsh web --config ./web-profile.cordis.yml +dsh web --dump-default-config +dsh web --dump-config +``` + +The production Web runner needs built package and frontend artifacts (`pnpm run build`). It serves `http://127.0.0.1:3080` by default. Binding all interfaces also trusts the machine's discovered LAN IP literals; `--trusted-host` adds named authorities accepted by the `/api` browser-trust fence. + +`dsh -p "task"` uses the same base and Web composition with the startup personal config, starts its Web host on an OS-assigned port, runs one fresh persisted session, prints the final answer, and exits. It accepts neither `--config` nor raw config-dump flags. + +Web and headless process shutdown gives the plugin tree up to five seconds to dispose. The first `SIGINT`/`SIGTERM` starts that graceful drain; a second signal forces immediate exit. If headless normal completion is already stuck in disposal, the first `Ctrl+C` is the escalation and exits immediately instead of being swallowed. + +Both modes treat the invoking directory as the default workspace root, load applicable `AGENTS.md` or `CLAUDE.md` instructions with a 65,536-byte render budget, and use an in-memory SQLite session content index. Web watches valid personal config edits; headless reads the file once at startup. The [app-boot personal-config contract](../../../packages/ui/app-boot/README.md#personal-config) owns layer precedence, credential storage, live-update failure behavior, and `$DSH_HOME` resolution. + +New sessions default to the `workspace-write` permission preset. Bash and filesystem mutations are restricted to the session workspace and platform temporary roots; reads, network access, and process visibility are not confined. `DSH_PERMISSION_MODE` changes the process fallback. Stored General-settings permissions affect later Web sessions, not an already-open one. + +`DSH_TOOLS_MODE` selects `native`, `code`, or `both` for the Web/headless process; another value fails at boot. [`config/core-web.cordis.yml`](../config/core-web.cordis.yml) is an optional Web overlay that reduces the native model surface to persistent `bash` and `str_replace_editor` while retaining the shipped host, browser, workspace, persistence, and permission composition. + +## Shared deployment behavior + +The base mounts the native DeepSeek adapter, settings and credential providers, stable `web_search`, repository Plugin support, and session telemetry. Provider credentials live in `$DSH_HOME/.env` or the ambient environment and remain rotatable because the launcher never hoists the credential file into `process.env`. Search uses `DEEPSEEK_API_KEY` and accepts `DEEPSEEK_SEARCH_BASE_URL`; `web_fetch` is disabled unless an overlay inserts a provider and enables it. + +Session events stream as OTLP/HTTP logs by default. `DSH_TELEMETRY_OTLP_URL` selects another collector. Any non-empty `DSH_TELEMETRY_DISABLED` disables the telemetry row before boot. The shipped base has no telemetry redaction rule, so exported records can contain message text, tool arguments and results, and workspace paths; the [telemetry Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md) owns that deployment decision. + +The empty `repository-plugins` row lets Web/headless personal config and raw overlays mount prepared immutable repository Plugin generations. See the [repository Plugin contract](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration). The CLI also ships `@deepseek-ai/dsh-mcp-client` as a dependency for overlays, but no MCP server is enabled by default because each server command is trusted executable code outside the agent sandbox. + +## Source launcher + +Link the source-running launcher onto PATH: ```sh ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh ``` -Source launches run `apps/cli/src/bin.ts` through tsx's ESM-only hook (`node --import tsx/esm`), which transforms TypeScript and projects the root tsconfig `paths` map into module resolution. Node's native TypeScript modes are not used: Node 26 removed `--experimental-transform-types`, and strip-only mode rejects syntax the source graph relies on (vendored parameter properties, decorators, runtime enums/namespaces). The CJS hook stays off because the source graph is ESM-only and the CJS resolver adds ~0.4s of startup. `bin/dsh` pins `TSX_TSCONFIG_PATH` to the checkout's root tsconfig so resolution is cwd-independent, and the `dsh-source-launch-smoke` node-compat gate runs this exact launch vector on every supported Node line. tsx applies the `paths` map without checking dependency declarations, so declaration completeness rests on the static gates: the TUI configs resolve bare plugins through `examples/package.json`, the Web/headless `cordis.yml` through this package's `dependencies`, and `verify-cordis-config` requires every configured bare plugin to be declared, while allowing unrelated dependencies. - -`pnpm run dsh` runs the same entry from the repo root and forwards arguments directly, for example `pnpm run dsh -p "task"`. The built form (`lib/bin.js`, via `pnpm run build`) boots the same config under plain Node. +It resolves the checkout through its real path and launches `apps/cli/src/bin.ts` with `node --import tsx/esm`. `TSX_TSCONFIG_PATH` is pinned to the checkout root, so workspace package resolution is independent of the invoking directory. `pnpm run dsh` uses the same entry and forwards arguments. The built form is `apps/cli/lib/bin.js` after `pnpm run build`. diff --git a/apps/cli/reference/README.zh.md b/apps/cli/reference/README.zh.md index 00016ebc2f..ca29808a6c 100644 --- a/apps/cli/reference/README.zh.md +++ b/apps/cli/reference/README.zh.md @@ -2,81 +2,75 @@ [English](README.md) | 中文 -Argv 只会通过 [Commander](https://github.com/tj/commander.js) 适配器([`src/args.ts`](../src/args.ts))解析一次:同一个程序的默认形式(无子命令)是 TUI/无头界面(`--config`、`-p`/`--prompt`、`--resume`、`--dump-config`、`--dump-default-config`),`meta` 子命令是以本 checkout 为 workspace 的同一个 TUI,`upgrade` 子命令是引导会话入口,`web` 子命令则是浏览器 UI。`meta` 与 `upgrade` 是实验性命令:只有带上各自的 `--experimental` 标志或在环境中设置 `DSH_EXPERIMENTAL=1` 才会运行,否则明确报错(stderr,退出码 1)。`src/bin.ts` 按解析后的 mode 分支,仅动态导入该 mode 的模块。`dsh --help` 列出所有 mode,`dsh web --help` 渲染 Web 用法,`dsh --version` 打印此应用的版本;未知选项或拼错的 `--resume` 会明确报错(stderr,退出码 1),而不会被错路由。凡与默认界面不共享任何选项的子命令(`upgrade`、`web`、`meta`)都会拒绝泄漏进来的 `--config`/`-p`/`--resume`/dump 标志,而不会照常运行并丢弃它。`dsh web` 的 `--host`/`--port` 是未验证的直通覆盖:`dsh-host-webserver` schema 是默认值(标志缺失时使用已交付的 Web 覆盖层值)和有效性的唯一真源,并在启动时拒绝错误值。`--trusted-host` 为 /api 浏览器信任栅栏追加具名权威;全接口绑定还会自行推导本机的 LAN IP 字面量([`src/app-cli-entry.ts`](../src/app-cli-entry.ts)),因此打印出的 LAN URL 无需任何标志即可使用。 +本参考定义原始配置、Web 和无头命令模式。参数由 [`src/args.ts`](../src/args.ts) 统一解析,[`src/bin.ts`](../src/bin.ts) 只动态导入选中的运行器。 -TUI 界面: +## 原始配置 -- 通过 [`dsh-app-boot`](../../../packages/ui/app-boot/README.md) 启动 `base.cordis.yml` 与 `tui.cordis.yml`;`--config ` 应用一个补丁列表覆盖并替代个人覆盖,而 `--config-replace ` 将指定文件作为完整配置树启动; -- 使用 `dsh --resume ` 恢复已持久化会话。当 Node 宿主公开 `process.execve` 时,还会提供 TUI 的原地移交宿主:选择器预检并刷新当前会话后,宿主会对应用执行 dispose(资源释放),并以规范化的恢复调用替换进程;不支持进程替换的运行时会让会话继续运行并给出提示。会话身份与退出行由本 CLI 拥有,而非由配置指定:它创建或选定 `main` 会话 id,并把该 id 以及可复现本次调用的确切命令一起提供到启动上下文([`MAIN_SESSION_ID_KEY`](../../../packages/ui/tui/README.md) 与 `TUI_GOODBYE_MESSAGE_KEY`)。任何 `cordis.yml` 键都无法移除恢复能力;缺失或无法读取的 id 会明确报错,而不会创建新会话; -- 将 **调用目录** 视为 workspace:会话、相对路径和 workspace 指令都从 cwd 解析(`dsh meta` 是唯一例外,见下文); -- 告知 agent(智能体)自身源码所在位置:启动后添加一个命名此 harness checkout 的提示词段。该路径从启动器的真实路径解析,因此在 PATH 符号链接和任意 cwd 下仍然有效,使自指的 `cordis` 工具集可以读取并修改它; -- 应用 `~/.dsh` 中的个人覆盖(参见 [app-boot 的个人配置](../../../packages/ui/app-boot/README.md#personal-config)):`config.yaml` 修补已启动的树,而那里的 `.env` 是凭据提供方自己的存储(绝不会被提升进环境,因此密钥始终可轮换)。环境优先级为环境中已有的值 > 项目 `.env`。已交付配置树中的 Cordis HMR(热模块替换)会持续应用 `config.yaml` 的变更;显式 `--config` 配置树会替代该个人覆盖,未包含 HMR 的配置树只在启动时读取该文件。 -- 当 `DSH_HOME` 下不存在不可变确认标记时,通过已挂载的 TUI overlay 服务呈现[版本化首次运行欢迎页](../../../.agents/notes/implemented/feature/2026-07-30-versioned-tui-first-run-welcome.md);只有 Enter 会创建该版本的标记,Escape、资源释放或进程退出仍保留展示资格。官方 DeepSeek 图标、响应式终端栅格图、所有 locale 共用的中文文案和通知版本均由静态本地文件持有;overlay 不会写入会话事件或模型上下文。 -- 注册裸 `/compact`:agent 空闲时,即使未达到自动压力,也会摘要有效的较早历史;该命令拒绝参数,并只在独立替换标记对持久化后报告成功。压缩(compaction)期间提交的提示词保留其队列身份,并在该检查点之后启动;注入的上下文仍保持可见。 - -`dsh meta` 是以本 harness checkout 为 workspace 的同一个 TUI,因此开发 dsh 自身无需 `cd`。它在环境确定之后才 chdir 到 checkout 根目录(从启动器的真实路径解析,与源码路径提示词段所指的根目录相同),因此环境优先级不变,而会话 cwd 与 HMR 监视根目录会一并移动。Meta 始终创建新会话,不接受默认界面的任何选项;恢复已持久化会话应使用普通的 `dsh --resume `。 - -`dsh upgrade` 是默认 TUI 界面之上的引导式全新会话入口:它在调用目录中创建一个全新会话,并以内置 `dsh-upgrade` skill(技能)播种其首轮,效果等同于用户手动键入 `/skill:`。启动器将 skill 名称提供到启动上下文([`INITIAL_SKILL_KEY`](../../../packages/ui/tui/README.md)),TUI 在聊天就绪后自动调用它。该命令除实验性门槛外不接受任何选项——`--config`、`-p`、`--resume` 都会明确报错——且仅在首次启动时播种,因此之后 `dsh --resume ` 恢复该会话时是普通 TUI 会话,不会重复注入。 - -`dsh --dump-config` 和 `dsh web --dump-config` 把合成后的配置树——已交付的基础配置、界面覆盖层,以及 `--config` 或个人覆盖层,恰好是该界面启动时组装的那些层——以 YAML 打印到 stdout 后退出,不启动任何东西;`--dump-default-config` 止步于界面覆盖层,因此对两份输出做 diff 就能精确看出用户层改了什么。每段连续的行之前都有一条 `# ==` 注释,标明该段来自哪个文件以及被哪些层修补过(例如 `# == base.cordis.yml, patched by tui.cordis.yml`),因此输出既展示来源,又仍是一份可加载的文档。合成通过 include 自己的补丁算法和 YAML 方言(`@cordisjs/plugin-include` 的 `applyEntryPatches`/`entryListSchema`)完成,因此 dump 不可能与实际启动漂移;`!!js` 表达式原样打印、不求值,目标行不存在的补丁会连同其所在层报到 stderr,与 Loader 启动时的警告一致。由启动器持有的启动上下文值(会话身份、CLI 标志补丁)是每次调用的事实,位于配置树之外,不会出现。dump 标志会拒绝仅用于启动的标志(`-p`、`--resume`、`--config-replace`)而不是静默忽略它们,`--dump-default-config` 不接受 `--config`。 - -Web 和无头界面启动 `base.cordis.yml` 与 `web.cordis.yml`,随后应用 `$DSH_HOME/config.yaml`;显式的 `--config ` 会替代该个人覆盖。除此之外,两者共享同一套组合:两者都会告知编码 agent 所用模型和会话工作目录,将调用目录视为默认项目和 Workspace 根目录,除非通过 `--workspace-root ` 覆盖,否则会在该根目录下创建具名 Workspace;它们会把适用的 `AGENTS.md`/`CLAUDE.md` 指令加载到每个 agent-loop 请求前缀中,渲染预算为 65,536 字节,选用首条消息模型标题,采用与 TUI 相同的有界暂时性模型请求重试策略,并挂载一个可丢弃的内存 SQLite 内容索引服务。Web 还会明确说明交互界面是 DeepSeek Harness Web GUI、当前 checkout 是自身源码位置,并在提示词及受管的 `$DSH_WEB_URL`/`$DSH_WEB_MODE` 中提供该进程的规范本地 URL 和模式;因此,「这个页面」等表述会指向该 GUI,但 agent 不会声称可以访问未显式提供的 DOM、路由或截图状态。在生产模式下,宿主会在下次请求时读取重新构建的前端 dist 和客户端 bundle,因此刷新现有 URL 即可更新该 GUI,无须替换其进程。`dsh web --dev` 会挂载客户端插件的 HMR 接收端,但要实现无刷新更新,还需在同一 checkout 中运行 `pnpm run dev:web`,以监视并重新构建插件 bundle;shell 和普通包(package)的更改仍需重新构建并刷新页面。直接使用裸 `apps/web` Vite 服务会在开始监听前失败,因为它无法注入 `window.__DSH_BOOT__`。索引服务在启动时处于 ACTIVE 状态,但其 `node:sqlite` 模块与数据库句柄分别要到首次内容搜索才会导入和打开。这样可使 Node 22 在尚未使用搜索时的启动输出不出现 SQLite 实验性警告;首次实际搜索仍可能发出运行时警告。每个服务实例独占自己的数据库,因此并行调用既不会共享不受支持的 SQLite 状态,也不会留下派生索引文件,首次搜索还会惰性对账实时日志与持久化日志。无头界面唯一的差异是监听操作系统分配的端口(并行 `dsh -p` 运行绝不冲突;stderr 打印的 URL 会在浏览器中打开实时会话)。两者都需要先构建前端 dist 和客户端 bundle(`pnpm run build && pnpm run build:web`)。 - -共享组合把新建 TUI、Web 和无头会话的权限默认设为 `workspace-write` preset(`workspace-write` 文件模式加 `ask` 审批策略)。由沙箱强制约束的 bash 与文件系统修改只能写入会话工作区和平台临时根目录;读取、网络访问和进程可见性不受该策略约束。浏览器可以应答一次性审批请求,并提供 Access 选择器;TUI 提供 `/permission`,但没有审批请求应答者,因此自动请求更宽权限的重试会以拒绝方式关闭,直到用户主动更改会话 preset。`DSH_PERMISSION_MODE` 会更改进程回退值,而「通用」设置中已存储的「权限」值只适用于之后的会话,不会更改已打开的会话。 - -三个界面都会使用 `$DSH_HOME/config.yaml`;TUI 和 Web 实时应用有效编辑,而一次性无头运行只在启动时读取。已交付的配置树包含一个空的 `repository-plugins` 配置项,因此独立用户无需 SDK 项目或安装命令,只需配置即可添加已准备的 GitHub 插件: - -```yaml -- id: repository-plugins - name: '@deepseek-ai/dsh-repository-plugin' - config: - repositories: - - 'github:PolyArch/humanize#' -``` - -仓库必须包含已准备的 `.dsh-plugin` 包;[仓库插件契约](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)说明创作方式、嵌套插件路径、不可变缓存、信任边界和失败语义。实时编辑失败时,最后一个可用树保持运行,并发出 Cordis 的 HMR 事件 `hmr/config-update-failed`。 - -已交付的 TUI 和 Web 组合会注册原生 DeepSeek 适配器,以及 pi-ai 的 OpenAI 和 Anthropic 提供方配置。凭据和端点覆盖来自启动分层环境中的提供方标准变量对:`DEEPSEEK_API_KEY` / `DEEPSEEK_BASE_URL`、`OPENAI_API_KEY` / `OPENAI_BASE_URL` 和 `ANTHROPIC_API_KEY` / `ANTHROPIC_BASE_URL`。 - -每个界面也都会注册 `web_search`,且只注册 `web_search`。搜索使用 DeepSeek 的 Anthropic 兼容 Messages 端点,每次调用都会解析同一个 `DEEPSEEK_API_KEY` 凭据引用,并接受独立的 `DEEPSEEK_SEARCH_BASE_URL` 端点覆盖;每次搜索都是一次辅助模型请求,会产生独立的延迟与 token 成本。`web_fetch` 仍处于禁用状态,组合也未挂载默认抓取提供方;需要任意页面抓取能力的部署必须通过覆盖层选择启用。部署决策及其安全边界见[默认 Web 搜索 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/feature/2026-07-31-web-default-search.md)。 - -`DSH_TOOLS_MODE` 为整个 Web/无头进程选择工具呈现模式:`native`(未设置时的 schema 默认值)、`code`(仅含 `run_code` 的 Code Mode 线路)或 `both`;任何其他值都会经由 `dsh-tools` 配置 schema 在启动时明确报错。由于 Loader 组合是静态的,该设置作用于整个进程。TUI 界面会忽略该变量并固定为 `native`。 - -[`core-web.cordis.yml`](../config/core-web.cordis.yml) 是一个可选启用的 `dsh web --config` 覆盖层:它保留已交付的 Web 宿主、浏览器、Workspace、持久化与权限组合,同时将默认的原生模型界面精简为以所有者为作用域的持久 `bash` 以及 `str_replace_editor`。PTY 后端和编辑器分别消费现有的 Web 沙箱与文件系统提供方。持久 shell 处于打开状态时,会阻止所属会话更改权限模式;因此,在较宽权限下创建的 shell 无法在降权后继续存活。`DSH_TOOLS_MODE` 仍控制由此得到的双工具注册表采用原生/Code Mode 呈现。 - -在源码 checkout 中,用以下命令启动这个精简 Web profile: +原始 `dsh` 必须提供显式 patch 列表配置: ```sh -pnpm run dsh web --config apps/cli/config/core-web.cordis.yml +dsh --config ./app.cordis.yml ``` -每个 `dsh` 界面——TUI、Web 与无头——都默认上报会话遥测(该行位于共享的 `base.cordis.yml`):每条会话日志事件以 OTLP/HTTP 日志记录的形式、按 10 秒批处理节奏流向 `https://harness-telemetry.deepseeksvc.com/v1/logs`。`DSH_TELEMETRY_OTLP_URL` 可将 exporter 指向其他 collector;将 `DSH_TELEMETRY_DISABLED` 设为**任意非空值**——包括 `0` 或 `false`——都会在该行加载前将其关停(隐私开关取「宁可误关、不可误开」)。该组合当前未挂载任何脱敏规则:导出记录即原始捕获副本,包含消息正文、工具参数与结果、以及会话工作目录路径。部署口径见 [web-telemetry-default-mount Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)。 +指定文件通过 Include 插件的 patch 算法直接应用到 [`config/base.cordis.yml`](../config/base.cordis.yml) 之上。它不是完整替代树,也不会添加个人 `$DSH_HOME/config.yaml` 或其他 surface overlay。基础配置刻意不包含启动 agent(智能体)或交互前端入口;必填 overlay 负责选择这些部署细节。相对配置路径从调用目录解析。配置解析、schema 校验、模块解析或插件启动失败会得到报告并以非零状态退出。收到 SIGINT 或 SIGTERM 时,挂载的根节点会先 dispose(资源释放)再退出。 -MCP 服务器不是交付默认值,因为默认值必须点名一台服务器:`@deepseek-ai/dsh-mcp-client` 每一行只挂载一台服务器,并把它作为子进程 spawn;该进程不经 `ctx.bash`,因此也不受沙箱策略约束。该包是本 CLI 的运行时依赖,所以已安装的 `dsh` 无需源码检出即可从 `$DSH_HOME/config.yaml` 或 `--config` 覆盖层挂载你自己的服务器: +patch 通过 `id` 定位基础配置行,并替换该行完整的 `config` 值,而不是深度合并各键。patch 列表也可插入新行,只要随附 Loader 能解析其插件模块: ```yaml -- insert: - - id: mcp-github - name: '@deepseek-ai/dsh-mcp-client' - config: - serverName: github - transport: stdio - command: npx - args: ['-y', '@modelcontextprotocol/server-github'] - env: - GITHUB_TOKEN: !!js process.env.GITHUB_TOKEN +- id: agent-loop + config: + agents: + - id: main + provider: deepseek-official + model: deepseek-v4-flash ``` -模型随后会看到 `mcp__github__*`。Streamable HTTP 传输与完整字段表见 [mcp-client README](../../../packages/mcp/mcp-client/README.md)。 +可在不启动的情况下检查生效的配置树: -## 安装(开发机) +```sh +dsh --dump-default-config +dsh --config ./app.cordis.yml --dump-config +``` -将从源码运行的启动器符号链接到 PATH 上;它通过自身真实路径解析 checkout,因此代码更改会在下次启动时生效,无需构建: +`--dump-default-config` 只打印随附基础配置。`--dump-config` 必须与 `--config` 同时使用,并打印基础配置和带来源注释的 overlay。组合使用 `@cordisjs/plugin-include` 的 `applyEntryPatches` 与 `entryListSchema`;`!!js` 表达式保持未求值,找不到目标的 patch 会报告到 stderr。 + +## Web 与无头模式 + +`dsh web` 启动 `base.cordis.yml` 加 [`config/web.cordis.yml`](../config/web.cordis.yml),并在 `$DSH_HOME/config.yaml` 存在时继续加载它。`dsh web --config ` 用显式 patch 列表替代该个人层。`--host`、`--port`、`--workspace-root` 和可重复的 `--trusted-host` 值会成为 Web 宿主 patch;负责这些值的插件 schema 会在启动时验证它们。`--dev` 挂载客户端插件 HMR(热模块替换)接收器;若要无刷新更新客户端 bundle,还需单独运行 `pnpm run dev:web` watcher。 + +```sh +dsh web +dsh web --config ./web-profile.cordis.yml +dsh web --dump-default-config +dsh web --dump-config +``` + +生产 Web 运行器需要已构建的包和前端产物(`pnpm run build`)。默认服务地址是 `http://127.0.0.1:3080`。绑定所有接口时,还会信任机器自动发现的 LAN IP 字面量;`--trusted-host` 可添加 `/api` 浏览器信任围栏接受的具名 authority。 + +`dsh -p "task"` 使用同一基础配置和 Web 组合,并加载启动时的个人配置;它在 OS 分配的端口上启动 Web 宿主,运行一个新的持久化会话,打印最终答案并退出。它不接受 `--config` 或原始配置 dump flag。 + +Web 和无头进程关闭时会给插件树最多 5 秒完成 dispose。第一次 `SIGINT`/`SIGTERM` 启动该优雅排空;第二次信号强制立即退出。如果无头模式正常结束时已经卡在 dispose 中,第一次 `Ctrl+C` 就会升格并立即退出,而不会被吞掉。 + +两种模式都将调用目录作为默认 workspace 根目录,以 65,536 字节渲染预算加载适用的 `AGENTS.md` 或 `CLAUDE.md` 指令,并使用内存 SQLite 会话内容索引。Web 监视有效的个人配置编辑;无头模式只在启动时读取该文件。[app-boot 个人配置契约](../../../packages/ui/app-boot/README.md#personal-config)负责配置层优先级、凭据存储、实时更新失败行为和 `$DSH_HOME` 解析。 + +新会话默认使用 `workspace-write` 权限预设。Bash 和文件系统修改仅限于会话 workspace 与平台临时根目录;读取、网络访问和进程可见性不受限制。`DSH_PERMISSION_MODE` 更改进程后备值。General settings 中存储的权限影响后续 Web 会话,不改变已打开的会话。 + +`DSH_TOOLS_MODE` 为 Web/无头进程选择 `native`、`code` 或 `both`;其他值会导致启动失败。[`config/core-web.cordis.yml`](../config/core-web.cordis.yml) 是可选 Web overlay:它在保留随附宿主、浏览器、workspace、持久化和权限组合的同时,把原生模型 surface 缩减为持久 `bash` 和 `str_replace_editor`。 + +## 共享部署行为 + +基础配置挂载原生 DeepSeek 适配器、settings 与凭据提供方、稳定的 `web_search`、repository Plugin 支持和会话遥测。提供方凭据存放在 `$DSH_HOME/.env` 或环境中;启动器从不把凭据文件提升到 `process.env`,因此凭据可以轮换。搜索使用 `DEEPSEEK_API_KEY` 并接受 `DEEPSEEK_SEARCH_BASE_URL`;只有 overlay 插入提供方并启用 `web_fetch` 后,该工具才可用。 + +会话事件默认作为 OTLP/HTTP 日志流式发送。`DSH_TELEMETRY_OTLP_URL` 选择其他 collector。任何非空 `DSH_TELEMETRY_DISABLED` 都会在启动前禁用遥测配置行。随附基础配置没有遥测脱敏规则,因此导出的记录可能包含消息文本、工具参数与结果以及 workspace 路径;该部署决策由[遥测 Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.md)负责。 + +空 `repository-plugins` 行让 Web/无头个人配置和原始 overlay 能够挂载已准备的不可变 repository Plugin generation。参见 [repository Plugin 契约](../../../packages/cordis/repository-plugin/README.md#standalone-app-configuration)。CLI 还随附 `@deepseek-ai/dsh-mcp-client` 作为 overlay 的依赖,但默认不启用 MCP 服务器,因为每条服务器命令都是 agent 沙箱之外的受信任可执行代码。 + +## 源码启动器 + +把源码运行启动器链接到 PATH: ```sh ln -sf "$(pwd)/bin/dsh" ~/.local/bin/dsh ``` -源码启动会通过 tsx 的 ESM-only hook(`node --import tsx/esm`)运行 `apps/cli/src/bin.ts`,由它转换 TypeScript 并将根 tsconfig 的 `paths` 映射投射到模块解析中。不使用 Node 原生 TypeScript 模式:Node 26 移除了 `--experimental-transform-types`,而 strip-only 模式无法接受源码图依赖的语法(vendor 中的参数属性、装饰器、运行时 enum/namespace)。CJS hook 保持关闭,因为源码图是纯 ESM,而 CJS 解析器会增加约 0.4s 启动耗时。`bin/dsh` 将 `TSX_TSCONFIG_PATH` 固定到 checkout 的根 tsconfig,使解析与 cwd 无关;node-compat 门禁 `dsh-source-launch-smoke` 会在每条受支持的 Node 版本线上运行这一精确启动向量。tsx 应用 `paths` 映射时不检查依赖声明,声明完整性由静态门禁保障:TUI 配置通过 `examples/package.json` 解析裸插件,Web/无头 `cordis.yml` 通过本包的 `dependencies` 解析;`verify-cordis-config` 要求每个已配置的裸插件均已声明,同时允许存在无关依赖。 - -`pnpm run dsh` 从仓库根目录运行同一入口并直接转发参数,例如 `pnpm run dsh -p "task"`。构建形式(`lib/bin.js`,通过 `pnpm run build`)会在普通 Node 下启动同一配置。 +它通过 real path 解析 checkout,并使用 `node --import tsx/esm` 启动 `apps/cli/src/bin.ts`。`TSX_TSCONFIG_PATH` 固定到 checkout 根目录,因此 workspace 包解析不依赖调用目录。`pnpm run dsh` 使用同一入口并转发参数。运行 `pnpm run build` 后,构建形式为 `apps/cli/lib/bin.js`。 diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index ac6c92d5f0..a4498e0cf2 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -56,7 +56,7 @@ import type {} from '@deepseek-ai/dsh-agent' import { prepareWebRuntimeContext } from '../../cli/src/web.ts' import { DIST_INDEX, REPO_ROOT, requireDist } from './support.ts' -/** Snapshot mode for the lane, from $DSH_SNAPSHOT (same vocabulary as the ACP/TUI suites). */ +/** Snapshot mode for the lane, from $DSH_SNAPSHOT (same vocabulary as the other snapshot suites). */ export type WebSnapshotMode = 'replay' | 'record' | 'refresh' /** @@ -592,9 +592,9 @@ export async function compareOrRefreshGolden(goldenPath: string, actual: string, } /** - * Fixture-inventory guard (the TUI afterAll shape): the scenario directory - * holds exactly the expected files and every committed JSONL is a scrub - * fixed-point without a run-local browser RPC id. + * Fixture-inventory guard: the scenario directory holds exactly the expected + * files and every committed JSONL is a scrub fixed-point without a run-local + * browser RPC id. * @param dir - the scenario snapshot directory. * @param expected - the exact expected file inventory. */ diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index b5136c31df..720c38de26 100644 --- a/docs/architecture.i18n.yaml +++ b/docs/architecture.i18n.yaml @@ -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/architecture.md -architecture.md: ee50e249f8e588a3f92f57db929c6bbfb1c853dd -architecture.zh.md: c2e596cd10c21be2bcaad11323277d04ef9e74a1 +architecture.md: 296aea2056294d08693845e1780a9ac268b21bfc +architecture.zh.md: c5f87c967d95c8934e919c4c01bc4acd4d3a1a70 diff --git a/docs/architecture.md b/docs/architecture.md index c4665bce6e..296aea2056 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -186,4 +186,4 @@ New behavior attaches to a documented extension point; a loop change updates thi | Fork a live session | call `ctx.sessions.fork(source, boundary?, childSessionId?)` | | Scope a registration to one agent | use its `agent.ctx` (see Agent Scope) | -The [extension cookbook](cookbook/extension-cookbook.md) has plugin skeletons and the feature-to-seam map; guides cover [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [vendored packages](cookbook/adding-a-vendored-package.md). +The [extension cookbook](cookbook/extension-cookbook.md) has plugin skeletons; guides cover [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [vendored packages](cookbook/adding-a-vendored-package.md). diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 41a5a9de7c..c5f87c967d 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -186,4 +186,4 @@ idle inject: | fork 活跃会话 | 调用 `ctx.sessions.fork(source, boundary?, childSessionId?)` | | 将注册项限定到单个 agent | 使用其 `agent.ctx`(参见 Agent 作用域) | -[扩展实操手册(cookbook)](cookbook/extension-cookbook.md)提供插件骨架和功能到服务边界的映射;指南涵盖[包](cookbook/adding-a-package.md)、[工具](cookbook/adding-a-tool.md)、[LLM 适配器](cookbook/adding-an-llm-adapter.md)和 [vendored 包](cookbook/adding-a-vendored-package.md)。 +[扩展实操手册(cookbook)](cookbook/extension-cookbook.md)提供插件骨架;指南涵盖[包](cookbook/adding-a-package.md)、[工具](cookbook/adding-a-tool.md)、[LLM 适配器](cookbook/adding-an-llm-adapter.md)和 [vendored 包](cookbook/adding-a-vendored-package.md)。 diff --git a/docs/cookbook/adding-a-tool.i18n.yaml b/docs/cookbook/adding-a-tool.i18n.yaml index e317b64965..ca64363bd6 100644 --- a/docs/cookbook/adding-a-tool.i18n.yaml +++ b/docs/cookbook/adding-a-tool.i18n.yaml @@ -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-tool.md -adding-a-tool.md: 0688e4a46b1eb2ca282a7ee970546a34b3c1cc68 -adding-a-tool.zh.md: e12d5e23a93d08f15fb0d1ac40229c5c4ab26e38 +adding-a-tool.md: cb418a9118901cc6572fb17125351bdda922434b +adding-a-tool.zh.md: d8b73a51d1dde8647e7a032648f8a6344fcc83eb diff --git a/docs/cookbook/adding-a-tool.md b/docs/cookbook/adding-a-tool.md index 0688e4a46b..cb418a9118 100644 --- a/docs/cookbook/adding-a-tool.md +++ b/docs/cookbook/adding-a-tool.md @@ -1,8 +1,8 @@ -# Cookbook: adding a tool +# Tool authoring reference English | [中文](adding-a-tool.zh.md) -How to give the model a new capability. The minimal shape below shows the contract; `packages/bash/tool-bash` is the production-grade three-package seam. +Reference for the contracts a model-facing tool must satisfy. For an ordered first tool, follow [Build a tool](../user/develop/basic/tool.md). `packages/bash/tool-bash` is the production-grade three-package example. ## The minimal shape @@ -35,7 +35,7 @@ export function apply(ctx: Context) { } ``` -Registration is effect-based: disposing the plugin fiber unregisters the tool (write the HMR test). Schemas flow into the system-prompt assembly automatically. +Registration is effect-based: disposing the plugin fiber unregisters the tool. Schemas flow into the system-prompt assembly automatically. ## Rules of the execute() contract @@ -89,6 +89,6 @@ Hard rules (they bite if broken): The neutral vocabulary lives in `dsh-tools`; tools never import a UI or transport type. Host/client runtimes map each `card` into their own view. The design and the why are in [the render-intent-union Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md); `dsh-tool-fs` (generic/diff) and `dsh-tool-bash` (terminal) are the reference implementations. -## Tests every tool needs +## Verification -Cover argument rejection, every canonical value and Native rendering shape, output-schema rejection, and HMR disposal. For a side-effecting tool, drive the real tool through the agent loop with a scripted `MockAdapter` and assert its `tool/call` and projected `tool/result` session events; prove the canonical value itself is not persisted. For a UI card, assert the exact `presentCall` and `presentResult` views and exercise the owning host/client projection. Add an assembled snapshot for the shipped model or UI behavior the tool changes. +Follow the [repository testing policy](../testing.md) and the owning package's test documentation. A shipped model- or UI-visible change requires the assembled coverage specified there. diff --git a/docs/cookbook/adding-a-tool.zh.md b/docs/cookbook/adding-a-tool.zh.md index e12d5e23a9..d8b73a51d1 100644 --- a/docs/cookbook/adding-a-tool.zh.md +++ b/docs/cookbook/adding-a-tool.zh.md @@ -1,8 +1,8 @@ -# 实操手册:添加工具 +# 工具编写参考 [English](adding-a-tool.md) | 中文 -如何为模型赋予一项新能力。下文的最小形态展示这项契约;`packages/bash/tool-bash` 是生产级、由三个包(package)构成的 seam。 +面向模型的工具必须满足哪些契约,均以本文为准。如需按步骤构建第一个工具,请阅读[构建工具](../user/develop/basic/tool.md)。`packages/bash/tool-bash` 是生产级的三包示例。 ## 最小形态 @@ -35,7 +35,7 @@ export function apply(ctx: Context) { } ``` -注册基于副作用:dispose(资源释放)插件 fiber 即注销该工具(请编写 HMR(热模块替换)测试)。schema 会自动流入系统提示词的组装过程。 +注册基于副作用:dispose(资源释放)插件 fiber 即注销该工具。schema 会自动流入系统提示词的组装过程。 ## execute() 契约的规则 @@ -89,6 +89,6 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的 中性词汇定义在 `dsh-tools` 中;工具绝不导入 UI 或传输类型。host/client 运行时将每个 `card` 映射到各自的视图。设计与原因见[渲染意图联合体 Agent Note](../../.agents/notes/implemented/architecture/2026-07-02-tool-render-intent-union.md);`dsh-tool-fs`(generic/diff)和 `dsh-tool-bash`(terminal)是参考实现。 -## 每个工具必须的测试 +## 验证 -覆盖参数拒绝、每种规范值和 Native 渲染形态、输出 schema 拒绝以及 HMR dispose。对于有副作用的工具,使用脚本化的 `MockAdapter` 驱动真实工具通过 agent loop(智能体循环),并断言其 `tool/call` 和投影后的 `tool/result` 会话事件;同时证明规范值本身未被持久化。对于 UI 卡片,断言 `presentCall` 和 `presentResult` 的精确视图,并实际运行所属 host/client 投影。如果工具改变了已交付的模型或 UI 行为,请添加组装应用快照。 +遵循[仓库测试策略](../testing.md)和所属包的测试文档。已交付且面向模型或 UI 的变更必须提供其中规定的组装覆盖。 diff --git a/docs/cookbook/extension-cookbook.i18n.yaml b/docs/cookbook/extension-cookbook.i18n.yaml index a4c20672c0..632e840682 100644 --- a/docs/cookbook/extension-cookbook.i18n.yaml +++ b/docs/cookbook/extension-cookbook.i18n.yaml @@ -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/extension-cookbook.md -extension-cookbook.md: 07073c39f8a9b998b09b0815257d995b174c8be7 -extension-cookbook.zh.md: 10664af9a39f0a1663c316869ba8ef02f67c29fe +extension-cookbook.md: 66cdee676936029e4db03bf0c1a0436d58f29bcd +extension-cookbook.zh.md: 7e29d34b5777a4a4b19c8d3da2fc078df519c6d4 diff --git a/docs/cookbook/extension-cookbook.md b/docs/cookbook/extension-cookbook.md index 07073c39f8..66cdee6769 100644 --- a/docs/cookbook/extension-cookbook.md +++ b/docs/cookbook/extension-cookbook.md @@ -2,9 +2,7 @@ English | [中文](extension-cookbook.zh.md) -> FIXME: This important guide has not received sufficient human design review; complete that review before the first release. - -The three plugin shapes you write against the harness extension surface, as illustrative snippets (elided imports and helper stubs — not copy-paste-complete). For the full step-by-step guides see [adding a package](adding-a-package.md), [adding a tool](adding-a-tool.md), and [adding an LLM adapter](adding-an-llm-adapter.md); for the seams these hook into see [docs/architecture.md](../architecture.md). +Reference shapes for the harness extension surface. The snippets omit imports and helper implementations and are not copy-paste-complete. For concrete authoring paths, see the [package checklist](adding-a-package.md), [first-tool tutorial](../user/develop/basic/tool.md), [tool reference](adding-a-tool.md), and [LLM adapter guide](adding-an-llm-adapter.md); the [architecture](../architecture.md) owns the system and extension-seam map. ## A tool plugin @@ -92,38 +90,3 @@ export function apply(ctx: Context) { ## Runnable wirings Runnable leaves load their plugin trees from `examples/*/cordis.yml`; the root `demo:*` scripts and those leaf directories are the authoritative inventory. Non-interactive leaves use [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo), ACP leaves use [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo), JSON-RPC leaves use [`@deepseek-ai/dsh-jsonrpc-demo`](../../packages/examples/jsonrpc-demo), and the app packages share [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo). - -## The feature → mechanism map - -Every product feature maps to a listener on a documented extension seam — the microkernel claim made checkable ([microkernel Agent Note](../../.agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.md)). No row modifies the loop. - -`system-prompt/assemble` is an expert cooperative whole-assembly transform: its returned assembly is authoritative, so listener authors own preserving active Code Mode and structured-output protocol contributions. Prefer `ctx.tools.restrict()` for tool filtering that must stay aligned across presentation, lookup, and execution. - -| Product feature | Plugin mechanism | -|---|---| -| Hook system (user + project level) | listeners on `agent/session-start`, `agent/prompt-submit`, `agent/request`, `tools/pre-execute`, `tools/post-execute`, and `agent/turn-stopping`; the waterfall seams return typed decisions, while `agent/turn-stopping` may steer another step; the `dsh-hooks-claude` / `dsh-hooks-codex` bridges map hook config files onto these seams | -| `/goal` | `ctx.goals` owns durable state, `dsh-goal-session` schedules same-session rounds through the public `Agent`, and separate command/tool producers expose human/model control | -| `/loop` | on the `turn/end` session event, `followup()` the next iteration; or force-continue | -| Dynamic workflow | `ctx.workflows` + the worker-thread engine + the `workflow` tool; structured in-process children enforce output with scoped prompt/tool registrations, a monotonic tool guard, final `tools/result` commit (including enclosing `run_code`), and the structured-output execution's monotonic `concludeTurn()` marker | -| Queued + steering messages | core `Agent.followup()` / `Agent.steer()` | -| Context compaction (auto + manual) | the `ctx.compact` seam + `dsh-compact-basic`; automatic pressure runs on serial `agent/step`, canonical overflow recovery runs on `agent/request-error`, and manual callers use the same compact service ([compaction Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md) — the model-facing `/compact` consumer tool is deferred) | -| System prompt configurability | `ctx.systemPrompt.section()` with ordering and scope-local shadowing | -| AGENTS.md (root) | a section provider reading the file | -| AGENTS.md (subdir, on-touch) + file-change notices | `agent.inject()` from a watcher / tool-result listener | -| Built-in tools | `ctx.tools.register()`; schemas flow into the assembly automatically — the `dsh-tool-*` families (bash, fs, web, subagent, todo) are the shipped examples | -| ToolSearch / progressive disclosure | replace a scoped `ctx.tools.restrict()` registration as the visible set changes; the registry keeps presentation, lookup, and execution aligned | -| Tool deadline / retry / metrics | wrap core dispatch with `tools/execute`; a wrapper may replace `exec.signal`, delegate, and inspect the normalized result in one lexical lifetime | -| Final tool-result metrics / audit / capture | observe immutable authoritative outcomes with `tools/result`; use `tools/post-execute` instead only when the plugin must transform the result or attach context | -| Monotonic terminal turn policy | call `ToolExecution.concludeTurn()` from the successful terminal tool; later tool calls in the same response remain guardable, and the loop stops after the step | -| Subprocess sandbox (landlock / sandbox-exec) | use a `ctx.sandbox` backend through `dsh-bash-sandbox`; use `tools/pre-execute` for capability-level denial | -| Permission system / AskUserQuestion | return `ask` from `tools/pre-execute` and answer through `ctx.approval`; register a separate model-facing ask tool for ordinary user questions | -| Plan mode | Shipped: [`@deepseek-ai/dsh-plan-mode`](../../packages/plan/plan-mode/README.md) — logged `plan/mode` state, the `plan:policy` guidance section, `/plan [message]` entry, `/plan off` direct exit, and the user-reviewed `exit_plan_mode` exit; enforcement stays on the independent sandbox/approval axes | -| Sub-agent delegation | the `ctx.subagents` provider registry (`dsh-subagent-spawn`/`-fork`/`-acp`) + `dsh-tool-subagent` exposing one configured provider to the model | -| MCP | one plugin per server: discover tools → `ctx.tools.register()` | -| Skills | section + tool registration; `inject()` skill content on invocation | -| Memory | section provider + tool | -| Scheduled tasks (cron) | a plugin registers model-callable scheduling tools; timer fires → `followup(…, {source: {kind: 'cron', …}})` when idle / `inject()` notification when busy | -| UI (GUI; CLI emits JSONL) | listen `session/event` (assistant chunks, boundaries, tool activity); input → `followup()` | -| Telemetry / replayable trace | `session/event` → JSONL; replay = `sessions.create(id, { seed })` | -| Model adapters | `LlmAdapter` subclass via `registerAdapter` (`dsh-llm-deepseek`, `dsh-llm-pi-ai`) | -| Plugin hot-reload | every registration is a `ctx.effect` → vendored HMR just works | diff --git a/docs/cookbook/extension-cookbook.zh.md b/docs/cookbook/extension-cookbook.zh.md index 10664af9a3..7e29d34b57 100644 --- a/docs/cookbook/extension-cookbook.zh.md +++ b/docs/cookbook/extension-cookbook.zh.md @@ -2,9 +2,7 @@ [English](extension-cookbook.md) | 中文 -> FIXME:这篇重要指南尚未经过充分的人工设计审查;请在首次发布前完成审查。 - -针对 harness 扩展表面编写的三种插件形态,以示意性代码片段呈现(省略了 import 和辅助桩——不可直接复制运行)。完整的分步指南见[添加包(package)](adding-a-package.md)、[添加工具](adding-a-tool.md)和[添加 LLM(大语言模型)适配器](adding-an-llm-adapter.md);这些插件所挂接的 seam 见 [docs/architecture.md](../architecture.md)。 +harness 扩展表面的参考形态。代码片段省略了 import 和辅助实现,无法直接复制运行。具体编写路径见[包检查清单](adding-a-package.md)、[第一个工具教程](../user/develop/basic/tool.md)、[工具参考](adding-a-tool.md)和 [LLM(大语言模型)适配器指南](adding-an-llm-adapter.md);系统与扩展 seam 映射由[架构文档](../architecture.md)负责。 ## 工具插件 @@ -92,38 +90,3 @@ export function apply(ctx: Context) { ## 可运行的组装示例 可运行叶子从 `examples/*/cordis.yml` 加载各自的插件树;根目录的 `demo:*` 脚本和这些叶子目录是权威清单。非交互式叶子使用 [`@deepseek-ai/dsh-cli-demo`](../../packages/examples/cli-demo),ACP 叶子使用 [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo),JSON-RPC 叶子使用 [`@deepseek-ai/dsh-jsonrpc-demo`](../../packages/examples/jsonrpc-demo),应用包共享 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo)。 - -## 功能→机制映射 - -每个产品功能都映射到一个文档化扩展 seam 上的监听器——微内核声明由此可验证([微内核 Agent Note](../../.agents/notes/implemented/architecture/2026-06-11-microkernel-event-taxonomy.md))。没有任何一行修改循环本身。 - -`system-prompt/assemble` 是一个专家协作式的整体装配变换:其返回的装配结果具有权威性,因此监听器作者有责任保留活跃的 Code Mode 和结构化输出协议的贡献。对于需要在展示、查找和执行之间保持对齐的工具过滤,优先使用 `ctx.tools.restrict()`。 - -| 产品功能 | 插件机制 | -|---|---| -| 钩子系统(用户级 + 项目级) | `agent/session-start`、`agent/prompt-submit`、`agent/request`、`tools/pre-execute`、`tools/post-execute` 和 `agent/turn-stopping` 上的监听器;waterfall seam 返回类型化决策,`agent/turn-stopping` 则可通过 steering 触发下一步;`dsh-hooks-claude` / `dsh-hooks-codex` 桥接器将钩子配置文件映射到这些 seam 上 | -| `/goal` | `ctx.goals` 管理持久状态,`dsh-goal-session` 通过公共 `Agent` 调度同会话回合,独立的命令/工具生产方分别提供人类/模型控制 | -| `/loop` | 在 `turn/end` 会话事件上 `followup()` 下一次迭代;或强制继续 | -| 动态工作流 | `ctx.workflows` + worker-thread 引擎 + `workflow` 工具;结构化的进程内子任务通过作用域化的 prompt/工具注册、单调工具守卫、最终 `tools/result` 提交(包括外层 `run_code`)和结构化输出执行的单调 `concludeTurn()` 标记来强制输出 | -| 排队消息 + steering(中途引导) | 核心 `Agent.followup()` / `Agent.steer()` | -| 上下文压缩(context compaction)(自动 + 手动) | `ctx.compact` seam + `dsh-compact-basic`;自动压力检查运行在串行 `agent/step`,规范化溢出恢复运行在 `agent/request-error`,手动调用方使用同一个压缩服务([压缩 Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md)——面向模型的 `/compact` 消费方工具已推迟) | -| 系统提示词可配置性 | `ctx.systemPrompt.section()`,支持排序与作用域局部覆盖 | -| AGENTS.md(根目录) | 一个读取该文件的 section provider | -| AGENTS.md(子目录,按需触发)+ 文件变更通知 | 从 watcher / tool-result 监听器调用 `agent.inject()` | -| 内置工具 | `ctx.tools.register()`;schema 自动流入装配——`dsh-tool-*` 系列(bash、fs、web、subagent、todo)是已交付的示例 | -| ToolSearch / 渐进式披露 | 当可见集变化时替换一个作用域化的 `ctx.tools.restrict()` 注册;注册表保持展示、查找和执行三者对齐 | -| 工具截止时间 / 重试 / 指标 | 用 `tools/execute` 包裹核心分发;包装器可替换 `exec.signal`、委托执行,并在同一词法生命周期内检视规范化结果 | -| 最终工具结果指标 / 审计 / 捕获 | 用 `tools/result` 观察不可变的权威结果;仅当插件需要变换结果或附加上下文时才使用 `tools/post-execute` | -| 单调终端轮次策略 | 从成功的终端工具调用 `ToolExecution.concludeTurn()`;同一响应中后续工具调用仍可由守卫阻止,循环在该步骤后停止 | -| 子进程沙箱(landlock / sandbox-exec) | 通过 `dsh-bash-sandbox` 使用 `ctx.sandbox` 后端;能力级别的拒绝使用 `tools/pre-execute` | -| 权限系统 / AskUserQuestion | 从 `tools/pre-execute` 返回 `ask` 并通过 `ctx.approval` 应答;为普通用户提问注册一个独立的面向模型的 ask 工具 | -| Plan mode | 已交付:[`@deepseek-ai/dsh-plan-mode`](../../packages/plan/plan-mode/README.md) — 落日志的 `plan/mode` 状态、`plan:policy` 引导段、`/plan [message]` 入口、`/plan off` 直接退出,以及经用户评审的 `exit_plan_mode` 出口;强制约束留在独立的沙箱/审批轴上 | -| 子 agent 委派 | `ctx.subagents` 提供方注册表(`dsh-subagent-spawn`/`-fork`/`-acp`)+ `dsh-tool-subagent` 向模型暴露一个已配置的提供方 | -| MCP | 每个服务器一个插件:发现工具 → `ctx.tools.register()` | -| Skill(技能) | section + 工具注册;调用时通过 `inject()` 注入 skill 内容 | -| 记忆 | section provider + 工具 | -| 定时任务(cron) | 插件注册面向模型的调度工具;定时器触发 → 空闲时 `followup(…, {source: {kind: 'cron', …}})`/忙碌时 `inject()` 通知 | -| UI(GUI;CLI 输出 JSONL) | 监听 `session/event`(助手分片、边界、工具活动);输入 → `followup()` | -| 遥测 / 可回放 trace | `session/event` → JSONL;回放 = `sessions.create(id, { seed })` | -| 模型适配器 | 通过 `registerAdapter` 注册 `LlmAdapter` 子类(`dsh-llm-deepseek`、`dsh-llm-pi-ai`) | -| 插件热重载 | 每个注册都是一个 `ctx.effect` → vendor 的 HMR(热模块替换)直接生效 | diff --git a/docs/core-data-structures/llm-streaming.i18n.yaml b/docs/core-data-structures/llm-streaming.i18n.yaml index 2919081f15..11d492eb23 100644 --- a/docs/core-data-structures/llm-streaming.i18n.yaml +++ b/docs/core-data-structures/llm-streaming.i18n.yaml @@ -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/core-data-structures/llm-streaming.md -llm-streaming.md: 28f775f81049d3aa0198e75fbb6c4544302e772d -llm-streaming.zh.md: db6b28acb844033025b3c5ffef075c9c3153151e +llm-streaming.md: 8f5b917ee19044eeef70aadf68cff752b8fba76a +llm-streaming.zh.md: 10e04f0856a96f86c2145cdca51edb9e28020e67 diff --git a/docs/development.i18n.yaml b/docs/development.i18n.yaml index 619aa92d76..95ed34cce0 100644 --- a/docs/development.i18n.yaml +++ b/docs/development.i18n.yaml @@ -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/development.md -development.md: 77087a95fdb4cfbeb02111ef2560d6989cde5686 -development.zh.md: 18e130e2f3aa1c53f6efa70366cdb629df71ad85 +development.md: 30a2bd0a2c97df8d3d75ec50f47b861b3a65590e +development.zh.md: 5582a85429c97c3e31517a495c69392b80885f7d diff --git a/docs/development.md b/docs/development.md index 2ebe4e0869..30a2bd0a2c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -2,16 +2,18 @@ English | [中文](development.zh.md) -This onboarding guide helps project contributors get started with the local environment, daily workflow, and CI flow; see the Agent Notes for design rationale and technical trade-offs. +The setup tutorial takes a new contributor from prerequisites to a checked checkout. The contributor reference that follows covers repository layout, daily workflow, and CI shape. Design rationale and implementation details belong to the linked Agent Notes and scripts. -## Prerequisites +## Setup tutorial + +### Prerequisites - Node.js supports 22.19+ and 24+. CI covers 22.19, 24, and 26; see the [Node engine floor Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md). - Corepack-enabled pnpm. The repo pins `pnpm@11.7.0` in `package.json`; run `corepack enable` if `pnpm --version` does not resolve through Corepack. - Git 2.26 or newer; hook setup enables Git's worktree-specific configuration extension. - Optional: a DeepSeek API key for the Web, headless, and ACP automation demos and real-API e2e tests. -## First-time setup +### First-time setup Install dependencies from the repo root: @@ -19,7 +21,7 @@ Install dependencies from the repo root: pnpm install ``` -The install also runs the root `postinstall` script, which installs lefthook from the repo dev dependency through `scripts/install-lefthook.mjs`. With `CI=true` or `GITHUB_ACTIONS=true`, the wrapper returns before Git discovery because automated jobs do not consume contributor hooks. Otherwise, it requires Git 2.26 or newer and gives the current worktree an explicit hook directory under its own Git directory; linked worktrees therefore use their own lefthook binary and configuration instead of rewriting common hooks. The first install enables Git's worktree-specific configuration extension and repository format 1; see the [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md). +The install also configures worktree-local lefthook hooks through `scripts/install-lefthook.mjs`. The [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) owns the safety and migration contract. If hooks are missing because dependencies were restored from cache or `postinstall` was skipped, install them manually: @@ -27,11 +29,7 @@ If hooks are missing because dependencies were restored from cache or `postinsta node scripts/install-lefthook.mjs ``` -The wrapper refuses user-owned `core.hooksPath` values. An inherited system, global, or common-repository path requires `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`. When Git seeds a new worktree with another registered worktree's marker-backed hook path, the wrapper replaces that copied value with the new worktree's own path; command-scoped and other worktree-scoped paths must be integrated or removed explicitly. - -Before enabling worktree config, migrate direct `extensions.*` in a format-0 common config, direct `core.worktree` or `core.bare=true`, and any non-empty dormant `config.worktree`. The common config and every worktree config must be regular files, while the owned hook directory may contain only unaliased regular files. - -After moving a checkout, rerun the wrapper to relocate its owned path and regenerate hooks. For a stale or invalid installer lock, first confirm no installer is running, then remove the reported lock and retry. If installation and hook-path rollback both fail, inspect the reported worktree config before retrying. The [worktree-local hooks Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) owns the full safety contract. +If the wrapper rejects existing Git configuration or reports a stale lock, follow its diagnostic and the linked Agent Note rather than editing worktree metadata speculatively. After moving a checkout, rerun the wrapper to regenerate the owned path. Run typecheck once after a fresh clone: @@ -39,9 +37,13 @@ Run typecheck once after a fresh clone: pnpm run typecheck ``` -That first typecheck runs the whole-repo `tsc -b` graph: it emits every package/vendor `lib/types` and checks examples, tests, and scripts through the two no-emit aggregates described below. +Setup is complete when `pnpm run typecheck` exits successfully. -## TypeScript project layout +## Contributor reference + +### TypeScript project layout + +The repository typecheck runs the whole-repo `tsc -b` graph: it emits every package/vendor `lib/types` and checks examples, tests, and scripts through two no-emit aggregates. The repository's TypeScript configuration has exactly three roles; every tsconfig file plays one of them. @@ -68,7 +70,7 @@ pnpm run build `pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files, and `verify-node-next-types`, which validates built declarations against a temporary NodeNext consumer. A fresh worktree has no bundled JS or declarations until `pnpm run build` runs; ordinary commits and pushes do not require that build unless their selected checks consume it. -## Environment variables +### Environment variables The real DeepSeek adapter and key-backed agent demos read credentials from the environment or from a gitignored `.env` at the repo root: @@ -79,7 +81,7 @@ DEEPSEEK_BASE_URL=https://... # optional `DEEPSEEK_BASE_URL` is optional and defaults to the public API. Never commit real credentials. The real-API e2e suites self-skip when `DEEPSEEK_API_KEY` is not set. -## Git hooks +### Git hooks lefthook is configured in `lefthook.yml` as a fast local checkpoint: @@ -92,15 +94,15 @@ The hooks intentionally do not run tests, snapshots, documentation checks, build Contributors can opt into the comprehensive local gate set with `pnpm run check:all`. The command is independent of both Git hooks and is not an agent instruction. -## CI gates +### CI gates The keyless [CI workflow](../.github/workflows/ci.yml) groups independent gates into broad lanes and runs a smaller compatibility signal across supported Node versions. Artifact consumers wait for one build within their lane. The separate real-API workflow runs `pnpm run test:e2e` with its configured worker bound. See [scripts/run-gates.ts](../scripts/run-gates.ts) and the workflow files for the current gate and job inventory. -## Daily commands +### Daily commands The root [contributor instructions](../AGENTS.md#commands) summarize common commands, while [`package.json`](../package.json) and [scripts/run-gates.ts](../scripts/run-gates.ts) own the current script and gate inventories. Select the smallest checks that cover the changed surface. Documentation changes use `pnpm run doc-sync`; package-public behavior changes also update the owning README or JSDoc, and built-artifact checks require `pnpm run build` first. -## Demos +### Demos The one-shot Headless coding agent needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`: @@ -120,7 +122,7 @@ The ACP automation server exposes fresh agent sessions over JSON-RPC stdio and a pnpm run demo:acp ``` -## TODO markers +### TODO markers Use one of three comment tags to flag known issues in the code, ordered by urgency: @@ -130,7 +132,7 @@ Use one of three comment tags to flag known issues in the code, ordered by urgen Pick the tag that matches the urgency so anyone scanning the code can tell a release blocker from a someday-maybe. -## Documenting types verbatim (`ts type-equiv`) +### Documenting types verbatim (`ts type-equiv`) The [core data structures](core-data-structures/core.md) docs paste source-equivalent declarations together with their original JSDoc so a reader sees the exact shape and source contract. To keep a paste from drifting when source changes, fence it as ` ```ts type-equiv ` (instead of ` ```ts `) and register it in `scripts/type-equiv.manifest.json` with the source file and symbol it mirrors: @@ -139,7 +141,3 @@ The [core data structures](core-data-structures/core.md) docs paste source-equiv ``` `pnpm run verify-type-equiv` (part of `doc-sync`) then extracts that symbol's declaration and attached JSDoc from source via the TypeScript parser and asserts the block matches both. For a class whose implementation bodies do not belong in the catalog, use ` ```ts public-api ` and set `"projection": "public-api"`; the checked projection retains the public fields, constructor, accessors, methods, and original class/member JSDoc while omitting bodies and private or protected members. Comparison ignores whitespace and non-JSDoc comments but requires every original JSDoc comment, including member documentation, so readers see the source contract beside the exact shape. The gate enforces a 1:1 correspondence by document, symbol, and projection between primary blocks and manifest entries; a paired `.zh.md` block reuses its unsuffixed sibling's entry only when the whole tracked fence sequence is byte-identical and ordered identically. `doc-typecheck` applies the same derivative rule to compilable fences, while skipping both source-equivalence fence kinds from compilation and its opt-out ratio. When you change a documented declaration or its JSDoc, the gate fails until you update the paste; when you add or remove a primary block, update the manifest in the same change. - -## Architecture context - -Read `docs/architecture.md` before changing anything under `packages/`. The codebase is built around Cordis plugins, event-sourced sessions, typed service seams, and explicit extension points. diff --git a/docs/development.zh.md b/docs/development.zh.md index 995b479ea6..5582a85429 100644 --- a/docs/development.zh.md +++ b/docs/development.zh.md @@ -2,16 +2,18 @@ [English](development.md) | 中文 -本指南覆盖参与 DeepSeek Harness 开发所需的本地环境搭建、日常工作流与 CI 流程;设计动机与技术权衡请查阅相应 Agent Note。 +搭建教程引导新贡献者从准备前置条件开始,直到检出通过检查。后面的贡献者参考介绍仓库布局、日常工作流和 CI 形态。设计依据与实现细节属于链接的 Agent Note 和脚本。 -## 前置条件 +## 搭建教程 + +### 前置条件 - Node.js 支持 22.19+ 与 24+。CI 覆盖 22.19、24 和 26;见 [Node 引擎下限 Agent Note](../.agents/notes/implemented/process/2026-07-06-node-engine-floor.md)。 - 启用了 Corepack 的 pnpm。仓库在 `package.json` 中固定使用 `pnpm@11.7.0`;如果 `pnpm --version` 无法通过 Corepack 解析,请先运行 `corepack enable`。 - Git 2.26 或更高版本;钩子设置会启用 Git 的 worktree 专属配置扩展。 - 可选:一个 DeepSeek API key,用于 Web、headless 和 ACP(Agent Client Protocol)自动化 agent(智能体)演示以及真实 API 的 e2e 测试。 -## 首次搭建 +### 首次搭建 在仓库根目录安装依赖: @@ -19,7 +21,7 @@ pnpm install ``` -安装过程同时会运行根目录的 `postinstall` 脚本,该脚本通过 `scripts/install-lefthook.mjs` 从仓库 dev 依赖安装 lefthook。当 `CI=true` 或 `GITHUB_ACTIONS=true` 时,该脚本会在探测 Git 前返回,因为自动化任务不会使用贡献者钩子。否则,包装脚本要求使用 Git 2.26 或更高版本,并会为当前 worktree 在其自身的 Git 目录下设置显式钩子目录;因此,关联 worktree 会使用各自的 lefthook 二进制文件和配置,而不会改写共用钩子。首次安装会启用 Git 的 worktree 专属配置扩展和仓库格式 1;见 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md)。 +安装过程还会通过 `scripts/install-lefthook.mjs` 配置 worktree 本地的 lefthook 钩子。其安全与迁移契约由 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) 负责。 如果依赖是从缓存恢复或 `postinstall` 被跳过而导致缺少钩子,请手动安装: @@ -27,11 +29,7 @@ pnpm install node scripts/install-lefthook.mjs ``` -包装层会拒绝用户自有的 `core.hooksPath` 值。继承自系统、全局或共用仓库配置的路径必须设置 `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`。当 Git 使用另一个已注册 worktree 中由所有权标记佐证的钩子路径初始化新 worktree 时,包装层会将这个复制值替换为新 worktree 自有的路径;命令作用域和其他 worktree 作用域的路径必须显式集成或移除。 - -启用 worktree 配置之前,请迁移格式 0 共用配置中直接设置的 `extensions.*`,并迁移直接设置的 `core.worktree` 或 `core.bare=true`,以及任何非空且尚未生效的 `config.worktree`。共用配置和每个 worktree 配置都必须是常规文件,而自有钩子目录只能包含不带别名的常规文件。 - -检出目录移动后,请重新运行包装层,使其重新定位自有路径并重新生成钩子。对于陈旧或无效的安装程序锁,请先确认没有安装程序正在运行,再移除报告的锁并重试。若安装和钩子路径回滚都失败,请在重试前检查报告的 worktree 配置。完整安全契约由 [worktree 本地钩子 Agent Note](../.agents/notes/implemented/process/2026-07-27-worktree-local-lefthook.md) 统一定义。 +如果包装脚本拒绝现有 Git 配置或报告陈旧锁,请遵循其诊断和所链接的 Agent Note,不要凭猜测编辑 worktree 元数据。移动检出目录后,请重新运行包装脚本以重新生成自有路径。 新克隆后请先运行一次类型检查: @@ -39,9 +37,13 @@ node scripts/install-lefthook.mjs pnpm run typecheck ``` -首次类型检查会执行全仓 `tsc -b tsconfig.json` 图:发射每个 package/vendor 的 `lib/types`,并通过下述两个 no-emit 聚合检查示例、测试和脚本。 +`pnpm run typecheck` 成功退出即表示搭建完成。 -## TypeScript 项目布局 +## 贡献者参考 + +### TypeScript 项目布局 + +仓库类型检查会执行全仓 `tsc -b` 图:它会发射每个 package/vendor 的 `lib/types`,并通过两个 no-emit 聚合检查示例、测试和脚本。 仓库的 TypeScript 配置只有三种角色;每个 tsconfig 文件恰好扮演其中一种。 @@ -68,7 +70,7 @@ pnpm run build `pnpm run hygiene` 包含 `publint`(用构建出的 `lib/*.js` 文件校验 package 入口点)和 `verify-node-next-types`(用一个临时的 NodeNext 消费方校验构建出的声明文件)。新 worktree 在 `pnpm run build` 运行之前没有打包的 JS 和声明文件;普通提交和推送无需构建,除非所选检查会使用这些产物。 -## 环境变量 +### 环境变量 真实的 DeepSeek 适配器和需要密钥的 agent 演示从环境变量或仓库根目录一个被 gitignore 的 `.env` 文件读取凭证: @@ -79,7 +81,7 @@ DEEPSEEK_BASE_URL=https://... # optional `DEEPSEEK_BASE_URL` 可选,默认为公开 API。请勿提交真实凭证。未设置 `DEEPSEEK_API_KEY` 时,真实 API 的 e2e 套件会自动跳过。 -## Git 钩子 +### Git 钩子 lefthook 在 `lefthook.yml` 中配置,作为快速的本地检查点: @@ -92,15 +94,15 @@ vendor manifest 守卫检查 `vendor/*/src` 下的改动是否连同对应的 `v 贡献者可以选择运行 `pnpm run check:all`,执行全面的本地门禁集。该命令独立于两个 Git 钩子,也不是对 agent 的指令。 -## CI 门禁 +### CI 门禁 keyless [CI 工作流](../.github/workflows/ci.yml) 将独立门禁分组到若干宽粒度 lane,并在受支持的 Node 版本上运行一组较小的兼容性检查。产物消费方在各自 lane 内等待一次 build。单独的真实 API 工作流按其配置的 worker 上限运行 `pnpm run test:e2e`。当前门禁和 job 清单以 [scripts/run-gates.ts](../scripts/run-gates.ts) 和工作流文件为准。 -## 日常命令 +### 日常命令 根目录的[贡献者说明](../AGENTS.md#commands)概述常用命令,[`package.json`](../package.json) 与 [scripts/run-gates.ts](../scripts/run-gates.ts) 则负责当前脚本和门禁清单。请选择覆盖变更表面的最小检查集。文档变更使用 `pnpm run doc-sync`;package 公开行为变更还需更新所属 README 或 JSDoc,而基于构建产物的检查需要先运行 `pnpm run build`。 -## 演示 +### 演示 单次运行的 Headless coding agent 需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`: @@ -120,7 +122,7 @@ ACP 自动化服务器通过 JSON-RPC stdio 提供全新 agent 会话,同样 pnpm run demo:acp ``` -## TODO 标记 +### TODO 标记 请使用以下三种注释标签之一标记代码中的已知问题,按紧急程度排序: @@ -130,7 +132,7 @@ pnpm run demo:acp 请选择与紧急程度匹配的标签,让浏览代码的人一眼分清「发布阻塞」和「有空再说」。 -## 逐字记录类型(`ts type-equiv`) +### 逐字记录类型(`ts type-equiv`) [核心数据结构](core-data-structures/core.md)文档会把与源码等价的声明及其原始 JSDoc 一并粘贴,让读者看到确切形状和源码契约。为防止粘贴内容在源码变化时漂移,请将其围栏为 ` ```ts type-equiv `(而不是 ` ```ts `),并在 `scripts/type-equiv.manifest.json` 中登记它镜像的源文件和符号: @@ -139,7 +141,3 @@ pnpm run demo:acp ``` `pnpm run verify-type-equiv`(`doc-sync` 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明及其附带的 JSDoc,并断言代码块同时匹配两者。对于不应把实现体写进目录的类,请使用 ` ```ts public-api ` 并设置 `"projection": "public-api"`;门禁检查的投影会保留公共字段、构造函数、访问器、方法以及类和成员的原始 JSDoc,同时省略实现体和私有或受保护成员。比对会忽略空白和非 JSDoc 注释,但要求保留每条原始 JSDoc(包括成员文档),让读者同时看到源码契约和确切形状。该门禁按文档、符号和投影,在主块与 manifest 条目之间强制 1:1 对应;只有当配对 `.zh.md` 块的完整受跟踪围栏序列与其无后缀兄弟文件按字节一致且顺序相同时,才会复用后者的条目。`doc-typecheck` 对可编译围栏应用同一派生规则,同时跳过两种源码等价围栏的编译,并将其排除在 opt-out 比例之外。当你改动一个已记录的类型声明或其 JSDoc 时,门禁会失败直到你更新粘贴内容;当你增删一个主块时,请在同一个变更里更新 manifest。 - -## 架构上下文 - -在修改 `packages/` 目录下的任何内容之前,请先阅读 `docs/architecture.md`。这套代码围绕 Cordis 插件、事件溯源的会话、类型化的服务 seam 与显式扩展点构建。 diff --git a/docs/testing.i18n.yaml b/docs/testing.i18n.yaml index 81929ba150..2fcbea62e4 100644 --- a/docs/testing.i18n.yaml +++ b/docs/testing.i18n.yaml @@ -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: 728c65f1911cbaa098f653af9436a92336fbe068 -testing.zh.md: 72b7c3bcfa69f4c65fc128dbcbb19c080fb0fbc4 +testing.md: 514ca4e1df7505b02350470d5de4a5ee3647634b +testing.zh.md: 6d24d8e74d726d53fa3500f57e34238483c5481c diff --git a/docs/testing.md b/docs/testing.md index 728c65f191..514ca4e1df 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -9,7 +9,7 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning - **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. 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. 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)). +- **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)). - **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. diff --git a/docs/testing.zh.md b/docs/testing.zh.md index 72b7c3bcfa..6d24d8e74d 100644 --- a/docs/testing.zh.md +++ b/docs/testing.zh.md @@ -9,7 +9,7 @@ - **单元测试**(`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% 覆盖。未覆盖的行往往是门禁正确标记出的死代码(应删除),而非需要补写的测试。行覆盖率是必要条件,但永远不是充分条件:它证明行被执行过,不证明功能按交付预期工作。`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`。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))。 +- **快照**(`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))。 - **Web 浏览器快照**(`pnpm run test:web`;必需的 Linux PR(Pull 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)会移除该命令及这些链接。 diff --git a/docs/user/develop/basic/config.i18n.yaml b/docs/user/develop/basic/config.i18n.yaml index 2287626cde..7fca045189 100644 --- a/docs/user/develop/basic/config.i18n.yaml +++ b/docs/user/develop/basic/config.i18n.yaml @@ -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/user/develop/basic/config.md -config.md: 26d2d48ebede74194fbf306aa97d214bdb99b722 -config.zh.md: 1ca727b56b661df315026902d48fbcaf507fe08b +config.md: 11a2311464789f74537cc7c4435f83ec07ca26fd +config.zh.md: 4e827ecafa6bfaf87c3e3f118425e656e1254787 diff --git a/docs/user/develop/basic/config.md b/docs/user/develop/basic/config.md index 26d2d48ebe..11a2311464 100644 --- a/docs/user/develop/basic/config.md +++ b/docs/user/develop/basic/config.md @@ -31,13 +31,15 @@ export function apply(ctx: Context, config: Config) { } ``` -Configure it in `cordis.yml`: +Add the configuration to the inserted local plugin row in `scratch-plugin/cordis.yml`: ```yaml -- name: './src/my-plugin.ts' - config: - greeting: 'Hi there' - maxRetries: 5 +- insert: + - id: hello + name: './src/my-plugin.ts' + config: + greeting: 'Hi there' + maxRetries: 5 ``` When loading the plugin, Cordis uses the exported schema to validate configuration and fill defaults. Do not export a plain object as `Config`; it does not implement the Standard Schema interface required by Cordis. @@ -91,22 +93,7 @@ The test is whether `cordis.yml` can change the value without a code edit. ### Fail loudly on invalid configuration -If configuration refers to an unregistered LLM provider route or another nonexistent resource, fail early instead of silently skipping it: - -```ts -import type { Context } from 'cordis' -import type {} from '@deepseek-ai/dsh-llm' - -export interface ModelConfig { - provider: string -} - -export function apply(ctx: Context, config: ModelConfig) { - if (!ctx.llm.listProviders().some(provider => provider.id === config.provider)) { - throw new Error(`LLM provider "${config.provider}" is not registered`) - } -} -``` +Express self-contained constraints in the schema so invalid configuration fails while the plugin loads. References to services or registered resources require dependency injection; the [services tutorial](../framework/service.md) introduces that contract. ## Work with HMR diff --git a/docs/user/develop/basic/config.zh.md b/docs/user/develop/basic/config.zh.md index 1ca727b56b..4e827ecafa 100644 --- a/docs/user/develop/basic/config.zh.md +++ b/docs/user/develop/basic/config.zh.md @@ -31,13 +31,15 @@ export function apply(ctx: Context, config: Config) { } ``` -用户在 `cordis.yml` 中这样使用: +在 `scratch-plugin/cordis.yml` 新插入的本地插件行中添加配置: ```yaml -- name: './src/my-plugin.ts' - config: - greeting: 'Hi there' - maxRetries: 5 +- insert: + - id: hello + name: './src/my-plugin.ts' + config: + greeting: 'Hi there' + maxRetries: 5 ``` 插件加载时,Cordis 会通过导出的 schema 校验配置,并填充未提供字段的默认值。不要导出普通对象作为 `Config`,因为它不满足 Cordis 要求的 Standard Schema 接口。 @@ -91,22 +93,7 @@ export interface Config { ### 配置错误要响亮 -如果配置引用了未注册的 LLM 提供方路由或其他不存在的资源,应该尽早报错,而不是静默跳过: - -```ts -import type { Context } from 'cordis' -import type {} from '@deepseek-ai/dsh-llm' - -export interface ModelConfig { - provider: string -} - -export function apply(ctx: Context, config: ModelConfig) { - if (!ctx.llm.listProviders().some(provider => provider.id === config.provider)) { - throw new Error(`LLM provider "${config.provider}" is not registered`) - } -} -``` +在 schema 中表达自身完备的约束,使无效配置在插件加载时失败。对服务或已注册资源的引用需要依赖注入;[服务教程](../framework/service.md)会介绍这项契约。 ## 配合 HMR diff --git a/docs/user/develop/basic/index.i18n.yaml b/docs/user/develop/basic/index.i18n.yaml index 1ad27114bd..4298808fc7 100644 --- a/docs/user/develop/basic/index.i18n.yaml +++ b/docs/user/develop/basic/index.i18n.yaml @@ -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/user/develop/basic/index.md -index.md: 5a9f8dfb8f2d87dfbd2ba30b4d09d002ae9b635c -index.zh.md: 6f8926aeac5dcf667e4d311505ef7b2a321659c2 +index.md: 45c8dfe495cd99da46a7b259b407af8deff570b3 +index.zh.md: 9d8ee47e6f07fb4a897f49487cb7327904573c1b diff --git a/docs/user/develop/basic/index.md b/docs/user/develop/basic/index.md index 95efe5c232..45c8dfe495 100644 --- a/docs/user/develop/basic/index.md +++ b/docs/user/develop/basic/index.md @@ -2,7 +2,15 @@ English | [中文](index.zh.md) -This guide creates a minimal Harness plugin and loads it into an agent. +This tutorial creates a minimal Harness plugin and loads it into the Web UI. Start from a repository checkout that has completed the [quick start](../../guide/quickstart.md). + +## Create a local project + +From the repository root, create a scratch project for the tutorial: + +```sh +mkdir -p scratch-plugin/src +``` ## What is a plugin? @@ -22,7 +30,7 @@ That is the complete shape. ## Create the plugin file -Create `src/my-plugin.ts` in your project: +Create `scratch-plugin/src/my-plugin.ts`: ```ts import type { Context } from 'cordis' @@ -37,14 +45,21 @@ export function apply(ctx: Context) { ## Register it in cordis.yml -Add an entry to `cordis.yml`: +Create `scratch-plugin/cordis.yml` as a Web overlay that inserts the local plugin: ```yaml -- id: hello - name: './src/my-plugin.ts' +- insert: + - id: hello + name: './src/my-plugin.ts' ``` -After startup, the console prints `[hello-plugin] plugin loaded!`. +Start the Web UI with that overlay: + +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml +``` + +Open `http://127.0.0.1:3080`. The terminal prints `[hello-plugin] plugin loaded!` during startup. ## Automatic cleanup diff --git a/docs/user/develop/basic/index.zh.md b/docs/user/develop/basic/index.zh.md index 6f8926aeac..9d8ee47e6f 100644 --- a/docs/user/develop/basic/index.zh.md +++ b/docs/user/develop/basic/index.zh.md @@ -2,7 +2,15 @@ [English](index.md) | 中文 -本文带你编写一个最小的 Harness 插件并加载到 agent(智能体)中。 +本教程会创建一个最小的 Harness 插件,并将其加载到 Web UI 中。请从已完成[快速开始](../../guide/quickstart.md)的仓库检出开始。 + +## 创建本地项目 + +在仓库根目录创建本教程使用的临时项目: + +```sh +mkdir -p scratch-plugin/src +``` ## 插件是什么 @@ -22,7 +30,7 @@ export function apply(ctx: Context) { ## 创建插件文件 -在你的项目目录下创建 `src/my-plugin.ts`: +创建 `scratch-plugin/src/my-plugin.ts`: ```ts import type { Context } from 'cordis' @@ -37,14 +45,21 @@ export function apply(ctx: Context) { ## 注册到 cordis.yml -在你的 `cordis.yml` 中添加一条: +创建 `scratch-plugin/cordis.yml`,作为插入本地插件的 Web 覆盖层: ```yaml -- id: hello - name: './src/my-plugin.ts' +- insert: + - id: hello + name: './src/my-plugin.ts' ``` -启动后你会在控制台看到 `[hello-plugin] plugin loaded!`。 +使用该覆盖层启动 Web UI: + +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml +``` + +打开 `http://127.0.0.1:3080`。启动期间,终端会打印 `[hello-plugin] plugin loaded!`。 ## 自动清理 @@ -120,35 +135,6 @@ export default class MyService extends Service { 大多数情况下,函数形式足够了。当插件需要向其他插件提供服务时,可使用类形式(见 [服务与依赖](../framework/service.md))。 -## 完整示例 - -最小的工具插件会在 `ctx.tools` 上注册其定义: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const name = 'greet-tool' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'greet', - description: 'Greet the named person.', - parameters: { - name: { type: 'string', required: true }, - }, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args) { - return `Hello, ${args.name}!` - }, - })) -} -``` - ## 下一步 - [开发一个工具](./tool.md) — 详细了解工具定义 DSL diff --git a/docs/user/develop/basic/tool.i18n.yaml b/docs/user/develop/basic/tool.i18n.yaml index f38706cead..0aa1bbb4cb 100644 --- a/docs/user/develop/basic/tool.i18n.yaml +++ b/docs/user/develop/basic/tool.i18n.yaml @@ -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/user/develop/basic/tool.md -tool.md: 0d7cbc3f0b86f88fb67aeff6aa61181dff2912ee -tool.zh.md: 37362c510a07fcc32e9eee2b578d82907788e471 +tool.md: 93a1a96feba814a564f8800c8e7b865fe9c0cb73 +tool.zh.md: 18e6b9b5d9c17b00c26aa7b98e6ac4531315dc5e diff --git a/docs/user/develop/basic/tool.md b/docs/user/develop/basic/tool.md index 0d7cbc3f0b..93a1a96feb 100644 --- a/docs/user/develop/basic/tool.md +++ b/docs/user/develop/basic/tool.md @@ -2,15 +2,17 @@ English | [中文](tool.zh.md) -A tool is a capability the model can call. This guide builds one with `defineTool`. +This tutorial adds a `greet` tool to the Web UI. Complete [Your first plugin](./) first and keep its `scratch-plugin` directory. -## Minimal example +## Create the tool plugin + +Replace `scratch-plugin/src/my-plugin.ts` with: ```ts import type { Context } from 'cordis' import { defineTool } from '@deepseek-ai/dsh-tools' -export const name = 'my-tool' +export const name = 'greet-tool' export const inject = ['tools'] export function apply(ctx: Context) { @@ -25,221 +27,26 @@ export function apply(ctx: Context) { render: (_args, value) => [{ type: 'text', text: value }], }, async execute(args) { - // args is inferred as { name: string }. return `Hello, ${args.name}!` }, })) } ``` -## Parameter definitions +`inject` makes Cordis wait for the tool registry. `defineTool` infers and validates `args` from `parameters`; `execute` returns the canonical value declared by `output.schema`, and `output.render` converts that value to model-facing content. -`parameters` uses a compact format that the framework converts to the JSON Schema sent to the model. +## Run and call the tool -### Primitive types +Restart the development command if it is not running: -```ts -export const parameters = { - path: { type: 'string', required: true }, - limit: { type: 'integer' }, - recursive: { type: 'boolean' }, - parent: { type: 'null' }, -} -// Inferred type: { path: string; limit?: number; recursive?: boolean; parent?: null } +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml ``` -### Enums - -```ts -export const parameters = { - mode: { type: 'string', required: true, enum: ['read', 'write', 'append'] }, -} -// Inferred type: { mode: 'read' | 'write' | 'append' } -``` - -### Nested objects - -```ts -export const parameters = { - options: { - type: 'object', - additionalProperties: true, - properties: { - timeout: { type: 'number' }, - retries: { type: 'number' }, - }, - }, -} -// The declared fields are inferred; additional JSON-valued keys are allowed. -``` - -### Arrays - -```ts -export const parameters = { - tags: { - type: 'array', - items: { type: 'string' }, - }, -} -// Inferred type: { tags?: string[] } -``` - -### Property fields - -| Field | Type | Meaning | -|------|------|------| -| `type` | `'string' \| 'number' \| 'integer' \| 'boolean' \| 'null' \| 'object' \| 'array' \| 'json'` | Value type; `json` accepts any lossless JSON value | -| `required` | `true` | Marks the property required and affects inference | -| `description` | `string` | Description sent to the model | -| `enum` / `const` | matching scalar values | Allowed literal values, checked at author and runtime boundaries | -| `properties` | `ParameterSchemaSpec` | Nested properties for an object | -| `additionalProperties` | `true \| false` | Required on every explicit object node | -| `items` | `ValueSchemaSpec` | Element schema for an array | -| `oneOf` | at least two `ValueSchemaSpec` branches | Requires exactly one matching branch; used instead of `type` | - -The outer `parameters` map is an implicit open object. Explicit nested objects choose their openness; raw JSON Schema registered without `defineTool` keeps JSON Schema's open-by-default behavior. - -## The execute function - -`execute` receives validated, inferred `args` and an `exec` execution context: - -```ts -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const tool = defineTool({ - name: 'example', - description: 'Return an example result.', - parameters: {}, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args, exec) { - // args: inferred from parameters - // exec: ToolExecution context - - // Return the value declared by output.schema. - void args - void exec - return 'result here' - }, -}) -``` - -### Return value - -`execute` returns the lossless JSON value declared by `output.schema`. `output.render(args, value)` separately turns that validated value into the Native/model-facing content: - -```ts ignore-check -output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - path: { type: 'string', required: true }, - content: { type: 'string', required: true }, - }, - }, - render: (_args, value) => [{ type: 'text', text: value.content }], -}, -async execute(args) { - return { path: args.path, content: await readFile(args.path, 'utf8') } -} -``` - -The canonical value is available to execution-time programmatic callers and is not persisted in `tool/result`; the rendered content and optional `presentationMeta` are the replayable projections. A body value that does not satisfy the schema, or is not lossless JSON, becomes an `INVALID_TOOL_OUTPUT` failure. - -### Argument validation - -Before calling `execute`, `defineTool` validates model-generated arguments. Invalid input raises `ToolArgsError`; the framework turns it into an `isError` result so the model can correct its call. - -Do not repeat type validation inside `execute`. - -## Presentation - -A tool can define transport-neutral presentation methods for terminal and web clients: - -```ts ignore-check -defineTool({ - name: 'bash', - // ... - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - presentCall(args) { - return { - card: 'terminal', - title: args.command, - } - }, - presentResult(args, result) { - return { - card: 'terminal', - output: result.content.map(b => b.type === 'text' ? b.text : '').join(''), - } - }, -}) -``` - -`presentCall` and `presentResult` are **pure functions**. Streaming UI and session replay may call them more than once. - -## Registration and unloading - -`ctx.tools.register()` returns a disposer, but a registration made through `ctx` is already tracked by the framework. Unloading the plugin removes the tool automatically, so the plugin does not call the disposer itself. - -```ts ignore-check -// This is sufficient: -ctx.tools.register(defineTool({ /* ... */ })) - -// No saved disposer or extra cleanup registration is needed. -``` - -## Complete example - -This tool counts files in a directory: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' -import { readdir } from 'node:fs/promises' - -export const name = 'file-counter' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'count_files', - description: 'Count files in a directory.', - parameters: { - path: { type: 'string', required: true, description: 'Directory path' }, - extension: { type: 'string', description: 'Filter by extension (e.g. ".ts")' }, - }, - output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - count: { type: 'integer', required: true }, - files: { type: 'array', required: true, items: { type: 'string' } }, - }, - }, - render: (_args, value) => [{ type: 'text', text: `Found ${value.count} files.` }], - }, - async execute(args) { - const entries = await readdir(args.path, { withFileTypes: true }) - let files = entries.filter(e => e.isFile()) - if (args.extension) { - files = files.filter(f => f.name.endsWith(args.extension!)) - } - return { count: files.length, files: files.map(file => file.name) } - }, - })) -} -``` +Open `http://127.0.0.1:3080` and ask: `Use the greet tool to greet Ada.` The model can call `greet` and receives `Hello, Ada!` as the tool result. ## Next steps -- [Plugin configuration](./config.md) — make the tool configurable -- [Capability layering](../practice/) — understand the interface/implementation/consumer pattern +- [Plugin configuration](./config.md) — make the greeting configurable. +- [Tool authoring reference](../../../cookbook/adding-a-tool.md) — look up nested schemas, canonical values, background work, policy hooks, Code Mode, and UI cards. +- [Capability layering](../practice/) — split a replaceable capability into interface, implementation, and consumer packages. diff --git a/docs/user/develop/basic/tool.zh.md b/docs/user/develop/basic/tool.zh.md index 37362c510a..18e6b9b5d9 100644 --- a/docs/user/develop/basic/tool.zh.md +++ b/docs/user/develop/basic/tool.zh.md @@ -1,16 +1,18 @@ -# 开发一个工具 +# 构建工具 [English](tool.md) | 中文 -工具是模型可以调用的能力。本文介绍如何用 `defineTool` 编写一个工具。 +本教程会在 Web UI 中添加一个 `greet` 工具。请先完成[第一个插件](./),并保留其中的 `scratch-plugin` 目录。 -## 最小示例 +## 创建工具插件 + +将 `scratch-plugin/src/my-plugin.ts` 替换为: ```ts import type { Context } from 'cordis' import { defineTool } from '@deepseek-ai/dsh-tools' -export const name = 'my-tool' +export const name = 'greet-tool' export const inject = ['tools'] export function apply(ctx: Context) { @@ -25,221 +27,26 @@ export function apply(ctx: Context) { render: (_args, value) => [{ type: 'text', text: value }], }, async execute(args) { - // args is inferred as { name: string }. return `Hello, ${args.name}!` }, })) } ``` -## 参数定义 +`inject` 让 Cordis 等待工具注册表就绪。`defineTool` 根据 `parameters` 推导并校验 `args`;`execute` 返回 `output.schema` 声明的规范值,`output.render` 再将该值转换为面向模型的内容。 -`parameters` 用一种简洁的格式描述参数,框架会自动转换为模型需要的 JSON Schema。 +## 运行并调用工具 -### 基本类型 +如果开发命令未在运行,请重新启动: -```ts -export const parameters = { - path: { type: 'string', required: true }, - limit: { type: 'integer' }, - recursive: { type: 'boolean' }, - parent: { type: 'null' }, -} -// Inferred type: { path: string; limit?: number; recursive?: boolean; parent?: null } +```sh +pnpm run dsh web --config ./scratch-plugin/cordis.yml ``` -### 枚举 - -```ts -export const parameters = { - mode: { type: 'string', required: true, enum: ['read', 'write', 'append'] }, -} -// Inferred type: { mode: 'read' | 'write' | 'append' } -``` - -### 嵌套对象 - -```ts -export const parameters = { - options: { - type: 'object', - additionalProperties: true, - properties: { - timeout: { type: 'number' }, - retries: { type: 'number' }, - }, - }, -} -// The declared fields are inferred; additional JSON-valued keys are allowed. -``` - -### 数组 - -```ts -export const parameters = { - tags: { - type: 'array', - items: { type: 'string' }, - }, -} -// Inferred type: { tags?: string[] } -``` - -### 每个属性的字段 - -| 字段 | 类型 | 说明 | -|------|------|------| -| `type` | `'string' \| 'number' \| 'integer' \| 'boolean' \| 'null' \| 'object' \| 'array' \| 'json'` | 值类型;`json` 接受任意无损 JSON 值 | -| `required` | `true` | 标记为必填(影响类型推导) | -| `description` | `string` | 发送给模型的描述 | -| `enum` / `const` | 匹配类型的标量值 | 允许的字面量值,在编写和运行时边界校验 | -| `properties` | `ParameterSchemaSpec` | 对象的嵌套属性 | -| `additionalProperties` | `true \| false` | 每个显式对象节点都必须声明 | -| `items` | `ValueSchemaSpec` | 数组的元素 schema | -| `oneOf` | 至少两个 `ValueSchemaSpec` 分支 | 要求恰好匹配一个分支;代替 `type` 使用 | - -外层 `parameters` 映射是一个隐式的开放对象。显式嵌套对象需自行选择是否开放;不通过 `defineTool` 注册的原始 JSON Schema 保持 JSON Schema 的默认开放语义。 - -## execute 函数 - -`execute` 接收经过校验的 `args`(类型自动推导)和一个 `exec` 上下文对象: - -```ts -import { defineTool } from '@deepseek-ai/dsh-tools' - -export const tool = defineTool({ - name: 'example', - description: 'Return an example result.', - parameters: {}, - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - async execute(args, exec) { - // args: inferred from parameters - // exec: ToolExecution context - - // Return the value declared by output.schema. - void args - void exec - return 'result here' - }, -}) -``` - -### 返回值 - -`execute` 返回由 `output.schema` 声明的无损 JSON 值。`output.render(args, value)` 会将经过校验的值另外转换为 Native/模型可见的内容: - -```ts ignore-check -output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - path: { type: 'string', required: true }, - content: { type: 'string', required: true }, - }, - }, - render: (_args, value) => [{ type: 'text', text: value.content }], -}, -async execute(args) { - return { path: args.path, content: await readFile(args.path, 'utf8') } -} -``` - -执行期间的程序化调用方可以使用规范值,但 `tool/result` 不会持久化该值;渲染后的内容和可选的 `presentationMeta` 才是可回放的投影。工具主体返回的值若不满足 schema 或不是无损 JSON,就会变为 `INVALID_TOOL_OUTPUT` 失败。 - -### 参数校验 - -`defineTool` 在调用 `execute` 之前会自动校验模型生成的参数。如果参数不合法,会抛出 `ToolArgsError`,框架将其转换为 `isError` 结果返回给模型,让模型自行修正。 - -你不需要在 `execute` 里手动校验参数类型。 - -## 展示层(Presentation) - -工具可以定义与传输方式无关的展示方法,供终端和 Web 客户端使用: - -```ts ignore-check -defineTool({ - name: 'bash', - // ... - output: { - schema: { type: 'string' }, - render: (_args, value) => [{ type: 'text', text: value }], - }, - presentCall(args) { - return { - card: 'terminal', - title: args.command, - } - }, - presentResult(args, result) { - return { - card: 'terminal', - output: result.content.map(b => b.type === 'text' ? b.text : '').join(''), - } - }, -}) -``` - -`presentCall` 和 `presentResult` 是**纯函数**,不能有副作用——UI 可能在流式传输中和会话回放中多次调用它们。 - -## 注册与卸载 - -`ctx.tools.register()` 返回值就是 disposer。但由于你在 `ctx` 上调用,框架已经自动追踪了这个注册——插件卸载时会自动移除工具。你不需要手动调用 disposer。 - -```ts ignore-check -// This is sufficient: -ctx.tools.register(defineTool({ /* ... */ })) - -// No saved disposer or extra cleanup registration is needed. -``` - -## 完整示例 - -一个文件计数工具: - -```ts -import type { Context } from 'cordis' -import { defineTool } from '@deepseek-ai/dsh-tools' -import { readdir } from 'node:fs/promises' - -export const name = 'file-counter' -export const inject = ['tools'] - -export function apply(ctx: Context) { - ctx.tools.register(defineTool({ - name: 'count_files', - description: 'Count files in a directory.', - parameters: { - path: { type: 'string', required: true, description: 'Directory path' }, - extension: { type: 'string', description: 'Filter by extension (e.g. ".ts")' }, - }, - output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - count: { type: 'integer', required: true }, - files: { type: 'array', required: true, items: { type: 'string' } }, - }, - }, - render: (_args, value) => [{ type: 'text', text: `Found ${value.count} files.` }], - }, - async execute(args) { - const entries = await readdir(args.path, { withFileTypes: true }) - let files = entries.filter(e => e.isFile()) - if (args.extension) { - files = files.filter(f => f.name.endsWith(args.extension!)) - } - return { count: files.length, files: files.map(file => file.name) } - }, - })) -} -``` +打开 `http://127.0.0.1:3080`,然后输入:`Use the greet tool to greet Ada.` 模型可以调用 `greet`,并收到 `Hello, Ada!` 这一工具结果。 ## 下一步 -- [插件配置](./config.md) — 让你的工具可配置 -- [能力分层](../practice/) — 了解接口/实现/消费方模式 +- [插件配置](./config.md) — 让问候语可配置。 +- [工具编写参考](../../../cookbook/adding-a-tool.md) — 查阅嵌套 schema、规范值、后台工作、策略钩子、Code Mode 和 UI 卡片。 +- [能力分层](../practice/) — 将可替换能力拆分为接口、实现和消费方包。 diff --git a/docs/user/develop/practice/index.i18n.yaml b/docs/user/develop/practice/index.i18n.yaml index 7446e7f633..65057da78e 100644 --- a/docs/user/develop/practice/index.i18n.yaml +++ b/docs/user/develop/practice/index.i18n.yaml @@ -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/user/develop/practice/index.md -index.md: e197d499d7f5bd9911ea60bebf584251cd4ed915 -index.zh.md: cacf13e8b23060ea5c309d30e51d732f09c76000 +index.md: c3306725f47993aa9d3322423261a260754c1d5f +index.zh.md: 3609cbce21e17ce9ca0a40b69999b293dee29012 diff --git a/docs/user/develop/practice/index.md b/docs/user/develop/practice/index.md index ea517aa925..c3306725f4 100644 --- a/docs/user/develop/practice/index.md +++ b/docs/user/develop/practice/index.md @@ -2,6 +2,10 @@ English | [中文](index.zh.md) +This page has two parts: a concept reference for the three-layer capability pattern, followed by an advanced tutorial that builds one capability. Complete the [basic plugin path](../basic/) and [services tutorial](../framework/service.md) first. + +## Concept reference + When a capability is general enough to need replaceable implementations, such as Bash execution, Harness splits it into three packages: an **interface**, an **implementation**, and a **consumer**. Each layer can evolve or be replaced independently. ## Bash example @@ -49,17 +53,9 @@ The interface and tool remain unchanged while the implementation changes. - The consumer depends on the interface. - The implementation and consumer **do not depend on each other**. -## Built-in three-layer capabilities +The [capability-seam reference](../../../capability-seams.md) owns the current built-in families and package links. -| Capability | Interface | Implementation | Consumer | -|------|-------------|------|---------------| -| Bash | `dsh-bash` | `dsh-bash-local` | `dsh-tool-bash` | -| Filesystem | `dsh-fs` | `dsh-fs-local` + `dsh-fs-policy` | `dsh-tool-fs` | -| Web | `dsh-web` | `dsh-web-fetch-local` / `dsh-web-search-*` | `dsh-tool-web` | -| Subagent | `dsh-subagent` | `dsh-subagent-spawn` / `dsh-subagent-fork` | `dsh-tool-subagent` | -| Compaction | `dsh-compact` | `dsh-compact-basic` | The implementation consumes agent-loop extension events | - -## Develop a three-layer capability +## Tutorial: develop a three-layer capability ### Step 1: define the interface diff --git a/docs/user/develop/practice/index.zh.md b/docs/user/develop/practice/index.zh.md index 7aaa5abb5f..3609cbce21 100644 --- a/docs/user/develop/practice/index.zh.md +++ b/docs/user/develop/practice/index.zh.md @@ -2,6 +2,10 @@ [English](index.md) | 中文 +本文分为两部分:先参考三层能力模式的概念,再通过高级教程构建一项能力。请先完成[基础插件路径](../basic/)和[服务教程](../framework/service.md)。 + +## 概念参考 + 当一项能力足够通用,需要支持可替换的实现时(例如 Bash 执行),Harness 会将其拆成三个包:**接口**、**实现**和**消费方**。这样便可独立替换其中任何一层。 ## 以 Bash 为例 @@ -49,17 +53,9 @@ - 消费方依赖接口。 - 实现和消费方**互不依赖**。 -## Harness 中内置的三件套 +当前内置系列及其包链接由[能力 seam 参考](../../../capability-seams.md)负责。 -| 能力 | 接口(seam) | 实现 | 消费方(工具) | -|------|-------------|------|---------------| -| Bash | `dsh-bash` | `dsh-bash-local` | `dsh-tool-bash` | -| 文件系统 | `dsh-fs` | `dsh-fs-local` + `dsh-fs-policy` | `dsh-tool-fs` | -| Web | `dsh-web` | `dsh-web-fetch-local` / `dsh-web-search-*` | `dsh-tool-web` | -| 子代理 | `dsh-subagent` | `dsh-subagent-spawn` / `dsh-subagent-fork` | `dsh-tool-subagent` | -| 压缩 | `dsh-compact` | `dsh-compact-basic` | 由实现插件消费 agent-loop 的扩展事件 | - -## 开发你自己的三件套 +## 教程:开发三层能力 ### 第一步:定义接口 diff --git a/docs/user/guide/config.i18n.yaml b/docs/user/guide/config.i18n.yaml index 7d99e45437..372e0e6c73 100644 --- a/docs/user/guide/config.i18n.yaml +++ b/docs/user/guide/config.i18n.yaml @@ -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/user/guide/config.md -config.md: 4e438cc3a400de71934d047108024ff5be4ef7d2 -config.zh.md: e0b0285b110a808b1284209a84f78e114634df52 +config.md: ddf4df264e5534fc3b74991941c2f3f82376d53f +config.zh.md: 56ac0146ddae83dbfc86f479030efdb5772a3aaf diff --git a/docs/user/guide/config.md b/docs/user/guide/config.md index 4e438cc3a4..ddf4df264e 100644 --- a/docs/user/guide/config.md +++ b/docs/user/guide/config.md @@ -47,7 +47,7 @@ A minimal configuration is a list of plugin entries: toolName: my_tool ``` -Plugins load in file order. Place plugins that depend on services after the applications or capability plugins that provide them. Missing models, tools, and plugins fail as early as possible instead of being silently ignored. +Cordis starts sibling entries concurrently. A plugin declares required services through `inject`; Cordis waits for those services before applying the plugin, so file order does not establish dependency readiness. Missing models, tools, and plugins fail as early as possible instead of being silently ignored. ## CLI overlays diff --git a/docs/user/guide/config.zh.md b/docs/user/guide/config.zh.md index e0b0285b11..56ac0146dd 100644 --- a/docs/user/guide/config.zh.md +++ b/docs/user/guide/config.zh.md @@ -2,7 +2,7 @@ [English](config.md) | 中文 -Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的参数。配置文件负责组合能力;每个包真正支持的字段和默认值由源码生成的配置目录负责记录,避免两份手写表格逐渐不一致。 +Harness 使用 `cordis.yml` 描述 agent(智能体)加载哪些插件以及每个插件的参数。配置文件负责组合能力;每个包真正支持的字段和默认值由源码生成的配置目录负责记录,避免两份手写表格逐渐不一致。 ## 从真实配置开始 @@ -47,7 +47,7 @@ Harness 使用 `cordis.yml` 描述 Agent 加载哪些插件以及每个插件的 toolName: my_tool ``` -插件按文件中的顺序加载。依赖其他服务的插件应该排在提供这些服务的应用或能力插件之后;引用不存在的模型、工具或插件会尽早报错,而不是被静默忽略。 +Cordis 会并发启动同级配置项。插件通过 `inject` 声明必需服务;Cordis 会等到这些服务就绪后再应用该插件,因此文件顺序不能保证依赖已就绪。引用不存在的模型、工具或插件会尽早报错,而不是被静默忽略。 ## CLI 覆盖层 diff --git a/docs/web-styling.i18n.yaml b/docs/web-styling.i18n.yaml index 9c79aa8a99..0cbbafeec9 100644 --- a/docs/web-styling.i18n.yaml +++ b/docs/web-styling.i18n.yaml @@ -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/web-styling.md -web-styling.md: 173ce4482ec01f95cf38c62ebc2879e03a2d8192 -web-styling.zh.md: b38ba267cc7cc66ed12f000a93744dc81d940d06 +web-styling.md: 5296cc7f83f712532262eadda6da098ae9f63ec7 +web-styling.zh.md: 6beec368f2f7fd5eb7606acfaae315d56899c739 diff --git a/docs/web-styling.md b/docs/web-styling.md index 173ce4482e..5296cc7f83 100644 --- a/docs/web-styling.md +++ b/docs/web-styling.md @@ -1,109 +1,25 @@ -# Web GUI Style Guide +# Web UI style reference English | [中文](web-styling.zh.md) -> **[The token system has been replaced—the table in § 1 is retained only for historical reference]** The `--bg-*`/`--text-*`/`--accent` token families documented here and their host package, `packages/client/web-ui`, were retired during the plugin refactor. The sole current token source is `packages/client/ui-theme/src/styles/`, which defines the `--dsw-*` system (a static color scale plus a semantic alias layer, with dark-mode overrides under `body[data-ds-dark-theme]`). The sheet is authoritative and component audits use it as the baseline. **The following rules remain in force**: CSS Modules + clsx, no component library, no Tailwind, no hard-coded color values in components, pair every font size with a line height, use spacing in multiples of 4, and do not put monospace last in the code font stack. +This reference defines styling ownership and component rules for browser client packages. The current token values live in [`packages/client/ui-theme/src/styles/`](../packages/client/ui-theme/src/styles/); this document does not duplicate that generated-by-source inventory. -> Status: formerly a “living document” that evolved with `packages/client/web-ui`. The visual baseline came from empirical study of the deepseekchat frontend repository. The [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) owns the framework decisions and engineering constraints; this document does not repeat their rationale. +## Ownership -## 1. Design token table (authoritative definitions) +[`ui-theme`](../packages/client/ui-theme/README.md) owns the `--dsw-*` static scale, semantic aliases, typography, motion, gradients, shadows, scrollbar styles, and light/dark preference. [`ui-layout`](../packages/client/ui-layout/README.md) applies the resolved theme snapshot to the document. Feature packages consume semantic aliases and do not define another global theme. -All tokens live in `packages/client/web-ui/src/style/global.css`: `:root` contains the light-theme values, and the `[data-theme='dark']` block overrides the same variables (columns that were not complete are marked as placeholders). Component CSS references tokens only and contains no literal color values. +Global style sheets belong in `ui-theme/src/styles/`. Component styles live beside their component as CSS Modules. A component may define a local custom property when its value is part of that component's layout or presentation contract; shared colors, typography, elevation, and motion belong to the theme package. -### 1.1 Colors (two layers: comments identify the base-palette source, while variable names are semantic aliases) +## Component rules -| token | Light value | Dark value (placeholder) | Purpose | -| --- | --- | --- | --- | -| `--bg-base` | `#ffffff` | `#151517` | Page background | -| `--bg-layer` | `#ffffff` | `#232324` | Floating layer/panel | -| `--bg-sidebar` | `#f9fafb` | `#1b1b1c` | Sidebar background | -| `--text-primary` | `#0f1115` | `#f9fafb` | Body text | -| `--text-secondary` | `#61666b` | `#cfd3d6` | Secondary text | -| `--text-tertiary` | `#81858c` | `#adb2b8` | Supporting/descriptive text | -| `--border-l1` | `rgba(0,0,0,.04)` | `rgba(255,255,255,.06)` | Subtle separator (sidebar right edge) | -| `--border-l2` | `rgba(0,0,0,.1)` | `rgba(255,255,255,.12)` | Standard border | -| `--hover-bg` | `rgba(38,49,72,.06)` | `rgba(255,255,255,.08)` | Hover-state background | -| `--active-bg` | `rgba(38,49,72,.1)` | `rgba(255,255,255,.14)` | Pressed/active-state background | -| `--accent` | `#3964fe` | `#5686fe` | Brand blue (deepseek-500; one step lighter in dark mode) | -| `--accent-soft` | `#edf3fe` | `#28313f` | Soft brand background (emphasis blocks) | -| `--accent-item` | `#e4edfd` | `#35363a` | Selected sidebar-item background | -| `--bubble-bg` | `#edf3fe` | `#2c2c2e` | User-message bubble background | -| `--ok` / `--error` / `--warn` | `#22c55e` / `#ec1313` / `#f59e0b` | Same values | Semantic status colors | -| `--text-on-solid` | `#ffffff` | Same value | Text on solid backgrounds (accent/error badges, etc.) | -| `--ok-soft` / `--error-soft` | `#e6faed` / `#fee2e2` | `#233c2c` / `#570c0c` | Soft semantic status backgrounds (badges); green-100/red-100, with the 900 shades in dark mode | -| `--color-frame-mux` / `--color-frame-host` | `#8250df` / `#0969da` | Same values | RPC debugger direction colors (project-specific, not part of the baseline) | -| `--frame-mux-soft` / `--frame-host-soft` | `rgba(130,80,223,.1)` / `rgba(9,105,218,.1)` | Same colors at `.24` | Soft direction-color backgrounds (badges) | -| `--scroll-color` / `--scroll-color-hover` | `rgba(0,0,0,.08)` / `.15` | `rgba(255,255,255,.15)` / `.24` | Scrollbar colors (for `.scrollable` only) | +- Use CSS Modules and `clsx`; do not add a component library or Tailwind. +- Use `--dsw-alias-*` semantic tokens in feature components. Do not copy static palette values or write literal colors there. +- Keep theme selectors out of feature component CSS. Light/dark overrides belong to the theme owner. +- Pair font sizes with line heights and use the theme typography variables when an existing role matches. +- Keep source text, terminal output, and diff lines unwrapped when their component contract requires column preservation; use the shared scrollbar styles rather than component-specific scrollbar selectors. +- Put presentation in CSS. Inline React styles may pass component-local custom-property values but must not encode theme branches. +- Preserve keyboard focus visibility and reduced-motion behavior when adding transitions or hover-only controls. -### 1.2 Non-color tokens +## Changing the system -| token | Value | Description | -| --- | --- | --- | -| `--font-ui` | `Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | Body-text stack | -| `--font-mono` | `Menlo, Monaco, Consolas, 'JetBrains Mono', 'Courier New', sans-serif` | Code stack; **do not put monospace last** (prevents SimSun fallback for Chinese on Windows) | -| `--fw-strong` | `600` | Unified bold weight | -| `--ease` | `cubic-bezier(.4,0,.2,1)` | Sole easing curve | -| `--dur` / `--dur-fast` / `--dur-slow` | `.2s` / `.1s` / `.3s` | Three transition durations | -| `--radius-s` / `--radius-m` / `--radius-l` / `--radius-bubble` / `--radius-xl` | `8px` / `12px` / `16px` / `22px` / `24px` | Semantic radius steps: small controls / list items and blocks inside panels / floating layers / bubbles / input cards (same as the baseline inputWrapper); use `999px` directly for pills | -| `--shadow-panel` | `0 0 1px rgba(0,0,0,.2), 0 0 4px rgba(0,0,0,.02), 0 12px 32px rgba(0,0,0,.08)` | Floating-layer shadow (baseline lv3) | -| `--shadow-float` | `0 0 1px rgba(0,0,0,.24), 0 4px 12px rgba(0,0,0,.06), 0 16px 48px rgba(0,0,0,.16)` | Strong floating panel (enhanced lv3, such as the RPC debugger overlay) | -| `--shadow-card` | `0 4px 10px rgba(0,0,0,.02), 0 2px 4px rgba(0,0,0,.04)`; dark value `none` | Subtle input-card shadow (baseline: borders plus a subtle shadow distinguish same-color light surfaces; a lighter background distinguishes dark surfaces, with the shadow disabled) | - -Font sizes and spacing are **not tokenized** (matching the baseline repository's decision): components specify font sizes in px and **always pair them with line heights**. Common pairs are 16/24 (bubbles), 14/22 (UI default), and 12/18 (supporting text); spacing uses multiples of 4. - -## 2. Visual baseline (from deepseekchat) - -- Sidebar: width `260px + 1px` right border (`--border-l1`); background `--bg-sidebar`. -- Sidebar items: height `40px`, radius `--radius-m`, font size 14px; hover background `--hover-bg` or a sidebar-specific gray; **selected items use `--accent-item` without changing text color**. -- Sidebar group headings: 12px / weight 500 / `--text-tertiary` / sticky at the top (using the sidebar background to cover scrolling content). -- Conversation column: centered at `max-width: 840px`, reduced to 712px below 1024px. -- Message stream: **only user messages have bubbles**: background `--bubble-bg`, radius `--radius-bubble`, padding `10px 16px`, font size 16px/24px, and `max-width: calc(100% - 88px)`; **assistant messages are a plain document flow without a background**. -- Message action bar: `opacity: 0` by default; fades in when its parent is hovered or contains focus (`--dur` + `--ease`). -- Input card: floats centered at the same width as the conversation column (840px, reduced to 712px below 1024px) with bottom spacing; radius `--radius-xl`, border `--border-l2`, background `--bg-base`, shadow `--shadow-card`; two internal vertical sections = textarea (16px/24px, minimum 2 lines, maximum 14 lines = 336px, auto-growing through a mirror div) + action row (a 34px primary round button nested at bottom right); focus does not change the border or shadow (matching the baseline). -- Primary input button (the three decisions made on 2026-07-20, visually based on the Codex App): a 32px solid circular icon button (inline SVG). Idle = `--accent` background with a white ↑ “Send” arrow; while running it changes in place to an accent ■ “Stop” icon on `--accent-soft` (the same color family, not a warning, and not red). **Input is locked while running** (decision 3, replacing the earlier hover-menu design): the textarea is disabled (gray, with draft content still visible), there is no queue/interjection menu, and Stop is the only action. When the turn ends, input unlocks and regains focus. Enter sends; Ctrl/Meta+Enter inserts a newline (the keyboard path is disabled with the locked input while running). -- Scrollbars: nearly invisible, darkening on hover, with `scrollbar-gutter: stable` so they do not consume layout space (always use `.scrollable`; see § 3.9). -- Four-quadrant RPC direction symbols (the official visual vocabulary, using the spatial metaphor that up goes to the server, down comes from the server; single line = client-initiated exchange, double line = server-initiated exchange): - -| Symbol | Quadrant | Badge colors | -| --- | --- | --- | -| `↑` | client-request (unary outbound) | `--accent` / `--accent-soft` | -| `↓` | server-response (unary response) | ok `--ok`/`--ok-soft`, error `--error`/`--error-soft` | -| `⇟` | server-request (downlink stream) | mux `--color-frame-mux`/`--frame-mux-soft`, host `--color-frame-host`/`--frame-host-soft` | -| `⇞` | client-response (reply to server request) | `--accent`/`--accent-soft` at reduced opacity | - -## 3. Style implementation rules (review checklist) - -1. Colors, radii, motion, and font stacks reference only the § 1 tokens. Reject literal color values in component CSS (except for special effects such as gradient masks, which require an explanatory comment). -2. Component CSS must not contain `[data-theme]` selectors; dark-mode differences belong only in the global.css token table. If a theme must change a non-token value such as a gradient endpoint, define a local CSS variable in the component and have the theme block override only that variable (a variable bridge). -3. Use camelCase class names; use a single adjective for state classes (`.active` `.show`) and attach them with clsx: `clsx(styles.x, cond && styles.active, className)`. -4. Public components must accept `className` and merge it into the root element. -5. Do not use `composes`; share through tokens and extracted components. -6. Use `:global` only to pierce third-party or cross-package class names; do not use it to define new global classes. -7. All interaction transitions use `var(--dur*) var(--ease)` and transition only opacity / transform / background color / shadow. Wrap hover-only reveal elements in `@media (hover: hover)`. -8. Prefer opacity-based tokens for hover/active backgrounds because they compose over any elevation background; do not add new solid grays. -9. Apply the `.scrollable` utility class from global.css to every scroll container; do not write `::-webkit-scrollbar` inside components. -10. Put media queries at the end of the component CSS, next to the rules they override. The only current breakpoint is 1024px (where the conversation column steps down); record a second breakpoint in this document before adding it. -11. Dynamic styles in JS set only CSS variables (`style={{'--x': v}}`), while rules remain in CSS; do not assemble style objects in TSX to branch by theme or state. -12. Use only the three `--text-primary/secondary/tertiary` levels for gray text; do not add another gray. - -## 4. File organization - -- `src/style/global.css` always uses this section order: ① token table (`:root` + `[data-theme='dark']`), ② global foundations (box-sizing, body, button reset), ③ global utility classes (`.scrollable`, etc.; keep the total in single digits). -- Place each `*.module.css` beside the component with the same name; use one module file per component. -- Use the existing `css-modules.d.ts` wildcard declaration. Reassess introducing tcm to generate exact `.css.d.ts` files only after the component count exceeds 20. -- PostCSS feature allowlist: currently **no plugins** (flat CSS plus native nesting when needed). Record nested/custom-media in this document before introducing either. - -## 5. Evolution rules and deviation log - -- **Adding a token**: add it to the § 1 table first (including the dark-placeholder column), then use it in the component. Reject any new `--` variable that has not been added to the table (except for component-local variable bridges). -- **Deviating from the baseline**: if an implementation differs from any constant in § 2, add one row to the deviation table below (date / item / rationale). -- **Dark-table completion acceptance**: after `[data-theme='dark']` overrides every placeholder column in § 1, compare the RPC panel, sidebar, and conversation stream manually or with screenshots. Acceptance requires all three to match and no component-level theme selector to remain. - -| Date | Deviation | Rationale | -| --- | --- | --- | -| (none) | | | - -## 6. Related documentation - -- [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) (decision record for the five framework rules and engineering constraints) -- Client consumption architecture and layered protocols: [Web client architecture RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md), [GUI layering and RPC protocol RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) +Add or change a shared token in the owning `ui-theme` sheet, then consume its semantic alias from feature packages. Update the owning package reference when a public styling contract changes. Visual behavior follows the [testing policy](testing.md); the [styling-system Agent Note](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) records framework rationale. diff --git a/docs/web-styling.zh.md b/docs/web-styling.zh.md index b38ba267cc..6beec368f2 100644 --- a/docs/web-styling.zh.md +++ b/docs/web-styling.zh.md @@ -1,109 +1,25 @@ -# Web GUI 样式规范 +# Web UI 样式参考 [English](web-styling.md) | 中文 -> **【token 体系已换代——§1 表格仅历史参考】** 本文的 `--bg-*`/`--text-*`/`--accent` token 族与其宿主包 `packages/client/web-ui` 已随插件化重构退役。现行 token 唯一来源=`packages/client/ui-theme/src/styles/` 的 `--dsw-*` 体系(static 色阶+alias 语义层,暗色=`body[data-ds-dark-theme]` 覆写),sheet 即权威、组件对账以它为准。**仍然有效**:工程约束(CSS Modules + clsx、无组件库、无 tailwind、组件禁 hardcode 色值)、字号成对写行高、间距 4 倍数、代码字体栈末位不放 monospace。 +本文规定浏览器客户端包的样式职责归属与组件规则。当前 token 值位于 [`packages/client/ui-theme/src/styles/`](../packages/client/ui-theme/src/styles/);本文不重复这份由源码生成的清单。 -> 状态:原「活文档」(随 `packages/client/web-ui` 演进)。视觉基线源自对 deepseekchat 前端仓的实测调研。框架决策与工程约束由 [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) 拍板,本文不重复论证。 +## 职责归属 -## 1. 设计 token 表(权威定义) +[`ui-theme`](../packages/client/ui-theme/README.md) 负责 `--dsw-*` 静态色阶、语义别名、排版、动效、渐变、阴影、滚动条样式以及明暗主题偏好。[`ui-layout`](../packages/client/ui-layout/README.md) 将解析后的主题快照应用到文档。功能包使用语义别名,不得另行定义全局主题。 -所有 token 住 `packages/client/web-ui/src/style/global.css`:`:root` 亮色实值,`[data-theme='dark']` 块覆盖同名变量(未补全前列为占位)。组件 CSS 只引 token,不出现字面量色值。 +全局样式表归 `ui-theme/src/styles/` 所有。组件样式以 CSS Modules 形式放在组件旁。当某个值属于组件自身的布局或呈现契约时,组件可以定义局部自定义属性;共享颜色、排版、层级和动效属于主题包。 -### 1.1 颜色(两层:注释里是 base 色板出处,变量名即语义别名) +## 组件规则 -| token | 亮色实值 | 暗色(占位) | 用途 | -| --- | --- | --- | --- | -| `--bg-base` | `#ffffff` | `#151517` | 页面底 | -| `--bg-layer` | `#ffffff` | `#232324` | 浮层/面板 | -| `--bg-sidebar` | `#f9fafb` | `#1b1b1c` | 侧边栏底 | -| `--text-primary` | `#0f1115` | `#f9fafb` | 正文 | -| `--text-secondary` | `#61666b` | `#cfd3d6` | 次要文字 | -| `--text-tertiary` | `#81858c` | `#adb2b8` | 辅助/说明 | -| `--border-l1` | `rgba(0,0,0,.04)` | `rgba(255,255,255,.06)` | 弱分隔(侧边栏右缘) | -| `--border-l2` | `rgba(0,0,0,.1)` | `rgba(255,255,255,.12)` | 常规边框 | -| `--hover-bg` | `rgba(38,49,72,.06)` | `rgba(255,255,255,.08)` | hover 态底 | -| `--active-bg` | `rgba(38,49,72,.1)` | `rgba(255,255,255,.14)` | 按压/激活态底 | -| `--accent` | `#3964fe` | `#5686fe` | 品牌蓝(deepseek-500;暗提亮一档) | -| `--accent-soft` | `#edf3fe` | `#28313f` | 淡品牌底(强调块) | -| `--accent-item` | `#e4edfd` | `#35363a` | 侧边栏选中条目底 | -| `--bubble-bg` | `#edf3fe` | `#2c2c2e` | 用户消息气泡底 | -| `--ok` / `--error` / `--warn` | `#22c55e` / `#ec1313` / `#f59e0b` | 同值 | 语义状态色 | -| `--text-on-solid` | `#ffffff` | 同值 | 实色底(accent/error 徽标等)上的文字 | -| `--ok-soft` / `--error-soft` | `#e6faed` / `#fee2e2` | `#233c2c` / `#570c0c` | 语义状态软底(徽章);green-100/red-100,暗为 900 档 | -| `--color-frame-mux` / `--color-frame-host` | `#8250df` / `#0969da` | 同值 | RPC 调试面板方向色(自有,非基线) | -| `--frame-mux-soft` / `--frame-host-soft` | `rgba(130,80,223,.1)` / `rgba(9,105,218,.1)` | 同色 `.24` | 方向色软底(徽章) | -| `--scroll-color` / `--scroll-color-hover` | `rgba(0,0,0,.08)` / `.15` | `rgba(255,255,255,.15)` / `.24` | 滚动条(`.scrollable` 专用) | +- 使用 CSS Modules 和 `clsx`;不得添加组件库或 Tailwind。 +- 功能组件使用 `--dsw-alias-*` 语义 token。不得复制静态色板值或在其中写入颜色字面量。 +- 功能组件 CSS 不得包含主题选择器。明暗主题覆盖属于主题所有方。 +- 字体大小必须与行高配对;已有角色匹配时使用主题排版变量。 +- 当组件契约要求保留列结构时,源码文本、终端输出和 diff 行不得换行;使用共享滚动条样式,不得定义组件专用滚动条选择器。 +- 呈现规则写在 CSS 中。React 内联样式可以传递组件局部自定义属性值,但不得编码主题分支。 +- 添加过渡动画或仅悬停可见的控件时,保留清晰可见的键盘焦点和减少动态效果行为。 -### 1.2 非颜色 +## 变更系统 -| token | 值 | 说明 | -| --- | --- | --- | -| `--font-ui` | `Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | 正文栈 | -| `--font-mono` | `Menlo, Monaco, Consolas, 'JetBrains Mono', 'Courier New', sans-serif` | 代码栈;**末位不放 monospace**(防 Windows 中文回退宋体) | -| `--fw-strong` | `600` | 粗体统一权重 | -| `--ease` | `cubic-bezier(.4,0,.2,1)` | 唯一缓动曲线 | -| `--dur` / `--dur-fast` / `--dur-slow` | `.2s` / `.1s` / `.3s` | 过渡三档 | -| `--radius-s` / `--radius-m` / `--radius-l` / `--radius-bubble` / `--radius-xl` | `8px` / `12px` / `16px` / `22px` / `24px` | 圆角语义档:小控件 / 列表条目与面板内块 / 浮层 / 气泡 / 输入卡片(基线 inputWrapper 同值);胶囊直接写 `999px` | -| `--shadow-panel` | `0 0 1px rgba(0,0,0,.2), 0 0 4px rgba(0,0,0,.02), 0 12px 32px rgba(0,0,0,.08)` | 浮层阴影(基线 lv3) | -| `--shadow-float` | `0 0 1px rgba(0,0,0,.24), 0 4px 12px rgba(0,0,0,.06), 0 16px 48px rgba(0,0,0,.16)` | 强浮动面板(lv3 加强档,如 RPC 调试浮层) | -| `--shadow-card` | `0 4px 10px rgba(0,0,0,.02), 0 2px 4px rgba(0,0,0,.04)`;暗色 `none` | 输入卡片微阴影(基线:亮色同底靠边框+微影区分,暗色靠提亮底、阴影关闭) | - -字号与间距**不 token 化**(基线仓同款决策):字号在组件里写 px 且**成对写行高**,常用对 16/24(气泡)、14/22(UI 默认)、12/18(辅助);间距用 4 的倍数。 - -## 2. 视觉基线(源自 deepseekchat) - -- 侧边栏:宽 `260px + 1px` 右边框(`--border-l1`);底色 `--bg-sidebar`。 -- 侧边栏条目:高 `40px`、圆角 `--radius-m`、字号 14px;hover 底 `--hover-bg` 或 sidebar 专属灰、**选中底 `--accent-item` 且不改文字色**。 -- 侧边栏分组标题:12px / weight 500 / `--text-tertiary` / sticky 顶部(底色同侧边栏遮滚动内容)。 -- 会话列:`max-width: 840px` 居中,<1024px 降 712px。 -- 消息流:**仅用户侧有气泡**——`--bubble-bg` 底、圆角 `--radius-bubble`、padding `10px 16px`、字号 16px/24px、`max-width: calc(100% - 88px)`;**助手侧纯文档流无底色**。 -- 消息操作条:默认 `opacity: 0`,父块 hover/focus-within 淡入(`--dur` + `--ease`)。 -- 输入卡片:与会话列同宽(840px,<1024px 降 712px)居中悬浮(距底留白带);圆角 `--radius-xl`、边框 `--border-l2`、底 `--bg-base`、阴影 `--shadow-card`;内部上下两段=textarea(16px/24px,min 2 行 max 14 行=336px,镜像 div 自增高)+ 操作行(右下嵌 34px 主圆钮);focus 无边框/阴影变化(基线同款)。 -- 输入主按钮(拍板 2026-07-20 三连,视觉参照 Codex App):32px 实心正圆图标钮(内联 SVG)——空闲=`--accent` 底白↑箭头「发送」,运行中原地变 `--accent-soft` 底 accent ■「停止」(同色系不告警、不用红)。**运行中锁输入**(拍板 3,取代早先 hover 菜单方案):textarea disabled(灰、草稿内容保留可见)、无任何排队/插话菜单,停止是唯一动作;turn 结束解禁并 refocus。键盘 Enter=发送、Ctrl/Meta+Enter=换行(运行中键盘路径随锁失效)。 -- 滚动条:近隐形、hover 加深、`scrollbar-gutter: stable` 不占布局(统一走 `.scrollable`,见 §3-9)。 -- RPC 四象限方向符(官方视觉词汇,空间隐喻:上=去 server、下=来自 server;单线=客户端发起的交互、双线=服务端发起的交互): - -| 符号 | 象限 | 徽章配色 | -| --- | --- | --- | -| `↑` | client-request(unary 出站) | `--accent` / `--accent-soft` | -| `↓` | server-response(unary 回包) | ok `--ok`/`--ok-soft`,error `--error`/`--error-soft` | -| `⇟` | server-request(下行流) | mux `--color-frame-mux`/`--frame-mux-soft`,host `--color-frame-host`/`--frame-host-soft` | -| `⇞` | client-response(对 server request 的回应) | `--accent`/`--accent-soft` 降透明度 | - -## 3. 样式编码规范(review 对照打勾) - -1. 颜色/圆角/动效/字体栈只引 §1 token;组件 CSS 出现字面量色值即打回(渐变遮罩等特效除外,须注释说明)。 -2. 组件 CSS 禁止出现 `[data-theme]` 选择器;暗色差异只在 global.css token 表做。确需按主题换非 token 值(渐变端点等),组件定义局部 CSS 变量、主题块只覆写变量(变量桥)。 -3. 类名 camelCase;状态类用单形容词(`.active` `.show`),由 clsx 挂载:`clsx(styles.x, cond && styles.active, className)`。 -4. 对外组件必须透传 `className` 并合入根元素。 -5. 禁用 `composes`;复用靠 token 与组件抽取。 -6. `:global` 仅用于穿透第三方/跨包类名;禁止用它定义新全局类。 -7. 交互过渡一律 `var(--dur*) var(--ease)`,只过渡 opacity / transform / 背景色 / 阴影;纯 hover 展示型元素包 `@media (hover: hover)`。 -8. hover/active 底色优先用透明度制 token(叠任意海拔底色都成立),不新造实色灰。 -9. 滚动容器统一挂 global.css 的 `.scrollable` 工具类;组件内禁写 `::-webkit-scrollbar`。 -10. 媒体查询写在组件 css 尾部、贴着被覆盖规则;断点当前仅 1024px 一档(会话列降档),加第二档需先记入本文档。 -11. 动态样式 JS 侧只写 CSS 变量(`style={{'--x': v}}`),规则留在 CSS;禁止在 TSX 里拼接样式对象做主题/状态分支。 -12. 文字灰阶只用 `--text-primary/secondary/tertiary` 三级,不新造灰色。 - -## 4. 文件组织 - -- `src/style/global.css` 固定分区顺序:① token 表(`:root` + `[data-theme='dark']`)② 全局基础(box-sizing、body、button reset)③ 全局工具类(`.scrollable` 等,总数保持个位数)。 -- `*.module.css` 与组件同目录同名;一个组件一个 module 文件。 -- 类型声明用现有 `css-modules.d.ts` 通配;组件数超 20 再评估引入 tcm 生成精确 `.css.d.ts`。 -- PostCSS 特性白名单:当前**零插件**(平铺 CSS + 原生嵌套按需);引入 nested/custom-media 需先记入本文档。 - -## 5. 演进规则与偏离记录 - -- **加新 token**:先进 §1 表(含暗色占位列)再在组件使用;review 见到未入表的 `--` 新变量即打回(组件局部变量桥除外)。 -- **偏离基线**:与 §2 任一常数不一致的实现,须在下方偏离表记一行(日期/项/理由)。 -- **暗色表补全验收**:`[data-theme='dark']` 覆盖 §1 全部占位列后,用 RPC 面板 + 侧边栏 + 会话流三个界面人工/截图核对一遍,无组件级主题选择器即达标。 - -| 日期 | 偏离项 | 理由 | -| --- | --- | --- | -| (空) | | | - -## 6. 相关文档 - -- [web-styling-system RFC](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md)(框架五条与工程约束的裁决记录) -- 客户端消费架构与分层协议:[Web 客户端架构 RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md)、[GUI 分层与 RPC 协议 RFC](../.agents/notes/implemented/architecture/2026-07-19-gui-layering-and-rpc-protocol.md) +在所属 `ui-theme` 样式表中添加或修改共享 token,然后在功能包中使用其语义别名。公共样式契约发生变化时,更新所属包的参考文档。视觉行为遵循[测试策略](testing.md);[样式系统 Agent Note](../.agents/notes/implemented/process/2026-07-19-web-styling-system.md) 记录框架依据。 diff --git a/examples/README.i18n.yaml b/examples/README.i18n.yaml index 8841eca967..f898c19e9a 100644 --- a/examples/README.i18n.yaml +++ b/examples/README.i18n.yaml @@ -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 examples/README.md -README.md: 64e9804eb69367588e926791039c453b0a9aede9 -README.zh.md: f0a174a2d59a21ed89fa1a83fc80d3fb05764244 +README.md: 5d021d9d9c7abae90b5f96bccd6447f4e2c3dc57 +README.zh.md: 66b355a93c0a0e6b53d1353de4024b7f86e82f7c diff --git a/examples/README.zh.md b/examples/README.zh.md index f0a174a2d5..66b355a93c 100644 --- a/examples/README.zh.md +++ b/examples/README.zh.md @@ -2,32 +2,24 @@ [English](README.md) | 中文 -展示 harness 如何接线的可运行演示(不是 workspace)。每个示例都是一个 **轻量叶节点**:要么是一份选择可替换后端、加载一个应用包的 `cordis.yml` 配置树,要么是一个 **overlay**——由 `dsh --config` 叠加到交付组合([`apps/cli/config/base.cordis.yml`](../apps/cli/config/base.cordis.yml) 加一份 surface overlay)之上的 patch 列表。成组的组合位于 [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo)、[`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 及它们共享的 [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) 组合包中;`dsh` 的各 surface 则改用平铺 config tree。没有 `start.ts`;终端 `demo:*` 脚本通过 [`dsh`](../apps/cli/README.md) CLI(命令行界面)启动,无头/ACP(Agent Client Protocol)脚本则调用 `cli-demo`/`acp-demo` bin。 +展示 DeepSeek Harness 主要接口和扩展点的可运行演示。每个子目录负责自己的配置、前置条件、命令和详细行为。 ## mcp-memory -三份默认关闭的参考 overlay 通过通用 MCP 客户端连接一个记忆 MCP 服务器。选择其中一份文件传给 `dsh --config`;DSH 不负责安装或配置上游记忆系统。版本固定的前置条件、身份映射、可选的共用提示词,以及「写入 → 新会话召回 → 使用」验证流程详见 [mcp-memory/README.md](mcp-memory/README.md)。 +通过通用 MCP 客户端连接受支持第三方记忆服务器的可选 overlay。详见[记忆示例参考](mcp-memory/README.md)。 ## headless-agent -非交互式 agent(智能体)演示:接受一个位置任务,在 `@deepseek-ai/dsh-cli-demo` 应用上运行一个完整模型/工具轮次,持久化新会话,打印 `text`、`json` 或 `stream-json`,然后退出。 - -运行:`pnpm run demo:headless "task"`(需要 `DEEPSEEK_API_KEY`)。输出契约、安全边界和快照套件详见 [headless-agent/README.md](headless-agent/README.md)。 +非交互式 agent(智能体):接受一项任务并运行,然后以选定的机器可读或人类可读格式输出结果。详见[无头示例参考](headless-agent/README.md)。 ## jsonrpc-agent -通过 Python SDK 驱动的无人值守编码 agent:JSON-RPC stdio、仅前台 `bash`、`read`/`write`/`edit`、一个前台 `subagent`、`todo_write`、JSONL 持久化和压缩。它不包含终端 UI、stdout 日志、批准、skill 和后台任务控制。详见 [jsonrpc-agent/README.md](jsonrpc-agent/README.md)。 +由 Python SDK 和 JSON-RPC 驱动的无人值守编码 agent。详见 [JSON-RPC 示例参考](jsonrpc-agent/README.md)。 ## web-cordis -**自指** 演示:编码主干加 [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis),其三个工具(`cordis_inspect`/`cordis_mount`/`cordis_unmount`)使 agent 可以检查当前 DSH 进程、挂载模型编写的临时 Plugin(事件监听器、一个全新工具,或一个供另一临时 Plugin 注入的服务),并再次卸载它们。这些 Plugin 只存在于内存中,共享一个内部 `cordis-dynamic` fiber 子树;`ctx.fs`/`ctx.web` 仅作为它们可用的能力提供方。 - -使用 `pnpm run demo:cordis` 在 `http://127.0.0.1:3081` 启动浏览器 UI,或使用 `pnpm run demo:cordis acp` 启动 ACP 服务器(两者均需 `DEEPSEEK_API_KEY`)。设计与沙箱注意事项详见[工具集 Agent Note](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)。 +能够检查并更改内存中 Cordis 插件树的自指 agent。详见 [web-cordis 示例参考](web-cordis/README.md)。 ## acp-agent -作为 **Agent Client Protocol (ACP)** 自动化服务器通过 JSON-RPC stdio 公开的 agent,由 [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 提供。程序化客户端可以创建新会话、发送文本提示词、消费已提交的 assistant 文本、回答一次性权限请求并取消工作。它拥有 ACP 无密钥快照套件。 - -运行:`pnpm run demo:acp`(需要 `DEEPSEEK_API_KEY`);`pnpm run demo:code-mode` 通过 `code-mode.cordis.yml` 覆盖以 Code Mode 启动同一服务器。协议与快照测试契约详见 [acp-agent/README.md](acp-agent/README.md)。 - -默认 `cordis.yml` 组合 [`@deepseek-ai/dsh-sandbox-local`](../packages/sandbox/sandbox-local)、[`@deepseek-ai/dsh-bash-sandbox`](../packages/bash/bash-sandbox) 和 [`@deepseek-ai/dsh-user-approval`](../packages/ui/user-approval)。`workspace-write` 将 bash 和文件系统变更限制在每个会话 workspace 中;范围更广的重试会通过 ACP 成为一次性机器权限请求。 +面向程序化客户端的 ACP(Agent Client Protocol)自动化服务器,支持会话、权限和取消操作。详见 [ACP 示例参考](acp-agent/README.md)。 diff --git a/examples/acp-agent/README.i18n.yaml b/examples/acp-agent/README.i18n.yaml index 5c7abee2a4..0bfa1735ce 100644 --- a/examples/acp-agent/README.i18n.yaml +++ b/examples/acp-agent/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write examples/acp-agent/README.md README.md: 61c6efafe9dde4f91385beebdfd426c57006187b -README.zh.md: 0b0dec487dda0e7915f8e6eed82ed338dc23cf81 +README.zh.md: 343e55722a4dda7c5cc5e0de6b5fecf250b9ab13 diff --git a/examples/acp-agent/README.zh.md b/examples/acp-agent/README.zh.md index 0b0dec487d..343e55722a 100644 --- a/examples/acp-agent/README.zh.md +++ b/examples/acp-agent/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -通过 JSON-RPC stdio 提供的面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com)服务器。它面向 parent agent(父智能体)、subagent 提供方和其他程序化客户端,而非产品 UI。 +通过 JSON-RPC stdio 提供的面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com) 服务器。它面向 parent agent(父智能体)、subagent 提供方和其他程序化客户端,而非产品 UI。 ```sh pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env) diff --git a/examples/headless-agent/README.i18n.yaml b/examples/headless-agent/README.i18n.yaml index 179ab416ed..de50cce818 100644 --- a/examples/headless-agent/README.i18n.yaml +++ b/examples/headless-agent/README.i18n.yaml @@ -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 examples/headless-agent/README.md -README.md: 445804a2611e5e8093eadf345ad10a2a7984c012 -README.zh.md: f1cf1964bbe15bc2b15abcd78a12fc1ffe7ea767 +README.md: 670a91ab402cf98585f2ece70787beb3b4aaf4dc +README.zh.md: 6c8b3b5694403c5e09f2904f5c3ca18fe569163e diff --git a/native/README.i18n.yaml b/native/README.i18n.yaml index ef2ada251f..a55273be29 100644 --- a/native/README.i18n.yaml +++ b/native/README.i18n.yaml @@ -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 native/README.md -README.md: 84808b2ee9dafa4f9f980c35a81ebe12480a4f5d -README.zh.md: d413c519b8460037af9f47ab39f6d533c927e22e +README.md: a79d9ca5747d4c4fbfa50745b3eece07b96aea58 +README.zh.md: 708430c0a087e5a9da1dae6fd078cb477db65d9b diff --git a/native/README.zh.md b/native/README.zh.md index d413c519b8..708430c0a0 100644 --- a/native/README.zh.md +++ b/native/README.zh.md @@ -2,21 +2,12 @@ [English](README.md) | 中文 -`node-addon-landlock-run` 的权威源码位于此处:这是 harness 从 npm 引入并使用的 Landlock「先限制自身、再执行」启动器(`packages/sandbox/sandbox-local`、`packages/bash/bash-sandbox`)。启动器在此处开发,与消费方相邻;独立仓库是打包并发布 npm 包系列的发布镜像。 +`node-addon-landlock-run` 的真源;它是供 harness 使用、先施加 Landlock 自限再执行命令的启动器。[`landlock-run/` workspace](landlock-run/README.md)负责其架构、包家族、平台支持、开发工作流和发布流程。独立仓库是发布镜像。 ## 发布镜像 -| 目录 | 镜像仓库 | 上次导出的发布版 | Commit | +| 目录 | 镜像仓库 | 最近导出的版本 | Commit | |---|---|---|---| | `landlock-run/` | https://github.com/deepseek-harness/node-addon-landlock-run | `v0.0.1` | `614f7fd7dc11e6eaceefba9e7ff1fbe28b51ba22` | -该子树是一个自包含的 pnpm workspace,拥有自己的 `AGENTS.md`、文档、门禁和锁文件;它不属于 harness workspace(`pnpm-workspace.yaml` 不包含它),因此 harness 的安装、构建和 CI 门禁绝不会触及它。镜像的 `.github/` 不进入该子树;[.github/workflows/landlock-run.yml](../.github/workflows/landlock-run.yml)(手动触发)在此处运行子树的 CI 任务,对这些任务的更改会在下次导出时镜像到镜像仓库的 `ci.yml`。 - -## 导出流程(发布新版本) - -1. 先通过常规 harness PR 将启动器更改落地于此;触发 `Landlock Run` 工作流,并确保其所有任务通过。 -2. 在镜像 checkout 中替换 `.github/` 以外的所有内容:`git -C rm -rq -- . ':!.github'`,然后执行 `git -C archive HEAD:native/landlock-run | tar -x -C `,最后执行 `git -C add -A` 并提交。 -3. 在镜像中按照其发布清单(`docs/release.md`)操作:`pnpm release:commit ` → 合并 → 标记 `vX.Y.Z` → 两阶段 `Release` 工作流(先以 `publish=false` 预演,再从标签以 `publish=true` 发布)。 -4. 使用已发布的标签/commit 更新上方 manifest(元数据清单)表,并在同一更改中上调 harness 消费方的依赖版本范围。 - -发布镜像不得与此处的权威源码产生分歧:如果更改直接提交到镜像中(例如发布期间的热修复),必须在下次导出前将其移植回此处。 +该子树是自包含的 pnpm workspace,不属于 harness workspace。[启动器发布参考](landlock-run/docs/release.md)负责导出和发布工作流。镜像不得发生分歧:下次导出前,必须把任何直接施加于镜像的热修复移植回此处。 diff --git a/packages/acp/acp/README.i18n.yaml b/packages/acp/acp/README.i18n.yaml index 6ee3e25ed1..441b6b9b7a 100644 --- a/packages/acp/acp/README.i18n.yaml +++ b/packages/acp/acp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/acp/acp/README.md -README.md: 583025e94d72c1ab03d282f8f4eb101c4e6f4740 -README.zh.md: ffffd97daa911636336ee9a515bf01bad33d31ae +README.md: 7bcf3c2581b25cb3c5223d00c05d9df6a7f6d61e +README.zh.md: 97a8e9c164bab71cb367ad54ce0bcc19ab4314a9 diff --git a/packages/acp/acp/README.md b/packages/acp/acp/README.md index 583025e94d..7bcf3c2581 100644 --- a/packages/acp/acp/README.md +++ b/packages/acp/acp/README.md @@ -4,7 +4,7 @@ English | [中文](README.zh.md) Automation-only [Agent Client Protocol](https://agentclientprotocol.com) server over JSON-RPC stdio. Programmatic clients create fresh harness agents, send text prompts, collect committed assistant text, resolve one-shot permission requests by policy, and cancel work. The primary in-repository client is [`dsh-subagent-acp`](../../subagent/subagent-acp/README.md). -This package is a transport adapter, not a UI integration or a capability seam. It does not expose editor navigation, transcript replay, commands, modes, configuration pickers, elicitation, reasoning, plans, titles, or tool presentation. Interactive rendering and human questions belong to the web and TUI modules. +This package is a transport adapter, not a UI integration or a capability seam. It does not expose editor navigation, transcript replay, commands, modes, configuration pickers, elicitation, reasoning, plans, titles, or tool presentation. Interactive rendering and human questions belong to the Web host and client modules. ## Plugin diff --git a/packages/acp/acp/README.zh.md b/packages/acp/acp/README.zh.md index ffffd97daa..97a8e9c164 100644 --- a/packages/acp/acp/README.zh.md +++ b/packages/acp/acp/README.zh.md @@ -4,7 +4,7 @@ 通过 JSON-RPC stdio 提供的仅面向自动化的 [ACP(Agent Client Protocol)](https://agentclientprotocol.com) 服务器。程序化客户端可以创建新 harness agent(智能体)、发送文本提示词、收集已提交的 assistant 文本、按策略响应一次性权限请求并取消工作。仓库中的主要客户端是 [`dsh-subagent-acp`](../../subagent/subagent-acp/README.md)。 -此包是传输适配器,而非 UI 集成或能力 seam。它不公开编辑器导航、transcript(文本记录)回放、命令、模式、配置选择器、信息征集、推理(reasoning)、计划、标题或工具展示。交互式渲染与向用户提问属于 Web 和 TUI 模块。 +此包是传输适配器,而非 UI 集成或能力 seam。它不公开编辑器导航、transcript(文本记录)回放、命令、模式、配置选择器、信息征集、推理(reasoning)、计划、标题或工具展示。交互式渲染与向用户提问属于 Web 宿主和客户端模块。 ## 插件 diff --git a/packages/bash/README.i18n.yaml b/packages/bash/README.i18n.yaml index 66cc852e04..4c4a2c6180 100644 --- a/packages/bash/README.i18n.yaml +++ b/packages/bash/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/README.md -README.md: ef82e9f4684ecf551ac7701d812088dd6b2ef6d0 -README.zh.md: 84ff244ec3d1ff5d385a3eb334e4cf9f1fe31e03 +README.md: 601782caad24d3555a206365a3f1954d81af1cf0 +README.zh.md: 8b96c4f80ba8776bfd8bdde178178cea946ff36c diff --git a/packages/bash/README.md b/packages/bash/README.md index ef82e9f468..601782caad 100644 --- a/packages/bash/README.md +++ b/packages/bash/README.md @@ -2,16 +2,16 @@ English | [中文](README.zh.md) -The capability family spans the canonical executor seam, its implementations, the shared shell environment, and the model-facing tools. All **product** packages. +The capability family spans the canonical executor seam, its implementations, the shared shell environment, and the model-facing tools. All are **product** packages. | Package | Role | ctx key | |---|---|---| -| `bash/` | Abstract bash executor seam (interface + vocabulary; sandbox result facts carry the [`sandbox/`](../sandbox/README.md) seam's mode/enforcement vocabulary, and the managed-env/output vocabulary is re-exported from the [`subprocess/`](../subprocess/README.md) seam) | `ctx.bash` | -| `bash-local/` | Local `BashExecutor` implementation over the [`subprocess/`](../subprocess/README.md) service (command defaulting, deadlines, terminal env, background-read merge) | (registers `ctx.bash`) | -| `bash-sandbox/` | Sandbox-consuming `BashExecutor` (wraps every command argv via `ctx.sandbox`, stamps denial/enforcement facts; extends `bash-local`'s mechanics) | (registers `ctx.bash`) | -| `pwsh-local/` | Local PowerShell `BashExecutor` implementation over the [`subprocess/`](../subprocess/README.md) service (executable resolution, UTF-8-pinned spawn, Windows termination semantics) | (registers `ctx.bash`) | -| `bash-env/` | Tool-independent managed `DSH_*` shell environment registry shared by the shell tools (built-in facts + effect-scoped contributors) | (registers `ctx.bashEnv`) | -| `tool-bash/` | Model-facing `bash` schema; background processes register with the generic [`tasks/`](../tasks/README.md) runtime | (registers on `ctx.tools`) | -| `tool-pwsh/` | Model-facing PowerShell-dialect `pwsh` schema (behavior mirrors `tool-bash` minus the sandbox surface); background processes register with the generic [`tasks/`](../tasks/README.md) runtime | (registers on `ctx.tools`) | +| [`bash/`](bash/README.md) | Defines the executor contract shared by implementations and consumers. | `ctx.bash` | +| [`bash-local/`](bash-local/README.md) | Executes commands through the local [`subprocess`](../subprocess/README.md) service. | (registers `ctx.bash`) | +| [`bash-sandbox/`](bash-sandbox/README.md) | Applies the configured [`sandbox`](../sandbox/README.md) backend before local execution. | (registers `ctx.bash`) | +| [`pwsh-local/`](pwsh-local/README.md) | Executes PowerShell commands with Windows-specific process behavior. | (registers `ctx.bash`) | +| [`bash-env/`](bash-env/README.md) | Provides the managed `DSH_*` environment shared by shell tools. | `ctx.bashEnv` | +| [`tool-bash/`](tool-bash/README.md) | Exposes Bash execution and background-task integration to the model. | (registers on `ctx.tools`) | +| [`tool-pwsh/`](tool-pwsh/README.md) | Exposes PowerShell execution to the model. | (registers on `ctx.tools`) | -The interface lives at `bash/bash/`. `bash-sandbox` replacing `bash-local` without touching the interface or the tool is the split doing exactly what it exists for — a leaf `cordis.yml` picks one executor entry, plus a `ctx.sandbox` provider entry for the confined one (see [the acp-agent example's default composition](../../examples/acp-agent/)). +A leaf `cordis.yml` selects one executor implementation and the model-facing tools it needs. A sandboxed composition also selects a `ctx.sandbox` provider; the [ACP example](../../examples/acp-agent/) shows one complete wiring. diff --git a/packages/bash/README.zh.md b/packages/bash/README.zh.md index 84ff244ec3..8b96c4f80b 100644 --- a/packages/bash/README.zh.md +++ b/packages/bash/README.zh.md @@ -1,17 +1,17 @@ -# bash/:bash 能力家族 +# bash/ — bash 能力家族 [English](README.md) | 中文 -能力家族横跨规范执行器 seam、其实现、共享 shell 环境与面向模型的工具。这些全是**产品**包。 +该能力家族涵盖规范执行器 seam、其实现、共享 shell 环境和面向模型的工具。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `bash/` | 抽象 bash 执行器 seam(接口 + 词汇;沙箱结果事实携带 [`sandbox/`](../sandbox/README.md) seam 的模式/强制执行词汇,受管环境/输出词汇则从 [`subprocess/`](../subprocess/README.md) seam 重导出) | `ctx.bash` | -| `bash-local/` | 构建在 [`subprocess/`](../subprocess/README.md) 服务之上的本地 `BashExecutor` 实现(命令默认值补全、deadline、终端环境、后台读取合并) | (注册 `ctx.bash`) | -| `bash-sandbox/` | 消费沙箱的 `BashExecutor`(通过 `ctx.sandbox` 包装每个命令 argv,标记拒绝/强制执行事实;扩展 `bash-local` 的机制) | (注册 `ctx.bash`) | -| `pwsh-local/` | 构建在 [`subprocess/`](../subprocess/README.md) 服务之上的本地 PowerShell `BashExecutor` 实现(可执行文件解析、UTF-8 固定 spawn、Windows 终止语义) | (注册 `ctx.bash`) | -| `bash-env/` | 工具无关的受管 `DSH_*` shell 环境注册表,由 shell 工具共享(内置事实 + 受 effect 作用域约束的 contributor) | (注册 `ctx.bashEnv`) | -| `tool-bash/` | 面向模型的 `bash` schema;后台进程注册到通用 [`tasks/`](../tasks/README.md) 运行时 | (注册到 `ctx.tools`) | -| `tool-pwsh/` | 面向模型的 PowerShell 方言 `pwsh` schema(行为镜像 `tool-bash`,减去 sandbox 面);后台进程注册到通用 [`tasks/`](../tasks/README.md) 运行时 | (注册到 `ctx.tools`) | +| [`bash/`](bash/README.md) | 定义实现与消费方共享的执行器契约。 | `ctx.bash` | +| [`bash-local/`](bash-local/README.md) | 通过本地 [`subprocess`](../subprocess/README.md) 服务执行命令。 | (注册 `ctx.bash`) | +| [`bash-sandbox/`](bash-sandbox/README.md) | 在本地执行前应用已配置的 [`sandbox`](../sandbox/README.md) 后端。 | (注册 `ctx.bash`) | +| [`pwsh-local/`](pwsh-local/README.md) | 以 Windows 专用进程行为执行 PowerShell 命令。 | (注册 `ctx.bash`) | +| [`bash-env/`](bash-env/README.md) | 提供 shell 工具共享的托管 `DSH_*` 环境。 | `ctx.bashEnv` | +| [`tool-bash/`](tool-bash/README.md) | 向模型公开 Bash 执行和后台任务集成。 | (注册到 `ctx.tools`) | +| [`tool-pwsh/`](tool-pwsh/README.md) | 向模型公开 PowerShell 执行。 | (注册到 `ctx.tools`) | -接口位于 `bash/bash/`。以 `bash-sandbox` 替换 `bash-local`,同时不改动接口或工具,正是这种拆分存在的意义:叶级 `cordis.yml` 选择一个执行器插件条目;受限实现还需再选择一个 `ctx.sandbox` 提供方插件条目(见 [acp-agent 示例的默认组合](../../examples/acp-agent/))。 +叶节点 `cordis.yml` 选择一个执行器实现和所需的面向模型工具。沙箱化组合还会选择一个 `ctx.sandbox` 提供方;[ACP(Agent Client Protocol)示例](../../examples/acp-agent/)展示一套完整接线。 diff --git a/packages/bash/bash-env/README.i18n.yaml b/packages/bash/bash-env/README.i18n.yaml index 90d43daa7b..47f4cf01cc 100644 --- a/packages/bash/bash-env/README.i18n.yaml +++ b/packages/bash/bash-env/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/bash-env/README.md README.md: 7b939326d4effd14fc83ef0ad4e133f019f1011f -README.zh.md: aeb33629def3fcc10294bbca19b37d43dcc73a0c +README.zh.md: b6f3aca41771f1ca990a4b708cd53119b8e8ad78 diff --git a/packages/bash/bash-env/README.zh.md b/packages/bash/bash-env/README.zh.md index aeb33629de..b6f3aca417 100644 --- a/packages/bash/bash-env/README.zh.md +++ b/packages/bash/bash-env/README.zh.md @@ -17,7 +17,7 @@ ## Managed environment -每次前台与后台模型 shell 调用都会收到一份新收集的受信任 `DSH_*` 环境。`DSH_HOME` 是由 [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) 解析的 Harness 主目录绝对路径(`dshHome` 配置,然后环境变量 `$DSH_HOME`,然后 `~/.dsh`),`DSH_SHELL=1` 标识受管理的子进程。带 agent 的调用额外收到 `DSH_SESSION_ID=agent.session.header.id`;当活动的持久化 seam 定位到 JSONL 工件时,它们还会收到 `DSH_SESSION_JSONL=<绝对目标路径>`。JSONL 路径只是位置提示:首次 flush 之前它可能不存在,也不一定包含当前缓冲中的轮次,并且它不是授权凭据。 +每次前台与后台模型 shell 调用都会收到一份新收集的受信任 `DSH_*` 环境。`DSH_HOME` 是由 [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) 解析的 Harness 主目录绝对路径(`dshHome` 配置,然后环境变量 `$DSH_HOME`,然后 `~/.dsh`),`DSH_SHELL=1` 标识受管理的子进程。带 agent(智能体)的调用额外收到 `DSH_SESSION_ID=agent.session.header.id`;当活动的持久化 seam 定位到 JSONL 工件时,它们还会收到 `DSH_SESSION_JSONL=<绝对目标路径>`。JSONL 路径只是位置提示:首次 flush 之前它可能不存在,也不一定包含当前缓冲中的轮次,并且它不是授权凭据。 `ctx.bashEnv` 负责收集。其他插件可以注册一个受 effect 作用域约束的 contributor,带有稳定名称、已声明的键/描述以及 `resolve(execution: ToolExecution)`;重复所有权与未声明的运行时键会响亮失败,而 `list()` 只枚举声明、不执行 provider。Harness 内置键保留 `DSH_HOME`、`DSH_SHELL` 与 `DSH_SESSION_ID`;本插件的持久化翻译器通过读取与后端无关的 `sessionPersistence.locate()` seam 拥有 `DSH_SESSION_JSONL`。 diff --git a/packages/bash/bash-local/README.i18n.yaml b/packages/bash/bash-local/README.i18n.yaml index 9235db170e..4d0214386f 100644 --- a/packages/bash/bash-local/README.i18n.yaml +++ b/packages/bash/bash-local/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/bash-local/README.md -README.md: 694b7a7686ea6c38da5a354ff6b6e6d2c4520706 -README.zh.md: 8d6e3b8d2fdd92faeb7b1e6e373a938ae2064cd4 +README.md: bb87ad6fe021e3144cef4adced3d798bf3d94d67 +README.zh.md: d2f8c9091072bbf3d75909f6826432601001ab88 diff --git a/packages/bash/bash-local/README.zh.md b/packages/bash/bash-local/README.zh.md index 8d6e3b8d2f..d2f8c90910 100644 --- a/packages/bash/bash-local/README.zh.md +++ b/packages/bash/bash-local/README.zh.md @@ -20,15 +20,13 @@ graceMs: 3000 # kill escalation and post-exit pipe-drain grace ``` -## 行为(以及设计来源) +## 行为 -设计时调研了 Claude Code、OpenCode、Codex 和 pi 的 bash 工具,主要取舍如下: - -- **每次调用都 spawn,不保留 shell 状态**:每次调用都启动新的非登录 `bash -c`(行为确定,不读取 rc 文件)。调研的四种工具均会每次调用单独 spawn。`XXX(stateful-shell)` 位于 `src/index.ts`,记录了两种已验证的有状态设计(Claude Code 仅持久化 cwd;Codex 使用 PTY exec 会话),供真实工作流需要时采用。 -- **在受管进程组之上应用配置预算**:`resolve()` 从配置补全 `workdir`/`timeoutMs`/`stdoutMaxBytes`,每次 spawn 都向服务传入显式的字节上限、spill 上限与 `graceMs`(默认 3 秒,沿用 OpenCode 的升级策略)。进程组终止、退出后的管道排空宽限期、尾部保留截断与有界 spill 文件是 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) 的机制。前台 `BashExecRequest.stdoutMaxBytes` 可为某个受信任调用方提高单次 stdout 捕获预算;stderr 和后台运行仍使用 `maxOutputBytes`。 +- **每次调用都 spawn,不保留 shell 状态**:每次调用都启动新的非登录 `bash -c`,且不读取 rc 文件。 +- **在受管进程组之上应用配置预算**:`resolve()` 从配置补全 `workdir`/`timeoutMs`/`stdoutMaxBytes`,每次 spawn 都向服务传入显式的字节上限、spill 上限与 `graceMs`。进程组终止、退出后管道排空、尾部保留与有界 spill 文件是 [`dsh-subprocess-local`](../../subprocess/subprocess-local/README.md) 的机制。前台 `BashExecRequest.stdoutMaxBytes` 可为某个受信任调用方提高单次 stdout 捕获预算;stderr 和后台运行仍使用 `maxOutputBytes`。 - **超时与取消分类**:`run()` 通过同一个 deadline 把经配置钳位的超时与调用方的信号融合;只有执行器自身的超时报告 `timedOut`,上游取消报告 `aborted`,自身因信号终止的命令两者皆不报告(见[超时库 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md))。 -- **适合模型的终端环境**:设置 `NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat`(Codex 硬编码的集合),防止分页器与 ANSI 颜色破坏结果;这些条目作为普通 env 合并,遵循服务的凭据清除与 `DSH_*` 通道规则;调用方的显式条目依旧优先。详见 [stdin/env Agent Note](../../../.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)。 -- **后台进程**:`start()` 会立即返回活动的 `BashProcess` 句柄,不应用超时(Claude Code 在转为后台时会解除超时);句柄的 `readOutput()` 把服务基于偏移量的 stdout/stderr 读取合并为一条带分节标记的增量,并以消费游标记录读取进度。仍在运行的进程则由 subprocess 服务负责,因此它能在执行器重载后存活,并随服务的 dispose 被终止且等待退出。所有具有任务形态的事项(id、所有权、轮询、通知)都属于通用 [`ctx.tasks` 运行时](../../tasks/tasks/README.md),工具层会在其中注册该句柄;本执行器不会接触会话或注册表。 +- **适合模型的终端环境**:`NO_COLOR=1 TERM=dumb PAGER=cat GIT_PAGER=cat` 防止分页器与 ANSI 颜色破坏结果。这些值作为普通 env 合并,遵循服务的凭据清除与 `DSH_*` 通道规则;调用方的显式条目依旧优先。详见 [stdin/env Agent Note](../../../.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)。 +- **后台进程**:`start()` 会立即返回活动的 `BashProcess` 句柄且不应用超时;`readOutput()` 把基于偏移量的 stdout/stderr 读取合并为一条消费式增量,并在存在 stderr 时将其置于 `[stderr]` 标记下。运行中的进程属于 subprocess 服务,可在执行器重载后存活,并在服务 dispose 时被终止且等待退出。task id、所有权、轮询和通知属于通用 [`ctx.tasks` 运行时](../../tasks/tasks/README.md),工具层会在其中注册该句柄。 ## 模型体验 diff --git a/packages/bash/pwsh-local/README.i18n.yaml b/packages/bash/pwsh-local/README.i18n.yaml index 1b78ca75f9..ef6a984d07 100644 --- a/packages/bash/pwsh-local/README.i18n.yaml +++ b/packages/bash/pwsh-local/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/pwsh-local/README.md -README.md: 9deba9c1b63ccfdb9e1805b9896db33f144839bf -README.zh.md: e45c820e1d5e31aebd9ed365c6130850f1db2a62 +README.md: 2914c46ab91dd9555dab04551e52321f6eac05bf +README.zh.md: ce9696b276a2e60acf116d7124cd5cd256d7ebde diff --git a/packages/bash/pwsh-local/README.md b/packages/bash/pwsh-local/README.md index 9deba9c1b6..2914c46ab9 100644 --- a/packages/bash/pwsh-local/README.md +++ b/packages/bash/pwsh-local/README.md @@ -46,7 +46,7 @@ No direct invalidation; the named consumer owns any request-prefix changes. ## Known Limitations and Deferred Work - **Unconfined by itself** — this executor always runs commands with the harness process's authority; deployments needing confinement compose a sandboxing bash executor or policy instead. -- **No persistent shell or PTY** — every call starts a fresh `pwsh -Command`; interactive terminal sessions remain deferred until the roadmap's pwsh TUI/GUI rendering work lands. +- **No persistent shell or PTY** — every call starts a fresh `pwsh -Command`. - **The command string is PowerShell text** — the `-Command` domain has no shell-quoting layer, but a model-facing command is parsed by PowerShell itself, so PowerShell syntax errors are command failures, not launch failures. - **A background spawn-failure note is single-delivery** — the subprocess service buffers no output for a process that never ran, so the executor injects `spawn failed: …` into exactly one `readOutput()` delta; a reader that discards that delta cannot recover it. - **Windows termination reports no signal** — a force-killed process settles as exit 1 with `signal: null`, so signal-based status classification (POSIX `killed`) does not apply on Windows; `kill()`-initiated stops still stamp `killed` directly. diff --git a/packages/bash/pwsh-local/README.zh.md b/packages/bash/pwsh-local/README.zh.md index e45c820e1d..ce9696b276 100644 --- a/packages/bash/pwsh-local/README.zh.md +++ b/packages/bash/pwsh-local/README.zh.md @@ -46,7 +46,7 @@ ## 已知局限与延期工作 - **自身不设沙箱**——本执行器始终以 harness 进程的权限运行命令;需要约束的部署应组合沙箱化 bash 执行器或策略。 -- **无持久 shell 或 PTY**——每次调用都是全新的 `pwsh -Command`;交互式终端会话在路线图的 pwsh TUI/GUI 渲染工作落地之前保持延期。 +- **无持久 shell 或 PTY**——每次调用都是全新的 `pwsh -Command`。 - **命令字符串是 PowerShell 文本**——`-Command` 域没有 shell 引号层,但面向模型的命令由 PowerShell 自己解析,因此 PowerShell 语法错误是命令失败,而非启动失败。 - **后台 spawn 失败提示只投递一次**——subprocess 服务不会为从未运行的进程缓冲输出,因此执行器只把 `spawn failed: …` 注入一次 `readOutput()` 增量;丢弃该增量的读取方无法恢复它。 - **Windows 终止不报告信号**——被强制终止的进程以退出码 1、`signal: null` 结束,因此基于信号的状态分类(POSIX `killed`)在 Windows 上不适用;`kill()` 发起的停止仍会直接盖上 `killed`。 diff --git a/packages/bash/tool-bash/README.i18n.yaml b/packages/bash/tool-bash/README.i18n.yaml index fb33cffefb..e37f3a2859 100644 --- a/packages/bash/tool-bash/README.i18n.yaml +++ b/packages/bash/tool-bash/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/tool-bash/README.md README.md: 35a5647365dab6daa903c14cba8b83702b50305d -README.zh.md: eb7901f3445117988d77e6d39b73681480b7a754 +README.zh.md: 0c7b1c1ec83c5d30dd97bf5cdae1f626f4065ab7 diff --git a/packages/bash/tool-bash/README.zh.md b/packages/bash/tool-bash/README.zh.md index eb7901f344..0c7b1c1ec8 100644 --- a/packages/bash/tool-bash/README.zh.md +++ b/packages/bash/tool-bash/README.zh.md @@ -42,7 +42,7 @@ ## 工具仅使用具名参数构建请求 -`BashExecRequest` seam 携带可选的 `stdoutMaxBytes`、`stdin`、普通 `env` 和托管 `dshEnv`,供可信进程内插件及此工具的环境注册表使用。模型侧工具不公开 `stdoutMaxBytes`、`stdin` 或 `env`:它使用具名的命令/工作目录/超时/信号/沙箱字段,加上从注册表收集的 `dshEnv` 来构建请求。额外模型键会被忽略,无法替换托管值。Shell 语法可以提供等价的命令级行为,而本地执行器会清除环境中的凭据和陈旧 `DSH_*` 值。参见 [stdin/env Agent Note(agent 决策记录)](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)。 +`BashExecRequest` seam 携带可选的 `stdoutMaxBytes`、`stdin`、普通 `env` 和托管 `dshEnv`,供可信进程内插件及此工具的环境注册表使用。模型侧工具不公开 `stdoutMaxBytes`、`stdin` 或 `env`:它使用具名的命令/工作目录/超时/信号/沙箱字段,加上从注册表收集的 `dshEnv` 来构建请求。额外模型键会被忽略,无法替换托管值。Shell 语法可以提供等价的命令级行为,而本地执行器会清除环境中的凭据和陈旧 `DSH_*` 值。参见 [stdin/env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md)。 ## 权限与升权 diff --git a/packages/bash/tool-pwsh/README.i18n.yaml b/packages/bash/tool-pwsh/README.i18n.yaml index 030d24c7c2..303648b718 100644 --- a/packages/bash/tool-pwsh/README.i18n.yaml +++ b/packages/bash/tool-pwsh/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/bash/tool-pwsh/README.md README.md: dfe26a63684d61dcdd6f969c2c2261dac79325c7 -README.zh.md: 2344f8477e5b15f2c4d366dd82b46358eacbc1b7 +README.zh.md: 87a30130c2f4c56be34199dca39399c45eb4b323 diff --git a/packages/bash/tool-pwsh/README.zh.md b/packages/bash/tool-pwsh/README.zh.md index 2344f8477e..87a30130c2 100644 --- a/packages/bash/tool-pwsh/README.zh.md +++ b/packages/bash/tool-pwsh/README.zh.md @@ -19,8 +19,8 @@ | `command` | string (required) | 通过 `pwsh -Command` 运行。调用之间不保留状态——用 `workdir`,不要用 `cd`。 | | `description` | string (required) | 命令的一行主动语态摘要(5-10 词),仅用于 UI/日志展示——不影响执行。 | | `timeoutMs` | number | 超时覆盖值(毫秒)。执行器应用其配置的默认值与上限。 | -| `workdir` | string | 本次调用的工作目录。默认取调用 agent 的会话 cwd(`session.header.cwd`),使每个会话在自己的工作区运行;相对 `workdir` 基于同一身份解析。 | -| `run_in_background` | boolean | 立即返回任务 id;不适用超时。 | +| `workdir` | string | 本次调用的工作目录。默认取调用 agent(智能体)的会话 cwd(`session.header.cwd`),使每个会话在自己的工作区运行;相对 `workdir` 基于同一身份解析。 | +| `run_in_background` | boolean | 立即返回 task id;不适用超时。 | `command`、`workdir` 与 `timeoutMs` 在执行前经 `ctx.bash.resolve()` 按执行器配置默认值解析。workdir 默认值在工具层于 `resolve()` 之前从调用 agent 的 `session.header.cwd` 取得——每次会话的 cwd 必须来自 `exec.agent`,因为 N 个会话共享一个执行器;仅当没有会话 cwd 时执行器才回退到自己的配置 / `process.cwd()`。 diff --git a/packages/client/AGENTS.md b/packages/client/AGENTS.md index 8bf351220c..0378137364 100644 --- a/packages/client/AGENTS.md +++ b/packages/client/AGENTS.md @@ -60,16 +60,16 @@ One UI feature = one plugin package (`src/client/` browser half). A multi-domain ## Styling -[docs/web-styling.md](../../docs/web-styling.md) is authoritative. In short: design tokens live in `web-ui/src/style/global.css` (`:root` light values, `[data-theme='dark']` overrides); component CSS references tokens only — no literal color values. CSS Modules + `clsx`; no component library, no tailwind ([framework ruling](../../.agents/notes/implemented/process/2026-07-19-web-styling-system.md)). Product copy is Chinese; code comments are English. +[docs/web-styling.md](../../docs/web-styling.md) is authoritative. Shared `--dsw-*` tokens and global sheets live in `ui-theme/src/styles/`; feature components consume semantic aliases through CSS Modules and `clsx`, with no literal colors, component library, or Tailwind. Product copy is Chinese; code comments are English. ## Testing and coverage The GUI test structure (three tiers, lane map) is settled in the [GUI testing system note](../../.agents/notes/implemented/process/2026-07-20-gui-testing-system.md); repo-wide policy in [docs/testing.md](../../docs/testing.md). -- **Both client packages are inside the per-file 100% coverage gate** (`pnpm run test:coverage`). `web-runtime` is covered by node-env object/protocol suites; `web-ui` rides the jsdom lane. Genuinely unreachable defensive arms take a `/* v8 ignore -- */` comment with a real reason, never a bare ignore. -- **web-ui specs are end-to-end behavior checks, not unit tests.** A jsdom spec renders the component with realistic props (or a driven fixture runtime) and asserts what the user would see — never class names, hook internals, or render counts. Components are consumables: behavior-shaped specs survive a rewrite, implementation-shaped specs don't. -- The jsdom environment comes from a per-file `// @vitest-environment jsdom` pragma on the spec's first line — the shared config stays node-env. Start a new spec from an existing one (`web-ui/tests/tool-card.spec.tsx` is a good template). -- **Each tier asserts its own layer.** Data-layer semantics (state machines, wire shapes, reference stability) belong to the `web-runtime` and `apiproxy` suites — don't re-assert them from component specs. +- Client source packages are inside the per-file 100% coverage gate (`pnpm run test:coverage`). Genuinely unreachable defensive arms take a `/* v8 ignore -- */` comment with a real reason, never a bare ignore. +- Component specs render with realistic props or a driven fixture runtime and assert user-visible behavior, not class names, hook internals, or render counts. +- The jsdom environment comes from a per-file `// @vitest-environment jsdom` pragma on the spec's first line; the shared config stays node-env. +- Each tier asserts its own layer. Data-layer semantics belong to the runtime and host suites; component specs cover presentation behavior. ## Before you push: the local check ladder diff --git a/packages/client/README.i18n.yaml b/packages/client/README.i18n.yaml index 210228ae88..503a747087 100644 --- a/packages/client/README.i18n.yaml +++ b/packages/client/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/README.md -README.md: 31d884c04a8b0233713b77b82b3d9cc7052003ca -README.zh.md: 9c95db529306519136d6d68758889350e5dd65e4 +README.md: b950772d4cad6d873426f8aee6416fa56afca2ee +README.zh.md: 8f1f7f46777b7037e8baa04c9ec16ef74ffd478d diff --git a/packages/client/README.md b/packages/client/README.md index 31d884c04a..b950772d4c 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -2,33 +2,38 @@ English | [中文](README.zh.md) -The browser side of the dsh web GUI: shell kernel, module system, wire consumer, React-free object services, the slot system, and the `ui-*` feature-plugin roster. Authoring rules live in [AGENTS.md](AGENTS.md); the host half is [`host/`](../host/README.md). All **product** packages, named `@deepseek-ai/dsh-client-`. +The browser side of the dsh web GUI: shell boot, browser-host communication, shared UI services, and feature plugins. Authoring rules live in [AGENTS.md](AGENTS.md); the host half is [`host/`](../host/README.md). All except `test-runtime` are **product** packages named `@deepseek-ai/dsh-client-`. -| Package | Role | ctx key / slot | -|---|---|---| -| `web/` | Shell kernel: `AppWebEntry` runs the two-stage boot over the host-pushed entry graph | (boots the tree) | -| `modules/` | Client module system: browser peer of Node's ESM loader as a lazy CJS table under the vendored cordis Loader | (module face) | -| `web-react/` | Shell-side React glue: `createSlotRenderer` + `SessionProvider` render seats | (renderer install) | -| `connection/` | Wire consumer both ends: browser `ctx.connection` (shared api client + stream loop) and the node half mounting the `/api` route with its browser-trust fence | `ctx.connection` | -| `runtime/` | Client cordis boot and React-free object services: slots, Sessions, Workspaces, per-session bindings | `ctx.slots` `ctx.sessions` `ctx.workspaces` | -| `hmr/` | Dev-only hot reload for script-loaded client plugins (`--dev` graphs) | (dev entry) | -| `locale/` | Browser locale preference (`zh`/`en`) plus the ns×locale dictionary registry | `ctx.locale` | -| `ui-slots/` | Slot registry pure core: SlotMap merging, single `register` API, the four-share props family | (types + core) | -| `ui-theme/` | Theme preference over the `--dsw-*` token stylesheets (`light`/`dark`/`system`) | `ctx.theme` | -| `ui-primitives/` | Pure React atoms: icons, Button/Pill/Menu/Modal/Input, markdown family | (component library) | -| `ui-layout/` | Shell three-column AppFrame; declares `sidebar` / `conversation` / `details` / `conversation.empty` | `ctx.layout` | -| `ui-sidebar/` | Sidebar shell: Workspace/session rail, search, collapse; declares `sidebar.workspaces` | (slot host) | -| `ui-workspace/` | Shared Workspace picker: browser region + hero picker over the same creation flow | (fills `sidebar.workspaces`, `conversation.hero.workspace`) | -| `ui-conversation/` | Conversation domain: skeleton, chat view, input dock, per-tool row slots | (slot host) | -| `ui-trajectory/` | Trajectory/Waterfall view tabs; the minimal pure-consumer plugin exemplar | (fills `conversation.view`) | -| `ui-command/` | Command surface: session-keyed directory cache, `/` source, three-kind dispatch | `ctx.command` | -| `ui-slash/` | Input trigger pipeline: `/` and `@` detection, grouped candidate menu, source roster | `ctx.slash` | -| `ui-skill/` | `/`-trigger skill reference source over the `skill.list` RPC | (registers into `ctx.slash`) | -| `ui-subagent/` | `@`-trigger subagent reference source over the sessions snapshot | (registers into `ctx.slash`) | -| `ui-model/` | Model selection: `/model` popupSelect + the composer model seat over `ModelService` | `ctx.models` | -| `ui-question/` | Web `ask_user_question`: host half mounts the tool, browser half fills the composer seat | (fills `conversation.composer`) | -| `ui-settings/` | Settings shell: trigger chrome + modal panel; declares the `settings.*` slots | (slot host) | -| `ui-settings-general/` | Settings ownerless copy: chrome content + General section skeleton | (fills `settings.*`) | -| `ui-models/` | Models settings nav entry (content column lands in a later phase) | (fills `settings.section`) | +| Package | Purpose | +|---|---| +| [`web/`](web/README.md) | Boots the browser shell from the client entry graph. | +| [`modules/`](modules/README.md) | Loads browser-side client modules. | +| [`web-react/`](web-react/README.md) | Connects the shell runtime to React rendering. | +| [`connection/`](connection/README.md) | Maintains browser-host RPC communication and event delivery. | +| [`runtime/`](runtime/README.md) | Provides shared client services for sessions, workspaces, and UI composition. | +| [`hmr/`](hmr/README.md) | Refreshes client plugins during development. | +| [`locale/`](locale/README.md) | Provides localization preferences and message dictionaries. | +| [`schema-form/`](schema-form/README.md) | Provides schema-backed draft handling for settings editors. | +| [`test-runtime/`](test-runtime/README.md) | Provides shared repository test support for client feature packages. | +| [`ui-slots/`](ui-slots/README.md) | Defines how UI features register and compose extension slots. | +| [`ui-theme/`](ui-theme/README.md) | Applies the selected color theme. | +| [`ui-primitives/`](ui-primitives/README.md) | Provides shared React controls, icons, and content renderers. | +| [`ui-layout/`](ui-layout/README.md) | Arranges the main application regions. | +| [`ui-sidebar/`](ui-sidebar/README.md) | Presents workspace and session navigation. | +| [`ui-workspace/`](ui-workspace/README.md) | Provides workspace selection and creation surfaces. | +| [`ui-conversation/`](ui-conversation/README.md) | Presents the active conversation and its input surface. | +| [`ui-goal/`](ui-goal/README.md) | Presents and manages the current goal. | +| [`ui-trajectory/`](ui-trajectory/README.md) | Presents alternate views of agent activity. | +| [`ui-command/`](ui-command/README.md) | Provides session-aware command discovery and dispatch. | +| [`ui-slash/`](ui-slash/README.md) | Coordinates inline command and reference suggestions. | +| [`ui-skill/`](ui-skill/README.md) | Adds skill references to inline suggestions. | +| [`ui-subagent/`](ui-subagent/README.md) | Provides subagent navigation, child transcript states, and inline references. | +| [`ui-model/`](ui-model/README.md) | Provides model selection in conversation surfaces. | +| [`ui-permission/`](ui-permission/README.md) | Configures default permissions and switches the current session's access. | +| [`ui-plan/`](ui-plan/README.md) | Presents active plan-mode status and its exit control. | +| [`ui-question/`](ui-question/README.md) | Presents interactive questions requested by the agent. | +| [`ui-settings/`](ui-settings/README.md) | Hosts the settings interface and its extension areas. | +| [`ui-settings-general/`](ui-settings-general/README.md) | Provides the general settings section. | +| [`ui-models/`](ui-models/README.md) | Provides model-provider configuration and DeepSeek onboarding. | -Feature UI composes only through the slot system (`ctx.slots.register`) — the [slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) is the definitive model; the [web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) owns the loading chain and object layer. +Each child reference owns its contract and detailed behavior. The [slot system standard](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md) and [web client architecture note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) own the cross-package composition and loading decisions. diff --git a/packages/client/README.zh.md b/packages/client/README.zh.md index 9c95db5293..8f1f7f4677 100644 --- a/packages/client/README.zh.md +++ b/packages/client/README.zh.md @@ -2,33 +2,38 @@ [English](README.md) | 中文 -dsh web GUI 的浏览器侧:shell 内核、模块系统、协议消费层、无 React 依赖的对象服务、slot 系统,以及 `ui-*` 特性插件阵列。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.md)。全部为**产品**包,命名为 `@deepseek-ai/dsh-client-`。 +dsh web GUI 的浏览器侧:shell 启动、浏览器与宿主通信、共享 UI 服务和特性插件。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.md)。除 `test-runtime` 外,均为命名成 `@deepseek-ai/dsh-client-` 的**产品**包。 -| 包 | 角色 | ctx 键/slot | -|---|---|---| -| `web/` | shell 内核:`AppWebEntry` 基于宿主推送的条目图运行两阶段启动 | (启动整棵树) | -| `modules/` | 客户端模块系统:Node ESM 加载器的浏览器对等物,是 vendored cordis Loader 之下的惰性 CJS 表 | (模块面) | -| `web-react/` | shell 侧 React 胶水:`createSlotRenderer` + `SessionProvider` 渲染座位 | (渲染器安装) | -| `connection/` | 协议两端的消费者:浏览器侧 `ctx.connection`(共享 api 客户端 + 流循环),node 半侧挂载带浏览器信任栅栏的 `/api` 路由 | `ctx.connection` | -| `runtime/` | 客户端 cordis 启动与无 React 对象服务:slots、Session、Workspace、逐会话绑定 | `ctx.slots` `ctx.sessions` `ctx.workspaces` | -| `hmr/` | 仅开发用的外部脚本加载型客户端插件热重载(`--dev` 图) | (开发条目) | -| `locale/` | 浏览器语言偏好(`zh`/`en`)与 ns×locale 词典注册表 | `ctx.locale` | -| `ui-slots/` | slot 注册表纯核心:SlotMap 合并、单一 `register` API、四份额 props 族 | (类型 + 核心) | -| `ui-theme/` | 基于 `--dsw-*` token 样式表的主题偏好(`light`/`dark`/`system`) | `ctx.theme` | -| `ui-primitives/` | 纯 React 原子:图标、Button/Pill/Menu/Modal/Input、markdown 族 | (组件库) | -| `ui-layout/` | shell 三栏 AppFrame;声明 `sidebar`/`conversation`/`details`/`conversation.empty` | `ctx.layout` | -| `ui-sidebar/` | 侧栏 shell:Workspace/会话栏、搜索、折叠;声明 `sidebar.workspaces` | (slot 宿主) | -| `ui-workspace/` | 共享 Workspace 选择器:浏览区域 + hero 选择器共用同一创建流程 | (填充 `sidebar.workspaces`、`conversation.hero.workspace`) | -| `ui-conversation/` | 会话域:骨架、聊天视图、输入坞、逐工具行 slot | (slot 宿主) | -| `ui-trajectory/` | Trajectory/Waterfall 视图标签;最小纯消费者插件范例 | (填充 `conversation.view`) | -| `ui-command/` | 命令面:按会话键控的目录缓存、`/` 源、三类分发 | `ctx.command` | -| `ui-slash/` | 输入触发流水线:光标下的 `/` 与 `@` 检测、分组候选菜单、源名册 | `ctx.slash` | -| `ui-skill/` | 基于 `skill.list` RPC 的 `/` 触发技能引用源 | (注册进 `ctx.slash`) | -| `ui-subagent/` | 基于会话快照的 `@` 触发子代理引用源 | (注册进 `ctx.slash`) | -| `ui-model/` | 模型选择:`/model` popupSelect + 输入坞模型座位,均由 `ModelService` 驱动 | `ctx.models` | -| `ui-question/` | Web `ask_user_question`:宿主半侧挂载工具,浏览器半侧填充输入坞座位 | (填充 `conversation.composer`) | -| `ui-settings/` | 设置 shell:触发 chrome + 模态面板;声明 `settings.*` slot | (slot 宿主) | -| `ui-settings-general/` | 设置的无主文案:chrome 内容 + General 分区骨架 | (填充 `settings.*`) | -| `ui-models/` | 模型设置导航项(内容列留待后续阶段) | (填充 `settings.section`) | +| 包 | 目的 | +|---|---| +| [`web/`](web/README.md) | 从客户端条目图启动浏览器 shell。 | +| [`modules/`](modules/README.md) | 加载浏览器侧客户端模块。 | +| [`web-react/`](web-react/README.md) | 连接 shell 运行时与 React 渲染。 | +| [`connection/`](connection/README.md) | 维护浏览器与宿主之间的 RPC 通信和事件传递。 | +| [`runtime/`](runtime/README.md) | 为会话、Workspace 和 UI 组合提供共享客户端服务。 | +| [`hmr/`](hmr/README.md) | 在开发期间刷新客户端插件。 | +| [`locale/`](locale/README.md) | 提供本地化偏好与消息词典。 | +| [`schema-form/`](schema-form/README.md) | 为设置编辑器提供 schema 驱动的草稿处理。 | +| [`test-runtime/`](test-runtime/README.md) | 为客户端特性包提供共享的仓库测试支持。 | +| [`ui-slots/`](ui-slots/README.md) | 定义 UI 特性注册和组合扩展 slot 的方式。 | +| [`ui-theme/`](ui-theme/README.md) | 应用所选颜色主题。 | +| [`ui-primitives/`](ui-primitives/README.md) | 提供共享 React 控件、图标和内容渲染器。 | +| [`ui-layout/`](ui-layout/README.md) | 排列应用的主要区域。 | +| [`ui-sidebar/`](ui-sidebar/README.md) | 展示 Workspace 与会话导航。 | +| [`ui-workspace/`](ui-workspace/README.md) | 提供 Workspace 选择与创建界面。 | +| [`ui-conversation/`](ui-conversation/README.md) | 展示当前会话及其输入界面。 | +| [`ui-goal/`](ui-goal/README.md) | 展示和管理当前目标。 | +| [`ui-trajectory/`](ui-trajectory/README.md) | 提供 agent(智能体)活动的其他视图。 | +| [`ui-command/`](ui-command/README.md) | 提供会话感知的命令发现与分发。 | +| [`ui-slash/`](ui-slash/README.md) | 协调内联命令和引用建议。 | +| [`ui-skill/`](ui-skill/README.md) | 向内联建议添加 skill(技能)引用。 | +| [`ui-subagent/`](ui-subagent/README.md) | 提供 subagent 导航、子会话记录状态和内联引用。 | +| [`ui-model/`](ui-model/README.md) | 在会话界面中提供模型选择。 | +| [`ui-permission/`](ui-permission/README.md) | 配置默认权限并切换当前会话的访问模式。 | +| [`ui-plan/`](ui-plan/README.md) | 展示生效中的 plan mode 状态及其退出控件。 | +| [`ui-question/`](ui-question/README.md) | 展示 agent 请求的交互式问题。 | +| [`ui-settings/`](ui-settings/README.md) | 承载设置界面及其扩展区域。 | +| [`ui-settings-general/`](ui-settings-general/README.md) | 提供常规设置分区。 | +| [`ui-models/`](ui-models/README.md) | 提供模型提供方配置与 DeepSeek 配置引导。 | -特性 UI 只通过 slot 系统组合(`ctx.slots.register`)——[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)是权威模型;[web 客户端架构 Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md) 拥有加载链与对象层。 +每个子文档负责自身的契约和详细行为。[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)与 [Web 客户端架构 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md)负责跨包组合与加载决策。 diff --git a/packages/client/connection/README.i18n.yaml b/packages/client/connection/README.i18n.yaml index 8662c365e1..79102ffddc 100644 --- a/packages/client/connection/README.i18n.yaml +++ b/packages/client/connection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/connection/README.md -README.md: be521feb4826f9ad804f61532dee0aa85f39de7e -README.zh.md: bd58ce649c75143b94c1e5dea0241e1f9a934e69 +README.md: a2ca8a1d2cdebaeb7c4223aae33e5cd9e1a60884 +README.zh.md: 6b031e58425c53a8adc4153a70a04ec1c1486b97 diff --git a/packages/client/connection/README.md b/packages/client/connection/README.md index be521feb48..a2ca8a1d2c 100644 --- a/packages/client/connection/README.md +++ b/packages/client/connection/README.md @@ -6,7 +6,7 @@ Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared a ## /api browser-trust fence -The node half guards every entry under `/api` before bridging or upgrading (`src/api-request-trust.ts`). Every request — browser-marked or not — must present a `Host` that is a loopback authority or matches a `trustedHosts` entry: exact on `host:port` entries, any port on port-less entries, both sides compared through WHATWG normalization (DNS-rebinding defense). There is deliberately no shortcut for unmarked HTTP requests: over plain HTTP a browser attaches neither `Origin` nor Fetch-Metadata to image and navigation reads, so an unmarked request may still be a rebound browser read with a readable response, and Host is the one header rebinding cannot forge; a browser WebSocket handshake carries `Origin` and passes the same comparison. Non-browser clients pass the same fence via loopback, the CLI-derived LAN IP literals, or a declared authority. When markers are present, an attached `Origin` must equal the Host authority, and an explicit `sec-fetch-site: cross-site` marker is refused. A `trustedHosts` entry that is not a bare, canonical `host[:port]` authority — one WHATWG parsing reads back exactly as written — fails the plugin load loudly: parsing would otherwise quietly authorize the hostname inside `harness.internal/path`, or broaden a dangling-colon or zero-padded port to an any-port grant. HTTP failures answer plain 403 before any RPC dispatch; upgrade failures reject the handshake before any event stream starts. A non-loopback (`--host 0.0.0.0`) deployment therefore needs its serving authorities trusted: the dsh CLI derives the machine's LAN IP literals itself and its `--trusted-host` flag declares named ones, so `trustedHosts` in cordis.yml is for compositions the CLI does not boot. The fence is deliberately not an authentication layer — reachability policy stays with the webserver binding, and auth remains deferred work. Decision record: [the api browser-trust boundary Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md). +The node half guards every entry under `/api` before bridging or upgrading (`src/api-request-trust.ts`). Every request — browser-marked or not — must present a `Host` that is a loopback authority or matches a `trustedHosts` entry: exact on `host:port` entries, any port on port-less entries, both sides compared through WHATWG normalization (DNS-rebinding defense). There is deliberately no shortcut for unmarked HTTP requests: over plain HTTP a browser attaches neither `Origin` nor Fetch-Metadata to image and navigation reads, so an unmarked request may still be a rebound browser read with a readable response, and Host is the one header rebinding cannot forge; a browser WebSocket handshake carries `Origin` and passes the same comparison. Non-browser clients pass the same fence via loopback, the CLI-derived LAN IP literals, or a declared authority. When markers are present, an attached `Origin` must equal the Host authority, and an explicit `sec-fetch-site: cross-site` marker is refused. A `trustedHosts` entry that is not a bare, canonical `host[:port]` authority — one WHATWG parsing reads back exactly as written — fails the plugin load loudly: parsing would otherwise quietly authorize the hostname inside `harness.internal/path`, or broaden a dangling-colon or zero-padded port to an any-port grant. HTTP failures answer plain 403 before any RPC dispatch; upgrade failures reject the handshake before any event stream starts. A non-loopback (`--host 0.0.0.0`) deployment therefore needs its serving authorities trusted: the dsh CLI derives the machine's LAN IP literals itself and its `--trusted-host` flag declares named ones, so `trustedHosts` in cordis.yml is for compositions the CLI does not boot. The fence is a reachability policy, not authentication; the Web carrier provides no authentication layer. Decision record: [the api browser-trust boundary Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md). ## `/api` WebSocket downlinks @@ -26,5 +26,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **history's implicit resume is arguable** — opening history on an unattached session pulls an agent up host-side; the pure-persistence-read alternative is recorded in the rt-core reconciliation ledger, unchanged in P-I. This package's consumers see it as latency on first open. -- **`ToolEventView`/`ToolCallView`/`ToolResultView` re-exports are scheduled for removal** — they fall when the toolview migration deletes the host `viewFor` line (presentation belongs to the client); the fixture keeps a local `viewFor` mirror until then. +- **History resumes an unattached session** — opening history may create the host-side agent and add latency to the first open; there is no persistence-only read path. diff --git a/packages/client/connection/README.zh.md b/packages/client/connection/README.zh.md index bd58ce649c..6b031e5842 100644 --- a/packages/client/connection/README.zh.md +++ b/packages/client/connection/README.zh.md @@ -6,7 +6,7 @@ ## /api 浏览器信任栅栏 -node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-request-trust.ts`)。每个请求——无论是否带浏览器标记——`Host` 都必须是回环地址权威,或与某个 `trustedHosts` 条目匹配:带端口的 `host:port` 条目精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化后比较(DNS rebinding 防御)。刻意不为无浏览器标记的 HTTP 请求开捷径:明文 HTTP 下浏览器的图片与导航读取既不带 `Origin` 也不带 Fetch-Metadata,因此无标记请求仍可能是被重绑页面发起的、响应可被读走的读取,而 Host 是重绑唯一伪造不了的请求头;WebSocket 浏览器握手会带 `Origin` 并通过同一道比较。非浏览器客户端经由回环地址、CLI 推导的 LAN IP 字面量或已声明的权威通过同一道栅栏。当标记存在时,`Origin` 必须与 Host 权威完全一致;显式的 `sec-fetch-site: cross-site` 标记一律拒绝。不是纯的、规范形 `host[:port]` 权威的 `trustedHosts` 条目——即 WHATWG 解析读回后与原文不完全一致的——会让插件加载大声失败:否则解析会悄悄授权 `harness.internal/path` 这类笔误里的 hostname,或把悬空冒号、补零端口放大成任意端口授权。HTTP 失败在任何 RPC 分发之前以纯 403 应答,upgrade 失败在启动任何 event stream 前拒绝握手。因此非回环(`--host 0.0.0.0`)部署需要让自己的服务权威被信任:dsh CLI 会自行推导本机的 LAN IP 字面量,其 `--trusted-host` flag 用于声明具名权威,所以 cordis.yml 中的 `trustedHosts` 面向 CLI 不参与引导的组合。这道栅栏刻意不承担认证职责——可达性策略归 webserver 绑定配置,认证仍是延期工作。决策记录:[api 浏览器信任边界 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md)。 +node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-request-trust.ts`)。每个请求——无论是否带浏览器标记——`Host` 都必须是回环地址权威,或与某个 `trustedHosts` 条目匹配:带端口的 `host:port` 条目精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化后比较(DNS rebinding 防御)。刻意不为无浏览器标记的 HTTP 请求开捷径:明文 HTTP 下浏览器的图片与导航读取既不带 `Origin` 也不带 Fetch-Metadata,因此无标记请求仍可能是被重绑页面发起的、响应可被读走的读取,而 Host 是重绑唯一伪造不了的请求头;WebSocket 浏览器握手会带 `Origin` 并通过同一道比较。非浏览器客户端经由回环地址、CLI 推导的 LAN IP 字面量或已声明的权威通过同一道栅栏。当标记存在时,`Origin` 必须与 Host 权威完全一致;显式的 `sec-fetch-site: cross-site` 标记一律拒绝。不是纯的、规范形 `host[:port]` 权威的 `trustedHosts` 条目——即 WHATWG 解析读回后与原文不完全一致的——会让插件加载大声失败:否则解析会悄悄授权 `harness.internal/path` 这类笔误里的 hostname,或把悬空冒号、补零端口放大成任意端口授权。HTTP 失败在任何 RPC 分发之前以纯 403 应答,upgrade 失败在启动任何 event stream 前拒绝握手。因此非回环(`--host 0.0.0.0`)部署需要让自己的服务权威被信任:dsh CLI 会自行推导本机的 LAN IP 字面量,其 `--trusted-host` flag 用于声明具名权威,所以 cordis.yml 中的 `trustedHosts` 面向 CLI 不参与引导的组合。这道栅栏是可达性策略,而不是认证;Web 载体不提供认证层。决策记录:[api 浏览器信任边界 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md)。 ## `/api` WebSocket 下行 @@ -26,5 +26,4 @@ node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-r ## 已知限制与暂缓事项 -- **history 的隐式恢复存在争议**:在未附加的会话上打开 history,会在主机侧拉起 agent;纯持久化读取的替代方案记录在 rt-core 协调账本中,P-I 不作改变。该包的消费方会在首次打开时感受到这段延迟。 -- **计划移除 `ToolEventView`/`ToolCallView`/`ToolResultView` 的重新导出**:当 toolview 迁移删除主机 `viewFor` 行时,它们会一并移除(呈现属于客户端);在此之前,fixture 保留一份局部 `viewFor` 镜像。 +- **History 会恢复未附加的会话**:打开 history 可能创建宿主侧 agent,并增加首次打开的延迟;没有仅从持久化读取的路径。 diff --git a/packages/client/hmr/README.i18n.yaml b/packages/client/hmr/README.i18n.yaml index ce015be85f..86859e79f6 100644 --- a/packages/client/hmr/README.i18n.yaml +++ b/packages/client/hmr/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/hmr/README.md -README.md: f91a6c6f685c88a1ea19312985ad3e933222192a -README.zh.md: 1d20a22d211c13d62089fb5618f40636ab7ae60a +README.md: 454c03cc3cd11722943efd025d164d9ca8233d25 +README.zh.md: fc4100c48e5db9ed6781117dd652232bbd7c7aaa diff --git a/packages/client/hmr/README.md b/packages/client/hmr/README.md index f91a6c6f68..454c03cc3c 100644 --- a/packages/client/hmr/README.md +++ b/packages/client/hmr/README.md @@ -17,5 +17,5 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **Reload is coarse by design** — a fresh fiber and fresh components; React state inside the reloaded plugin is lost while the data layer (connection/runtime fibers, Session objects) is untouched. react-refresh-grade state preservation conflicts with "re-executing the bundle re-runs the factory" and is deliberately out. -- **No failure rollback** — a reload that fails leaves the entry FAILED and loud in the loader status projection; restoring the previous bundle automatically is deferred until a real need shows. -- **Graph rev is not refreshed by rebuilt frames** — the stale rev is harmless (the bundle endpoint serves no-cache); rev refresh lands with the reconnect-handshake mechanism. +- **No failure rollback** — a reload that fails leaves the entry FAILED and visible in the loader status projection; the previous bundle is not restored automatically. +- **Graph rev is not refreshed by rebuilt frames** — the stale rev is harmless because the bundle endpoint serves no-cache; reconnect is the only refresh boundary. diff --git a/packages/client/hmr/README.zh.md b/packages/client/hmr/README.zh.md index 1d20a22d21..fc4100c48e 100644 --- a/packages/client/hmr/README.zh.md +++ b/packages/client/hmr/README.zh.md @@ -17,5 +17,5 @@ ## 已知限制与暂缓事项 - **重载有意保持粗粒度**:会创建全新的 fiber 和组件;重载插件中的 React 状态会丢失,数据层(连接 fiber、运行时 fiber 和 Session 对象)不受影响。react-refresh 级状态保留与「重新执行组合包会重新运行 factory」冲突,因此有意排除。 -- **失败时不回滚**:失败的重载会使配置项处于 FAILED 状态,并在 loader 状态投影中明确显示;自动恢复先前组合包会等到实际需要出现后再实现。 -- **重建帧不会刷新图 rev**:陈旧 rev 无害(组合包端点以 no-cache 提供内容);rev 刷新将在重新连接握手机制中实现。 +- **失败时不回滚**:失败的重载会使配置项处于 FAILED 状态,并在 loader 状态投影中显示;系统不会自动恢复先前组合包。 +- **重建帧不会刷新图 rev**:陈旧 rev 无害,因为组合包端点以 no-cache 提供内容;只有重新连接时才会刷新。 diff --git a/packages/client/locale/README.i18n.yaml b/packages/client/locale/README.i18n.yaml index 6a0ddc4296..d1ef53207f 100644 --- a/packages/client/locale/README.i18n.yaml +++ b/packages/client/locale/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/locale/README.md -README.md: 7f780092af9bc7079cc5080c06e986bef2dfdbce -README.zh.md: 288982b1247f93fa1e8578a9ece2fcf8ed86666d +README.md: f1efefde4557e1c29c0556f8b670f1534430ab79 +README.zh.md: a8b5704d28ea121e668cbd500dd3d217d4f96291 diff --git a/packages/client/locale/README.md b/packages/client/locale/README.md index 7f780092af..f1efefde45 100644 --- a/packages/client/locale/README.md +++ b/packages/client/locale/README.md @@ -14,5 +14,5 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Most surfaces keep inline copy** — the standard seat is adopted by the Settings rows, sidebar, question composer, and model select; the remaining packages migrate in follow-up PRs. +- **Some surfaces keep inline copy** — Settings rows, the sidebar, question composer, and model select use locale seats; other packages still own static text directly. - **Registry-held text reads its translation once** — copy captured at registration time outside the slot render path (e.g. the `/model` command description in the command registry) keeps the language it was registered under until re-registration; slot-rendered copy follows switches live. diff --git a/packages/client/locale/README.zh.md b/packages/client/locale/README.zh.md index 288982b124..a8b5704d28 100644 --- a/packages/client/locale/README.zh.md +++ b/packages/client/locale/README.zh.md @@ -14,5 +14,5 @@ locale 插件:LocaleService——浏览器 locale 偏好(`zh`/`en`,以 ` ## 已知限制与暂缓事项 -- **多数界面仍保留内联文案**——标准席位已由设置行、侧边栏、问题作答器和模型选择接入;其余包在后续 PR 中迁移。 +- **部分界面仍保留内联文案**——设置行、侧边栏、问题作答器和模型选择使用 locale seat;其他包仍直接拥有静态文本。 - **注册表持有的文本只读取一次翻译**——在 slot 渲染路径之外于注册时捕获的文案(例如 command 注册表中的 `/model` 命令描述)在重新注册前保持注册时的语言;slot 渲染的文案随切换实时更新。 diff --git a/packages/client/runtime/README.i18n.yaml b/packages/client/runtime/README.i18n.yaml index 7a785911c3..ec7183a3dc 100644 --- a/packages/client/runtime/README.i18n.yaml +++ b/packages/client/runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/runtime/README.md -README.md: dd780369a1d888dde2579e436afe2ce1e6dcfdd1 -README.zh.md: 5574ad6452c6a2d94fb63da7e53b98e8d074f1c9 +README.md: e921b86379173df59575c7f4a43eaedacedb9585 +README.zh.md: b3ed3fbf86a7003e552ef82f78f8cc34d613ae17 diff --git a/packages/client/runtime/README.md b/packages/client/runtime/README.md index dd780369a1..e921b86379 100644 --- a/packages/client/runtime/README.md +++ b/packages/client/runtime/README.md @@ -70,6 +70,6 @@ Changing the target can change or invalidate provider-side cache reuse; this pac ## Known Limitations and Deferred Work -- **`loader.unload` is a stub (throws not-implemented)** — the full chain (fiber dispose → registration cascade → style removal) lands with the HMR project. +- **`loader.unload` is a stub** — it throws not-implemented; the client has no unload chain from fiber disposal through registration and style removal. - **Scope teardown is stage-driven, single-occupant today** — the staged session follows `list.current` exactly (staging is the open signal: the event window opens ⟺ the session is on stage); a removed-while-staged session's scope survives frozen until the stage moves on, not until true observer count reaches zero. Resolution (`binding()`/`scope()`) is pure addressing, render-safe; the render layer reads the current bundle through the `currentProvideInfo` observable. The staged state can widen to a multi-pane list when concurrent panes land. - **Value imports of this package from plugin bundles must use the `/client` subpath** — the bare package name is not in the loader externals table and inlines a second module instance, whose private scope-tag Symbol never matches (the empty-state P0 postmortem). diff --git a/packages/client/runtime/README.zh.md b/packages/client/runtime/README.zh.md index 5574ad6452..b3ed3fbf86 100644 --- a/packages/client/runtime/README.zh.md +++ b/packages/client/runtime/README.zh.md @@ -70,6 +70,6 @@ Session 对象会在事件 wire 边界依据生产方的完整字段契约,验 ## 已知限制与暂缓事项 -- **`loader.unload` 是 stub(抛出 not-implemented)**:完整链路(fiber dispose → 注册级联 → 样式移除)随 HMR(热模块替换)项目落地。 +- **`loader.unload` 是 stub**:它会抛出 not-implemented;客户端没有从 fiber dispose 到注册与样式移除的卸载链。 - **scope 拆卸由阶段驱动,目前只能有一个占用者**:已 staged 的会话精确跟随 `list.current`(staging 就是打开信号:事件窗口打开 ⟺ 会话位于 stage);在 staged 状态下被移除的会话,其 scope 会冻结保留,直到 stage 转向其他会话,而非直到真实观察者数量降为零。解析(`binding()`/`scope()`)只是纯寻址,可安全用于渲染;渲染层经 `currentProvideInfo` observable 读取当前 bundle。并发 pane 落地时,staged 状态可以扩展为多 pane 列表。 - **插件组合包从该包导入值时必须使用 `/client` 子路径**:裸包名不在 loader externals 表中,会内联第二个模块实例;其私有 scope-tag Symbol 永远无法匹配。这是空状态 P0 的事故复盘(postmortem)所记录的问题。 diff --git a/packages/client/schema-form/README.i18n.yaml b/packages/client/schema-form/README.i18n.yaml index 4038b8ae7d..1c3c759ceb 100644 --- a/packages/client/schema-form/README.i18n.yaml +++ b/packages/client/schema-form/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/schema-form/README.md -README.md: 5dcef89cbffc8b03c3f2d874e870fa9767360d3c -README.zh.md: ebaa9e0d0a134a6fade5729215168a1a47fd375c +README.md: 716cc7ba3c24f3a4de081e2d26f803b905235fac +README.zh.md: 65b8767df84f90eedd70e9f8ac27d7d419f06f46 diff --git a/packages/client/schema-form/README.md b/packages/client/schema-form/README.md index 5dcef89cbf..716cc7ba3c 100644 --- a/packages/client/schema-form/README.md +++ b/packages/client/schema-form/README.md @@ -18,6 +18,6 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Rehydration executes the served envelope** — `rehydrateSchema` reconstructs a live schemastery validator, and schemastery revives serialized callbacks through `new Function`, so the schema envelope is executable content rather than inert data. That is acceptable only because the envelope comes from the same host that serves the page; a browser schema protocol should carry a description the client cannot execute, which is deferred with the settings seam's [wire-boundary work](../../settings/settings/README.md#known-limitations-and-deferred-work). -- **Validation is draft-level, not per-field** — `validateDraft` reports schemastery's first failure message (which names the `$.path`); per-field error mapping is deferred until a consumer needs it. -- **No generic renderer** — a schema-driven form component was built and then replaced by the hand-written Models editor ([Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md)); if a future page needs to edit arbitrary sections, it starts from these helpers, not from a resurrected generic renderer, unless the note's trade-off changes. +- **Rehydration executes the served envelope** — `rehydrateSchema` reconstructs a live schemastery validator, and schemastery revives serialized callbacks through `new Function`, so the schema envelope is executable content rather than inert data. This is safe only for an envelope from the same trusted host that serves the page; the protocol provides no inert cross-trust representation. +- **Validation is draft-level, not per-field** — `validateDraft` reports schemastery's first failure message, including its `$.path`; it does not map errors onto individual controls. +- **No generic renderer** — consumers build feature-specific forms over these helpers. The [Web config-plane Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md) records that trade-off. diff --git a/packages/client/schema-form/README.zh.md b/packages/client/schema-form/README.zh.md index ebaa9e0d0a..65b8767df8 100644 --- a/packages/client/schema-form/README.zh.md +++ b/packages/client/schema-form/README.zh.md @@ -18,6 +18,6 @@ ## Known Limitations and Deferred Work -- **重建 schema 会执行所收到的信封**——`rehydrateSchema` 会重建一个活的 schemastery 校验器,而 schemastery 通过 `new Function` 复活序列化过的 callback,因此 schema 信封是可执行内容,而非惰性数据。这只有在信封来自提供该页面的同一 host 时才可接受;面向浏览器的 schema 协议应当传递客户端无法执行的描述,此项与 settings seam 的[协议边界工作](../../settings/settings/README.md#known-limitations-and-deferred-work)一并暂缓。 -- **校验是草稿级的,而非逐字段**——`validateDraft` 报告 schemastery 的第一条失败消息(其中会点名 `$.path`);逐字段的报错映射延后到出现需要它的消费方再做。 -- **没有通用渲染器**——一个 schema 驱动的表单组件曾被构建出来,随后被手写的 Models 编辑器取代([Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md));若未来有页面需要编辑任意分节,起点是这些辅助函数,而不是复活后的通用渲染器——除非该 note 的权衡发生变化。 +- **重建 schema 会执行所收到的信封**——`rehydrateSchema` 会重建一个活的 schemastery 校验器,而 schemastery 通过 `new Function` 复活序列化过的 callback,因此 schema 信封是可执行内容,而非惰性数据。只有信封来自提供该页面的同一受信任 host 时才安全;该协议没有跨信任边界使用的惰性表示。 +- **校验是草稿级的,而非逐字段**——`validateDraft` 报告 schemastery 的第一条失败消息及其 `$.path`;它不会把错误映射到各个控件。 +- **没有通用渲染器**——消费方在这些辅助函数上构建功能专用表单。[Web 配置面 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md)记录该权衡。 diff --git a/packages/client/test-runtime/README.i18n.yaml b/packages/client/test-runtime/README.i18n.yaml index fe40088b11..26845c1d5c 100644 --- a/packages/client/test-runtime/README.i18n.yaml +++ b/packages/client/test-runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/test-runtime/README.md -README.md: dc8ee8cadf5e61af15f04b1b9842af1eb658c031 -README.zh.md: a4c889d8a0291b52c8509403748df6b93567788e +README.md: 74da8fde7fd9cc3733d2d1ae03dd3d213e4d553e +README.zh.md: a86b9e469a5632886891628267002a14588afeaa diff --git a/packages/client/test-runtime/README.md b/packages/client/test-runtime/README.md index dc8ee8cadf..74da8fde7f 100644 --- a/packages/client/test-runtime/README.md +++ b/packages/client/test-runtime/README.md @@ -20,5 +20,5 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Consumed through repository source aliases only.** Specs resolve the package through tsconfig `paths` to `src`; the built `lib/` artifact re-exports `@deepseek-ai/dsh-client-runtime/client`, whose bundle is a browser loader script with no Node ESM exports, so `lib/index.js` is not importable under plain Node. Acceptable while every consumer is an in-repo Vitest suite; a Node-compatible runtime entry is deferred until an out-of-repo consumer exists. +- **Consumed through repository source aliases only.** Specs resolve the package through tsconfig `paths` to `src`; the built `lib/` artifact re-exports `@deepseek-ai/dsh-client-runtime/client`, whose bundle is a browser loader script with no Node ESM exports, so `lib/index.js` is not importable under plain Node. Every consumer is an in-repository Vitest suite; there is no Node-compatible runtime entry. - **Conversation snapshots are fixture data, not replayed history.** `updateSnapshot` writes the snapshot store directly; the wire-to-snapshot computation stays covered by the runtime package's own tests and the replay e2e. A fixture can therefore express states the production projection would never produce. diff --git a/packages/client/test-runtime/README.zh.md b/packages/client/test-runtime/README.zh.md index a4c889d8a0..a86b9e469a 100644 --- a/packages/client/test-runtime/README.zh.md +++ b/packages/client/test-runtime/README.zh.md @@ -20,5 +20,5 @@ ## Known Limitations and Deferred Work -- **仅可经仓内源码别名消费。** spec 通过 tsconfig `paths` 解析到 `src`;构建产物 `lib/` 再导出 `@deepseek-ai/dsh-client-runtime/client`,而该 bundle 是无 Node ESM 导出的浏览器 loader 脚本,故 `lib/index.js` 在纯 Node 下不可导入。当前所有消费方都是仓内 Vitest 套件,可接受;Node 兼容的运行时入口待出现仓外消费方再补。 +- **仅可经仓内源码别名消费。** spec 通过 tsconfig `paths` 解析到 `src`;构建产物 `lib/` 再导出 `@deepseek-ai/dsh-client-runtime/client`,而该 bundle 是无 Node ESM 导出的浏览器 loader 脚本,故 `lib/index.js` 在纯 Node 下不可导入。所有消费方都是仓内 Vitest 套件;不存在 Node 兼容的运行时入口。 - **会话快照是 fixture 数据,不是重放历史。** `updateSnapshot` 直写快照 store;wire 到快照的运算仍由 runtime 包自身测试与 replay e2e 把守。因此 fixture 可以表达生产投影永不产出的状态。 diff --git a/packages/client/ui-command/README.i18n.yaml b/packages/client/ui-command/README.i18n.yaml index e1b579faee..4c29ba83d2 100644 --- a/packages/client/ui-command/README.i18n.yaml +++ b/packages/client/ui-command/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-command/README.md -README.md: c892f2f244d7924014ad1b4d6e9fe16ff4e044e4 -README.zh.md: e5d109af8ca94515e0b574c62c57968796af5ce8 +README.md: 7846af7789f20cc8e19bf2b2f25d867063ce6936 +README.zh.md: fa703c8e94d906ede8108ae73b9a885bcf40cc9c diff --git a/packages/client/ui-command/README.md b/packages/client/ui-command/README.md index c892f2f244..7846af7789 100644 --- a/packages/client/ui-command/README.md +++ b/packages/client/ui-command/README.md @@ -22,5 +22,5 @@ None directly; this package neither assembles nor sends a provider request. Comm ## Known Limitations and Deferred Work -- **The popupSelect shell has no shipped business consumer** — model selection (host `selectModel`) is the design's reference case and lands with its own feature work; until then the shell is exercised by package tests only. +- **The popupSelect shell has no product consumer** — package tests exercise the shell directly. - **Detached-result notices fall back to the console off-session** — the fire-and-forget paths route results to the triggering session's composer via `SessionInput.notify`; after session teardown the console line is the only remaining surface. diff --git a/packages/client/ui-command/README.zh.md b/packages/client/ui-command/README.zh.md index e5d109af8c..fa703c8e94 100644 --- a/packages/client/ui-command/README.zh.md +++ b/packages/client/ui-command/README.zh.md @@ -22,5 +22,5 @@ ## 已知限制与暂缓事项 -- **popupSelect 壳还没有已上架的业务消费方**:模型选择(host `selectModel`)是设计的参照用例,将随其自身的功能工作落地;在此之前,壳只由包测试演练。 +- **popupSelect 壳没有产品消费方**:包测试会直接演练该壳。 - **脱离会话后,detached result 的 notice 回退到 console**:fire-and-forget 路径经 `SessionInput.notify` 把结果送到触发会话的编辑器;会话拆除后,console 输出行是仅剩的呈现面。 diff --git a/packages/client/ui-conversation/README.i18n.yaml b/packages/client/ui-conversation/README.i18n.yaml index 6ebbbed744..ddd024e34e 100644 --- a/packages/client/ui-conversation/README.i18n.yaml +++ b/packages/client/ui-conversation/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-conversation/README.md -README.md: 0d00eac1db5aed7feec9bb714fe3d8976cd39943 -README.zh.md: 4219950a9d51eb1037051ca00d61fa0d5ffa6fd2 +README.md: d7433c151b7ab8da66d319ce71dd24f533739176 +README.zh.md: 338cb3a8673a24a387a96c4c5e65a528e01c4c68 diff --git a/packages/client/ui-conversation/README.md b/packages/client/ui-conversation/README.md index 0d00eac1db..d7433c151b 100644 --- a/packages/client/ui-conversation/README.md +++ b/packages/client/ui-conversation/README.md @@ -8,7 +8,7 @@ Compaction renders as one collapsed row at the checkpoint's flow position withou The resident conversation shell survives no-session and session transitions. Without a current session it renders a disabled input bar; its root-scoped `conversation.hero.workspace` slot hosts the Workspace picker. Selecting a Workspace connects or reuses its Host-owned blank session and opens that session without replacing the shell. Blank sessions render the same composer body as active sessions, while the InputHub carries drafts across Workspace switches and mirrors them into the session store. In the active phase the session header shows only the current session title and view tabs as ordinary column chrome; fork lineage remains session data and is not projected into the header. Beneath it a scrollport (`data-conversation-scroll`) holds the flowing views and the sticky composer stack (stats dock + input docks + bar). Wheel over the textarea chains: the capped draft scrolls locally until its edge, then forwards to that host. -The view ring IS a slot: the conversation registration declares the `'conversation.view'` list slot (session scope) in its `children` table, ConversationRoot renders the active entry through its renderSlot share (`only: `), and view tabs project from the ring ledger's registration options (`id`/`order`/`label`). The chat view is this package's own ring entry; other plugins (ui-trajectory) contribute tabs through plain `ctx.slots.register` — the former package-local view registry (`registerView`/`ViewEntry`/`ConversationViewMap` and the chrome attachment table) is retired, with per-view chrome dissolved into the view components themselves. +The view ring is a slot: the conversation registration declares the session-scoped `'conversation.view'` list in its `children` table, ConversationRoot renders the active entry through its renderSlot share (`only: `), and view tabs project from registration options (`id`/`order`/`label`). The chat view is this package's own entry; plugins such as ui-trajectory contribute tabs through `ctx.slots.register`, and each view owns its chrome. Approvals take over the composer through the chain this package declares: `ApprovalPanel` registers as a selector-routed `'conversation.composer'` entry (the ui-question pattern) and occupies the composer in place of the InputBar while an approval wait is pending (amber strip, justification headline, paired command line from the running call's args, one-shot refuse/allow). The `PendingApproval` domain face in `contract/slots.ts` owns the wire encoding — the `ApprovalResponsePayload` value with the audit correlation — over the runtime's `PendingWait` carrier; the broadcast `approval/resolved` frame settles the wait and restores the composer. The runtime manager projects every approval or question wait through `SessionSummary.pendingInteraction`, including sessions never instantiated; `ui-workspace` owns its sidebar presentation. Pending waits leave the message flow entirely: questions (ui-question) and approvals (ApprovalPanel) both answer through the composer takeover, so no display-only placeholder card remains. The composer's bottom-row Access seat mounts `PermissionSelect`, fed by the host-computed `permissions` projection through the standard-kit `useProjection` (key absence hides the chip); the chip opens a Menu-primitive dropdown whose kebab-case preset names render as title-case labels. Safe preset picks submit `/permission ` immediately through the bar's injected `command` callback, while `danger-full-access` is presented as `Full access` and first opens an in-page Modal risk confirmation. The enabling action stays disabled until the user checks the acknowledgement; cancel, Escape, close, and mask click submit nothing. @@ -20,7 +20,7 @@ A Think row stays collapsed by default and exposes live reasoning throughput wit Generic tool rows classify the built-in bash, read, search, write, edit, and run_code names into dedicated visual variants. The filesystem variants render the edit icon and a path summary; that path is a hover-underline link that opens the file with the host OS default application (`host.openPath`, relative paths resolve against the session cwd). Tool rows are not whole-row click targets and do not open the details panel. The code variant summarizes with the model-authored `description` and expands to the program itself; its logged sub-dispatches render as always-visible nested rows through the SAME keyed toolview hole (custom registrations and the GenericToolCard fallback apply to sub-rows unchanged). Cordis lifecycle tools reuse those generic variants while presenting `Inspect`, `Mount temporary Plugin`, and `Unmount temporary Plugin` with a shared Cordis accent; mount keeps the code variant's expandable source rendering. -A tool call declaring the `terminal` render intent renders its command output inline, at both conversation render sites, through ui-primitives' `TerminalBlock`. `contract/terminal-card-model.ts` is the single derivation from the snapshot's `callView`/`resultView` pair, so the sites cannot disagree about a command, its cwd, or its exit status; it yields null — the generic path — for any other card tag, including one this client version does not know. Both sites therefore also show the card's run-state dot, which is the same `StateDot` semantic a tool row's leading icon carries, so a row and its own card always agree about one command's state. A multi-line command gets one prompt row per line, with the dot marking the call once on the first row — the exit status is the whole call's, so a dot per line would claim a per-line outcome bash does not report. The keyed `BashRow` carries the card resident below its summary row; since tool rows are no longer details-panel click targets, the card's copy and expand controls are the row's only interactions. The render-site fallback row keeps the card behind its existing expand control. Rows cap at `CHAT_TERMINAL_MAX_LINES` (8) against the panel's 16, which is what keeps a summary surface bounded — the panel stays the single-call reading surface. Inline output is licensed per render intent — the terminal and web cards, each with its own bound. A Bash execution failure that settles on the generic path instead exposes its original arguments and full error through the same bounded IN/OUT disclosure, while successful generic results such as a background-start acknowledgement remain summary-only ([decision](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)). +A tool call declaring the `terminal` render intent renders its command output inline, at both conversation render sites, through ui-primitives' `TerminalBlock`. `contract/terminal-card-model.ts` is the single derivation from the snapshot's `callView`/`resultView` pair, so the sites cannot disagree about a command, its cwd, or its exit status; it yields null — the generic path — for any other card tag, including one this client version does not know. Both sites therefore also show the card's run-state dot, which is the same `StateDot` semantic a tool row's leading icon carries, so a row and its own card always agree about one command's state. A multi-line command gets one prompt row per line, with the dot marking the call once on the first row — the exit status is the whole call's, so a dot per line would claim a per-line outcome bash does not report. The keyed `BashRow` carries the card below its summary row; tool rows are summary surfaces, so the card's copy and expand controls are the row's only interactions. The render-site fallback row keeps the card behind its existing expand control. Rows cap at `CHAT_TERMINAL_MAX_LINES` (8) against the panel's 16, which keeps the summary bounded; the panel stays the single-call reading surface. Inline output is licensed per render intent — the terminal and web cards, each with its own bound. A Bash execution failure that settles on the generic path instead exposes its original arguments and full error through the same bounded IN/OUT disclosure, while successful generic results such as a background-start acknowledgement remain summary-only ([decision](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)). A tool call declaring the `web` render intent renders its web retrieval inline, at both conversation render sites, through ui-primitives' `WebBlock`. `contract/web-card-model.ts` is the single derivation from the snapshot's `resultView`, mirroring the terminal card, so the sites cannot disagree about what a web call shows; it yields null — the generic path — for a running call, a non-web result view, a generic result view, a `card` tag this client version does not know, or a web card whose `kind` this client version does not know (a newer host's value, which the wire cannot be trusted to be `search` or `fetch`). The keyed `WebRow` registers one component under both `web_search` and `web_fetch`, discriminating on the tool name only for its icon and title; it composes the shared `ToolRow`, feeding the card as ToolRow's `web` body, so the retrieval is the row's collapsed-by-default expanded card (the same unified expand every card row has). A web-declaring tool without a keyed row lands on the `GenericToolCard` fallback, which routes the card through ToolRow the same way, and the details panel renders it and, below the card, the flattened model-visible result content — a fetch body is readable only there, since its card carries only the URL and status. Both render sites show the same complete source list — the one the tool returned and the model saw — bounded only by the card's own scroll container height, with no row-versus-panel cap ([decision](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md), [source scroll](../../../.agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.md)). @@ -32,7 +32,7 @@ The chat flow projects consecutive model-retry nodes across retry turns into one A `grep`/`glob` call declaring the `search` render intent renders its result inline, at the same render sites, through ui-primitives' `SearchBlock` — grep's matches grouped by file (each a collapsible header of `lineNumber: line` rows), glob's flat path list. `contract/search-card-model.ts` is the single derivation from the snapshot's `resultView`; unlike the terminal card it reads no `callView`, since a search has no matches or paths before `execute`, so a running search shows its summary alone. It yields null — the generic path — for any non-search result view, a `card` or `kind` this client version does not compile, and (because those ride the untrusted wire frame) a known kind whose `files`/`paths` is malformed. The keyed `SearchRow`, registered under both `grep` and `glob` since the derived `kind` decides the shape, composes the shared `ToolRow`, feeding the card as ToolRow's `search` body, so it is the row's collapsed-by-default expanded card; the render-site fallback routes it the same way. Both cap at `CHAT_SEARCH_MAX_LINES` (8) against the panel's 16. A capped search drops rows from the card, but the locator to the rest — grep/glob's `Full … stored at …` footer — lives only in the result text, so the derivation surfaces that as a recovery footer below the card when (and only when) the result was truncated; a settled call with no card at all (an errored search, a nested `run_code` sub-dispatch, a legacy generic result) surfaces its flattened result text through ToolRow's Output section so nothing is lost behind a bare summary ([decision](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md)). -Tool rows are slots too — the standalone tool ring (`ToolViewRegistry`/`ctx.toolviews`/outlet) is retired. The chat entry declares the keyed `'conversation.chat.toolview'` hole (session scope; the key space is runtime-open); its render site dispatches per row via `entryKey: toolName` with `GenericToolCard` as the call-site `fallback`. The owner payload is the uniform `ToolRowOwnerProps` (`callId`/`toolName`/`block`/`openFile`) and `ToolRowProps` pre-composes it with the session standard kit. A registrant is a plain plugin: `ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)` with `inject: ['slots', 'conversation']` as the load-order seam (apply mounts ConversationService after the chat registration, so the service being present guarantees the slot is declared); the bash sample is the third-party-posture exemplar. Trajectory/waterfall toolview slots share this shape and land with their own render sites (RendersCheck rejects a declaration nobody renders). +Tool rows use the keyed, session-scoped `'conversation.chat.toolview'` slot; its render site dispatches via `entryKey: toolName` with `GenericToolCard` as the call-site fallback. The owner payload is the uniform `ToolRowOwnerProps` (`callId`/`toolName`/`block`/`openFile`), and `ToolRowProps` composes it with the session standard kit. A registrant is a plain plugin: `ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)` with `inject: ['slots', 'conversation']` as the load-order seam. Trajectory and waterfall toolview slots share this shape and use their own render sites; RendersCheck rejects a declaration nobody renders. The todo surfaces are two registrations over that shape, both plain registrant plugins with `inject: ['slots', 'conversation']`. `TodoRow` takes the `'conversation.chat.toolview'` key `todo_write` and summarizes what the call attempted (`/ 已完成 · ` parsed from its args, falling back to the generic summary on malformed or wrongly-shaped model JSON, and keeping the generic dot for non-ok execution states so a cancelled call never reads as a completed update). `TodoDock` takes the `'conversation.input.dock'` list slot at `order: 0` — before Goal and Queue — and is the plan strip: it reads the host-computed `todos` projection via `useProjection` (standing plan: latest `todo/write` with no later `turn/start`) and renders `TodoPanel`, which takes the plain list, hides itself while the list is empty, and starts collapsed as a header of title plus `"/ tasks · in progress"` (status glyphs are the figma check / progress / dashed-pending set). The dock adapter owns the selection so the panel stays a pure function of its props; the standing list lives here rather than in the row so the row stays one line. Anything the input-zone composer chain hides (a `conversation.composer` takeover such as ui-question's) hides the whole dock, this strip included. @@ -48,7 +48,7 @@ The composer bar declares session-scoped single seats for `'conversation.input.p The chat stats line takes its token accounting from two generic token-meter projections read through the standard-kit `useProjection`: `tokenUsage` for full-log billing (billed input is uncached input plus cache reads and writes; cache hit divides cache reads by that total) and `contextPressure` for context occupancy. Visible nodes supply only the turn and step counts plus the LLM and tool wall times, which are window-scoped facts about what is on screen rather than accounting; durable token and context groups remain visible when compaction leaves no assistant node in the loaded window. A deployment without token-meter drops the token groups, and occupancy stays hidden until both provider pressure and route capacity are known. Occupancy is deliberately an approximation — its numerator and capacity are independent last-wins projection fields, not one atomic request observation ([rationale](../../llm/token-meter/README.md)). The inline stats row remains the sole context UI; the model selector has no circle or accessory. -`src/client/` is organized for the future package split: `contract/` is the sole inter-domain shared face (`slots.ts` slot declarations + composed slot props including the tool-row contract, `views.ts` shared primitives, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` (sample registrants) domain directories import contract files and never each other; `apply.ts` is the only assembly point allowed to import all three domains. The `/client` export surface is the contract only — `apply`/`inject`, the two service classes, and the `contract/` type families; implementation components (skeleton, chat rows) and the store factory stay internal and reach the page exclusively through apply's slot registrations (tests take them via the `./src/*` subpath). +`src/client/` is organized by domain. `contract/` is the sole inter-domain shared face (`slots.ts` slot declarations and composed props, `views.ts` shared primitives, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` directories import contract files and never each other. `apply.ts` is the only assembly point allowed to import all three domains. The `/client` export surface is the contract only — `apply`/`inject`, the two service classes, and the `contract/` type families; implementation components and the store factory stay internal and reach the page through apply's slot registrations. ## Model Experience @@ -62,11 +62,11 @@ None; this package neither assembles nor sends a provider request. - **Compaction markers show no scale** — the row does not yet report how many messages or which range the checkpoint replaced. - **Stats-line durations cover the in-window flow only** — LLM and tool wall times fold the snapshot's assistant `timing` and tool call/result pairs, so nodes outside the loaded event window (older history) are not counted. -- **Details panel is the minimal form and currently has no entry point** — selected call args/result raw display; the Input/Output/Metadata switch, Prev/Next stepping, and See-in-trajectory deep link are deferred. Tool rows stopped being details-panel click targets and nothing replaced that gesture, so `ChatViewInjected.openDetails` is implemented but uncalled and the panel (including its terminal card) is unreachable in the assembled application; its rendering stays covered by mounting it with a selection directly. +- **The details panel has no entry point** — `ChatViewInjected.openDetails` is implemented but uncalled, so the raw selected-call display is unreachable in the assembled application. There is no Input/Output/Metadata switch, Prev/Next stepping, or trajectory deep link. - **Assistant per-message paging is a reserved slot** — drawn in the design, not implemented. The finalized content IconActions row (copy / clock / branch) ships under the last content-text assistant of each turn only; mid-turn narration and Think-only nodes stay chrome-free. Branch stays disabled unless that message is also the last transcript node of a completed turn; when enabled, it forks through that turn, increments the inherited title on the client, and opens the child. A fork or rename failure leaves the source selected ([decision](../../../.agents/notes/implemented/bug-fix/2026-08-02-message-fork-actions-require-completed-turn-tail.md)). - **Sent user messages cannot be edited** — user bubbles retain clock, copy, and branch; branch stays disabled unless a completed turn's transcript ends at that user message. Editing returns with the capability behind it: a client mutation over a settled user message, plus the host behavior for the turn that already consumed it ([decision](../../../.agents/notes/implemented/simplification/2026-07-31-drop-user-message-edit-stub.md)). - **The sparkle icon for the others tool row is a hand-drawn approximation** — the design glyph's vector geometry is not exportable locally; promotion into ui-primitives waits on an exact export. -- **The approval panel's "Always allow this type" is deferred** — durable grants need a grant-storage design; only allow-once/reject answer today. +- **The approval panel has no durable grant control** — it supports allow-once and reject only. - **TodoPanel truncates long item text to one ellipsized line** — the figma strip has no wrap or expand affordance; full text is not readable inline. - **Queue edit is text-only** — rows containing non-text blocks still show a flattened preview, but their edit control is disabled because the inline editor cannot preserve those blocks. A text row's edit mode replaces delete and strict steer with save and cancel; Enter saves and Escape cancels. - **Queue strict steer preserves complete messages** — while the Agent is running, the steer action atomically transfers the addressed Queue occurrence into the current next-step window. Mixed-content rows remain eligible because the action forwards the immutable message instead of the text projection. The placement-aware Host snapshot renders pending steering at the conversation tail until the consumed `steering/message` folds into the durable transcript, so immediate display, reconnect, and replay share one linear authority. diff --git a/packages/client/ui-conversation/README.zh.md b/packages/client/ui-conversation/README.zh.md index 4219950a9d..338cb3a867 100644 --- a/packages/client/ui-conversation/README.zh.md +++ b/packages/client/ui-conversation/README.zh.md @@ -8,7 +8,7 @@ 常驻会话壳会跨无会话与会话状态切换而保留。没有当前会话时,它会渲染禁用输入栏;其根作用域的 `conversation.hero.workspace` slot 承载 Workspace 选择器。选择 Workspace 会连接或复用由 Host 拥有的空白会话,并在不替换会话壳的情况下打开该会话。空白会话与活跃会话渲染相同的输入区主体;InputHub 则在 Workspace 切换间携带草稿,并将草稿镜像到会话 store。活跃阶段,会话标题栏作为普通列 chrome,仅显示当前会话标题和视图标签;fork 谱系仍保留为会话数据,不投影到标题栏。其下滚动容器(`data-conversation-scroll`)承载流动排版的各视图与 sticky 编辑器栈(统计 dock+输入区 dock+输入栏)。textarea 上的滚轮会链式处理:限高草稿先在本地滚动,到达边缘后再转交给该宿主。 -视图环本身就是 slot:会话注册声明 `'conversation.view'` 列表 slot(Session scope),并将其列在 `children` 表中;ConversationRoot 通过 renderSlot share 渲染活跃配置项(`only: `);视图标签页从环账本的注册选项(`id`/`order`/`label`)投影而来。聊天视图是该包(package)自身的环配置项;其他插件(ui-trajectory)通过普通的 `ctx.slots.register` 贡献标签页。先前包内的视图注册表(`registerView`/`ViewEntry`/`ConversationViewMap` 及 chrome 附加表)已退役,逐视图 chrome 则被拆入视图组件自身。 +视图环是一个 slot:会话注册在 `children` 表中声明 Session scope 的 `'conversation.view'` 列表,ConversationRoot 通过 renderSlot share 渲染活跃配置项(`only: `),视图标签页则从注册选项(`id`/`order`/`label`)投影而来。聊天视图是该包自身的配置项;ui-trajectory 等插件通过 `ctx.slots.register` 贡献标签页,每个视图负责自己的 chrome。 会话页头会在标题旁声明并渲染 Session scope 的 `'conversation.session.header.actions'` 列表,使功能插件无需进入骨架即可贡献控件。编辑器链的 currency 包含当前对话 `session`;ui-subagent 会选取 one-shot 或 parent 不可用的已寻址会话,并按原因显示只读文案,而普通 InputBar 会让所有已寻址 child 仅保留 Send,因为继续执行服务不公开逐 Activation 取消操作,`session.cancel` 也会绕过其所有权。 @@ -18,7 +18,7 @@ Think 行默认保持折叠,并在不展开思维链的情况下暴露实时 通用工具行把内置的 bash、read、search、write、edit 和 run_code 名称归入专用视觉变体。文件系统变体会渲染 edit 图标和路径摘要;该路径是悬停下划线链接,点击后通过宿主操作系统的默认应用打开文件(`host.openPath`,相对路径相对会话 cwd 解析)。工具行不再是整行点击目标,也不会打开 details 面板。code 变体以模型撰写的 `description` 作摘要,展开后显示程序本身;其已记录的子调用经由同一个键控 toolview 空位渲染为始终可见的嵌套行(自定义注册和 GenericToolCard fallback 原样适用于子行)。Cordis 生命周期工具复用这些通用变体,同时以统一的 Cordis 强调色呈现 `Inspect`、`Mount temporary Plugin` 和 `Unmount temporary Plugin`;mount 行保留 code 变体的可展开源码渲染。 -声明 `terminal` 渲染意图的工具调用,会在两个对话渲染点上都通过 ui-primitives 的 `TerminalBlock` 内联渲染其命令输出。`contract/terminal-card-model.ts` 是从快照的 `callView`/`resultView` 对推导的唯一位置,因此两个渲染点不可能在命令、cwd 或退出状态上产生分歧;对任何其他 card 标签——包括当前客户端版本不认识的标签——它返回 null,落回通用路径。因此两个渲染点也都显示卡片的运行状态点,它与工具行行首图标承载同一套 `StateDot` 语义,所以一行与其自身的卡片对同一条命令的状态总是一致。多行命令的每一行各占一个提示行,状态点只在第一行为整次调用标记一次——退出状态属于整次调用,因此每行一枚就会声称一个 bash 并不报告的逐行结果。键控的 `BashRow` 把卡片常驻在摘要行下方;由于工具行已不再是详情面板的点击目标,卡片的复制与展开控件就是该行唯一的交互。渲染点兜底行则保持其既有的展开控件。行的上限是 `CHAT_TERMINAL_MAX_LINES`(8),面板为 16,正是这一点让摘要面保持有界——面板仍是单次调用的阅读面。内联输出按渲染意图开放——终端卡片与 web 卡片,各有自己的上限。若 Bash 执行失败时落在通用路径,则改用同样有界的 IN/OUT 展开区暴露原始参数和完整错误;后台启动确认等成功的通用结果仍只显示摘要([决策](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md))。 +声明 `terminal` 渲染意图的工具调用,会在两个对话渲染点上都通过 ui-primitives 的 `TerminalBlock` 内联渲染其命令输出。`contract/terminal-card-model.ts` 是从快照的 `callView`/`resultView` 对推导的唯一位置,因此两个渲染点不可能在命令、cwd 或退出状态上产生分歧;对任何其他 card 标签——包括当前客户端版本不认识的标签——它返回 null,落回通用路径。因此两个渲染点也都显示卡片的运行状态点,它与工具行行首图标承载同一套 `StateDot` 语义,所以一行与其自身的卡片对同一条命令的状态总是一致。多行命令的每一行各占一个提示行,状态点只在第一行为整次调用标记一次——退出状态属于整次调用,因此每行一枚就会声称一个 bash 并不报告的逐行结果。键控的 `BashRow` 把卡片放在摘要行下方;工具行是摘要 surface,因此卡片的复制与展开控件是该行唯一的交互。渲染点兜底行则保持其既有的展开控件。行的上限是 `CHAT_TERMINAL_MAX_LINES`(8),面板为 16,因此摘要保持有界;面板仍是单次调用的阅读 surface。内联输出按渲染意图开放——终端卡片与 web 卡片各有自己的上限。若 Bash 执行失败时落在通用路径,则改用同样有界的 IN/OUT 展开区暴露原始参数和完整错误;后台启动确认等成功的通用结果仍只显示摘要([决策](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md))。 声明 `web` 渲染意图的工具调用,会在两个对话渲染点上都通过 ui-primitives 的 `WebBlock` 内联渲染其 web 检索。`contract/web-card-model.ts` 是从快照的 `resultView` 推导的唯一位置,镜像终端卡片,因此两个渲染点不可能对一次 web 调用的显示产生分歧;对运行中的调用、非 web 的 result view、generic result view、本客户端版本不认识的 `card` 标签,或本客户端版本不认识 `kind` 的 web 卡片(更新的 host 发来的值,wire 上不可信其为 `search` 或 `fetch`),它返回 null,落回通用路径。键控的 `WebRow` 把一个组件注册在 `web_search` 与 `web_fetch` 两个键下,仅根据工具名判别以选取图标与标题;它组合共享的 `ToolRow`,把卡片作为 ToolRow 的 `web` body 传入,因此检索成为该行默认折叠的展开卡片(与每个卡片行相同的统一展开交互)。没有自己键控行的 web 声明工具落到 `GenericToolCard` 兜底,它以同样方式经 ToolRow 渲染卡片,详情面板渲染它,并在卡片下方渲染摊平的模型可见结果内容——fetch 正文只在此处可读,因为其卡片只携带 URL 和状态。两个渲染点显示同一份完整来源列表——工具返回、模型看到的那一份——仅受卡片自身滚动容器的高度约束,不存在行与面板的两级上限([决策](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md)、[来源滚动](../../../.agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.md))。 @@ -30,7 +30,7 @@ Think 行默认保持折叠,并在不展开思维链的情况下暴露实时 声明 `search` 渲染意图的 `grep`/`glob` 调用,会在同样的渲染点上通过 ui-primitives 的 `SearchBlock` 内联渲染其结果——grep 的匹配按文件分组(每个是一个可折叠的头,下辖 `lineNumber: line` 行),glob 是扁平路径列表。`contract/search-card-model.ts` 是从快照的 `resultView` 推导的唯一位置;与终端卡片不同,它不读 `callView`,因为搜索在 `execute` 前没有匹配或路径,所以运行中的搜索只显示摘要。对任何非搜索的结果视图、当前客户端版本无法编译的 `card` 或 `kind`、以及(因为这些都与不可信的 wire 帧同行)一个 `files`/`paths` 格式错误的已知 kind,它都返回 null,落回通用路径。键控的 `SearchRow` 因推导出的 `kind` 决定形态而同时注册在 `grep` 与 `glob` 下,组合共享的 `ToolRow`,把卡片作为 ToolRow 的 `search` body 传入,因此它是该行默认折叠的展开卡片;渲染点兜底行以同样方式渲染它。两者上限都是 `CHAT_SEARCH_MAX_LINES`(8),面板为 16。被截断的搜索会从卡片里丢掉一些行,但通往其余部分的定位符——grep/glob 的 `Full … stored at …` 脚注——只存在于结果文本里,因此推导在(且仅在)结果被截断时把它作为恢复脚注画在卡片下方;一个完全没有卡片的已结算调用(出错的搜索、嵌套 `run_code` 子派发、旧日志的 generic 结果)则经 ToolRow 的 Output 区呈现其压平后的结果文本,从而不让任何内容丢失在一个光秃秃的摘要之后([决策](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md))。 -工具行同样是 slot:独立工具环(`ToolViewRegistry`/`ctx.toolviews`/outlet)已经退役。聊天配置项声明键控的 `'conversation.chat.toolview'` 空位(Session scope;key 空间在运行时开放);其渲染点逐行通过 `entryKey: toolName` 分发,并以 `GenericToolCard` 作为调用点 `fallback`。owner 载荷是统一的 `ToolRowOwnerProps`(`callId`/`toolName`/`block`/`openFile`),`ToolRowProps` 则预先将其与 Session 标准工具包组合。注册方只是普通插件:`ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)`,以 `inject: ['slots', 'conversation']` 作为加载顺序 seam(apply 在聊天注册后挂载 ConversationService,因此服务存在即可保证 slot 已声明);bash 示例是第三方姿态的范例。Trajectory/waterfall(瀑布式事件)工具视图 slot 共享此形状,并随各自的渲染点落地(RendersCheck 会拒绝没有任何渲染方的声明)。 +工具行使用键控、Session scope 的 `'conversation.chat.toolview'` slot;其渲染点通过 `entryKey: toolName` 分发,并以 `GenericToolCard` 作为调用点 fallback。owner 载荷是统一的 `ToolRowOwnerProps`(`callId`/`toolName`/`block`/`openFile`),`ToolRowProps` 将其与 Session 标准工具包组合。注册方只是普通插件:`ctx.slots.register({ name: 'conversation.chat.toolview', key: '', inject? }, Row)`,以 `inject: ['slots', 'conversation']` 作为加载顺序 seam。Trajectory 与 waterfall(瀑布式事件)工具视图 slot 共享此形状并使用各自的渲染点;RendersCheck 会拒绝没有任何渲染方的声明。 审批经由本包声明的链接管编辑器:`ApprovalPanel` 注册为按选择器路由的 `'conversation.composer'` 配置项(ui-question 模式),在审批等待未决期间取代 InputBar 占据编辑器(琥珀色条、理由标题、来自运行中调用参数的配对命令行、一次性的拒绝/允许)。`contract/slots.ts` 中的 `PendingApproval` 领域面在运行时 `PendingWait` 载体之上拥有 wire 编码——带审计关联的 `ApprovalResponsePayload` 值;广播的 `approval/resolved` 帧使等待落定并恢复编辑器。运行时 manager 会将所有审批或问题等待通过 `SessionSummary.pendingInteraction` 投影出来,未实例化的 Session 也不例外;`ui-workspace` 负责其侧边栏呈现。未决等待完全离开消息流:问题(ui-question)与审批(ApprovalPanel)都经编辑器接管作答,不再保留只读占位卡。编辑器底行的 Access 席位挂载 `PermissionSelect`,由 host 计算的 `permissions` 投影经标准工具包 `useProjection` 供数(key 缺席即隐藏 chip);chip 打开 Menu 原语下拉,其中 kebab-case 预设名渲染为 Title Case 标签;普通安全预设会立即经输入栏注入的 `command` 回调提交 `/permission `,而 `danger-full-access` 在界面中显示为 `Full access`,选择后先打开页面内的 Modal 风险确认。用户勾选确认项前启用按钮始终不可用;取消、Escape、关闭按钮与点击遮罩都不会提交命令。 diff --git a/packages/client/ui-conversation/src/client/contract/diff-card-model.ts b/packages/client/ui-conversation/src/client/contract/diff-card-model.ts index bc914e4820..ec19f1cce0 100644 --- a/packages/client/ui-conversation/src/client/contract/diff-card-model.ts +++ b/packages/client/ui-conversation/src/client/contract/diff-card-model.ts @@ -76,11 +76,8 @@ function narrowDiffs(diffs: unknown): DiffHunk[] | null { * * This derivation consumes only `diffs`; the render intent's `title` field is * deliberately dropped. The row supplies its own title (`Edit`/`Write · path` - * from the args) and that outranks the view's `title`, matching the TUI diff - * branch, which likewise draws no view title. A tool that names its own diff - * header therefore does not surface that text on the Web row — an accepted - * product choice, recorded here as the one asymmetry with the terminal card, - * whose derivation does consume the view's title. + * from the args), which outranks the view's `title`. A tool that names its own + * diff header therefore does not surface that text on the Web row. * @param block - RunningToolCall or ToolResultNode off the snapshot caches. * @returns the diff-card props, or null for the generic path. */ diff --git a/packages/client/ui-goal/README.i18n.yaml b/packages/client/ui-goal/README.i18n.yaml index 1897f1ba8a..3b0ab1b1bb 100644 --- a/packages/client/ui-goal/README.i18n.yaml +++ b/packages/client/ui-goal/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-goal/README.md -README.md: 3da9d97c801a0a742de2601e5261c09ba193cf33 -README.zh.md: 8a4c01394508d9ceb3eabb6cd38ad58abd7f0e38 +README.md: c9a8f330949ed0db9c4986e7043a5063b8a26805 +README.zh.md: 9df1a0091545436642ef5644d3258364e63a20ec diff --git a/packages/client/ui-goal/README.md b/packages/client/ui-goal/README.md index 3da9d97c80..c9a8f33094 100644 --- a/packages/client/ui-goal/README.md +++ b/packages/client/ui-goal/README.md @@ -16,4 +16,4 @@ None beyond the goal mutation's own context event, which appends to the log tail ## Known Limitations and Deferred Work -- **Durable phase only** — the projection value deliberately omits process-local activation (armed/disarmed), so the strip cannot distinguish an active-but-disarmed goal from an armed one; resume re-arms through the RPC side. A host-live-value channel is deferred until a real consumer needs it. +- **Durable phase only** — the projection omits process-local activation, so the strip cannot distinguish an active-but-disarmed goal from an armed one; resume re-arms through the RPC side. There is no host-live activation channel. diff --git a/packages/client/ui-goal/README.zh.md b/packages/client/ui-goal/README.zh.md index 8a4c013945..9df1a00915 100644 --- a/packages/client/ui-goal/README.zh.md +++ b/packages/client/ui-goal/README.zh.md @@ -16,4 +16,4 @@ Goal 界面插件(浏览器端部分):`GoalBar` 条带是 `conversation.in ## 已知限制与暂缓事项 -- **只反映持久 phase**——投影值有意省略进程本地的 activation(armed/disarmed),条带无法区分 active-but-disarmed 与 armed 状态;resume 通过 RPC 重新置为 armed 状态。host 活值通道待出现真实消费方后再议。 +- **只反映持久 phase**——投影省略进程本地 activation,因此条带无法区分 active-but-disarmed 与 armed 状态;resume 通过 RPC 重新置为 armed 状态。不存在 host 实时 activation 通道。 diff --git a/packages/client/ui-layout/README.i18n.yaml b/packages/client/ui-layout/README.i18n.yaml index 6cde469078..f021dbb3ca 100644 --- a/packages/client/ui-layout/README.i18n.yaml +++ b/packages/client/ui-layout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-layout/README.md -README.md: cb99023e6a9e3364c6f48190cf4a0cd71da2cbba -README.zh.md: a24dfa4d4eeb28fdc8df1d21daa3f6e9476d0062 +README.md: b01b35e9ec37e9b5b88046c3834c3aaeec70eb6e +README.zh.md: c3e4bf1e1b48feb87e68b3b7c73f7f9d11d7fea3 diff --git a/packages/client/ui-layout/README.md b/packages/client/ui-layout/README.md index cb99023e6a..b01b35e9ec 100644 --- a/packages/client/ui-layout/README.md +++ b/packages/client/ui-layout/README.md @@ -20,4 +20,4 @@ None; this package neither assembles nor sends a provider request. - **Panel geometry is transient** — reload restores the sidebar default and details closed; switching between distinct Session ids also closes details and forgets its dragged width, while unselected surfaces render details at zero width without modifying geometry. - **Concession-chain auto-close derives a zero width without touching the preferred width** — the panel restores itself when the window widens; consumers must not read the stored details width as the rendered truth. -- **Scroll anchoring during squeeze reflow is not implemented** — deferred with the virtualized-list project. +- **No scroll anchoring during squeeze reflow** — layout changes may move the reader's viewport. diff --git a/packages/client/ui-layout/README.zh.md b/packages/client/ui-layout/README.zh.md index a24dfa4d4e..c3e4bf1e1b 100644 --- a/packages/client/ui-layout/README.zh.md +++ b/packages/client/ui-layout/README.zh.md @@ -20,4 +20,4 @@ AppFrame 始终挂载会话栏和详情栏;已连接 Session 通过 `SessionPr - **面板几何信息是瞬时状态**:重新加载会恢复侧边栏默认值,并使详情栏保持关闭;在不同会话 id 之间切换同样会关闭详情栏,并忘记拖动后的宽度,而未选中表面会以零宽度渲染详情栏,但不会修改几何信息。 - **让步链自动关闭通过推导零宽度实现,不会改动宽度偏好**:窗口变宽时面板会自行恢复;消费方禁止把 store 中的详情宽度当作实际渲染状态。 -- **挤压重排期间尚未实现滚动锚定**:与虚拟化列表项目一并暂缓。 +- **挤压重排期间不提供滚动锚定**:布局变化可能移动读者的 viewport。 diff --git a/packages/client/ui-primitives/README.i18n.yaml b/packages/client/ui-primitives/README.i18n.yaml index 8c597efd5a..dabb138264 100644 --- a/packages/client/ui-primitives/README.i18n.yaml +++ b/packages/client/ui-primitives/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-primitives/README.md -README.md: 00e9560f43c83e1edc61c185a4fc562c6c923e8b -README.zh.md: 21226ab211106b7722139828762605cb71a4b498 +README.md: d4e6c2508f07f4832f2e12a836c2d447b656421e +README.zh.md: 76dfbebd5e9db494b49d65a2528977b7ac9fed15 diff --git a/packages/client/ui-primitives/README.md b/packages/client/ui-primitives/README.md index 00e9560f43..d4e6c2508f 100644 --- a/packages/client/ui-primitives/README.md +++ b/packages/client/ui-primitives/README.md @@ -14,7 +14,7 @@ Pure React atoms (zero cordis): StateDot, ic_ds_* icons, Button/Pill/Menu/Modal/ ## Terminal output -`TerminalBlock` renders a shell command as a terminal surface: one prompt row per line of the command (the shortened `cwd` label on the first row only, since the view knows one working directory and a `cd` moves later lines elsewhere, then that line), the command's output, a status pill for a non-zero exit code or a terminating signal, and a copy control that writes the raw `output` prop. A run-state `StateDot` marks the call once, on the first row, out of flow in a gutter the card reserves as its own left padding, so the dot sits inside the card box yet left of the prompt text. It reaches three of `StateDot`'s states — the chase while `running`, red for the same exit status that renders the pill, green otherwise — so a card states whether its command is still running rather than leaving that to be inferred from the presence of output; it carries one visually hidden text label because `StateDot` is `aria-hidden`. One dot regardless of line count is deliberate: the exit status is the whole call's, so a dot per line would claim a per-line outcome the view does not carry. Command text is `white-space: pre`, so repeated spaces, tabs, and an indented continuation render verbatim while the row stays single-line and ellipsizes. ANSI escape sequences are parsed with the `anser` runtime dependency into React spans; cursor movements replay into a per-line column buffer before inert controls are stripped, since carriage return and backspace only MOVE the cursor: `100%` + CR + `OK` alone shows `OK0%`, while the `\x1b[K` a spinner writes with its redraw erases the tail so `100%\r\x1b[KOK` shows `OK`. Erase-in-line is honored in all three parameter forms, the cursor advances by terminal columns (8-column tab stops, two for emoji and CJK, none for a combining mark), and SGR state is normalized per cell as a terminal stores it, threading across lines and closing at the state the line ended in; basic-16 foreground colors map onto `--dsw-*` tokens, while 256-palette and truecolor values pass through as literal rgb. Output keeps `white-space: pre` with horizontal scrolling, so column-aligned output holds its alignment instead of soft-wrapping, and collapses to a head slice plus a tail slice past `maxLines` (default 16, the TUI transcript's split arithmetic) behind an expand button. Rationale: [the web terminal card note](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md). +`TerminalBlock` renders a shell command as a terminal surface: one prompt row per line of the command (the shortened `cwd` label on the first row only, since the view knows one working directory and a `cd` moves later lines elsewhere, then that line), the command's output, a status pill for a non-zero exit code or a terminating signal, and a copy control that writes the raw `output` prop. A run-state `StateDot` marks the call once, on the first row, out of flow in a gutter the card reserves as its own left padding, so the dot sits inside the card box yet left of the prompt text. It reaches three of `StateDot`'s states — the chase while `running`, red for the same exit status that renders the pill, green otherwise — so a card states whether its command is still running rather than leaving that to be inferred from the presence of output; it carries one visually hidden text label because `StateDot` is `aria-hidden`. One dot regardless of line count is deliberate: the exit status is the whole call's, so a dot per line would claim a per-line outcome the view does not carry. Command text is `white-space: pre`, so repeated spaces, tabs, and an indented continuation render verbatim while the row stays single-line and ellipsizes. ANSI escape sequences are parsed with the `anser` runtime dependency into React spans; cursor movements replay into a per-line column buffer before inert controls are stripped, since carriage return and backspace only MOVE the cursor: `100%` + CR + `OK` alone shows `OK0%`, while the `\x1b[K` a spinner writes with its redraw erases the tail so `100%\r\x1b[KOK` shows `OK`. Erase-in-line is honored in all three parameter forms, the cursor advances by terminal columns (8-column tab stops, two for emoji and CJK, none for a combining mark), and SGR state is normalized per cell as a terminal stores it, threading across lines and closing at the state the line ended in; basic-16 foreground colors map onto `--dsw-*` tokens, while 256-palette and truecolor values pass through as literal rgb. Output keeps `white-space: pre` with horizontal scrolling, so column-aligned output holds its alignment instead of soft-wrapping, and collapses to a head slice plus a tail slice past `maxLines` (default 16) behind an expand button. Rationale: [the web terminal card note](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md). ## Read rendering @@ -22,7 +22,7 @@ Pure React atoms (zero cordis): StateDot, ic_ds_* icons, Button/Pill/Menu/Modal/ ## Diff rendering -`DiffBlock` renders a file mutation as an inline diff surface: one bold path header per file, the removed lines (`- `, error token) above the added lines (`+ `, success token), a `⋯` gap before a same-file second hunk, and a dim `└ +A -R · N file(s)` footer. Lines are `white-space: pre` with horizontal scrolling, so a source line holds its indentation instead of soft-wrapping, and the body collapses to a head slice plus a tail slice past `maxLines` (default 16, `TerminalBlock`'s split arithmetic) behind an expand button. A create (`oldText: null`) has no removed side. The copy control writes the prefixed diff text (path headers, `- `/`+ ` lines, the gap) so a multi-file copy stays attributable, and floats in the top-right corner rather than on a banner row of its own. Geometry mirrors `CodeBlock`/`TerminalBlock`. The `+`/`-` block form mirrors the TUI transcript's diff card so a diff reads the same across front ends. Rationale: [the web diff card note](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md). +`DiffBlock` renders a file mutation as an inline diff surface: one bold path header per file, the removed lines (`- `, error token) above the added lines (`+ `, success token), a `⋯` gap before a same-file second hunk, and a dim `└ +A -R · N file(s)` footer. Lines are `white-space: pre` with horizontal scrolling, so a source line holds its indentation instead of soft-wrapping, and the body collapses to a head slice plus a tail slice past `maxLines` (default 16, `TerminalBlock`'s split arithmetic) behind an expand button. A create (`oldText: null`) has no removed side. The copy control writes the prefixed diff text (path headers, `- `/`+ ` lines, the gap) so a multi-file copy stays attributable, and floats in the top-right corner rather than on a banner row of its own. Geometry mirrors `CodeBlock`/`TerminalBlock`. Rationale: [the web diff card note](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md). ## Search results @@ -44,6 +44,6 @@ None; this package neither assembles nor sends a provider request. - **Glyph-level icons are redrawn approximations** — the fish logo (and the sparkle held by ui-conversation) come from font glyphs whose vector geometry is not exportable from the local design data; hand-authored recreations stand in until an exact export path exists. - **Pill and Input have no design source** — both atoms are self-defined; the sidebar search field and view-tab strip that resemble them are consumer-owned compositions, not these atoms. -- **StateDot `Active` variant is a hidden placeholder in the design** — not implemented; the four shipped states (done/warning/ongoing/error) are the complete P-I surface. +- **No `Active` StateDot variant** — the supported states are done, warning, ongoing, and error. - **User-facing copy localizes through label props, defaulting to the original Chinese literals** — the atoms are zero-cordis and cannot reach `ctx.locale`, so `HoverCard` (`copyLabel`/`copiedLabel`), `TerminalBlock` (`labels`), `JsonTree` (`labels`), `CodeBlock` (`copyLabel`/`copiedLabel`), `MarkdownText` (`codeLabels`), `JsonBlock` (`truncatedLabel`), `ConnectionBanner` (`label`), and `Modal` (`closeLabel`) take their copy as optional props with the previous hardcoded strings as defaults. Localized plugins pass dictionary-driven labels from their own `t` seat; a consumer that passes nothing renders exactly the pre-localization output. `WebBlock` does not yet follow this pattern: its source-list and fetch truncation notes and its empty-search note stay inline Chinese, pending the same label-prop treatment. - **`TerminalBlock` is not a terminal emulator** — it renders settled or still-running command output, not an interactive session: SGR color and attributes are honored, and so are the in-line cursor movements a progress line uses — carriage return, backspace, erase-in-line, tab stops and character width. Absolute cursor positioning, screen clearing, and alternate-screen sequences are stripped. Basic-16 magenta and cyan have no token equivalent and stay literal rgb. diff --git a/packages/client/ui-primitives/README.zh.md b/packages/client/ui-primitives/README.zh.md index 21226ab211..76dfbebd5e 100644 --- a/packages/client/ui-primitives/README.zh.md +++ b/packages/client/ui-primitives/README.zh.md @@ -14,7 +14,7 @@ ## 终端输出 -`TerminalBlock` 将一条 shell 命令渲染为终端表层:命令的每一行各占一个提示行(缩短后的 `cwd` 标签只出现在第一行,因为视图只知道一个工作目录,而一个 `cd` 就会让后面的行去到别处,标签之后是该行)、命令输出、非零退出码或终止信号对应的状态胶囊,以及写入原始 `output` prop 的复制控件。一枚运行状态 `StateDot` 为整次调用标记一次,位于第一行,以脱离文档流的方式落在卡片以自身左内边距预留的落区中,因此它位于卡片盒之内、提示文字之左。它用到 `StateDot` 的三种状态——`running` 期间为追逐动画,与渲染状态胶囊相同的退出状态为红色,其余为绿色——因此卡片直接陈述其命令是否仍在运行,而不是让人从有无输出中推断;由于 `StateDot` 是 `aria-hidden`,它携带一处视觉隐藏的文本标签。无论多少行都只有一枚状态点是有意为之:退出状态属于整次调用,因此每行一枚就会声称一个视图并不携带的逐行结果。命令文本使用 `white-space: pre`,因此重复空格、制表符与缩进续行都原样呈现,同时该行仍保持单行并以省略号截断。ANSI 转义序列通过运行时依赖 `anser` 解析为 React span;光标移动在剥除无显示意义控制符之前先重放进逐行的列缓冲,因为回车与退格**只移动**光标:单是 `100%` 加回车再加 `OK` 显示为 `OK0%`,而 spinner 随重绘写出的 `\x1b[K` 会擦掉尾巴,因此 `100%\r\x1b[KOK` 显示为 `OK`。行内擦除的三种参数形式都被遵循,光标按终端列推进(8 列制表位;emoji 与 CJK 占两列;组合标记不占列),SGR 状态按单元格归一化存储,与终端一致,并跨行延续、在行结束时的状态处收束;基础 16 色前景色映射到 `--dsw-*` token,而 256 色板与真彩色值按字面 rgb 透传。输出保持 `white-space: pre` 并支持横向滚动,因此按列对齐的输出保留其对齐而不会软换行;超过 `maxLines`(默认 16,与 TUI 转录相同的切分算法)时折叠为头部切片加尾部切片,由展开按钮控制。原理:[Web 终端卡片笔记](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)。 +`TerminalBlock` 将一条 shell 命令渲染为终端表层:命令的每一行各占一个提示行(缩短后的 `cwd` 标签只出现在第一行,因为视图只知道一个工作目录,而一个 `cd` 就会让后面的行去到别处,标签之后是该行)、命令输出、非零退出码或终止信号对应的状态胶囊,以及写入原始 `output` prop 的复制控件。一枚运行状态 `StateDot` 为整次调用标记一次,位于第一行,以脱离文档流的方式落在卡片以自身左内边距预留的落区中,因此它位于卡片盒之内、提示文字之左。它用到 `StateDot` 的三种状态——`running` 期间为追逐动画,与渲染状态胶囊相同的退出状态为红色,其余为绿色——因此卡片直接陈述其命令是否仍在运行,而不是让人从有无输出中推断;由于 `StateDot` 是 `aria-hidden`,它携带一处视觉隐藏的文本标签。无论多少行都只有一枚状态点是有意为之:退出状态属于整次调用,因此每行一枚就会声称一个视图并不携带的逐行结果。命令文本使用 `white-space: pre`,因此重复空格、制表符与缩进续行都原样呈现,同时该行仍保持单行并以省略号截断。ANSI 转义序列通过运行时依赖 `anser` 解析为 React span;光标移动在剥除无显示意义控制符之前先重放进逐行的列缓冲,因为回车与退格**只移动**光标:单是 `100%` 加回车再加 `OK` 显示为 `OK0%`,而 spinner 随重绘写出的 `\x1b[K` 会擦掉尾巴,因此 `100%\r\x1b[KOK` 显示为 `OK`。行内擦除的三种参数形式都被遵循,光标按终端列推进(8 列制表位;emoji 与 CJK 占两列;组合标记不占列),SGR 状态按单元格归一化存储,与终端一致,并跨行延续、在行结束时的状态处收束;基础 16 色前景色映射到 `--dsw-*` token,而 256 色板与真彩色值按字面 rgb 透传。输出保持 `white-space: pre` 并支持横向滚动,因此按列对齐的输出保留其对齐而不会软换行;超过 `maxLines`(默认 16)时折叠为头部切片加尾部切片,由展开按钮控制。原理:[Web 终端卡片笔记](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)。 ## Read 渲染 @@ -22,7 +22,7 @@ ## Diff 渲染 -`DiffBlock` 将一次文件改动渲染为内联 diff 表层:每个文件一个粗体路径头、删除行(`- `,error token)在新增行(`+ `,success token)之上、同文件第二个 hunk 前一个 `⋯` gap,以及暗色 `└ +A -R · N file(s)` 页脚。各行使用 `white-space: pre` 并横向滚动,因此源码行保留其缩进而不软换行;超过 `maxLines`(默认 16,与 `TerminalBlock` 相同的切分算法)时折叠为头部切片加尾部切片,由展开按钮控制。新建(`oldText: null`)没有删除侧。复制控件写入带前缀的 diff 文本(路径头、`- `/`+ ` 行、gap),使多文件复制保持可归属,并浮在右上角而非占据自己的 banner 行。几何镜像 `CodeBlock`/`TerminalBlock`。`+`/`-` 块形式镜像 TUI 转录的 diff 卡片,使 diff 在两个前端读起来一致。原理:[Web diff 卡片笔记](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md)。 +`DiffBlock` 将一次文件改动渲染为内联 diff 表层:每个文件一个粗体路径头、删除行(`- `,error token)在新增行(`+ `,success token)之上、同文件第二个 hunk 前一个 `⋯` gap,以及暗色 `└ +A -R · N file(s)` 页脚。各行使用 `white-space: pre` 并横向滚动,因此源码行保留其缩进而不软换行;超过 `maxLines`(默认 16,与 `TerminalBlock` 相同的切分算法)时折叠为头部切片加尾部切片,由展开按钮控制。新建(`oldText: null`)没有删除侧。复制控件写入带前缀的 diff 文本(路径头、`- `/`+ ` 行、gap),使多文件复制保持可归属,并浮在右上角而非占据自己的 banner 行。几何镜像 `CodeBlock`/`TerminalBlock`。原理:[Web diff 卡片笔记](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md)。 ## 搜索结果 diff --git a/packages/client/ui-primitives/src/head-tail-cap.ts b/packages/client/ui-primitives/src/head-tail-cap.ts index 1ac540dd21..f0852052a4 100644 --- a/packages/client/ui-primitives/src/head-tail-cap.ts +++ b/packages/client/ui-primitives/src/head-tail-cap.ts @@ -1,6 +1,5 @@ // Head/tail height-cap arithmetic shared by the block primitives (TerminalBlock, -// SearchBlock) and matching the TUI transcript's collapsed tool card, so a long -// result's head and tail slices agree across every surface. The split is +// SearchBlock), so long results use consistent head and tail slices. The split is // `ceil(maxLines / 2)` head rows and the remainder as tail rows; a result within // the cap shows every row and hides none. diff --git a/packages/client/ui-sidebar/README.i18n.yaml b/packages/client/ui-sidebar/README.i18n.yaml index 1bd10f89a6..609542a3b7 100644 --- a/packages/client/ui-sidebar/README.i18n.yaml +++ b/packages/client/ui-sidebar/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-sidebar/README.md -README.md: 5bb697b3d2f9b5eaea9c382765d2510fa24806ce -README.zh.md: 302f66c540774b1f209fc797201e41c56b849310 +README.md: f251a211153b2fb317947501a051030c431698a1 +README.zh.md: f22ebfa34e51a267a21a548570770ba51cedc116 diff --git a/packages/client/ui-sidebar/README.md b/packages/client/ui-sidebar/README.md index 5bb697b3d2..f251a21115 100644 --- a/packages/client/ui-sidebar/README.md +++ b/packages/client/ui-sidebar/README.md @@ -24,6 +24,6 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **Session state-dot rendering is owned by [ui-workspace](../ui-workspace/README.md)** — done/error notification sources remain deferred. -- **Group-by menu ships by-workspace only** — Update/Status grouping strategies are drawn without specs and deferred. +- **Session state-dot rendering is owned by [ui-workspace](../ui-workspace/README.md)** — no done/error notification sources are available. +- **Group-by supports Workspace only** — Update and Status are not available strategies. - **"New task completed" unread marking is local viewing state** — completion-time > last-seen never reaches the host. diff --git a/packages/client/ui-sidebar/README.zh.md b/packages/client/ui-sidebar/README.zh.md index 302f66c540..f22ebfa34e 100644 --- a/packages/client/ui-sidebar/README.zh.md +++ b/packages/client/ui-sidebar/README.zh.md @@ -24,6 +24,6 @@ New Session 会启动运行时的页面局部前端 Session Intent;真实 Work ## 已知限制与暂缓事项 -- **Session 状态点渲染由 [ui-workspace](../ui-workspace/README.md) 持有**:done/error 的通知数据源仍暂缓实现。 -- **分组选单只提供按 Workspace 分组**:Update/Status 分组策略只有图稿而没有规范,暂缓实现。 +- **Session 状态点渲染由 [ui-workspace](../ui-workspace/README.md) 持有**:没有可用的 done/error 通知数据源。 +- **分组只支持 Workspace**:Update 和 Status 不是可用策略。 - **「New task completed」未读标记是本地查看状态**:完成时间 > 上次查看时间这一事实永远不会到达宿主。 diff --git a/packages/client/ui-slash/README.i18n.yaml b/packages/client/ui-slash/README.i18n.yaml index d27bf07bed..340f420504 100644 --- a/packages/client/ui-slash/README.i18n.yaml +++ b/packages/client/ui-slash/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-slash/README.md -README.md: 5d277a83c5f0bc4bcec5871e0618af28afb7b6d2 -README.zh.md: 08cc477edf657279c142a5e54a1471a118b9a021 +README.md: efb289aaf6b4a442b00e7a0b26ed044b7b061070 +README.zh.md: e2d99514d29b1b5aeeb1d7f42136ec73d961f3e7 diff --git a/packages/client/ui-slash/README.md b/packages/client/ui-slash/README.md index 5d277a83c5..efb289aaf6 100644 --- a/packages/client/ui-slash/README.md +++ b/packages/client/ui-slash/README.md @@ -22,4 +22,4 @@ None; this package neither assembles nor sends a provider request. - **Global source layer only** — session-scope source registration (per-session shadowing, ScopedLayers-alike) is designed but not enabled; the ledger tracks the trigger condition (a real per-session source need). - **`SlashCandidate.icon` renders as text** — MenuView drops the string into the icon slot verbatim; wiring to the design-system icon enum (iconFile five-variant family) lands when that enum ships. -- **Overlay SlotMap merge home is split from slot ownership** — the `conversation.input.overlay` merge lives here (sole copy) while the slot's owner semantics (anchor, children declaration, lifecycle) stay with ui-conversation; the dependency direction (ui-conversation → ui-slash) forces the split, so a future dependency reshuffle should revisit it. +- **Overlay SlotMap merge home is split from slot ownership** — the sole `conversation.input.overlay` merge lives here, while ui-conversation owns its anchor, children declaration, and lifecycle because the dependency direction is ui-conversation → ui-slash. diff --git a/packages/client/ui-slash/README.zh.md b/packages/client/ui-slash/README.zh.md index 08cc477edf..e2d99514d2 100644 --- a/packages/client/ui-slash/README.zh.md +++ b/packages/client/ui-slash/README.zh.md @@ -22,4 +22,4 @@ MenuView 把菜单 store 渲染进 `conversation.input.overlay` slot(列表类 - **只有全局 source 层**:会话 scope 的 source 注册(逐会话遮蔽、类 ScopedLayers 机制)已有设计但未启用;台账记录着触发条件(出现真实的逐会话 source 需求)。 - **`SlashCandidate.icon` 以文本渲染**:MenuView 把该字符串原样放进图标位;与设计系统图标枚举(iconFile 五变体家族)的接入将在该枚举交付后完成。 -- **overlay 的 SlotMap 合并归属与 slot 所有权分离**:`conversation.input.overlay` 的合并放在本包(唯一副本),而该 slot 的 owner 语义(锚点、children 声明、生命周期)留在 ui-conversation;依赖方向(ui-conversation → ui-slash)迫使这一拆分,未来依赖关系调整时应重新审视。 +- **overlay 的 SlotMap 合并归属与 slot 所有权分离**:唯一的 `conversation.input.overlay` 合并放在本包,而 ui-conversation 负责其锚点、children 声明和生命周期,因为依赖方向是 ui-conversation → ui-slash。 diff --git a/packages/client/ui-theme/README.i18n.yaml b/packages/client/ui-theme/README.i18n.yaml index ea7237402a..76bcbaf608 100644 --- a/packages/client/ui-theme/README.i18n.yaml +++ b/packages/client/ui-theme/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-theme/README.md README.md: 88e21fe214ec806b101050949690283d811be36d -README.zh.md: 4ed45070234acb78a2e5edef52578b504ae53077 +README.zh.md: ba781ba89a62292928a7b05ab94ea1cd930b4f50 diff --git a/packages/client/ui-theme/README.zh.md b/packages/client/ui-theme/README.zh.md index 4ed4507023..ba781ba89a 100644 --- a/packages/client/ui-theme/README.zh.md +++ b/packages/client/ui-theme/README.zh.md @@ -8,7 +8,7 @@ 滚动条重新绑定契约:`scrollbar.css` 在 `body` 上把 `--dsh-scrollbar-thumb` 与 `--dsh-scrollbar-thumb-hover` 绑定到 l1(基础表面)token,两条渲染路径都读取这一组变量。高层级表面(菜单、浮层、对话框)在自己的容器上设置 `--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2)` 与 `--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2)`;一次重新绑定即可为引擎实际走的那条路径换色。这组变量另一个合法的目标是 `transparent`,即完全不绘制滑块——[ui-sidebar](../ui-sidebar/README.md) 在指针不在栏内时就这样重新绑定自己的列。绑回 l1 那组不算重新绑定,它只是重述基础表面的默认值。 -两条路径在构造上互斥。`scrollbar-width`/`scrollbar-color` 写在 `@supports not selector(::-webkit-scrollbar)` 之内,因为这两个属性中的任一个只要取非 `auto` 值,Chromium 与 Safari 就会丢弃该元素上的全部 `::-webkit-scrollbar*` 规则,`::-webkit-scrollbar-thumb:hover` 也在其中——若无条件地同时声明,`--dsh-scrollbar-thumb-hover` 在任何引擎上都不会被渲染。因此 Firefox 走标准属性,WebKit 系引擎走伪元素,hover token 只经由伪元素这条路径渲染。推理过程与实测计算值见[滚动条 Agent Note(agent 决策记录)](../../../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md)。 +两条路径在构造上互斥。`scrollbar-width`/`scrollbar-color` 写在 `@supports not selector(::-webkit-scrollbar)` 之内,因为这两个属性中的任一个只要取非 `auto` 值,Chromium 与 Safari 就会丢弃该元素上的全部 `::-webkit-scrollbar*` 规则,`::-webkit-scrollbar-thumb:hover` 也在其中——若无条件地同时声明,`--dsh-scrollbar-thumb-hover` 在任何引擎上都不会被渲染。因此 Firefox 走标准属性,WebKit 系引擎走伪元素,hover token 只经由伪元素这条路径渲染。推理过程与实测计算值见[滚动条 Agent Note](../../../.agents/notes/implemented/bug-fix/2026-07-28-themed-scrollbars-and-reserved-gutter.md)。 ## 模型体验 diff --git a/packages/client/ui-trajectory/README.i18n.yaml b/packages/client/ui-trajectory/README.i18n.yaml index 86953e8e94..5ef1a8eb68 100644 --- a/packages/client/ui-trajectory/README.i18n.yaml +++ b/packages/client/ui-trajectory/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-trajectory/README.md -README.md: 5d0ea3bbbbfca2b8c0ee02ed07ca956fbd377e11 -README.zh.md: d88e4562296ccef8653f85ee74e2e6e856bd7d96 +README.md: 771e1a68e8027f02f20b17f78d0a8dd48d2d5bff +README.zh.md: ceb4d696e5fda117afff89cf9cd0a5426dfed2be diff --git a/packages/client/ui-trajectory/README.md b/packages/client/ui-trajectory/README.md index 5d0ea3bbbb..771e1a68e8 100644 --- a/packages/client/ui-trajectory/README.md +++ b/packages/client/ui-trajectory/README.md @@ -14,4 +14,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **In-flight Time stays blank** — `partial` / `runningCalls` rows show their running state without a fabricated duration until a live clock policy lands, so the Overview renders a start marker rather than inventing a live span; record and timeline selection are intentionally local to Trajectory; anchor deep-linking remains deferred. +- **In-flight Time stays blank** — `partial` and `runningCalls` rows show their running state without a fabricated duration, so the Overview renders a start marker rather than inventing a live span. Record and timeline selection are local to Trajectory, with no anchor deep links. diff --git a/packages/client/ui-trajectory/README.zh.md b/packages/client/ui-trajectory/README.zh.md index d88e456229..ceb4d696e5 100644 --- a/packages/client/ui-trajectory/README.zh.md +++ b/packages/client/ui-trajectory/README.zh.md @@ -14,4 +14,4 @@ Trajectory 渲染按轮次组织的事件记录表,其中可选择用户、助 ## 已知限制与暂缓事项 -- **进行中时,Time 保持空白**:`partial`/`runningCalls` 行会显示运行状态,但在实时钟策略落地前不会虚构耗时,因此 Overview 区域只渲染开始标记,而不会杜撰实时跨度;记录选择与时间线选择有意保持在 Trajectory 内部;锚点深链接仍暂缓实现。 +- **进行中时,Time 保持空白**:`partial` 与 `runningCalls` 行会显示运行状态,但不会虚构耗时,因此 Overview 区域只渲染开始标记,而不会杜撰实时跨度。记录选择与时间线选择位于 Trajectory 内部,不提供锚点深链接。 diff --git a/packages/client/ui-workspace/README.i18n.yaml b/packages/client/ui-workspace/README.i18n.yaml index 43d753fae5..1f3645727c 100644 --- a/packages/client/ui-workspace/README.i18n.yaml +++ b/packages/client/ui-workspace/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-workspace/README.md -README.md: 855bcdc7fa1850ee30a1887add10dc019d9887e2 -README.zh.md: acb35ab169f10444c26ae6d68aefda9c5b8703df +README.md: 7f17eb841368b5c04df0a8192716e2c9a3c6896a +README.zh.md: 574ac5b74e233d9366681991ede89a37c6fae79c diff --git a/packages/client/ui-workspace/README.md b/packages/client/ui-workspace/README.md index 855bcdc7fa..7f17eb8413 100644 --- a/packages/client/ui-workspace/README.md +++ b/packages/client/ui-workspace/README.md @@ -29,6 +29,6 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **No fuzzy content search or event deep links** — the content backend uses literal token/phrase matching, and selecting a result opens the Session rather than the matching event. -- **No Session deletion or unarchive control** — archiving replaces the former Delete placeholder; archived sessions have no viewing or unarchive surface yet, and Workspace registration deletion does not delete Sessions. +- **No Session deletion or unarchive control** — sessions can be archived, but archived sessions have no viewing or unarchive surface, and Workspace registration deletion does not delete Sessions. - **Pending user interaction is not aggregated into collapsed groups** — a waiting row inside a collapsed group lights no group-header indicator and becomes visible only after that group is expanded. - **Native folder selection depends on the local Host carrier** — under the `-native` composition, fixture-only or remote browser deployments cannot open a local operating-system dialog; platform failures are shown in a retryable modal. Remote-capable picking is the `-browse` composition's in-app flow. diff --git a/packages/client/ui-workspace/README.zh.md b/packages/client/ui-workspace/README.zh.md index acb35ab169..574ac5b74e 100644 --- a/packages/client/ui-workspace/README.zh.md +++ b/packages/client/ui-workspace/README.zh.md @@ -29,6 +29,6 @@ Session 行渲染运行时的实时 `pendingInteraction` 分类:审批显示** ## 已知限制与暂缓事项 - **没有模糊内容搜索或事件深链接**:内容后端采用字面 token/短语匹配,选择结果会打开 Session,而不是匹配的事件。 -- **没有 Session 删除与取消归档控件**:归档取代了原先的 Delete 占位;已归档会话尚无查看或取消归档入口;删除 Workspace 注册记录不会删除 Session。 +- **没有 Session 删除与取消归档控件**:会话可以归档,但已归档会话没有查看或取消归档入口;删除 Workspace 注册记录不会删除 Session。 - **待处理的用户交互不会聚合到折叠的分组上**:折叠分组内正在等待的行不会点亮分组头指示,只有展开该分组后才可见。 - **原生文件夹选择依赖本地 Host 载体**:在 `-native` 组合下,仅使用 fixture(测试前置数据)的部署或远程浏览器部署无法打开本地操作系统对话框;模态框会显示平台故障,并允许重试。可远程的选取是 `-browse` 组合的应用内流程。 diff --git a/packages/client/web-react/README.i18n.yaml b/packages/client/web-react/README.i18n.yaml index c88190f135..72afe765f0 100644 --- a/packages/client/web-react/README.i18n.yaml +++ b/packages/client/web-react/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/web-react/README.md -README.md: 7cc80f22bd5527838288d11c819e81b7ec4d17c4 -README.zh.md: 9019a9618d35b6fc92dcc2cc84f8f903fa1ee346 +README.md: 8f1a525af1e249282f2cc473e7f9652f68f914a8 +README.zh.md: 55ed3a0fec4bfba4c4934db8728bc6fa23a7321a diff --git a/packages/client/web-react/README.md b/packages/client/web-react/README.md index 7cc80f22bd..8f1a525af1 100644 --- a/packages/client/web-react/README.md +++ b/packages/client/web-react/README.md @@ -16,4 +16,4 @@ None; this package neither assembles nor sends a provider request. - **The persist middleware corrupts primitive-state stores** — it object-spreads state on save, so a `SnapshotStore` round-trips as a character map; the engine hand-rolls persistence instead (see `attachPersistence`). - **`UseSession` is deliberately wide (`object` snapshot)** — the dependency direction (runtime → web-react, never the reverse) keeps the real `ConversationSnapshot` type out of reach; session-slot consumers narrow once at their boundary. -- **renderSlot is the single P-I form** — no Suspense, no per-entry lazy loading; the progressive-rendering surface returns with its own project. +- **`renderSlot` is the only rendering form** — there is no Suspense integration or per-entry lazy loading. diff --git a/packages/client/web-react/README.zh.md b/packages/client/web-react/README.zh.md index 9019a9618d..55ed3a0fec 100644 --- a/packages/client/web-react/README.zh.md +++ b/packages/client/web-react/README.zh.md @@ -16,4 +16,4 @@ slot 终端设计的外壳侧 React 胶水:createSlotRenderer(外壳安装 - **persist 中间件会损坏原始值状态 store**:保存时它会对状态执行对象展开,因此 `SnapshotStore` 往返后会变成字符映射;引擎改为自行实现持久化(见 `attachPersistence`)。 - **`UseSession` 有意保持宽泛(`object` 快照)**:依赖方向(runtime → web-react,绝不反向)使真实 `ConversationSnapshot` 类型不可访问;会话 slot 消费方在其边界处缩窄一次。 -- **renderSlot 是唯一的 P-I 形式**:没有 Suspense,也没有逐配置项惰性加载;渐进式渲染能力将在其独立项目中恢复。 +- **`renderSlot` 是唯一的渲染形式**:没有 Suspense 集成或逐配置项惰性加载。 diff --git a/packages/client/web/README.i18n.yaml b/packages/client/web/README.i18n.yaml index 323f244b1e..1e0c8b649b 100644 --- a/packages/client/web/README.i18n.yaml +++ b/packages/client/web/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/web/README.md -README.md: b8b03dcb58442116cc01a2ff4c30e266e3233ee9 -README.zh.md: 280ec52602321367715ae2a71c22cff265908299 +README.md: 8425cb1b33db50a8b7e3667323b6b65bfb43ad32 +README.zh.md: 656cdcb1f2fc1edf0d8387ee9745b04476923797 diff --git a/packages/client/web/README.md b/packages/client/web/README.md index b8b03dcb58..8425cb1b33 100644 --- a/packages/client/web/README.md +++ b/packages/client/web/README.md @@ -23,4 +23,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **One-shot rendering by design** — the UI waits for the boot settle; a single entry failure keeps the loading page with a loud per-entry report, no partial availability (progressive rendering returns with its own project). -- **Narrow-window acceptance is deferred** — the concession chain is implemented in ui-layout but the shell-level narrow-viewport walkthrough is a P-II acceptance item. +- **Narrow-window shell behavior lacks an assembled walkthrough** — ui-layout implements the concession chain, but this package has no shell-level narrow-viewport acceptance case. diff --git a/packages/client/web/README.zh.md b/packages/client/web/README.zh.md index 280ec52602..656cdcb1f2 100644 --- a/packages/client/web/README.zh.md +++ b/packages/client/web/README.zh.md @@ -23,4 +23,4 @@ Web 外壳内核:`new AppWebEntry(el, seams?).run()` 通过两阶段启动(w ## 已知限制与暂缓事项 - **有意采用一次性渲染**:UI 等待启动 settle;只要一个配置项失败,加载页面就会保留并逐项显示醒目的报告,不提供部分可用性(渐进式渲染将作为独立项目恢复)。 -- **窄窗口验收暂缓**:ui-layout 已实现让步链,但外壳级窄视口演练是 P-II 验收项。 +- **窄窗口外壳行为缺少组装后演练**:ui-layout 已实现让步链,但该包没有外壳级窄 viewport 验收用例。 diff --git a/packages/code-runtime/README.i18n.yaml b/packages/code-runtime/README.i18n.yaml index d8eebec7aa..a04f1985c2 100644 --- a/packages/code-runtime/README.i18n.yaml +++ b/packages/code-runtime/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/code-runtime/README.md -README.md: dbe6b37ffa01d07c6902672a06ebf6f88548ff99 -README.zh.md: a5acbad3cce19366ca9ca4729f5285905ab026eb +README.md: 2d32a05071efdfa05c336211196bb769ed5a5fc7 +README.zh.md: 62c9a395ac3cf2b5cd55455ab5a273a1e276f6b7 diff --git a/packages/code-runtime/README.zh.md b/packages/code-runtime/README.zh.md index f3dfa3e7e9..62c9a395ac 100644 --- a/packages/code-runtime/README.zh.md +++ b/packages/code-runtime/README.zh.md @@ -1,12 +1,12 @@ -# code-runtime/:代码执行能力家族 +# code-runtime/ — 代码执行能力家族 [English](README.md) | 中文 -代码执行能力 seam(参见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):一个抽象运行时接口,用于针对宿主提供的异步绑定执行一段模型编写的程序,并捕获程序打印和返回的内容。消费方是工具注册表的 [Code Mode](../core/tools/README.md)(`tools: { mode: code }`,即 `run_code` 工具与生成的 TypeScript SDK);设计记录在 [Code Mode Agent Note](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md) 中。这些都是**产品**包。 +代码执行能力 seam(参见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):一个抽象运行时接口,用于对宿主提供的异步绑定执行模型编写的程序,并捕获它打印和返回的内容。消费方是工具注册表的 [Code Mode](../core/tools/README.md)(`tools: { mode: code }`,即 `run_code` 工具和生成的 TypeScript SDK);设计见 [Code Mode Agent Note](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)。这些全是**产品**包。 -| 包 | 职责 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| | [`code-runtime/`](code-runtime/README.md) | 代码执行 seam 与共享词汇 | `ctx.codeRuntime` | -| [`code-runtime-worker/`](code-runtime-worker/README.md) | worker 线程后端 | 注册 `ctx.codeRuntime` | +| [`code-runtime-worker/`](code-runtime-worker/README.md) | Worker 线程后端 | 注册 `ctx.codeRuntime` | -后端注册该 seam,无需改动消费方。语言、隔离与执行预算细节由子级 README 负责。 +后端在不改变消费方的情况下注册该 seam。子 README 负责语言、隔离和执行预算细节。 diff --git a/packages/compact/README.i18n.yaml b/packages/compact/README.i18n.yaml index b20270f991..17357bea7b 100644 --- a/packages/compact/README.i18n.yaml +++ b/packages/compact/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/compact/README.md -README.md: aa9fa6d9419de87a7df23a437f5ea8694d981b28 -README.zh.md: 7c8752d891880cdb3fd3015a5bd088c7b5e0f2b6 +README.md: 509ea2764250f42e492f787deba959a8dd9967b7 +README.zh.md: adc52690dbe9e904d8f24199cb78493dab3333a4 diff --git a/packages/compact/README.zh.md b/packages/compact/README.zh.md index 7c8752d891..adc52690db 100644 --- a/packages/compact/README.zh.md +++ b/packages/compact/README.zh.md @@ -1,14 +1,14 @@ -# compact/:压缩能力家族 +# compact/ — 压缩能力家族 [English](README.md) | 中文 -一个压缩(compaction)能力家族(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):抽象接口、摘要生成后端、不依赖模型的工具结果剪枝配套组件,以及面向用户的命令适配器。这些全是**产品**包。 +一个压缩(compaction)能力家族(参见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):抽象接口、摘要后端、无模型工具结果修剪配套工具,以及用户命令适配器。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `compact/` | 抽象压缩 seam(接口 + `compact/*` 事件 + `CompactionResult`) | `ctx.compact` | -| `compact-basic/` | 后端:`ctx.tokenMeter` 压力 + 按 token 预算保留内容 + `llm.stream()` 摘要生成 | (注册 `ctx.compact`) | -| `compact-tool-result-prune/` | 可选的不依赖模型的头/中/尾重写,在摘要压缩之前运行 | `ctx.toolResultPrune` | -| `command-compact/` | 面向用户的 `/compact` 命令,基于后端无关的 `compactNow()` seam | (注册到 `ctx.commands`) | +| [`compact/`](compact/README.md) | 压缩 seam 与事件词汇 | `ctx.compact` | +| [`compact-basic/`](compact-basic/README.md) | token 压力与摘要后端 | 注册 `ctx.compact` | +| [`compact-tool-result-prune/`](compact-tool-result-prune/README.md) | 可选的无模型工具结果修剪 | `ctx.toolResultPrune` | +| [`command-compact/`](command-compact/README.md) | 用户压缩命令 | 注册到 `ctx.commands` | -接口位于 `compact/compact/`,后端位于 `compact/compact-basic/`,确定性剪枝位于 `compact/compact-tool-result-prune/`,命令位于 `compact/command-compact/`。与 bash seam 不同,该接口依赖 `dsh-session` 和 `dsh-llm`,因为它的操作以 `Session` 为对象,输出则使用 `ContentBlock`。这项偏差记录在[压缩能力 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md) 中。token 测量仍是可复用的 LLM(大语言模型)家族服务;基于模板或模型的压缩器可以替换 `compact-basic`,而无需更改计量器、剪枝器、命令或自动调用方。 +后端、可选修剪器和用户命令通过该 seam 组合;token 测量仍是独立的 LLM(大语言模型)家族服务。[压缩能力 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md)负责依赖关系的设计原理。 diff --git a/packages/compact/command-compact/README.i18n.yaml b/packages/compact/command-compact/README.i18n.yaml index d03eb278c2..c39570db18 100644 --- a/packages/compact/command-compact/README.i18n.yaml +++ b/packages/compact/command-compact/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/compact/command-compact/README.md -README.md: 1445e76f8328a9ac1c5f9dd43094f1c1cd5d2ad4 -README.zh.md: b7357c0efc83c8ccce709518ac2dd2f5e66bbace +README.md: a32a6aeb9957f0fd5f8cff58b1edbb9bc29a4e3d +README.zh.md: c678f522115d9b0fd414b2f290b3cb54ce690722 diff --git a/packages/compact/command-compact/README.md b/packages/compact/command-compact/README.md index 1445e76f83..a32a6aeb99 100644 --- a/packages/compact/command-compact/README.md +++ b/packages/compact/command-compact/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Human-facing `/compact` control over [`ctx.compact`](../compact/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers it; the shipped TUI executes it without a model turn. The [queued manual compaction Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md) owns the admission, lock, and durability decisions. +Human-facing `/compact` control over [`ctx.compact`](../compact/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers and executes it without a model turn. The [queued manual compaction Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md) owns the admission, lock, and durability decisions. ## Command contract @@ -41,7 +41,7 @@ The producer injects `commands` and `compact`. Mount the command registry, one b name: '@deepseek-ai/dsh-command-compact' ``` -The TUI example and CLI host mount it beside `compact-basic`. Automation surfaces that compose no command registry keep automatic compaction only. +The shipped `dsh` base mounts it beside `compact-basic`, and the Web client provides the command adapter. Automation surfaces that compose no command adapter keep automatic compaction only. ## Model Experience diff --git a/packages/compact/command-compact/README.zh.md b/packages/compact/command-compact/README.zh.md index b7357c0efc..c678f52211 100644 --- a/packages/compact/command-compact/README.zh.md +++ b/packages/compact/command-compact/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -通过 [`ctx.compact`](../compact/README.md) 提供面向用户的 `/compact` 压缩(compaction)控制。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此组合中的每个命令适配器都能发现它;随附 TUI 无需模型轮次即可执行该命令。[排队手动压缩 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md)拥有接纳、锁与持久性决策。 +通过 [`ctx.compact`](../compact/README.md) 提供面向用户的 `/compact` 压缩(compaction)控制。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此组合中的每个命令适配器都能发现并执行它,无需模型轮次。[排队手动压缩 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-queued-manual-compaction.md)拥有接纳、锁与持久性决策。 ## 命令契约 @@ -41,7 +41,7 @@ busy 结果有意限定在进程范围内:活动的未匹配标记会阻塞, name: '@deepseek-ai/dsh-command-compact' ``` -TUI 示例与 CLI host 将它挂载在 `compact-basic` 旁。未组合命令注册表的自动化接口只保留自动压缩。 +随附 `dsh` 基础配置将它挂载在 `compact-basic` 旁,Web 客户端提供命令适配器。未组合命令适配器的自动化接口只保留自动压缩。 ## 模型体验 diff --git a/packages/context/README.i18n.yaml b/packages/context/README.i18n.yaml index 77176bd6ab..f3acd7f2bf 100644 --- a/packages/context/README.i18n.yaml +++ b/packages/context/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/README.md -README.md: b4adb18c912617588711d7a0641426ce2cf28d89 -README.zh.md: d129c63371682ae6fff3ca0c56f5906bdf6cfab3 +README.md: a0b5eef51740b3aa67b9e158a00357f849b8bf57 +README.zh.md: d6ed8050f3a64f740a951883f0dc321ac08e978a diff --git a/packages/context/README.md b/packages/context/README.md index b4adb18c91..a0b5eef517 100644 --- a/packages/context/README.md +++ b/packages/context/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Product plugins that add model-visible request context without defining a tool. `workspace-context` is included by the default `dsh-agent-spine-demo` bundle and can be disabled through bundle config; `time-context` and `tmux-context` are opt-in, while the standard TUI bundle composes `session-reference` explicitly. +Product plugins that add model-visible request context without defining a tool. `workspace-context` is included by the default `dsh-agent-spine-demo` bundle and can be disabled through bundle config; `time-context`, `tmux-context`, and `session-reference` are opt-in. | Package | Role | ctx key | |---|---|---| diff --git a/packages/context/README.zh.md b/packages/context/README.zh.md index d129c63371..d6ed8050f3 100644 --- a/packages/context/README.zh.md +++ b/packages/context/README.zh.md @@ -1,14 +1,14 @@ -# context/:请求上下文扩展 +# context/ — 请求上下文扩展 [English](README.md) | 中文 -这些产品插件无需定义工具,即可增加模型可见的请求上下文。`workspace-context` 包含在默认的 `dsh-agent-spine-demo` 组合包中,且可通过组合包配置将其禁用;`time-context` 与 `tmux-context` 均需显式启用,标准 TUI 组合包则会显式组合 `session-reference`。 +在不定义工具的情况下添加面向模型请求上下文的产品插件。`workspace-context` 包含在默认 `dsh-agent-spine-demo` 组合包中,可通过组合包配置禁用;`time-context`、`tmux-context` 和 `session-reference` 需主动启用。 | 包 | 职责 | ctx key | |---|---|---| | [`session-reference/`](session-reference/README.md) | 其他会话的有界快照 | `ctx.sessionReferences` | -| [`time-context/`](time-context/README.md) | 当前时间与已用时上下文 | — | +| [`time-context/`](time-context/README.md) | 当前时间与耗时上下文 | — | | [`tmux-context/`](tmux-context/README.md) | tmux 位置上下文 | — | -| [`workspace-context/`](workspace-context/README.md) | 工作区指令上下文 | — | +| [`workspace-context/`](workspace-context/README.md) | workspace 指令上下文 | — | -[`workspace-context` 决策记录](../../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)解释了每个 agent(智能体)和会话各自隔离的方式,以及相应的生命周期拆分。 +[`workspace-context` 决策记录](../../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)解释其逐 agent(智能体)/会话隔离和生命周期拆分。 diff --git a/packages/context/session-reference/README.i18n.yaml b/packages/context/session-reference/README.i18n.yaml index fff2b196ad..ec21c9fd89 100644 --- a/packages/context/session-reference/README.i18n.yaml +++ b/packages/context/session-reference/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/session-reference/README.md -README.md: 66df45b18df6d859239c8d3216d6c8b9fa61ab23 -README.zh.md: b59a98b86baf88f53429bc2409ad1776b1638736 +README.md: b1c76ef6865d6782941e13c2698fe76e38fb3abd +README.zh.md: cd083c9d5a49f2bb6e2358bbfbc10d8d299c2783 diff --git a/packages/context/session-reference/README.md b/packages/context/session-reference/README.md index 66df45b18d..b1c76ef686 100644 --- a/packages/context/session-reference/README.md +++ b/packages/context/session-reference/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -`ctx.sessionReferences` prepares bounded, read-only snapshots of other sessions as sourced model-facing context. It consumes `ctx.sessionQuery` and the backend-independent compact checkpoint marker; SQLite FTS is not required. The standard TUI bundle mounts it, while other hosts may call the service directly. +`ctx.sessionReferences` prepares bounded, read-only snapshots of other sessions as sourced model-facing context. It consumes `ctx.sessionQuery` and the backend-independent compact checkpoint marker; SQLite FTS is not required. Hosts that support cross-session mentions may opt into the service. ## Public API @@ -14,7 +14,7 @@ English | [中文](README.zh.md) Preparation calls `ctx.sessionQuery.readSurface()` once per distinct source and never rereads it after enqueue. It projects only direct-user `user/message`, direct-user `steering/message`, assistant text, and `user/message` checkpoints carrying the canonical `dsh-compact` source marker from the folded current surface. For a source prompt that already contains baked prefix context, projection reads only its model-hidden display content, preventing recursive snapshot propagation. Shadowed pre-compaction events, tools, reasoning, context, plugin-generated user messages other than marked compact checkpoints, and unfinished assistant chunks are excluded. A compacted source therefore contributes its latest checkpoint plus retained later conversation, not restored shadowed text. -The context source is `{ kind: 'session-reference', version: 1, references }`; each reference records its source id and label, capture seq, compact presence, retained/omitted message counts, omitted UTF-8 bytes, and truncation state. The standard TUI preserves admission ownership without attaching context to the generic inbox record: outside the next-step acceptance window, a one-shot `agent/prompt-submit` wrapper adds the snapshot only to an allowed decision; during prompt admission or an open turn, `inject()` and `steer()` stage beside each other for the same safe boundary. The target log therefore records a sourced context `user/message` followed by the readable direct `user/message` or `steering/message`. Later source mutation, compaction, or deletion cannot change target replay. +The context source is `{ kind: 'session-reference', version: 1, references }`; each reference records its source id and label, capture seq, compact presence, retained/omitted message counts, omitted UTF-8 bytes, and truncation state. A host captures the snapshot before submitting the associated prompt and appends it as sourced context at the same safe admission boundary. The target log therefore records the snapshot separately from the readable direct message. Later source mutation, compaction, or deletion cannot change target replay. ## Configuration diff --git a/packages/context/session-reference/README.zh.md b/packages/context/session-reference/README.zh.md index b59a98b86b..cd083c9d5a 100644 --- a/packages/context/session-reference/README.zh.md +++ b/packages/context/session-reference/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -`ctx.sessionReferences` 会把其他会话准备为有界、只读快照,作为带来源信息、面向模型的上下文。它消费 `ctx.sessionQuery` 与后端无关的 compact 检查点标记;不需要 SQLite FTS。标准 TUI bundle 会装载它,其他宿主也可直接调用该服务。 +`ctx.sessionReferences` 会把其他会话准备为有界、只读快照,作为带来源信息、面向模型的上下文。它消费 `ctx.sessionQuery` 与后端无关的 compact 检查点标记;不需要 SQLite FTS。支持跨会话 mention 的宿主可以主动启用该服务。 ## 公开 API @@ -14,7 +14,7 @@ 准备阶段会对每个不同源调用一次 `ctx.sessionQuery.readSurface()`,入队后绝不重读。它仅投影折叠后当前表层中的用户直接发出的 `user/message`、用户直接发出的 `steering/message`、assistant 文本,以及 `user/message` 检查点;这类检查点携带规范 `dsh-compact` 源标记。对于已经包含固化前缀上下文的源提示词,投影只读取其对模型隐藏的显示内容,以防止快照递归传播。已遮蔽的压缩(compaction)前事件、工具、推理(reasoning)、上下文、除已标记 compact 检查点外的插件生成 user 消息,以及未完成的 assistant 分片均会被排除。因此,已压缩源只会提供最新检查点及其后保留的会话内容,不会还原已遮蔽的文本。 -上下文源为 `{ kind: 'session-reference', version: 1, references }`;每条引用会记录其源 id 与 label、捕获 seq、是否存在 compact、已保留/已省略消息数、已省略 UTF-8 字节数与截断状态。标准 TUI 在不把上下文附加到通用 inbox 记录的情况下保留接纳归属:next-step 接收窗口之外,一次性 `agent/prompt-submit` 包装层只为获准决策添加快照;提示词接纳期间或轮次打开时,`inject()` 与 `steer()` 会并排暂存到同一安全边界。目标日志因此会先记录一条带来源信息的上下文 `user/message`,再记录可读的直接 `user/message` 或 `steering/message`。后续源变更、压缩或删除都无法改变目标回放。 +上下文源为 `{ kind: 'session-reference', version: 1, references }`;每条引用会记录其源 id 与 label、捕获 seq、是否存在 compact、已保留/已省略消息数、已省略 UTF-8 字节数与截断状态。宿主会在提交关联提示词之前捕获快照,并在同一安全接纳边界把它追加为带来源信息的上下文。因此,目标日志会把快照与可读的直接消息分开记录。后续源变更、压缩或删除都无法改变目标回放。 ## 配置 diff --git a/packages/context/tmux-context/README.i18n.yaml b/packages/context/tmux-context/README.i18n.yaml index 804a08bc3b..c8489fed39 100644 --- a/packages/context/tmux-context/README.i18n.yaml +++ b/packages/context/tmux-context/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/context/tmux-context/README.md -README.md: 053206797398aa952522298e82992a7320daf74c -README.zh.md: 444d144da362b196e0d1025739a04f5f6c18aa92 +README.md: 801a1f42624b9ab96eb86ec4fec4467c84021561 +README.zh.md: 23510883ff35b35cb69b71da42720d01b574fcf3 diff --git a/packages/context/tmux-context/README.md b/packages/context/tmux-context/README.md index 0532067973..801a1f4262 100644 --- a/packages/context/tmux-context/README.md +++ b/packages/context/tmux-context/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Opt-in durable context naming the tmux session, window, and pane this agent process runs in, plus the window's pane-tree layout. Sampled once per turn during model-request preparation. The shipped TUI mounts it; `dsh-agent-spine-demo` and the Web/headless surfaces do not. Decision record: [the tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md). +Opt-in durable context naming the tmux session, window, and pane this agent process runs in, plus the window's pane-tree layout. It is sampled once per turn during model-request preparation and is not part of the shipped Web/headless composition. Decision record: [the tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md). ## Config diff --git a/packages/context/tmux-context/README.zh.md b/packages/context/tmux-context/README.zh.md index 444d144da3..23510883ff 100644 --- a/packages/context/tmux-context/README.zh.md +++ b/packages/context/tmux-context/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -可选启用的持久上下文,记录本 agent 进程所在的 tmux session、window、pane,以及该 window 的 pane 树布局。在准备模型请求时每轮采样一次。已交付的 TUI 会挂载它;`dsh-agent-spine-demo` 与 Web/无头界面均不挂载。决策记录见:[tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md)。 +可选启用的持久上下文,记录本 agent(智能体)进程所在的 tmux session、window、pane,以及该 window 的 pane 树布局。在准备模型请求时每轮采样一次;随附 Web/无头组合不包含它。决策记录见:[tmux-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-27-tmux-location-context.md)。 ## 配置 diff --git a/packages/cordis/README.i18n.yaml b/packages/cordis/README.i18n.yaml index 67c5200fe9..2eacddb52c 100644 --- a/packages/cordis/README.i18n.yaml +++ b/packages/cordis/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/cordis/README.md -README.md: 485a6ce7858a77507c07b76138127faa411b354b -README.zh.md: d6fb91d8426f4a4d9dae0f948001f4fba4014757 +README.md: e8b0790f911d1acc1216d412191ca967e0e1ac27 +README.zh.md: c452f9a7ef69e7f5af8900153271d500f19f9e47 diff --git a/packages/cordis/README.zh.md b/packages/cordis/README.zh.md index d6fb91d842..c452f9a7ef 100644 --- a/packages/cordis/README.zh.md +++ b/packages/cordis/README.zh.md @@ -1,10 +1,10 @@ -# packages/cordis:Cordis 运行时集成 +# packages/cordis — Cordis 运行时集成 [English](README.md) | 中文 -这些插件把 harness 自有格式集成到 Cordis 运行时:包括自指的模型工具集,以及受限的 repository 插件运行时。 +把 Harness 所有的格式与 Cordis 运行时集成的插件:自指的面向模型工具集,以及受限的 repository Plugin 运行时。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| [`tool-cordis/`](tool-cordis/README.md) | `cordis_inspect`/`cordis_mount`/`cordis_unmount` 工具:读取当前进程运行时,并在一个自有分组 fiber 下管理内存中的临时插件 | 注册到 `ctx.tools` | -| [`repository-plugin/`](repository-plugin/README.md) | 通过 DSH 自有子插件准备并挂载静态 repository skill(技能)与通用 `.mcp.json` server | 注册一个 loader builtin | +| [`tool-cordis/`](tool-cordis/README.md) | 面向模型的运行时检查和临时插件工具 | 注册到 `ctx.tools` | +| [`repository-plugin/`](repository-plugin/README.md) | repository skill 与 MCP 组合 | 注册一个 Loader builtin | diff --git a/packages/cordis/repository-plugin/README.i18n.yaml b/packages/cordis/repository-plugin/README.i18n.yaml index e8dd5ddbf5..ea7a1305b3 100644 --- a/packages/cordis/repository-plugin/README.i18n.yaml +++ b/packages/cordis/repository-plugin/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/cordis/repository-plugin/README.md -README.md: 0ba1ce86d99a12e0f94e7a39fd3ae44dc29889a7 -README.zh.md: d3046db8155a908dd4afd8f50a5ab4774085a40f +README.md: 2555876734ddcab7b0bc0f25780010bef0c97a01 +README.zh.md: 41341f7d7ed92ac071dde274a0328784bc2888d4 diff --git a/packages/cordis/repository-plugin/README.md b/packages/cordis/repository-plugin/README.md index 0ba1ce86d9..2555876734 100644 --- a/packages/cordis/repository-plugin/README.md +++ b/packages/cordis/repository-plugin/README.md @@ -30,7 +30,7 @@ Place an ordinary package in the repository's `.dsh-plugin` directory: ## Standalone app configuration -The shipped `dsh` TUI, Web, and headless trees contain an empty `repository-plugins` row. A standalone user enables exact GitHub generations by replacing that row's config in `$DSH_HOME/config.yaml` (default `~/.dsh/config.yaml`): +The shipped `dsh` base used by raw-config, Web, and headless modes contains an empty `repository-plugins` row. A Web or headless user enables exact GitHub generations by replacing that row's config in `$DSH_HOME/config.yaml` (default `~/.dsh/config.yaml`); a raw-config deployment patches the same row in its explicit overlay: ```yaml - id: repository-plugins @@ -43,7 +43,7 @@ The shipped `dsh` TUI, Web, and headless trees contain an empty `repository-plug Each source must use `github:owner/repository#`. Omitting `&path:` selects `/.dsh-plugin`; an explicit path is absolute within the repository and must end in `.dsh-plugin`. A commit ref gives the clearest immutable identity, while tags and branches remain accepted exact config values. `cacheDir` may override the default `$DSH_HOME/cache/repository-plugins` cache root. -The TUI and Web watch `config.yaml` through Cordis HMR. A valid source-list change installs and swaps the complete repository Plugin generation; a failed fetch, prepare, import, or Plugin application keeps the last good tree and broadcasts `hmr/config-update-failed(filename, error)`. Headless runs consume the file only at startup. An identical source string permanently reuses its prepared cache entry, so selecting changed code requires a ref, path, or other source-config change. App integration rationale: [config-only repository Plugins Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md). +Web watches `config.yaml` through Cordis HMR. A valid source-list change installs and swaps the complete repository Plugin generation; a failed fetch, prepare, import, or Plugin application keeps the last good tree and broadcasts `hmr/config-update-failed(filename, error)`. Headless reads the file only at startup, and raw-config mode reads only its explicit overlay. An identical source string permanently reuses its prepared cache entry, so selecting changed code requires a ref, path, or other source-config change. App integration rationale: [config-only repository Plugins Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md). ## Preparation diff --git a/packages/cordis/repository-plugin/README.zh.md b/packages/cordis/repository-plugin/README.zh.md index d3046db815..41341f7d7e 100644 --- a/packages/cordis/repository-plugin/README.zh.md +++ b/packages/cordis/repository-plugin/README.zh.md @@ -30,7 +30,7 @@ ## 独立应用配置 -已交付的 `dsh` TUI、Web 和无头配置树包含一个空的 `repository-plugins` 配置项。独立用户只需在 `$DSH_HOME/config.yaml`(默认 `~/.dsh/config.yaml`)中替换该配置项的配置,即可启用精确指定的 GitHub generation: +随附 `dsh` 中供原始配置、Web 与无头模式使用的基础配置包含一个空 `repository-plugins` 配置项。Web 或无头用户可在 `$DSH_HOME/config.yaml`(默认 `~/.dsh/config.yaml`)中替换该配置项的配置,以启用精确指定的 GitHub generation;原始配置部署则在显式 overlay 中 patch 同一配置项: ```yaml - id: repository-plugins @@ -43,7 +43,7 @@ 每个源都必须采用 `github:owner/repository#`。省略 `&path:` 时选择 `/.dsh-plugin`;显式路径是仓库内的绝对路径,并且必须以 `.dsh-plugin` 结尾。commit ref 提供最清晰的不可变身份;tag 和 branch 仍可作为精确配置值使用。`cacheDir` 可覆盖默认缓存根 `$DSH_HOME/cache/repository-plugins`。 -TUI 和 Web 通过 Cordis HMR(热模块替换)监视 `config.yaml`。有效的源列表变更会安装并替换整套仓库插件 generation;拉取、准备、导入或插件应用失败时,最后一个可用树保持运行,并广播 `hmr/config-update-failed(filename, error)`。无头运行只在启动时使用该文件。相同的源字符串会永久复用其已准备缓存条目,因此必须改变 ref、路径或其他源配置,才能选择发生变化的代码。应用集成依据见[仅凭配置接入仓库插件的 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md)。 +Web 通过 Cordis HMR(热模块替换)监视 `config.yaml`。有效的源列表变更会安装并替换整套 repository Plugin generation;拉取、准备、导入或插件应用失败时,最后一个可用树保持运行,并广播 `hmr/config-update-failed(filename, error)`。无头模式只在启动时读取该文件,原始配置模式只读取其显式 overlay。相同的源字符串会永久复用其已准备缓存条目,因此必须改变 ref、路径或其他源配置,才能选择发生变化的代码。应用集成依据见[仅凭配置接入 repository Plugin 的 Agent Note](../../../.agents/notes/implemented/feature/2026-07-30-config-only-repository-plugins.md)。 ## 准备阶段 diff --git a/packages/core/README.i18n.yaml b/packages/core/README.i18n.yaml index 23e6e7d2b9..d43caa011e 100644 --- a/packages/core/README.i18n.yaml +++ b/packages/core/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/core/README.md -README.md: 63ca0f7711c2c9deb193d5a9a574ff909a04602e -README.zh.md: d4c1a85ef7dd33be5419245464d83da24756443a +README.md: d51ef73f7f545920f8cd527b2b0d597305ae86d0 +README.zh.md: 729f41fafb6b9585a66e58fe8d6ac63c1f235acc diff --git a/packages/core/README.zh.md b/packages/core/README.zh.md index d0f16fb033..729f41fafb 100644 --- a/packages/core/README.zh.md +++ b/packages/core/README.zh.md @@ -1,18 +1,18 @@ -# core/:产品 API 主干 +# core/ — 产品 API 主干 [English](README.md) | 中文 -会话日志、系统提示词组装、工具注册表、agent(智能体)词汇,以及构成 harness 默认控制主干的具体循环。这些是 **产品** 包,插件和消费方以其稳定接口为基础构建。 +构成 harness 默认控制主干的会话日志、系统提示词组装、工具注册表、agent(智能体)词汇和具体循环。这些是**产品**包,即插件和消费方构建所依赖的稳定 surface。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| [`scope/`](scope/README.md) | 带作用域的上下文注册原语 | 库,无 ctx 键 | -| [`session/`](session/README.md) | 事件溯源会话日志与内存存储 | `ctx.sessions` | -| [`system-prompt/`](system-prompt/README.md) | 提示词与工具 schema 组装注册表 | `ctx.systemPrompt` | -| [`tools/`](tools/README.md) | 带作用域的工具注册表与执行流水线 | `ctx.tools` | -| [`agent/`](agent/README.md) | Agent 接口、注册表与事件词汇 | `ctx.agents` | -| [`agent-loop/`](agent-loop/README.md) | 默认的具体 agent 驱动器 | `ctx.agentLoop` | +| [`scope/`](scope/README.md) | 作用域上下文注册原语 | 库,不使用 ctx key | +| [`session/`](session/README.md) | 事件溯源会话日志和内存存储 | `ctx.sessions` | +| [`system-prompt/`](system-prompt/README.md) | 提示词和工具 schema 组装注册表 | `ctx.systemPrompt` | +| [`tools/`](tools/README.md) | 作用域工具注册表和执行流水线 | `ctx.tools` | +| [`agent/`](agent/README.md) | Agent 接口、注册表和事件词汇 | `ctx.agents` | +| [`agent-loop/`](agent-loop/README.md) | 默认具体 agent 驱动器 | `ctx.agentLoop` | -`scope` 提供共享的作用域原语。`agent` 拥有公开 seam,`agent-loop` 则是其默认实现;扩展插件依赖 seam,使驱动器保持可替换。 +`scope` 提供共享作用域原语。`agent` 负责公开 seam,`agent-loop` 是其默认实现;扩展插件依赖该 seam,从而保持驱动器可替换。 -可运行组合属于 [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md);本组只负责可替换的主干组件。 +可运行组合属于 [`examples/agent-spine-demo`](../examples/agent-spine-demo/README.md);该分组只负责可替换的主干组件。 diff --git a/packages/examples/agent-spine-demo/README.i18n.yaml b/packages/examples/agent-spine-demo/README.i18n.yaml index 8cbf8ce122..3398b7296d 100644 --- a/packages/examples/agent-spine-demo/README.i18n.yaml +++ b/packages/examples/agent-spine-demo/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/examples/agent-spine-demo/README.md -README.md: 7ea2f4afbe5d0bea62cf0fc2c7ecdd1496d20aef -README.zh.md: cb4202ac0e72db7c85967425671144e743f31b62 +README.md: d1c3c28183956b4f66db7ed4a6f3a08588b5352c +README.zh.md: 936a317d23bf942802ecdd87f5ea4005974e4125 diff --git a/packages/examples/agent-spine-demo/README.md b/packages/examples/agent-spine-demo/README.md index 7ea2f4afbe..d1c3c28183 100644 --- a/packages/examples/agent-spine-demo/README.md +++ b/packages/examples/agent-spine-demo/README.md @@ -47,7 +47,7 @@ The spine is everything COMMON to every front door. The swappable and front-door - **model-backed session-title providers** — the bundle mounts the fallback service with overridable example limits (5 words, 40 fallback bytes, 80 accepted-title bytes); a leaf may opt into exactly one first-message or all-messages LLM provider. - **the bash executor** — the bundle ships `tool-bash` (the consumer schema); the leaf provides `ctx.bash` (`bash-local` or a sandboxed impl). - **non-local skill providers** — the bundle ships the skill registry, the local filesystem provider, and the `skill` tool; deployments can add other providers such as embedded or remote catalogs as siblings. -- **front-door + per-app infra** — the terminal TUI or ACP automation transport and `hmr`. App packages ([`dsh-cli-demo`](../cli-demo/README.md), [`dsh-acp-demo`](../acp-demo/README.md)) own those choices. `timer` is in the spine because it is common and stdout-silent; front doors own stdout and remain outside. +- **front-door + per-app infrastructure** — headless, ACP, and JSON-RPC app packages own transport, stdout, and reload choices. `timer` stays in the spine because it is common and stdout-silent. This is the [interface/implementation/consumer seam](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md) raised to the composition level: the bundle owns the shared spine, the leaf owns the backends, the app package owns the front door. @@ -59,7 +59,7 @@ import type { Config } from '@deepseek-ai/dsh-agent-spine-demo' // workspaceContext requires { maxBytes } or false; the other owner schemas supply defaults. ``` -The bundle FORWARDS each field to the child that owns it: `agents` and `maxParallelToolCalls` to `agent-loop` (`agents` defaults to `[]`; the cap defaults there), so each app supplies its own pre-created agents — TUI and headless apps pre-create `main`, while the ACP app creates agents on demand at `session/new`; `includeHarnessIdentity`, `persona`, and `toolOrder` to `dsh-system-prompt`; `tools` to the tool registry for its presentation mode; `sessionTitle` to the fallback title service; `skills.registry`, `skills.local`, and `skills.tool` to the skill registry, local provider, and model-facing consumer; the required `workspaceContext` choice to `dsh-workspace-context` (`{ maxBytes }` enables loading and `false` disables it); `invariants` to the invariant service; and `toolBash`/`toolTasks` to the two model-facing tool plugins the bundle owns. It always mounts `dsh-llm-retry`, while each leaf adapter owns its nested `retryPolicy`. Omitted `sessionTitle` uses the explicit example policy of 5 words, 40 fallback bytes, and 80 accepted-title bytes. A `goals` object opts into the persisted domain, model tools, and same-session driver while forwarding `goals.domain` and `goals.tool` to their owners; omission or `false` leaves the stack absent so headless callers retain one-turn settlement. Set `skills.enabled: false` to omit both the local provider and model-facing skill tool, set `toolBash: false` when another plugin owns the `bash` tool name, and set `toolTasks: false` to retain the task service for foreground producers without exposing `task_output` / `task_list` / `task_kill`. It resolves `dshHome` once through [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) and forwards that absolute value to the shared `bash-env` managed environment and enabled local skill discovery. An absent top-level `dshHome` adopts `skills.local.dshHome`; supplying both with different resolved paths fails loudly. `toolBash.enableRunInBackground` controls only the bundled bash producer; independently loaded producers keep their own config. Workspace instructions register before the skill catalog so their session-prefix message renders first. App packages use `pickSpineConfig()` to copy only these bundle-owned fields. +The bundle forwards each field to the child that owns it. App packages supply any pre-created agents: headless and JSON-RPC compositions create `main`, while the ACP app creates agents on demand at `session/new`. Prompt, tool, title, skill, workspace-context, invariant, goal, and task settings retain the schemas and defaults documented by their owning packages. `pickSpineConfig()` copies only fields owned by this bundle, and conflicting `dshHome` values fail during composition. For example, `{ invariants: { enabled: true, package_allowlist: ['^@deepseek-ai/dsh-'], package_blocklist: ['agent-loop$'] } }` keeps the package-owned companions mounted but suppresses the blocked owner. Blocklist matches override allowlist matches; see [`dsh-invariants`](../../support/invariants/README.md) for regex and lifecycle rules. diff --git a/packages/examples/agent-spine-demo/README.zh.md b/packages/examples/agent-spine-demo/README.zh.md index cb4202ac0e..936a317d23 100644 --- a/packages/examples/agent-spine-demo/README.zh.md +++ b/packages/examples/agent-spine-demo/README.zh.md @@ -47,7 +47,7 @@ - **基于模型的会话标题提供方**:组合包挂载带可覆盖示例限制的后备服务(5 个词、40 个后备字节、80 个可接受标题字节);叶节点可以恰好选用一个首消息或全消息 LLM 提供方。 - **bash 执行器**:组合包交付 `tool-bash`(消费方 schema);叶节点提供 `ctx.bash`(`bash-local` 或沙箱化实现)。 - **非本地 skill 提供方**:组合包交付 skill 注册表、本地文件系统提供方和 `skill` 工具;部署可以把嵌入式目录或远程目录等其他提供方作为同级插件添加。 -- **前端入口与各应用基础设施**:终端 TUI 或 ACP(Agent Client Protocol)自动化传输,以及 `hmr`。应用包([`dsh-cli-demo`](../cli-demo/README.md)、[`dsh-acp-demo`](../acp-demo/README.md))拥有这些选择。`timer` 位于主干中,因为它是共有组件且不写 stdout;前端入口拥有 stdout,因此留在组合包外。 +- **前端入口与各应用基础设施**:无头、ACP(Agent Client Protocol)和 JSON-RPC 应用包负责传输、stdout 与重新加载选择。`timer` 保留在主干中,因为它是共有组件且不写 stdout。 这把[接口/实现/消费方 seam](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md) 提升到组合层:组合包拥有共享主干,叶节点拥有后端,应用包拥有前端入口。 @@ -59,7 +59,7 @@ import type { Config } from '@deepseek-ai/dsh-agent-spine-demo' // workspaceContext requires { maxBytes } or false; the other owner schemas supply defaults. ``` -组合包将每个字段转发给拥有它的子节点:`agents` 与 `maxParallelToolCalls` 交给 `agent-loop`(`agents` 默认为 `[]`,上限在该处默认),因此每个应用提供自己的预创建 agent;TUI 和无头应用预创建 `main`,ACP 应用则在 `session/new` 按需创建 agent;`includeHarnessIdentity`、`persona` 与 `toolOrder` 交给 `dsh-system-prompt`;`tools` 交给工具注册表以配置呈现模式;`sessionTitle` 交给后备标题服务;`skills.registry`、`skills.local` 与 `skills.tool` 分别交给 skill 注册表、本地提供方和面向模型的消费方;必填的 `workspaceContext` 选择交给 `dsh-workspace-context`(`{ maxBytes }` 启用加载,`false` 禁用);`invariants` 交给不变式服务;`toolBash`/`toolTasks` 交给组合包拥有的两个面向模型工具插件。组合包始终挂载 `dsh-llm-retry`,而每个叶节点适配器拥有自己的嵌套 `retryPolicy`。省略 `sessionTitle` 时采用显式示例策略:5 个词、40 个后备字节、80 个可接受标题字节。`goals` 对象会选用持久化领域、模型工具和同会话 Goal Round 驱动器,并将 `goals.domain` 与 `goals.tool` 转发给各自拥有者;省略或设为 `false` 会让整个栈缺席,使无头调用方继续以单轮次结算。设置 `skills.enabled: false` 会同时省略本地提供方和面向模型的 skill 工具;当另一个插件拥有 `bash` 工具名时设置 `toolBash: false`;设置 `toolTasks: false` 会保留供前台生产方使用的任务服务,但不公开 `task_output`/`task_list`/`task_kill`。它对 `dshHome` 只解析一次,解析通过 [`@deepseek-ai/dsh-paths`](../../util/paths/README.md) 完成,并将所得绝对值转发给共享 `bash-env` 的托管环境和已启用的本地 skill 发现。顶层 `dshHome` 缺席时采用 `skills.local.dshHome`;两者同时提供但解析后的路径不同会明确失败。`toolBash.enableRunInBackground` 只控制内置 bash 生产方;独立加载的生产方保留各自配置。工作区指令先于 skill 目录注册,因此其会话前缀消息先渲染。应用包使用 `pickSpineConfig()`,只复制这些由组合包拥有的字段。 +组合包将每个字段转发给拥有它的子节点。应用包提供预创建的 agent:无头和 JSON-RPC 组合会创建 `main`,ACP 应用则在 `session/new` 按需创建 agent。提示词、工具、标题、skill、workspace context、不变式、goal 和任务设置沿用其所属包记录的 schema 与默认值。`pickSpineConfig()` 只复制该组合包拥有的字段,`dshHome` 值冲突会在组合时失败。 例如,`{ invariants: { enabled: true, package_allowlist: ['^@deepseek-ai/dsh-'], package_blocklist: ['agent-loop$'] } }` 会让包拥有的配套插件保持挂载,但抑制被阻止的拥有者。Blocklist 匹配优先于 allowlist 匹配;正则表达式与生命周期规则见 [`dsh-invariants`](../../support/invariants/README.md)。 diff --git a/packages/examples/jsonrpc-demo/README.i18n.yaml b/packages/examples/jsonrpc-demo/README.i18n.yaml index 4532a72e66..4e7935dd3a 100644 --- a/packages/examples/jsonrpc-demo/README.i18n.yaml +++ b/packages/examples/jsonrpc-demo/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/examples/jsonrpc-demo/README.md -README.md: 75e9e3943982c08e53afdbb73d1e9085b2e332bc -README.zh.md: d7732582e0ab62cd3a61dfe3d8e4192c06b18992 +README.md: 2cc496da3e22baf9e87afe4c3ca183ae3f16a5ae +README.zh.md: 98d397b3c346ee47e9a667b2ccec0179f009a05b diff --git a/packages/fs/README.i18n.yaml b/packages/fs/README.i18n.yaml index d4ff2d5539..e435f166ad 100644 --- a/packages/fs/README.i18n.yaml +++ b/packages/fs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/README.md -README.md: b5e0ac9d1c0c550eb372b8a66fc6358711fddc07 -README.zh.md: 7a7cafb814a1f6c4a79671cf23c7d4577ac2b716 +README.md: 108d7d862a1dc6307268e2a93fa00789c952e440 +README.zh.md: 8b037cc3192bf6ceb0f0671d62911a8e035db24c diff --git a/packages/fs/README.zh.md b/packages/fs/README.zh.md index 7a7cafb814..8b037cc319 100644 --- a/packages/fs/README.zh.md +++ b/packages/fs/README.zh.md @@ -1,21 +1,17 @@ -# fs/:文件系统能力族 +# fs/ - 文件系统能力家族 [English](README.md) | 中文 -文件系统栈包括:提供方 seam(文本 I/O 与带可选版本防护的原子变更)、本地实现、策略门禁插件(已观察状态、编辑前读取、版本防护的写入/编辑)、面向模型的文件工具与执行器,以及基于 bash 的发现工具。全部都是**产品**包。 +文件系统能力家族:提供方 seam、可互换后端、策略和面向模型工具。这些全是**产品**包。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| `fs/` | 提供方 seam:文本 I/O 与原子变更原语(可选版本防护);拥有 `fs/*` 策略事件 | `ctx.fs` | -| `fs-local/` | 本地文件系统 `FileSystem` 实现 | (注册 `ctx.fs`) | -| `fs-sandbox/` | 强制沙箱的 `FileSystem`:扩展 `fs-local`,并按每次调用的模式与工作区根目录策略约束写入/编辑(只读模式拒绝,工作区写入模式限制在会话工作区与临时根目录内);读取则直接放行 | (注册 `ctx.fs`) | -| `fs-policy/` | 策略门禁插件:通过 `fs/*` 事件门禁提供已观察状态、编辑前读取和版本防护的写入/编辑 | (无服务,仅有 `fs/*` 监听器) | -| `tool-fs/` | 面向模型的 `read`/`write`/`edit` 工具以及执行器(通过 `ctx.fs` 读取,拥有读取窗口逻辑,分派 `fs/*`);为会话 cwd 相对路径保留文件系统语义,并在已挂载的 `ctx.fs` 实施约束时声明沙箱升权字段 | (注册到 `ctx.tools`) | -| `tool-fs-search/` | 面向模型的 `glob`/`grep` 发现工具;当 `rg` 位于 bash 执行器 `PATH` 上时注册,通过 `ctx.bash` 运行固定 ripgrep 命令,而不是使用 `ctx.fs` 提供方方法 | (注册到 `ctx.tools`) | -| `tool-str-replace-editor/` | 基于 `ctx.fs` 提供查看/创建/唯一字面量替换/按行插入的模型可见 `str_replace_editor` | (注册到 `ctx.tools`) | +| [`fs/`](fs/README.md) | 文件系统提供方 seam 和策略事件词汇 | `ctx.fs` | +| [`fs-local/`](fs-local/README.md) | 本地文件系统后端 | 注册 `ctx.fs` | +| [`fs-sandbox/`](fs-sandbox/README.md) | 强制执行沙箱的后端 | 注册 `ctx.fs` | +| [`fs-policy/`](fs-policy/README.md) | 已观察状态和修改策略 | `fs/*` 监听器 | +| [`tool-fs/`](tool-fs/README.md) | 面向模型的文件工具 | 注册到 `ctx.tools` | +| [`tool-fs-search/`](tool-fs-search/README.md) | 基于进程的发现工具 | 注册到 `ctx.tools` | +| [`tool-str-replace-editor/`](tool-str-replace-editor/README.md) | 面向模型的字符串替换编辑器 | 注册到 `ctx.tools` | -接口位于 `fs/fs/`。沙箱化、远程或限定项目作用域的文件系统后端可以替换 `fs-local`,而无需更改 seam、策略门禁或面向模型的工具 schema;`fs-sandbox` 是第一个这样的替代实现(基于共享沙箱模式的进程内路径围栏;见[跨能力族 fs 沙箱 Agent Note](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md))。策略(`fs-policy/`)是一个只通过 `fs/*` 事件门禁参与的插件,不是工具注入的服务;因此移除它只会使策略失效,留下不受约束的裸提供方,而不会破坏工具。加载 `tool-fs/` 的部署也应加载该插件。模式围栏与编辑前读取门禁彼此正交,可以组合。发现(`tool-fs-search/`)有意不扩展提供方 seam:搜索是在 bash 执行器上运行 `rg`、基于进程的工作流,因此文件系统后端无需承担通用搜索契约;只有当执行器能找到 `rg` 时,其工具才会注册。如果 bash 工作目录与 `read` 根目录是同一工作区,其结果便可供后续读取,这也是其 README 所述的共置部署。 - -## 文件 I/O 不设超时 - -`read`/`write`/`edit` **不** 接受 `timeoutMs`,提供方 seam 也不设置截止期限。这与 bash 和 web(两者使用 [`@deepseek-ai/dsh-timeout`](../util/timeout/README.md))及基于 bash 的 `glob`/`grep` 不同(它们声明的 `timeoutMs` 由 `@deepseek-ai/dsh-timeout-policy` 强制执行):这些工作基于进程运行,截止期限可以实际终止工作。本地系统调用至多只能尽力中止:超时无法强制正在进行的 `fsync`/`rename` 停止,因此这里的截止期限会成为无法兑现承诺的配置项。在此添加截止期限还会在「显式优于隐式」明确禁止的地方引入隐式默认值。两个参考 agent(Claude Code、Codex)出于同一原因都不为文件 I/O 计时;取消仍通过工具执行信号传播,在系统调用边界尽力中止。 +后端可在 `ctx.fs` 后互相替换;策略和工具独立消费该 seam。发现功能仍由进程提供,不扩展提供方契约。子 README 负责围堵、修改、schema 和超时细节。 diff --git a/packages/fs/fs-sandbox/README.i18n.yaml b/packages/fs/fs-sandbox/README.i18n.yaml index 068b33da98..22f232b724 100644 --- a/packages/fs/fs-sandbox/README.i18n.yaml +++ b/packages/fs/fs-sandbox/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/fs-sandbox/README.md README.md: d6070f4971e7531e929e659b6b5ed476a672dc5d -README.zh.md: 9304095c2d077adb657af61f4b2b54e087a252c7 +README.zh.md: 60603c8f89ed1f5251c8e927e17c7e8b71ed58dd diff --git a/packages/fs/fs-sandbox/README.zh.md b/packages/fs/fs-sandbox/README.zh.md index 9304095c2d..60603c8f89 100644 --- a/packages/fs/fs-sandbox/README.zh.md +++ b/packages/fs/fs-sandbox/README.zh.md @@ -40,4 +40,4 @@ - **策略围栏,而非内核边界**:该检查是可信代码处理模型控制的路径,因此解析到系统调用之间残留的 TOCTOU 会被原位重新规范化缩小,但不会消除;对抗性宿主进程不在范围内。不可信代码的内核级隔离仍属于 `ctx.bash`。 - **围栏与 runner 的一致性来自派生,而非断言**:可写集合来自 `writableRoots`,该函数与 Seatbelt profile 共享,并由一致性测试固定;不通过该函数更改可写集合的 runner profile 会发生漂移。 -- **要求 `ctx.sandboxPolicy`**:工具使用它解析每个会话策略,后端用它处理无 agent 调用的回退;未组合该服务时,后端不会实施约束。 +- **要求 `ctx.sandboxPolicy`**:工具使用它解析每个会话策略,后端用它处理无 agent(智能体)调用的回退;未组合该服务时,后端不会实施约束。 diff --git a/packages/fs/fs/README.i18n.yaml b/packages/fs/fs/README.i18n.yaml index 785a193a2f..ddf57a98d0 100644 --- a/packages/fs/fs/README.i18n.yaml +++ b/packages/fs/fs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/fs/README.md -README.md: 9e6c954abad124fb2b30ebc01368a55746752013 -README.zh.md: c72b1f9a83485ca9c188344ba7a345c889951c7b +README.md: 6c80cc22f6f28e792c3458df3390b241b51d8202 +README.zh.md: 4772d799221efbfff9667cbc8b9e1df6af07dcff diff --git a/packages/fs/fs/README.zh.md b/packages/fs/fs/README.zh.md index c72b1f9a83..4772d79922 100644 --- a/packages/fs/fs/README.zh.md +++ b/packages/fs/fs/README.zh.md @@ -4,16 +4,7 @@ **文件系统提供方 seam**:抽象 `FileSystem` 服务(`ctx.fs`),定义后端提供的存储原语,包括路径解析、stat 元数据、不跟随链接的路径元数据、读取/流式读取文本、列出目录、原子写入和应用字面量编辑,但不规定实现方式。两个变更操作都**可选**接收版本防护,因此 `ctx.fs` 本身就是完整且不受约束的文本存储 seam。本包还拥有由工具分派、策略插件监听的 `fs/*` 策略事件词汇。 -本包是四层文件系统栈中的提供方 seam 层;该拆分使每个关注点可以独立演进和替换(见[能力 seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)、[文件系统能力 seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md)、[拆分文件系统 seam Agent Note](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md)和[文件上下文事件门禁 Agent Note](../../../.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md)): - -| 层 | 包 | 角色 | -|---|---|---| -| 工具/执行器 | `@deepseek-ai/dsh-tool-fs` | 面向模型的 `read`/`write`/`edit` schema、读取窗口和文本渲染;通过 `ctx.fs` 读取/写入/编辑,并分派 `fs/*` 事件 | -| 策略 | `@deepseek-ai/dsh-fs-policy` | 已观察状态、编辑前读取和版本防护的写入/编辑,通过 `fs/*` 事件门禁贡献(无服务) | -| 提供方 seam | `@deepseek-ai/dsh-fs`(本包) | `ctx.fs`:文本 I/O 与原子变更原语(可选版本防护);拥有 `fs/*` 事件词汇 | -| 提供方 | `@deepseek-ai/dsh-fs-local` | 宿主文件系统实现 | - -未来的沙箱化、虚拟或远程后端只需实现该接口,策略层和工具层无需改变。 +本包是[文件系统家族](../README.md)中的提供方 seam 层。[工具](../tool-fs/README.md)、[策略](../fs-policy/README.md)、[本地](../fs-local/README.md)与[沙箱化](../fs-sandbox/README.md)后端分别作为消费方与实现保持独立;能力 seam 决策负责该拆分([基础](../../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)、[文件系统 seam](../../../.agents/notes/implemented/architecture/2026-06-17-filesystem-capability-seam.md)、[提供方拆分](../../../.agents/notes/implemented/simplification/2026-06-26-fsspec-style-fs-seam.md)、[事件门禁](../../../.agents/notes/implemented/architecture/2026-06-26-file-context-as-event-gate.md))。 ## 服务 API(`ctx.fs`) @@ -46,6 +37,10 @@ `FsTargetKey` / `FsVersion` 是带品牌的不透明 id(见[品牌 id Agent Note](../../../.agents/notes/implemented/architecture/2026-06-20-branded-ids.md));消费方不得解析 `targetKey` 或解释 `version`,只有 `displayPath` 用于模型/UI 输出。`FsWriteIntent` 是显式的防护写入意图(`createIfAbsent` 创建缺失目标,并以 `FS_NOT_OBSERVED` 拒绝现有目标;`replaceIfVersion` 只在观察版本上替换,否则为 `FS_STALE_VERSION`);从 `writeText` 中省略该值就是第三种无条件状态。`FsPathInfo` 是可报告 `symlink` 的不跟随链接元数据形态,区别于目标级 `FsInfo`。失败会抛出 `FsError`(继承 `HarnessError`;见[结构化错误分类 Agent Note](../../../.agents/notes/implemented/architecture/2026-06-11-structured-error-taxonomy.md)),并携带稳定的 `FsErrorCode`(`FS_NOT_FOUND`、`FS_NOT_DIRECTORY`、`FS_NOT_TEXT`、`FS_NOT_REGULAR_FILE`、`FS_PERMISSION_DENIED`、`FS_IO_ERROR`、`FS_STALE_VERSION`、`FS_NOT_OBSERVED`、`FS_AMBIGUOUS_EDIT`、`FS_EDIT_NOT_FOUND`、`FS_ABORTED`);工具注册表公开 `{ name, code }`,并将其附在 `isError` 结果上。完整契约见 `src/types.ts`。 +## 无 I/O deadline + +文件系统原语接受可选 `AbortSignal`,但不会启动 deadline。本地 I/O 只能尽力取消:超时无法强制进行中的 `fsync` 或 `rename` 停止,因此固定 deadline 会承诺后端无法提供的控制能力。基于进程的发现功能拥有独立的超时契约。 + ## 模型体验 通过 `dsh-tool-fs` 间接产生影响;该消费方把提供方文本和错误渲染为有界且保留的文件系统工具结果。 @@ -58,5 +53,5 @@ - **契约只支持文本**:后端以 `FS_NOT_TEXT` 拒绝二进制/非 UTF-8 内容;二进制安全操作是[工具 schema Agent Note](../../../.agents/notes/implemented/feature/2026-06-17-filesystem-tool-schemas.md)有意延期的工作。 - **只有八个原语**:没有删除、重命名/移动、复制或监视;`listDir` 只支持一层,递归、glob、分页和搜索不在范围内,见[目录列出 Agent Note](../../../.agents/notes/archived/architecture/2026-07-03-filesystem-directory-listing-seam.md)。 -- **没有 I/O deadline**:该 seam 不启动超时;取消只是每个原语上尽力而为的可选 `AbortSignal`(见有意采用的 [fs 能力族立场](../README.md))。 +- **没有 I/O deadline**:取消只能在原语边界尽力执行。 - **先解析后操作使远程后端每次工具调用需要两次往返**:折叠或缓存解析由这种后端自行决定。 diff --git a/packages/fs/tool-fs/README.i18n.yaml b/packages/fs/tool-fs/README.i18n.yaml index ed1f9d2ba0..292b9f395c 100644 --- a/packages/fs/tool-fs/README.i18n.yaml +++ b/packages/fs/tool-fs/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/fs/tool-fs/README.md -README.md: a695d0ba8fb1d600689d2b68763e8423d1591da5 -README.zh.md: 7b7dc26f514cc757cac97283eab52276505bfaae +README.md: 1ecfa0d013e1208b7d9058b4a254990f16f118e0 +README.zh.md: 9d4de4c219e7818c83c5ac326d2f2d385a30ece7 diff --git a/packages/fs/tool-fs/README.zh.md b/packages/fs/tool-fs/README.zh.md index 8cd48bdc29..9d4de4c219 100644 --- a/packages/fs/tool-fs/README.zh.md +++ b/packages/fs/tool-fs/README.zh.md @@ -38,7 +38,7 @@ await ctx.plugin(ToolFs) // this package — re ## 工具就是执行器;策略是事件门禁 -工具**不**注入策略服务,也不检查任何缓存。每个工具通过 `ctx.fs.resolve(path, { cwd, signal })` 解析路径;它会传入调用 agent 的会话 cwd(`exec.agent.session.header.cwd`),使相对路径以会话工作区为基准解析并与 `dsh-tool-bash` 一致,同时把工具取消转发到解析过程(见[每会话 cwd Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.md))。随后执行: +工具**不**注入策略服务,也不检查任何缓存。每个工具通过 `ctx.fs.resolve(path, { cwd, signal })` 解析路径;它会传入调用 agent(智能体)的会话 cwd(`exec.agent.session.header.cwd`),使相对路径以会话工作区为基准解析并与 `dsh-tool-bash` 一致,同时把工具取消转发到解析过程(见[每会话 cwd Agent Note](../../../.agents/notes/implemented/architecture/2026-07-02-fs-per-session-cwd.md))。随后执行: - **read**:一次 `ctx.fs.stat`(用于类型、大小路由和版本),随后调用 `readText`/`streamText`,构建行窗口,再发出 `fs/observed`,使用普通 `ctx.emit`。(1 次 stat。) - **write**:调用 `ctx.waterfall('fs/write-intent', target, exec, () => undefined)` 取得可选防护,然后调用 `ctx.fs.writeText(target, content, intent)`,再发出 `fs/observed`。(0 次 stat。) diff --git a/packages/goal/command-goal/README.i18n.yaml b/packages/goal/command-goal/README.i18n.yaml index d7ee8c7505..f6f6685069 100644 --- a/packages/goal/command-goal/README.i18n.yaml +++ b/packages/goal/command-goal/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/goal/command-goal/README.md -README.md: 8e1a5b417467c8701ea935e25acfece11c5a70d4 -README.zh.md: ddb11d09d4a4d560af05b05ad7b586c1b0d365f9 +README.md: b7626f3ff14a563407c7a60dd057c9c8616fe994 +README.zh.md: f4c3c16b963ad16b1430efb25c91bd27fc75638f diff --git a/packages/goal/command-goal/README.md b/packages/goal/command-goal/README.md index 8e1a5b4174..b7626f3ff1 100644 --- a/packages/goal/command-goal/README.md +++ b/packages/goal/command-goal/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Human-facing `/goal` control over [`ctx.goals`](../goal/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers it; the shipped TUI executes it without a model turn. The [human goal-command Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md) owns the UX and composition decisions. +Human-facing `/goal` control over [`ctx.goals`](../goal/README.md). The plugin registers one global command through [`ctx.commands`](../../ui/commands/README.md), so every composed command adapter discovers and executes it without a model turn. The [human goal-command Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md) owns the UX and composition decisions. ## Command contract @@ -32,7 +32,7 @@ The producer injects `commands` and `goals`. A custom app mounts their owners pl name: '@deepseek-ai/dsh-command-goal' ``` -The TUI app enables the complete persisted-goal stack and this command by default. The ACP automation app enables the domain and model tools without mounting the command registry; `goals: false` removes that stack. The UI-less `agent-spine-demo` requires an explicit `goals: {}` so headless one-shot callers do not silently change from one physical turn to a multi-round operation. +The shipped `dsh` base enables the persisted-goal stack and this command; the Web client provides its interactive adapter. The ACP automation app enables the domain and model tools without a command adapter; `goals: false` removes that stack. The UI-less `agent-spine-demo` requires an explicit `goals: {}` so headless one-shot callers do not silently change from one physical turn to a multi-round operation. ## Model Experience @@ -55,4 +55,4 @@ Command discovery and direct output do not affect the cache. A mutation appends - **Plain-text interaction only** — the generic command registry has no modal edit form or replacement-confirmation callback; inline edit and explicit clear keep destructive intent deterministic across adapters. - **No per-command round-cap argument** — `defaultMaxGoalRounds` remains deployment config, while a direct human request may ask the model to edit `max_goal_rounds` through the separately authorized goal tool. - **No continuous status widget** — bare `/goal` is the portable observation surface; adapter-specific badges and reconnectable command output remain future UI work. -- **TUI only in the shipped apps** — the headless CLI, ACP automation, and JSON-RPC adapters do not consume `ctx.commands`. Ordinary prompts can still authorize model-facing goal tools when those are composed. +- **Web command adapter only in the shipped apps** — headless, ACP automation, and JSON-RPC adapters do not consume `ctx.commands`. Ordinary prompts can still authorize model-facing goal tools when those are composed. diff --git a/packages/goal/command-goal/README.zh.md b/packages/goal/command-goal/README.zh.md index ddb11d09d4..f4c3c16b96 100644 --- a/packages/goal/command-goal/README.zh.md +++ b/packages/goal/command-goal/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -面向用户的 `/goal` 控制,基于 [`ctx.goals`](../goal/README.md) 实现。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此每个已组合的命令适配器都能发现它;随附 TUI 无需模型轮次即可执行。[用户 goal 命令 Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md) 负责用户体验与组合决策。 +面向用户的 `/goal` 控制,基于 [`ctx.goals`](../goal/README.md) 实现。该插件通过 [`ctx.commands`](../../ui/commands/README.md) 注册一个全局命令,因此每个已组合的命令适配器都能发现并执行它,无需模型轮次。[用户 goal 命令 Agent Note](../../../.agents/notes/implemented/feature/2026-07-19-human-goal-command.md)负责用户体验与组合决策。 ## 命令契约 @@ -32,7 +32,7 @@ name: '@deepseek-ai/dsh-command-goal' ``` -TUI 应用默认启用完整的持久 goal 栈和此命令。ACP(Agent Client Protocol)自动化应用会启用领域与模型工具,但不挂载命令注册表;`goals: false` 会移除该栈。无 UI 的 `agent-spine-demo` 必须显式配置 `goals: {}`,避免无头单次调用方在不知情时从一个物理轮次变为包含多个 Round 的操作。 +随附 `dsh` 基础配置启用持久 goal 栈和此命令;Web 客户端提供其交互适配器。ACP(Agent Client Protocol)自动化应用启用领域与模型工具,但不挂载命令适配器;`goals: false` 会移除该栈。无 UI 的 `agent-spine-demo` 必须显式配置 `goals: {}`,避免无头单次调用方在不知情时从一个物理轮次变为包含多个 Round 的操作。 ## 模型体验 @@ -55,4 +55,4 @@ TUI 应用默认启用完整的持久 goal 栈和此命令。ACP(Agent Client - **仅纯文本交互**:通用命令注册表没有模态编辑表单或替换确认回调;内联 edit 与显式 clear 能在不同适配器中保持明确且一致的破坏性意图。 - **没有逐命令 Round 上限参数**:`defaultMaxGoalRounds` 仍是部署配置;用户直接请求时,可以要求模型通过另行授权的 goal 工具编辑 `max_goal_rounds`。 - **没有持续状态组件**:裸 `/goal` 是可移植的观察接口;适配器专用徽标和重连后可恢复的命令输出仍属于未来 UI 工作。 -- **随附应用中只有 TUI 使用此命令**:无头 CLI(命令行界面)、ACP 自动化和 JSON-RPC 适配器不消费 `ctx.commands`。如果组合中包含面向模型的 goal 工具,普通提示词仍能授权它们。 +- **随附应用中只有 Web 命令适配器使用此命令**:无头、ACP 自动化和 JSON-RPC 适配器不消费 `ctx.commands`。如果组合中包含面向模型的 goal 工具,普通提示词仍能授权它们。 diff --git a/packages/goal/goal-session/README.i18n.yaml b/packages/goal/goal-session/README.i18n.yaml index 25d4f35eee..34035d6d81 100644 --- a/packages/goal/goal-session/README.i18n.yaml +++ b/packages/goal/goal-session/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/goal/goal-session/README.md README.md: 6a1c3b9455c93762c2458109c753588ce9a08d9a -README.zh.md: 7e8b04f46f0345bce026ccd8c663882f27e036ab +README.zh.md: 6101d4e3724aea99875613c96bc9c004feb38ad3 diff --git a/packages/goal/goal-session/README.zh.md b/packages/goal/goal-session/README.zh.md index 7e8b04f46f..6101d4e372 100644 --- a/packages/goal/goal-session/README.zh.md +++ b/packages/goal/goal-session/README.zh.md @@ -21,7 +21,7 @@ ## Round 契约 -当对应的活跃 agent 实例处于 idle 状态,且目标 phase 为 active、已启用续行并有剩余容量时,驱动器先为待处理 goal 变更创建检查点,再预留 `roundsStarted + 1`,对应当前 `{ goalId, revision }`。它会排入一条 `` 提示词,并携带 `GoalMessageSource`。通过 `agent/prompt-submit` 准入时,会在下游提示词钩子前后验证完整的排队记录与当前 goal;只有被接受的 `user/message` 才会增加 `roundsStarted`。因陈旧而被拒绝的预留不会消耗 Round 编号。 +当对应的活跃 agent(智能体)实例处于 idle 状态,且目标 phase 为 active、已启用续行并有剩余容量时,驱动器先为待处理 goal 变更创建检查点,再预留 `roundsStarted + 1`,对应当前 `{ goalId, revision }`。它会排入一条 `` 提示词,并携带 `GoalMessageSource`。通过 `agent/prompt-submit` 准入时,会在下游提示词钩子前后验证完整的排队记录与当前 goal;只有被接受的 `user/message` 才会增加 `roundsStarted`。因陈旧而被拒绝的预留不会消耗 Round 编号。 一个 Goal Round 对应一个普通会话轮次,该轮次可以包含多个模型/工具步骤。驱动器只会把预留与 `message` 轮次配对,且该轮次必须携带完全相同的 `GoalMessageSource`;可通过声明合并扩展的插件轮次触发器不会准入或替换该预留。用户消息仍是普通轮次,不消耗 goal 上限。如果用户工作在预留前进入 inbox,或加入预留的待处理批次,自动工作会让行,直到用户工作结算;混合批次中的待处理自动提示词会被拒绝,只有 agent 再次 idle 后才重新预留。 diff --git a/packages/guard/README.i18n.yaml b/packages/guard/README.i18n.yaml index 07234247df..637a36063e 100644 --- a/packages/guard/README.i18n.yaml +++ b/packages/guard/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/guard/README.md -README.md: b7375fd2bb12ae0cec94b13e6a1012c6f143bdad -README.zh.md: 7092f50b05516a2fb358c5d6f496b967ebdc6819 +README.md: 8da2f2ddcc1432e7963e600b35549f5e26121af2 +README.zh.md: aa8b5bd1f7d10d90d1dc84d07a7aad81bfb4259c diff --git a/packages/guard/README.zh.md b/packages/guard/README.zh.md index 7092f50b05..aa8b5bd1f7 100644 --- a/packages/guard/README.zh.md +++ b/packages/guard/README.zh.md @@ -1,11 +1,9 @@ -# guard/:循环健康 guard 家族 +# guard/ — 循环卫生 guard 家族 [English](README.md) | 中文 -这组行为 guard 插件会监视 agent loop(智能体循环)中的低效模式,并提醒模型调整方向。这里只有一个**产品**包,不设接口/实现 seam:guard 是现有核心 seam(`tools/post-execute`、`agent/prompt-submit`、`agent/status`)的自包含消费方,并非可替换能力。 +行为 guard 插件监视 agent loop(智能体循环)中的无效模式,并推动模型回到正轨。guard 是 core seam 的自包含消费方,而非可替换能力。 -| 包 | 职责 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| `repeat-tool-guard/` | 当 agent 对完全相同的工具调用反复循环时给出提示 | (监听 `ctx.tools` 的 waterfall,即瀑布式事件) | - -提示以 `additionalContexts` 形式附在 `tools/post-execute` 决策中传递;agent loop 会在该步骤的工具结果之后,将其追加为有日志记录、来源为插件的 `user/message` 事件(参见[工具包](../core/tools))。因此,guard 告诉模型的所有内容都能从会话日志中重建。 +| [`repeat-tool-guard/`](repeat-tool-guard/README.md) | 针对重复工具调用的建议性提醒 | 监听工具和 agent 事件 | diff --git a/packages/hooks/README.i18n.yaml b/packages/hooks/README.i18n.yaml index 6b9e63e556..6000d1396f 100644 --- a/packages/hooks/README.i18n.yaml +++ b/packages/hooks/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/hooks/README.md -README.md: 23478fb5e9b813a3370ce465104b1f9db8b0a26a -README.zh.md: 778d5f334e5952a5b4ce08d52053144c048d4e8a +README.md: 35989292313c6cef47c02041eb671a73ff7c5786 +README.zh.md: b5187fed159b0eaf5b2bace192152e612a85bc55 diff --git a/packages/hooks/README.zh.md b/packages/hooks/README.zh.md index 0ab2202921..b5187fed15 100644 --- a/packages/hooks/README.zh.md +++ b/packages/hooks/README.zh.md @@ -1,13 +1,13 @@ -# hooks/:hook 桥接 + 共享协议 +# hooks/ — 钩子桥接与共享协议 [English](README.md) | 中文 -hooks 子系统让用户可以像使用 Claude Code 和 Codex 一样,在 agent(智能体)生命周期节点扩展 agent:把桥接插件指向现有的 `hooks.json`(或 settings),即可忠实运行这些外部 shell hook。规范的扩展表层本身是 harness 的类型化拦截 seam(见[拦截 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-30-interception-seams.md));「原生 hook」只是这些 seam 上的普通 Cordis 插件。这些包是把外部 shell-hook 协议转换到同一表层的**桥接**,另含它们共同依赖的共享协议格式(wire format)库。 +hooks 子系统让用户像使用 Claude Code 和 Codex 一样,在生命周期节点扩展 agent(智能体):把桥接插件指向现有 `hooks.json`(或 settings),即可忠实运行这些外部 shell 钩子。规范扩展 surface 本身是 harness 的类型化拦截 seam(参见[拦截 seam Agent Note](../../.agents/notes/implemented/feature/2026-06-30-interception-seams.md));“原生钩子”只是这些 seam 上的普通 Cordis 插件。这些包是把外部 shell 钩子协议转换到同一 surface 的**桥接**,也包括它们共同依赖的共享协议格式库。 | 包 | 职责 | 形态 | |---|---|---| -| [`hook-protocol/`](hook-protocol/README.md) | 共享 shell hook 协议库 | 库 | -| [`hooks-claude/`](hooks-claude/README.md) | Claude Code hook 桥接 | 插件 | -| [`hooks-codex/`](hooks-codex/README.md) | Codex hook 桥接 | 插件 | +| [`hook-protocol/`](hook-protocol/README.md) | 共享 shell 钩子协议库 | 库 | +| [`hooks-claude/`](hooks-claude/README.md) | Claude Code 钩子桥接 | 插件 | +| [`hooks-codex/`](hooks-codex/README.md) | Codex 钩子桥接 | 插件 | -共享库负责通用协议行为;每个桥接负责其方言特有的事件映射。相应契约由子级 README 记录。 +共享库负责通用协议行为;各桥接负责自身方言的事件映射。子 README 记录这些契约。 diff --git a/packages/host/README.i18n.yaml b/packages/host/README.i18n.yaml index fbc4c9f42e..178db5dcef 100644 --- a/packages/host/README.i18n.yaml +++ b/packages/host/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/README.md -README.md: 391adb7009a01d1ec95c8dcb809e8a2065aa0b31 -README.zh.md: 94136ed165d77df5d15102d9873c88efb3780463 +README.md: 7cd331f113eeec6c0a56f0ebc60554d9647aee75 +README.zh.md: 07b0e1569e17b9f0465a43f77fa2dbddcb1bae91 diff --git a/packages/host/README.zh.md b/packages/host/README.zh.md index 94136ed165..07b0e1569e 100644 --- a/packages/host/README.zh.md +++ b/packages/host/README.zh.md @@ -1,16 +1,16 @@ -# host/ — web GUI 宿主侧 +# host/ — Web GUI 宿主侧 [English](README.md) | 中文 -dsh web GUI 的宿主侧:所有客户端形态共用的 API 网关,以及承载它的纯 HTTP 服务器。浏览器侧位于 [`client/`](../client/README.md);组合后的应用是 [`apps/cli`](../../apps/cli/config/base.cordis.yml),它负责服务 [`apps/web`](../../apps/web/)。全部为**产品**包。 +dsh Web GUI 的宿主侧:所有客户端形态共享的 API 网关,以及承载它的普通 HTTP 服务器。浏览器侧位于 [`client/`](../client/README.md);组合应用是 [`apps/cli`](../../apps/cli/config/base.cordis.yml),由它提供 [`apps/web`](../../apps/web/)。这些全是**产品**包。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx key | |---|---|---| -| `apiproxy/` | 共享 API 网关:零 Node 依赖的 TS 协议契约(`src/api/`)、fetch 载体对(宿主侧 `toFetchHandler`、客户端侧 `AbstractApiClient`),以及基于 `ctx.agents`/`ctx.workspace` 的宿主实现 | `ctx.apiProxy` | -| `webserver/` | 纯 HTTP 路由注册载体:激活即监听的 `node:http` 服务器;路由以命名的 `exact`/`prefix` 处理器注册 | `ctx.httpServer` | -| `directory-picker/` | 工作区目录选择 seam:网关的 picker RPC 委托的可辨识 `native`/`browse` 能力 | `ctx.directoryPicker` | -| `directory-picker-native/` | 双面原生交互:OS 选择器后端(osascript/PowerShell/Zenity+KDialog,仅宿主屏幕可用)+ 由浏览器侧填入 ui-workspace 目录流 slot | (注册 `ctx.directoryPicker`) | -| `directory-picker-browse/` | 双面浏览交互:基于 Node 标准库的列举/创建原语(可远程)+ 由浏览器侧渲染应用内选择工作区目录对话框 | (注册 `ctx.directoryPicker`) | -| `directory-picker-auto/` | 自适应选择器:启动时一次性判定宿主处境(绑定宿主、SSH、显示),并把匹配的双面后端挂载为内存中的 Loader 条目 | (挂载一个后端行) | +| [`apiproxy/`](apiproxy/README.md) | 共享宿主 API 网关和协议契约 | `ctx.apiProxy` | +| [`webserver/`](webserver/README.md) | HTTP 路由载体 | `ctx.httpServer` | +| [`directory-picker/`](directory-picker/README.md) | workspace 目录选择 seam | `ctx.directoryPicker` | +| [`directory-picker-native/`](directory-picker-native/README.md) | 原生目录选择器后端和浏览器交互 | 注册 `ctx.directoryPicker` | +| [`directory-picker-browse/`](directory-picker-browse/README.md) | 应用内目录浏览器后端和交互 | 注册 `ctx.directoryPicker` | +| [`directory-picker-auto/`](directory-picker-auto/README.md) | 宿主自适应选择器组合 | 挂载一个后端 | -`apiproxy` 在设计上与传输方式无关——它不注册任何路由;载体自行包装 `ctx.apiProxy`。HTTP 载体路由(连同其 `/api` 浏览器信任栅栏)由 [`client/connection`](../client/connection/README.md) 的 node 半侧挂载,这正是该包住在 client 组的原因:它拥有这条协议链路的两端。 +`apiproxy` 保持传输无关;[`client/connection`](../client/connection/README.md) 提供浏览器/HTTP 载体。选择器实现可在共享 seam 后互相替换。 diff --git a/packages/host/apiproxy/README.i18n.yaml b/packages/host/apiproxy/README.i18n.yaml index e467677b97..786153c9cd 100644 --- a/packages/host/apiproxy/README.i18n.yaml +++ b/packages/host/apiproxy/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/apiproxy/README.md -README.md: 0ed3bd661cc489d1af64a3ee81aaf40d3427e638 -README.zh.md: 96acb16bf5a56a47648b388e154a34dfd40a78ea +README.md: b8f490b041c6f22ad371403f29942e8ebd5e70b8 +README.zh.md: f2c2389f00aca0c02793fba752334f1f1fa99f14 diff --git a/packages/host/apiproxy/README.md b/packages/host/apiproxy/README.md index 0ed3bd661c..b8f490b041 100644 --- a/packages/host/apiproxy/README.md +++ b/packages/host/apiproxy/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -The API gateway every client shape shares: the TS contract (`src/api/`, zero Node dependencies, importable from the browser), the fetch carrier pair (`src/fetch/`: `toFetchHandler` on the host side, `AbstractApiClient` plus platform subclasses on the client side), and the host-side implementation (`src/api-proxy.ts`: `createApiProxy` plus the default-exported `ApiProxyService` gateway plugin — config `{provider, model, workspaceRoot?}`, provides `ctx.apiProxy`). Transport-agnostic by design: this package registers no routes; carriers (HTTP today, IPC later) wrap `ctx.apiProxy` themselves. The shipped core composition lives in [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml). +The API gateway every client shape shares: the TS contract (`src/api/`, zero Node dependencies, importable from the browser), the fetch carrier pair (`src/fetch/`: `toFetchHandler` on the host side, `AbstractApiClient` plus platform subclasses on the client side), and the host-side implementation (`src/api-proxy.ts`: `createApiProxy` plus the default-exported `ApiProxyService` gateway plugin — config `{provider, model, workspaceRoot?}`, provides `ctx.apiProxy`). Transport-agnostic by design: this package registers no routes; carriers such as HTTP wrap `ctx.apiProxy` themselves. The shipped core composition lives in [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml). ## Contract layer (`/api`) @@ -16,7 +16,7 @@ Question responses are validated against their pending request before the first `session.history`'s tail page (`beforeSeq` absent) additionally carries an optional `projections` block — the watermark snapshot of every unit registered on `ctx.sessionProjections` (`@deepseek-ai/dsh-session-projection`), with `asOfSeq` = the last event seq the values reflect (`-1` on an empty log). The gateway also subscribes to the registry's change feed and mints a `session/projection` mux frame per changed unit (`{sessionId, key, value, seq}` — live push state, never logged; clients hold one generic per-session value store under higher-seq-wins). The carrier holds zero domain knowledge (each value passed its unit's own schema inside the registry; the wire schemas keep `values`/`value` wide); loadOlder pages never carry the block, and a composition without the registry serves histories without either surface. -Session titles ride the generic projection pair like every other domain — the history-tail `projections` block plus `session/projection` frames under the `title` key (the bespoke `session/title` frame is retired). Titles do not join `session.list`; cold sessions remain metadata-only there until an Agent-bound ordinary-session operation attaches their logs. `session.rename` accepts an explicit user title (resuming a cold session first), delegating to `ctx.sessionTitle.rename` — the accepted `session/title` event pins the title against automatic regeneration — and returns the normalized title plus its event seq so a client settles its `title` projection cell ahead of the push frame; a title that normalizes to empty returns `title-invalid`. +Session titles use the generic projection pair: the history-tail `projections` block plus `session/projection` frames under the `title` key. Titles do not join `session.list`; cold sessions remain metadata-only there until an Agent-bound ordinary-session operation attaches their logs. `session.rename` accepts an explicit user title (resuming a cold session first), delegates to `ctx.sessionTitle.rename`, and returns the normalized title plus its event seq so a client settles its `title` projection cell ahead of the push frame. The accepted `session/title` event pins the title against automatic regeneration; a title that normalizes to empty returns `title-invalid`. `session.fork` reads its source from attached state or persistence inspection without acquiring an Agent, then maps an optional event anchor to the first `turn/end` at or after it, letting a message action include that message's whole turn. An omitted or past-end anchor selects the last completed turn; an in-log anchor whose turn remains open returns `fork-unavailable` rather than clipping backward. The published ordinary child inherits the source's seeded history, cwd, latest logged provider/model/reasoning target, and lineage before joining the source Workspace, or the nearest workspace-owning ancestor when the source is a subagent. If Workspace attachment fails, `workspace-attach-failed` carries the already-published child id so clients can reconcile it. The [SessionStore fork decision](../../../.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md) owns the boundary rationale. @@ -56,8 +56,8 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work -- **`respond` routing is shipped, but pending-interaction state is host-side work** — the wire shape (POST `/api/respond`, `RpcReceipt`) is final; the pending table that makes late/duplicate answers meaningful lives in `src/api-proxy.ts` and is still minimal (questions only, no approvals). -- **Reserved seams stay out of `RpcMethodMap`** — `prompt.mode: 'inject'`, `task.list`, and a describe `hostInstanceId` are documented reservations (the former `host.listModels` reservation shipped as `llm.models`); an unknown method fails loud at envelope parse rather than getting a not-implemented code. +- **Pending-interaction state is host-side** — the wire shape is POST `/api/respond` plus `RpcReceipt`; the table in `src/api-proxy.ts` handles questions only and has no approval entries. +- **Reserved seams stay out of `RpcMethodMap`** — `prompt.mode: 'inject'`, `task.list`, and a describe `hostInstanceId` are documented reservations; model discovery uses `llm.models`. An unknown method fails loud at envelope parse rather than getting a not-implemented code. - **No protocol version field** — client and host ship together; `host.describe` gains a version negotiation field only when an independently released client exists. - **Search failures include provider diagnostics** — the gateway is a single-user local service. A carrier that exposes it to multiple users must replace internal search details with a public-safe diagnostic. - **Linux native picker requires desktop tooling** — under the `native` capability, `host.pickDirectory` reports an actionable error when neither Zenity nor KDialog is installed; the browse backend is the composition-level fallback (see the [native backend README](../directory-picker-native/README.md)). diff --git a/packages/host/apiproxy/README.zh.md b/packages/host/apiproxy/README.zh.md index 96acb16bf5..f2c2389f00 100644 --- a/packages/host/apiproxy/README.zh.md +++ b/packages/host/apiproxy/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -所有客户端形态共用的 API 网关:TS 契约(`src/api/`,不依赖 Node,可从浏览器导入)、fetch 载体对(`src/fetch/`:宿主侧的 `toFetchHandler`,以及客户端侧的 `AbstractApiClient` 与平台子类)和宿主侧实现(`src/api-proxy.ts`:`createApiProxy` 加上默认导出的 `ApiProxyService` 网关插件,其配置为 `{provider, model, workspaceRoot?}`,提供 `ctx.apiProxy`)。该包在设计上与传输方式无关,不注册任何路由;载体(目前为 HTTP,未来可以是 IPC)自行包装 `ctx.apiProxy`。已发布的核心组合位于 [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml)。 +所有客户端形态共用的 API 网关:TS 契约(`src/api/`,不依赖 Node,可从浏览器导入)、fetch 载体对(`src/fetch/`:宿主侧的 `toFetchHandler`,以及客户端侧的 `AbstractApiClient` 与平台子类)和宿主侧实现(`src/api-proxy.ts`:`createApiProxy` 加上默认导出的 `ApiProxyService` 网关插件,其配置为 `{provider, model, workspaceRoot?}`,提供 `ctx.apiProxy`)。该包在设计上与传输方式无关,不注册任何路由;HTTP 等载体自行包装 `ctx.apiProxy`。已发布的核心组合位于 [`apps/cli/config/base.cordis.yml`](../../../apps/cli/config/base.cordis.yml)。 ## 契约层(`/api`) @@ -16,7 +16,7 @@ `session.history` 的尾页(不带 `beforeSeq`)额外携带一个可选的 `projections` 块——`ctx.sessionProjections`(`@deepseek-ai/dsh-session-projection`)上每个已注册单元的水位线快照,`asOfSeq` = 这些值共同反映到的最后一个事件 seq(空日志为 `-1`)。网关还订阅注册表的变更流,为每个状态发生变化的单元生成一个 `session/projection` mux 帧(`{sessionId, key, value, seq}`——实时推送状态,绝不入日志;客户端按 seq 高者胜维护一个按会话的通用值仓)。载体不持有任何领域知识(每个值在注册表内部已过其单元自己的 schema;协议 schema 对 `values`/`value` 保持宽松);loadOlder 页永不携带该块,未装注册表的组合则两个面都不提供。 -会话标题与其他所有领域一样搭乘这对通用投影机制——历史尾页的 `projections` 块外加 `title` 键下的 `session/projection` 帧(专设的 `session/title` 帧已下线)。标题不会加入 `session.list`;冷会话在其中仍只有元数据,直到某项绑定到 Agent 的普通会话操作附加其日志。`session.rename` 接受用户显式标题(冷会话先恢复),委托给 `ctx.sessionTitle.rename`——被接受的 `session/title` 事件将标题钉住、不再被自动生成覆盖——并返回规范化后的标题及其事件 seq,让 client 在推送帧到达前就结算自己的 `title` 投影格;规范化后为空的标题返回 `title-invalid`。 +会话标题使用通用投影对:历史尾页的 `projections` 块,以及 `title` 键下的 `session/projection` 帧。标题不会加入 `session.list`;冷会话在其中仍只有元数据,直到某项绑定到 Agent 的普通会话操作附加其日志。`session.rename` 接受用户显式标题(冷会话先恢复),委托给 `ctx.sessionTitle.rename`,并返回规范化后的标题及其事件 seq,让 client 在推送帧到达前结算自己的 `title` 投影格。被接受的 `session/title` 事件将标题钉住、不再被自动生成覆盖;规范化后为空的标题返回 `title-invalid`。 `session.fork` 会从已附加状态或持久化检查中读取源会话而不获取 Agent,再将可选事件锚点映射到该锚点处或其后的首个 `turn/end`,使消息操作可包含该消息所在的完整轮次。锚点省略或超过末尾时,选择最后一个已完成轮次;若锚点已在日志中,而其所在轮次仍开放,则返回 `fork-unavailable`,不会向较早位置裁剪。发布后的普通子会话会先继承源会话的种子历史、cwd、日志中最新的提供方/模型/推理(reasoning)目标及谱系,再加入源 Workspace;若源会话是 subagent,则改为附加到最近拥有 Workspace 的祖先。如果附加到 Workspace 失败,`workspace-attach-failed` 会携带已发布的子会话 id,供客户端对账。[SessionStore fork 决策](../../../.agents/notes/implemented/feature/2026-06-30-session-store-fork-api.md)给出边界设计的理由。 @@ -56,8 +56,8 @@ Workspace 列表与 Session 列表是相互独立的重连基线。`workspace.cr ## 已知限制与暂缓事项 -- **`respond` 路由已经发布,但待处理交互状态仍属宿主侧工作**:协议形状(POST `/api/respond`、`RpcReceipt`)已经定型;使延迟或重复回答具有明确语义的待处理表位于 `src/api-proxy.ts`,目前仍很精简(只支持问题,不支持审批)。 -- **预留 seam 不进入 `RpcMethodMap`**:`prompt.mode: 'inject'`、`task.list` 和描述字段 `hostInstanceId` 都是已记录的预留项(先前预留的 `host.listModels` 已作为 `llm.models` 交付);未知方法会在信封解析时直接失败,而不会返回「尚未实现」错误码。 +- **待处理交互状态位于宿主侧**:协议形状为 POST `/api/respond` 加 `RpcReceipt`;`src/api-proxy.ts` 中的表只处理问题,不包含审批条目。 +- **预留 seam 不进入 `RpcMethodMap`**:`prompt.mode: 'inject'`、`task.list` 和描述字段 `hostInstanceId` 都是已记录的预留项;模型发现使用 `llm.models`。未知方法会在信封解析时直接失败,而不会返回「尚未实现」错误码。 - **没有协议版本字段**:客户端与宿主一同发布;只有出现独立发布的客户端后,`host.describe` 才会增加版本协商字段。 - **搜索失败会包含提供方诊断信息**:网关是单用户本地服务。将其暴露给多名用户的载体必须用可安全公开的诊断信息替代内部搜索细节。 - **Linux 原生选择器依赖桌面工具**:在 `native` 能力下,Zenity 和 KDialog 均未安装时,`host.pickDirectory` 会给出包含解决建议的错误提示;组合层面的回退是 browse 后端(见 [native 后端 README](../directory-picker-native/README.md))。 diff --git a/packages/host/directory-picker-browse/README.i18n.yaml b/packages/host/directory-picker-browse/README.i18n.yaml index 66ced876a1..60f133ae2b 100644 --- a/packages/host/directory-picker-browse/README.i18n.yaml +++ b/packages/host/directory-picker-browse/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/directory-picker-browse/README.md -README.md: 62384cc0b0e5756e56c1d608252721c506a0915f -README.zh.md: fd53cb69973268967e2170b74c068d87b4974cd8 +README.md: bd28be4119a542eda8c8ee2e53aa860f9c02c17d +README.zh.md: 03306f2fd1c54f9c3c5dbc808920abce944141e5 diff --git a/packages/host/directory-picker-browse/README.md b/packages/host/directory-picker-browse/README.md index 62384cc0b0..bd28be4119 100644 --- a/packages/host/directory-picker-browse/README.md +++ b/packages/host/directory-picker-browse/README.md @@ -20,4 +20,4 @@ None; this package neither assembles nor sends a provider request. - **Windows hidden attribute is not read** — Node dirents do not expose `FILE_ATTRIBUTE_HIDDEN`, so `hidden` means dot-prefixed on every platform until a native probe is worth its cost. - **No drive-root enumeration** — on Windows the ancestry stops at the drive root; crossing drives waits for the browser UI's path-entry affordance rather than an enumeration primitive here. -- **Whole-filesystem scope** — no per-deployment browse-root restriction; `workspace.create` accepts arbitrary paths today, so a root here would be UX scoping, not a boundary — deferred until a deployment needs it. +- **Whole-filesystem scope** — there is no per-deployment browse-root restriction. `workspace.create` accepts arbitrary paths, so a root here would be UX scoping rather than a security boundary. diff --git a/packages/host/directory-picker-browse/README.zh.md b/packages/host/directory-picker-browse/README.zh.md index fd53cb6997..03306f2fd1 100644 --- a/packages/host/directory-picker-browse/README.zh.md +++ b/packages/host/directory-picker-browse/README.zh.md @@ -20,4 +20,4 @@ - **不读取 Windows 隐藏属性**——Node 的 dirent 不暴露 `FILE_ATTRIBUTE_HIDDEN`,因此在所有平台上 `hidden` 都意味着点前缀,直到原生探测值得付出相应成本为止。 - **不枚举盘符根**——Windows 上祖先链止于盘符根;跨盘依赖浏览器 UI 的路径输入入口,而不是这里的枚举原语。 -- **全盘可浏览**——没有按部署限定的浏览根;`workspace.create` 今天就接受任意路径,这里的根只会是 UX 范围而非边界——等到有部署需要时再做。 +- **全盘可浏览**——没有按部署限定的浏览根。`workspace.create` 接受任意路径,因此这里的根会限定 UX 范围,而不是安全边界。 diff --git a/packages/host/directory-picker-native/README.i18n.yaml b/packages/host/directory-picker-native/README.i18n.yaml index c1b47710a7..4069bb2e53 100644 --- a/packages/host/directory-picker-native/README.i18n.yaml +++ b/packages/host/directory-picker-native/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/host/directory-picker-native/README.md -README.md: 3d270af441bd251c126c8fb3c3d2d7aec95655c9 -README.zh.md: b4a3d91b68c285aad7911ba711348e36ffc7a4c8 +README.md: 82c1e2a6adc5352362c3b4a065a29591249c8bd9 +README.zh.md: 5e69cababeea0ff13090fa6f8f4420d30446bc55 diff --git a/packages/host/directory-picker-native/README.md b/packages/host/directory-picker-native/README.md index 3d270af441..82c1e2a6ad 100644 --- a/packages/host/directory-picker-native/README.md +++ b/packages/host/directory-picker-native/README.md @@ -17,4 +17,4 @@ None; this package neither assembles nor sends a provider request. ## Known Limitations and Deferred Work - **Linux requires desktop tooling** — with neither Zenity nor KDialog installed, `pick` rejects with an actionable error; it does not fall back to a typed-path prompt (the browse backend is that fallback at the composition level). -- **Windows has no mechanism fallback** — the child-process picker is the only tier: koffi is a packaged dependency whose availability the install guarantees, so a failed pick (COM refusal, dialog crash) surfaces the failure instead of degrading to a PowerShell-hosted dialog (the former `pwsh` → Windows PowerShell 5.1 chain was removed). The browse backend remains the fallback at the composition level. +- **Windows has no mechanism fallback** — the child-process picker through packaged koffi is the only native tier, so a COM refusal or dialog crash surfaces the failure. The browse backend remains the fallback at the composition level. diff --git a/packages/host/directory-picker-native/README.zh.md b/packages/host/directory-picker-native/README.zh.md index b4a3d91b68..5e69cababe 100644 --- a/packages/host/directory-picker-native/README.zh.md +++ b/packages/host/directory-picker-native/README.zh.md @@ -17,4 +17,4 @@ ## 已知限制与延期工作 - **Linux 依赖桌面工具**——Zenity 与 KDialog 均未安装时,`pick` 以包含解决建议的错误拒绝;它不会回退为手输路径提示(组合层面的回退是 browse 后端)。 -- **Windows 没有机制级回退**——子进程选择器是唯一层级:koffi 是打包依赖,其可用性由安装保证,因此一次失败的 pick(COM 拒绝、对话框崩溃)直接上报失败,不会降级到 PowerShell 承载的对话框(原有的 `pwsh` → Windows PowerShell 5.1 链已删除)。组合层面的回退仍是 browse 后端。 +- **Windows 没有机制级回退**——通过打包依赖 koffi 运行的子进程选择器是唯一原生层级,因此 COM 拒绝或对话框崩溃会直接上报失败。组合层面的回退仍是 browse 后端。 diff --git a/packages/llm/README.i18n.yaml b/packages/llm/README.i18n.yaml index 009a799253..3c4ae19900 100644 --- a/packages/llm/README.i18n.yaml +++ b/packages/llm/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/llm/README.md -README.md: 66b7beabd73cc3fec7230f209a9da0da48a37c95 -README.zh.md: 08f561840d1a560cfc9bced6f8757a0b0fc5770a +README.md: 92d9fbfa2b8c8db4700562009db49229b2189ab3 +README.zh.md: 5c6e7aad1db6511bdb660b86e257652128db131f diff --git a/packages/llm/README.zh.md b/packages/llm/README.zh.md index 08f561840d..5c6e7aad1d 100644 --- a/packages/llm/README.zh.md +++ b/packages/llm/README.zh.md @@ -1,15 +1,15 @@ -# llm/:LLM(大语言模型)能力家族 +# llm/ — LLM 能力家族 [English](README.md) | 中文 -LLM seam 及其提供方适配器。接口包(`llm`)拥有抽象服务、内容块词汇和流分片组装器;适配器是在 `ctx.llm` 上注册的具体实现。这些全是**产品**包。 +LLM(大语言模型)seam 及其提供方适配器。接口包(`llm`)负责抽象服务、内容块词汇和流式分片组装器;适配器是注册到 `ctx.llm` 的具体实现。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `llm/` | 抽象 LLM 服务 + 内容块词汇 + 分片组装器 | `ctx.llm` | -| `token-meter/` | 感知回放的请求 token 与表层 token 测量 | `ctx.tokenMeter` | -| `llm-retry/` | 确切提供方的常规或无界请求重试策略 | (监听 `agent/request-error`) | -| `llm-deepseek/` | DeepSeek API 适配器,直接使用 fetch + eventsource-parser 和 SSE(Server-Sent Events) | (注册到 `ctx.llm`) | -| `llm-pi-ai/` | 通过 `@earendil-works/pi-ai` 实现的多提供方适配器 | (注册到 `ctx.llm`) | +| [`llm/`](llm/README.md) | LLM 服务和共享流式词汇 | `ctx.llm` | +| [`token-meter/`](token-meter/README.md) | 可感知回放的 token 测量 | `ctx.tokenMeter` | +| [`llm-retry/`](llm-retry/README.md) | 提供方作用域的重试策略 | 监听 `agent/request-error` | +| [`llm-deepseek/`](llm-deepseek/README.md) | 直接 DeepSeek 适配器 | 注册到 `ctx.llm` | +| [`llm-pi-ai/`](llm-pi-ai/README.md) | 多提供方 pi-ai 适配器 | 注册到 `ctx.llm` | -接口位于 `llm/llm/`;适配器、重试策略和可复用的 token 计量器以扁平结构并列在该分组下。请求按 `provider` 路由,而 `model` 会原样传给选中的适配器。负责该路由的适配器提供重试策略,并解析可用的确切模型身份、上下文容量和推理元数据;重试执行器与 token 计量器仍与提供方无关。新的提供方适配器只需在 `ctx.llm` 上注册一个或多个提供方路由,无需改动消费方。两个已交付实现见[双生 LLM 适配器](../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md),测量归属见[回放 token 计量器 Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-replay-token-meter-service.md),容量与压缩(compaction)策略归属见[路由模型上下文 Agent Note](../../.agents/notes/implemented/architecture/2026-07-20-routed-model-context-and-compaction-policy.md)。 +适配器在 seam 上注册提供方路由;重试与 token 测量仍是独立消费方。子 README 负责路由、元数据、回放和提供方协议细节;[LLM 架构决策](../../.agents/notes/implemented/architecture/2026-06-13-twin-llm-adapters.md)负责设计原理。 diff --git a/packages/llm/token-meter/README.i18n.yaml b/packages/llm/token-meter/README.i18n.yaml index ca54dba848..6c5abe03df 100644 --- a/packages/llm/token-meter/README.i18n.yaml +++ b/packages/llm/token-meter/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/llm/token-meter/README.md -README.md: 701893b342f9a93a75bec175634b1054f3d17151 -README.zh.md: 0731e05186bec3f21d1f723c5b94ab7945e4139d +README.md: 0935a48a5f5773fbb280bc45e07faaa05c0a4f6e +README.zh.md: 83282ab47e6d406cfca30bbd8af40e0c94050504 diff --git a/packages/llm/token-meter/README.md b/packages/llm/token-meter/README.md index 701893b342..0935a48a5f 100644 --- a/packages/llm/token-meter/README.md +++ b/packages/llm/token-meter/README.md @@ -29,13 +29,13 @@ When the composition provides `ctx.sessionProjections`, token-meter registers tw `contextPressure` carries optional `pressureTokens` — the newest provider-reported prompt size, summing uncached input plus cache reads and writes — and optional `contextWindow` from the newest `request/context` record. Pressure stays absent until a provider reports usage; capacity stays absent for a route whose adapter advertises none. Output is excluded, so the numerator holds still while a turn streams and steps forward when the next request reports its usage. -Both units use the standard projection baseline, live frame, higher-seq-wins store, and JSON checkpoint paths. Unloading token-meter removes both keys. A headless or TUI composition without the projection seam keeps the measurement service's existing behavior. +Both units use the standard projection baseline, live frame, higher-seq-wins store, and JSON checkpoint paths. Unloading token-meter removes both keys. A composition without the projection seam keeps the measurement service's existing behavior. ### Context occupancy is an approximation, by design `pressureTokens` and `contextWindow` are independent last-wins fields and are **not** one atomic observation of a single request. Switching models pairs the fresh capacity with the previous route's pressure until the next request reports usage, and `pressureTokens` describes the last request rather than the surface as it stands right now. -This is deliberate. An occupancy percentage is a user-facing reference figure, not a billing record or a gating input — nothing in the harness makes decisions from it, and compaction reads `measure()` instead. The TUI status line has always computed occupancy the same way, dividing a `measure()` total by a separately-resolved capacity for the selected model. +This is deliberate. An occupancy percentage is a user-facing reference figure, not a billing record or a gating input — nothing in the harness makes decisions from it, and compaction reads `measure()` instead. A UI computes occupancy by dividing measured pressure by the separately resolved capacity for the selected model. Making the pair atomic was tried and rejected: it required a transient non-replayable wire frame, which needed lifecycle fencing against cross-stream reordering and left occupancy blank after every reconnect. The [Agent Note](../../../.agents/notes/implemented/architecture/2026-07-29-projected-token-usage-and-request-context.md) records that comparison. Consumers that need an exact same-boundary figure should call `measure()` at their own request boundary rather than read this projection. @@ -62,4 +62,3 @@ No direct invalidation; the named consumer owns any request-prefix changes. - **Every measurement clones the current surface** — coherent immutable snapshots make reads O(surface), including below-threshold pressure checks. - **Provider usage is only reusable for an identical canonical envelope** — prompt, prefix, tools, provider, model, or call-config changes deliberately fall back to full heuristic estimation. - **Legacy provenance is conservative** — assistant messages without `sourceEventSeqs` cannot distinguish provider output from listener rewrites, so the fold avoids claiming a known empty or exact chunk stream. -- **The TUI and browser fixture retain parallel folds** — `tokenUsage` owns durable session-projection semantics; the TUI keeps its live per-step map because its composition does not mount the generic projection seam, while the browser fixture mirrors the unit for standalone demo data. diff --git a/packages/llm/token-meter/README.zh.md b/packages/llm/token-meter/README.zh.md index 0731e05186..83282ab47e 100644 --- a/packages/llm/token-meter/README.zh.md +++ b/packages/llm/token-meter/README.zh.md @@ -29,13 +29,13 @@ fold 跟踪完整请求标头快照、步骤边界、表层追加与替换、成 `contextPressure` 携带可选的 `pressureTokens`(提供方报告的最新提示词规模,为未缓存输入加缓存读取与写入之和),以及来自最新一条 `request/context` 记录的可选 `contextWindow`。提供方报告用量前压力保持缺失;路由适配器未公布容量时容量也保持缺失。输出不计入其中,因此轮次流式输出期间分子保持不动,等到下一个请求报告用量时才前进。 -两个单元都使用标准的投影基线、实时帧、seq 高者胜值仓和 JSON 检查点路径。卸载 token-meter 会移除这两个键。不带投影 seam 的 headless 或 TUI 组合会保留测量服务的既有行为。 +两个单元都使用标准的投影基线、实时帧、seq 高者胜值仓和 JSON 检查点路径。卸载 token-meter 会移除这两个键。不带投影 seam 的组合会保留测量服务的既有行为。 ### 上下文占用率是刻意为之的近似值 `pressureTokens` 与 `contextWindow` 是两个各自后者胜的独立字段,**不是**对单个请求的一次原子观测。切换模型时,新容量会与上一路由的压力配对,直到下一个请求报告用量为止;而 `pressureTokens` 描述的是最后一个请求,不是此刻的表层。 -这是刻意的选择。占用率百分比是面向用户的参考数字,既不是计费记录,也不是门控输入:harness 中没有任何环节依据它做决策,压缩改为直接读取 `measure()`。TUI 状态行一直以同样的方式计算占用率,即用 `measure()` 总量除以为所选模型单独解析出的容量。 +这是刻意的选择。占用率百分比是面向用户的参考数字,既不是计费记录,也不是门控输入:harness 中没有任何环节依据它做决策,压缩改为直接读取 `measure()`。UI 用测得的压力除以为所选模型单独解析出的容量来计算占用率。 让这对值保持原子已经尝试过并被否决:它需要一个临时且不可回放的协议帧,进而需要针对跨流重排序的生命周期栅栏,还会让占用率在每次重连后变为空白。[Agent Note](../../../.agents/notes/implemented/architecture/2026-07-29-projected-token-usage-and-request-context.md)记录了这项对比。需要同一边界精确数字的消费方应在自己的请求边界调用 `measure()`,而不是读取该投影。 @@ -62,4 +62,3 @@ fold 跟踪完整请求标头快照、步骤边界、表层追加与替换、成 - **每次测量都会克隆当前表层**:一致且不可变的快照使读取成为 O(surface),包括低于阈值的压力检查。 - **提供方用量只能为完全相同的规范 envelope 复用**:提示词、前缀、工具、提供方、模型或调用配置变更都会有意回退到完整启发式估算。 - **遗留溯源采取保守策略**:没有 `sourceEventSeqs` 的 assistant 消息无法区分提供方输出与 listener 改写,因此 fold 不会声称已知空流或精确分片流。 -- **TUI 与浏览器 fixture 仍保留并行 fold**:`tokenUsage` 拥有持久会话投影语义;TUI 的组合未挂载通用投影 seam,因此继续维护实时的逐步骤 map,而浏览器 fixture 会为独立 demo 数据镜像该单元。 diff --git a/packages/llm/token-meter/src/index.ts b/packages/llm/token-meter/src/index.ts index 1300343e99..369c6e5e85 100644 --- a/packages/llm/token-meter/src/index.ts +++ b/packages/llm/token-meter/src/index.ts @@ -93,8 +93,8 @@ export class TokenMeterService extends Service { super(ctx, 'tokenMeter') validateConfigKeys(config) - // Projection registration is an optional child: headless and TUI - // compositions without the generic registry keep the meter's old shape. + // Projection registration is an optional child: compositions without the + // generic registry keep the meter's standalone read shape. ctx.inject(['sessionProjections'], (projectionCtx) => { projectionCtx.sessionProjections.register(tokenUsageProjectionDefinition) projectionCtx.sessionProjections.register(contextPressureProjectionDefinition) diff --git a/packages/llm/token-meter/src/projection.ts b/packages/llm/token-meter/src/projection.ts index 2740b4954f..080f593774 100644 --- a/packages/llm/token-meter/src/projection.ts +++ b/packages/llm/token-meter/src/projection.ts @@ -25,8 +25,7 @@ export interface TokenUsageProjection { * `contextWindow` the newest recorded route capacity. Switching models can * therefore pair a fresh capacity with the previous route's pressure until the * next request reports usage. This is an intentional trade — the value is a - * user-facing reference, not a billing or gating input — and it matches how - * the TUI status line has always computed occupancy. See the token-meter + * user-facing reference, not a billing or gating input. See the token-meter * README for the full rationale. */ export interface ContextPressureProjection { diff --git a/packages/lsp/README.i18n.yaml b/packages/lsp/README.i18n.yaml index 837c07cdb1..a41b05f148 100644 --- a/packages/lsp/README.i18n.yaml +++ b/packages/lsp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/lsp/README.md -README.md: 7b5d9e0f50e733113539cf1ea1ed72e8651ad94c -README.zh.md: a0891a26b6cfbee6ecc2b1a758c43bff5aec1c84 +README.md: 4964d78f1096d1a4bc78fa80c6b5febaf24a5661 +README.zh.md: 93b872002cf1f53cdbb96ff42402bfeb9557575f diff --git a/packages/lsp/README.zh.md b/packages/lsp/README.zh.md index a0891a26b6..93b872002c 100644 --- a/packages/lsp/README.zh.md +++ b/packages/lsp/README.zh.md @@ -2,14 +2,12 @@ [English](README.md) | 中文 -语言服务器能力 seam:抽象 LSP 接口、通用 stdio 提供方,以及面向模型的 `lsp` 工具。这些全是**产品**包。 +语言服务器能力 seam:抽象 LSP 接口、通用 stdio 提供方和面向模型的 `lsp` 工具。这些全是**产品**包。 | 包 | 职责 | ctx key | |---|---|---| -| `lsp/` | 抽象 LSP seam(按品牌化 id + 扩展名映射组织的提供方注册表、逐查询选择、词汇、`LspError`) | `ctx.lsp` | -| `lsp-local/` | 通用多服务器本地后端(spawn、JSON-RPC、查询时临时打开文档) | (在 `ctx.lsp` 上注册提供方) | -| `tool-lsp/` | 面向模型的 `lsp` 工具(四种操作、从 1 开始的 UTF-16 光标坐标) | (注册到 `ctx.tools`) | +| [`lsp/`](lsp/README.md) | LSP 提供方 seam 和共享词汇 | `ctx.lsp` | +| [`lsp-local/`](lsp-local/README.md) | 本地 stdio 语言服务器后端 | 在 `ctx.lsp` 上注册提供方 | +| [`tool-lsp/`](tool-lsp/README.md) | 面向模型的语义导航工具 | 注册到 `ctx.tools` | -接口位于 `lsp/lsp/`。该 seam 恰好公开四种语义操作:`goToDefinition`、`findReferences`、`goToImplementation`、`hover`,且不提供通用 JSON-RPC 逃生口;因此,替换提供方不会改变模型请求导航的方式,也不会让协议载荷或未经评审的修改进入模型契约。提供方注册的是**能力**而非工具;`tool-lsp` 是面向模型名称、schema、提示词指引和呈现的唯一 owner。 - -设计原理见 [LSP 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md),其中也解释了文档为何在每次查询时临时打开、本地主机为何通过 Node API 而非 `ctx.fs` 读取,以及扩展名归属为何在同一运行时内互斥。 +提供方注册语义能力;工具负责面向模型的契约。子 README 记录操作、协议和呈现细节,[LSP 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md)负责设计原理。 diff --git a/packages/mcp/README.i18n.yaml b/packages/mcp/README.i18n.yaml index a102370f7b..3ce6f13ac3 100644 --- a/packages/mcp/README.i18n.yaml +++ b/packages/mcp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/mcp/README.md -README.md: 3bde9023368da318ec572a72e2e86a7fa2d3ad8d -README.zh.md: 46a11c94610a7b6d54d81bc104bfc582337f0466 +README.md: cc3440f6cb2e507277e6141393ccf39fc7022d7d +README.zh.md: 0a74f3e9016be95f76104303396dc690888d645d diff --git a/packages/mcp/README.zh.md b/packages/mcp/README.zh.md index 38a5c8b1c1..0a74f3e901 100644 --- a/packages/mcp/README.zh.md +++ b/packages/mcp/README.zh.md @@ -1,9 +1,9 @@ -# MCP:Model Context Protocol +# MCP — 模型上下文协议 [English](README.md) | 中文 -连接 harness 与 MCP 生态的包。 +将 harness 与 MCP 生态系统桥接的包。 -| 包 | 角色 | +| 包 | 职责 | |---|---| -| [`mcp-client/`](mcp-client/README.md) | MCP 客户端桥接:将外部服务器工具注册到 `ctx.tools` | +| [`mcp-client/`](mcp-client/README.md) | MCP 客户端桥接,将外部服务器工具注册到 `ctx.tools` | diff --git a/packages/plan/README.i18n.yaml b/packages/plan/README.i18n.yaml index 7af5d82aa6..3ae6755be6 100644 --- a/packages/plan/README.i18n.yaml +++ b/packages/plan/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/plan/README.md -README.md: eeb58703c34acae0eb2146b87b01a56815e1362a -README.zh.md: c7bfacf4b20c252ae2e81395aca2ca43a48a677a +README.md: 598974e105aaeaf3c35418aec0655de2a5ba6888 +README.zh.md: 0f54299af5d34555d86d82e83b92d185045e770f diff --git a/packages/plan/README.zh.md b/packages/plan/README.zh.md index c7bfacf4b2..0f54299af5 100644 --- a/packages/plan/README.zh.md +++ b/packages/plan/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -Plan mode 是一种按 agent(智能体)分开记录到日志的协作状态。它是单一**产品**包,而非通用模式注册表或能力 seam 三包组合。 +Plan mode 是按 agent(智能体)记录的协作状态,而不是通用模式注册表或能力 seam。 | 包 | 职责 | ctx 键 | |---|---|---| -| `plan-mode/` | `plan/mode` 词汇与折叠、在边界生效的状态、`plan:policy` 引导段、`/plan [message]` 进入命令与 `/plan off` 退出命令,以及面向模型的 `exit_plan_mode` 评审工具 | `ctx.planMode` | +| [`plan-mode/`](plan-mode/README.md) | 负责 plan mode 状态、指引、命令和评审流程 | `ctx.planMode` | -活跃状态是会话日志的纯函数,因此恢复和 fork 无需额外机制即可还原该状态。部署通过 Cordis 配置提供 plan 引导内容,而 `exit_plan_mode` 在 Plan mode 未激活时仍保持注册,以稳定请求工具目录。交互式适配器使用插件拥有的 `/plan` 命令;沙箱模式和审批策略仍是独立的强制执行设置。设计详见 [plan 专用协作状态](../../.agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md)。 +[plan 专用协作状态](../../.agents/notes/implemented/simplification/2026-07-22-plan-specific-collaboration-state.md)决策记录了该家族的设计。 diff --git a/packages/plan/plan-mode/README.i18n.yaml b/packages/plan/plan-mode/README.i18n.yaml index 937177785d..1330f33aa9 100644 --- a/packages/plan/plan-mode/README.i18n.yaml +++ b/packages/plan/plan-mode/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/plan/plan-mode/README.md -README.md: 6f0a9ac477b49b96ddfc2ce667e3556dec727569 -README.zh.md: 9d3d98ef387fa2f82bb87bf3e35b2a220bd643ec +README.md: 764b34ca6fc83661886c89d7d454cbe550df47e2 +README.zh.md: e43a06a8111fca39b957a90ff748800e11a37a94 diff --git a/packages/plan/plan-mode/README.md b/packages/plan/plan-mode/README.md index dfa7b7c9a7..764b34ca6f 100644 --- a/packages/plan/plan-mode/README.md +++ b/packages/plan/plan-mode/README.md @@ -18,7 +18,7 @@ The review question declares the `plan-review` presentation intent, naming `Appr When `ctx.commands` is composed, the package registers `/plan [message]` and reserves the exact argument `off` for direct exit. Bare `/plan` selects plan mode; any other non-empty argument selects it first and is then submitted through `agent.steer()`, so it becomes the next step's ordinary logged user message under plan guidance. `/plan off` selects inactive without sending model input; it also cancels a pending entry before plan mode reaches a request. -The TUI consumes the plugin-owned `/plan` command; other front doors may drive the same service directly without defining a second mode vocabulary. +The Web client consumes the plugin-owned `/plan` command; other front doors may drive the same service directly without defining a second mode vocabulary. ## Session projection @@ -94,4 +94,4 @@ Mode transitions do not change the tool catalog; plan arguments and review resul - Plan mode guides rather than enforces; deployments needing a hard boundary must combine independent sandbox and approval controls. - A pending selection made while idle is lost if the process exits before the next boundary, so the UI must reapply it. - Forked agents inherit logged plan state, while newly spawned agents begin inactive; there is no creation-time plan option. -- Only the Web UI renders the `plan-review` intent; the TUI presents the review through its generic question flow, which is answerable but does not read as a plan gate. +- Only the Web UI has a specialized `plan-review` renderer; another interaction provider may present the same request through its generic option flow. diff --git a/packages/plan/plan-mode/README.zh.md b/packages/plan/plan-mode/README.zh.md index 80876b16d0..e43a06a811 100644 --- a/packages/plan/plan-mode/README.zh.md +++ b/packages/plan/plan-mode/README.zh.md @@ -18,7 +18,7 @@ 组合 `ctx.commands` 时,该包会注册 `/plan [message]`,并将参数恰好为 `off` 的情况保留给直接退出。不带参数的 `/plan` 会启用 plan mode;任何其他非空参数都会先启用 plan mode,再通过 `agent.steer()` 提交,因此它会在 plan 引导下成为下一步骤的常规已记录用户消息。`/plan off` 会选择停用状态,不发送模型输入;它还可以在启用 plan mode 的待处理选择到达请求边界之前将其取消。 -TUI 使用该插件提供的 `/plan` 命令;其他入口可以直接驱动同一服务,无需定义第二套 mode 词汇。 +Web 客户端使用该插件提供的 `/plan` 命令;其他入口可以直接驱动同一服务,无需定义第二套 mode 词汇。 ## 会话投影 @@ -94,4 +94,4 @@ mode 转换不改变工具目录;plan 参数与评审结果按常规方式扩 - Plan mode 只进行引导,而不强制执行;需要硬边界的部署必须组合独立的沙箱与批准控制。 - 如果进程在下一个边界之前退出,空闲时作出的待生效选择会丢失,因此 UI 必须重新应用它。 - Fork 的 agent 会继承已记录的 plan 状态,新 spawn 的 agent 则从未激活状态开始;不存在创建时 plan 选项。 -- 只有 Web UI 渲染 `plan-review` 意图;TUI 通过其通用问题流程呈现该评审,可以回答,但读起来不像一个计划关口。 +- 只有 Web UI 具备专用的 `plan-review` 渲染器;其他交互提供方可以通过通用选项流程呈现同一请求。 diff --git a/packages/pty/README.i18n.yaml b/packages/pty/README.i18n.yaml index 2fe1655268..083a144b55 100644 --- a/packages/pty/README.i18n.yaml +++ b/packages/pty/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/pty/README.md -README.md: e54dcf64db083665f37b7dc19a7a92e21494442b -README.zh.md: 0518904bf047d0b03e3f59cc148ef64a039b3205 +README.md: 9c8206464d45b1be1d6ee3861c57c128e77686c5 +README.zh.md: 70d081e60a7db61443ed616b64586a93c119a640 diff --git a/packages/pty/README.zh.md b/packages/pty/README.zh.md index 0518904bf0..70d081e60a 100644 --- a/packages/pty/README.zh.md +++ b/packages/pty/README.zh.md @@ -2,13 +2,13 @@ [English](README.md) | 中文 -`PTY` 的全称是 **Pseudo-Terminal**(伪终端)。这项能力提供持久且限定所有者范围的终端会话,适用于需要跨工具调用保留状态或使用交互式 stdin 的工作流。PTY 是单次 bash 与文件系统工具的补充,不会取代后两者更严格的逐操作契约。 +本家族为交互式或有状态的终端工作提供持久且限定所有者范围的伪终端会话,是单次 bash 执行的补充。 | 包 | 职责 | ctx 键 | |---|---|---| -| [`pty`](pty/README.md)(`@deepseek-ai/dsh-pty`) | 后端注册表、品牌化 id、精确到 agent(智能体)的所有权、会话操作与等待清理完成的机制 | `ctx.pty` | -| `pty-local`(`@deepseek-ai/dsh-pty-local`) | 本地 `node-pty` 后端、就绪检测、有界终端状态、沙箱与进程会话监管 | 注册到 `ctx.pty` | -| `tool-pty`(`@deepseek-ai/dsh-tool-pty`) | 6 个面向模型的工具,以及用于后台发送的通用任务集成 | 注册到 `ctx.tools` | -| `tool-bash-persistent`(`@deepseek-ai/dsh-tool-bash-persistent`) | 一个由所有者隔离可复用 PTY shell 支撑的模型可见 `bash` | 消费 `ctx.pty`,注册到 `ctx.tools` | +| [`pty/`](pty/README.md) | 定义 PTY 服务和会话生命周期 | `ctx.pty` | +| [`pty-local/`](pty-local/README.md) | 提供本地持久终端会话 | 注册到 `ctx.pty` | +| [`tool-pty/`](tool-pty/README.md) | 向模型公开 PTY 会话操作 | 注册到 `ctx.tools` | +| [`tool-bash-persistent/`](tool-bash-persistent/README.md) | 公开可复用的 PTY 后端 bash 工具 | 注册到 `ctx.tools` | -设计与暂缓边界记录在[持久 PTY Agent Note](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md) 中。 +[持久 PTY 决策](../../.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md)记录了该家族的边界。 diff --git a/packages/sandbox/README.i18n.yaml b/packages/sandbox/README.i18n.yaml index 3a88ca7986..c851d420ae 100644 --- a/packages/sandbox/README.i18n.yaml +++ b/packages/sandbox/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sandbox/README.md -README.md: 4a651f262ee817edf5e9620d1cbf911b2d51b90b -README.zh.md: 9274ef8dccbb388170a0319410660b6ff0d52577 +README.md: 1ba56d22330c26302c9283b2319d983ebf902219 +README.zh.md: 5daf830854297fe14c6560dbcde8a380ad079a0e diff --git a/packages/sandbox/README.zh.md b/packages/sandbox/README.zh.md index 9274ef8dcc..5daf830854 100644 --- a/packages/sandbox/README.zh.md +++ b/packages/sandbox/README.zh.md @@ -2,14 +2,12 @@ [English](README.md) | 中文 -[能力 seam 拆分](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)中负责限制的一半:抽象提供方接口、平台后端和共享策略归属位置。消费方把即将 spawn 的精确 argv 交给 `ctx.sandbox`,改为 spawn 返回的已包装 argv;完整的 `SandboxExecutionPolicy`(模式 + 工作区根目录)随每次能力调用传递,其中受限制的子集成为提供方的 `SandboxPolicy`。因此,不同会话与消费方可以同时按不同策略施加限制。这些均为**产品**包。 +本家族将逐会话限制策略应用于进程执行。它覆盖与宿主共享文件系统和内核的子进程;隔离环境会替换完整的能力实现,而不是在此注册。 | 包 | 职责 | ctx key | |---|---|---| -| `sandbox/` | 抽象进程沙箱 seam(`SandboxProvider` 契约 + 模式/强制执行/策略词汇),加共享 ESCALATION 工具包(`approveEscalation`、权限逐级严格扩大的阶梯、拒绝/提示标记),以及所有强制执行方言共享的 `writableRoots` 派生 | `ctx.sandbox` | -| `sandbox-local/` | 按平台链选择的本地后端:Linux 使用 `bwrap`,否则使用 `landlock-run` launcher(通过 npm 分发的 [`node-addon-landlock-run`](https://www.npmjs.com/package/node-addon-landlock-run) 家族,在自身仓库构建发布);darwin 使用 `sandbox-exec`/Seatbelt。多候选链会执行功能探测,唯一候选项直接选择,结论缓存,失败时默认拒绝 | (注册 `ctx.sandbox`) | -| `sandbox-policy/` | 策略解析器:部署回退值,加每个会话的持久模式与不可变 cwd 根目录。两个强制执行家族都消费完整的逐调用结果,因此 bash 与 fs 不会限制到不同根目录 | `ctx.sandboxPolicy` | +| [`sandbox/`](sandbox/README.md) | 定义进程沙箱服务和共享升权词汇 | `ctx.sandbox` | +| [`sandbox-local/`](sandbox-local/README.md) | 提供本地平台限制后端 | 注册到 `ctx.sandbox` | +| [`sandbox-policy/`](sandbox-policy/README.md) | 解析持久的逐会话沙箱策略 | `ctx.sandboxPolicy` | -该 seam 只限制与宿主共享文件系统和内核的子进程。容器、microVM 和远程执行器都不是这里的后端:它们会以环境一致的分组替换整个能力实现(`ctx.bash`、`ctx.fs`);边界记录在[沙箱 Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)中。 - -当前消费方:[`bash/bash-sandbox`](../bash/bash-sandbox/)(包装 `['bash', '-c', command]` 时会调用 `ctx.sandbox`)和 [`fs/fs-sandbox`](../fs/fs-sandbox/)(进程内路径隔离,而非 argv 包装层;读取 `ctx.sandboxPolicy`,对写入/编辑强制执行共享模式)。跨家族边界是沙箱 Agent Note 的[跨家族 fs 沙箱](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md)阶段;共享词汇使两个家族可以向模型传授同一种拒绝标记与升权流程。 +[沙箱决策](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)记录了能力边界,[文件系统集成决策](../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md)记录了跨家族策略的使用方式。 diff --git a/packages/sandbox/sandbox-local/README.i18n.yaml b/packages/sandbox/sandbox-local/README.i18n.yaml index cb9aeeec8b..4476ca8df4 100644 --- a/packages/sandbox/sandbox-local/README.i18n.yaml +++ b/packages/sandbox/sandbox-local/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sandbox/sandbox-local/README.md -README.md: f7dd5619fa69ae226def78415a91d51ba99e0a65 -README.zh.md: c8abe05408ac8917b2c5dc39acdbdf9d9539744d +README.md: 17ae8c98a1cf9e69427806fe6ad3ba426892c114 +README.zh.md: 9c1fd304a487f3d3f19d3f5e9985229b5c78c6e7 diff --git a/packages/sdk/README.i18n.yaml b/packages/sdk/README.i18n.yaml index 31806b800f..738a0610ab 100644 --- a/packages/sdk/README.i18n.yaml +++ b/packages/sdk/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sdk/README.md -README.md: 0dcf3655fc4d20452981f83000f9b9b586ede17b -README.zh.md: cc5927f56654ece0caef7d329798e581b3217eeb +README.md: 3f99d6d45ddc64ac068457b0d03e533d21451a5f +README.zh.md: 003383c5f6238b37893927813959d52d96350c3e diff --git a/packages/sdk/README.zh.md b/packages/sdk/README.zh.md index cc5927f566..003383c5f6 100644 --- a/packages/sdk/README.zh.md +++ b/packages/sdk/README.zh.md @@ -2,18 +2,15 @@ [English](README.md) | 中文 -用于创建、编辑、构建和运行 DeepSeek Harness 项目的开发者工具,外加从另一进程驱动 harness 运行时的客户端 SDK 栈。 - -[功能 Agent Note](../../.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md)负责开发者工作流;[架构 Agent Note](../../.agents/notes/proposed/architecture/2026-07-15-sdk-project-editing-architecture.md)负责包与项目编辑边界;[TypeScript SDK Agent Note](../../.agents/notes/implemented/feature/2026-07-27-typescript-sdk-and-sdk-subagent-backend.md)负责客户端 SDK 栈。 +本分组包含 Harness 项目的开发者工具,以及从另一个进程驱动 Harness 运行时的客户端栈。 | 包 | 职责 | |---|---| -| [`helper`](helper/README.md) | 项目聚合、编辑会话、内置功能、项目文档、模板、包管理器与提示词抽象 | -| [`scripts`](scripts/README.md) | `dsh-sdk` 启动器:`start`、`dev`、`build` 和交互式 `config` | -| [`create-sdk`](create-sdk/README.md) | `npm create @deepseek-ai/sdk` 初始化器 | -| [`sdk-protocol`](sdk-protocol/README.md) | 共享的 SDK 运行时通信协议:以换行符分隔的 JSON-RPC 传输 + 具名请求/通知类型 | -| [`sdk-client`](sdk-client/README.md) | TypeScript 客户端 SDK:通过 stdio JSON-RPC 驱动 harness 运行时子进程(Python SDK 的设计孪生) | +| [`helper/`](helper/README.md) | 提供共享的项目编辑领域 | +| [`scripts/`](scripts/README.md) | 提供 `dsh-sdk` 项目命令 | +| [`create-sdk/`](create-sdk/README.md) | 创建新的 SDK 项目 | +| [`sdk-protocol/`](sdk-protocol/README.md) | 定义 SDK 运行时协议格式 | +| [`sdk-client/`](sdk-client/README.md) | 通过 TypeScript 客户端 API 驱动 Harness 运行时 | +| [`telemetry/`](telemetry/README.md) | 提供启动器遥测、同意和脱敏原语 | -`@deepseek-ai/create-sdk` 是仓库 `@deepseek-ai/dsh-*` 命名规则的唯一例外:npm 的 scoped initializer 约定要求使用该名称,才能支持 `npm create @deepseek-ai/sdk`。 - -生成的项目始终以 `cordis.yml` 作为唯一运行时插件树。`dsh-sdk dev` 只是围绕同一文件增加 TypeScript 与本地工作区解析,不会创建仅供开发环境使用的配置。 +`@deepseek-ai/create-sdk` 遵循 npm 的 scoped initializer 命名约定;其他包遵循仓库的 `@deepseek-ai/dsh-*` 约定。参见[开发者项目工作流](../../.agents/notes/proposed/feature/2026-07-14-sdk-developer-projects.md)、[项目编辑架构](../../.agents/notes/proposed/architecture/2026-07-15-sdk-project-editing-architecture.md)和 [TypeScript SDK 设计](../../.agents/notes/implemented/feature/2026-07-27-typescript-sdk-and-sdk-subagent-backend.md)。 diff --git a/packages/sdk/helper/src/project/project-edit-session.ts b/packages/sdk/helper/src/project/project-edit-session.ts index e8b88f91f3..4c3d39c07c 100644 --- a/packages/sdk/helper/src/project/project-edit-session.ts +++ b/packages/sdk/helper/src/project/project-edit-session.ts @@ -90,7 +90,7 @@ export class ProjectEditSession implements FeatureProjectView { this.profile = source.profile this.documents = source.cloneDocuments() for (const feature of registry.all()) { - /* v8 ignore next -- no current builtin is interface-specific after TUI removal */ + /* v8 ignore next -- no current built-in feature is interface-specific */ if (!feature.isApplicable(this.profile)) continue const installation = feature.inspect(this) this.states.set(feature.id, { @@ -508,7 +508,7 @@ export class ProjectEditSession implements FeatureProjectView { const view = this.projectView(profile) for (const feature of this.registry.all()) { const state = this.states.get(feature.id) - /* v8 ignore next 5 -- no current builtin is interface-specific after TUI removal */ + /* v8 ignore next 5 -- no current built-in feature is interface-specific */ if (!feature.isApplicable(profile)) { if (state?.state === 'enabled') { throw new Error(`feature ${feature.id} is not available for ${profile.runInterface}`) diff --git a/packages/sdk/scripts/src/config/config-workflow.ts b/packages/sdk/scripts/src/config/config-workflow.ts index 9c578f3297..73bb07ea02 100644 --- a/packages/sdk/scripts/src/config/config-workflow.ts +++ b/packages/sdk/scripts/src/config/config-workflow.ts @@ -135,7 +135,7 @@ export class ConfigWorkflow { runInterface: targetRunInterface(project.profile.runInterface, desiredByTarget), } for (const feature of features) { - /* v8 ignore next -- no current builtin is interface-specific after TUI removal */ + /* v8 ignore next -- no current built-in feature is interface-specific */ if (!feature.isApplicable(targetProfile)) desiredByTarget.delete(featureTarget(feature)) } diff --git a/packages/sdk/sdk-client/README.i18n.yaml b/packages/sdk/sdk-client/README.i18n.yaml index d26108b4e7..3a2a16ae0c 100644 --- a/packages/sdk/sdk-client/README.i18n.yaml +++ b/packages/sdk/sdk-client/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/sdk/sdk-client/README.md -README.md: eb0387292fb0093b3ed9360e8aec087201b611f0 -README.zh.md: c264557984f5067d65e7fc60c7afd855dcf26715 +README.md: 5bce396aa36d32b0a3bd7716e1feea8ca9c9038c +README.zh.md: cf72caa669d05358c989be4ed3f084541615052f diff --git a/packages/session-persistence/README.i18n.yaml b/packages/session-persistence/README.i18n.yaml index 8893fbd132..85eb2bd27d 100644 --- a/packages/session-persistence/README.i18n.yaml +++ b/packages/session-persistence/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-persistence/README.md -README.md: ac4e0a8310152b9d2ba5daae61fbbf1eb0ed54ec -README.zh.md: 0903bbc55b06702f748f80643666bf6c4086a633 +README.md: 060f757b3568318b0be4b01b3a10d019d28eac3b +README.zh.md: 67022565f7dabcbbccee30d5ce60fa559088ca39 diff --git a/packages/session-persistence/README.zh.md b/packages/session-persistence/README.zh.md index 0903bbc55b..67022565f7 100644 --- a/packages/session-persistence/README.zh.md +++ b/packages/session-persistence/README.zh.md @@ -2,13 +2,13 @@ [English](README.md) | 中文 -持久会话的持久化 seam 及其存储后端。接口包负责抽象 `SessionPersistence` 服务和共享写入协调器;后端是注册到 `ctx.sessionPersistence` 的具体实现。全部都是**产品**包。 +本家族定义持久会话数据的持久化机制、语义检查点策略以及随产品交付的存储后端。 | 包 | 职责 | ctx 键 | |---|---|---| -| `session-persistence/` | 持久化 seam + 共享写入协调器 | `ctx.sessionPersistence` | -| `session-checkpoint-policy/` | agent(智能体)请求和工具执行的语义持久性屏障 | (包装 `ctx.llm` / `ctx.tools`,监听 agent 事件) | -| `session-persistence-jsonl/` | JSONL 伴随文件持久化后端 | (注册到 `ctx.sessionPersistence`) | -| `session-persistence-sqlite/` | SQLite 持久化后端 | (注册到 `ctx.sessionPersistence`) | +| [`session-persistence/`](session-persistence/README.md) | 定义持久化服务和共享写入协调机制 | `ctx.sessionPersistence` | +| [`session-checkpoint-policy/`](session-checkpoint-policy/README.md) | 应用语义持久性检查点 | 包装 `ctx.llm` 和 `ctx.tools` | +| [`session-persistence-jsonl/`](session-persistence-jsonl/README.md) | 将会话持久化到 JSONL 文件 | 注册到 `ctx.sessionPersistence` | +| [`session-persistence-sqlite/`](session-persistence-sqlite/README.md) | 将会话持久化到 SQLite | 注册到 `ctx.sessionPersistence` | -接口位于 `session-persistence/session-persistence/`;后端是同级包。新存储后端归入此处,并注册到 `ctx.sessionPersistence`。详见[会话持久化](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md)。 +[会话持久化决策](../../.agents/notes/implemented/architecture/2026-06-14-session-persistence.md)记录了该家族的设计。 diff --git a/packages/session-projection/README.i18n.yaml b/packages/session-projection/README.i18n.yaml index 126a4fd726..5e03069b99 100644 --- a/packages/session-projection/README.i18n.yaml +++ b/packages/session-projection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-projection/README.md -README.md: ae80a905705d205adb4a1ee66c72fa28d0d8b6d6 -README.zh.md: 3d4a4aa3151cc92a8b887e85c911b7f6b59bf52f +README.md: f1fc8337bbea6663915c19f39fdf02f14190370d +README.zh.md: 3323166f496fabafabcd96abeaec707a28ce552a diff --git a/packages/session-projection/README.zh.md b/packages/session-projection/README.zh.md index 3d4a4aa315..3323166f49 100644 --- a/packages/session-projection/README.zh.md +++ b/packages/session-projection/README.zh.md @@ -1,10 +1,10 @@ -# session-projection/ +# session-projection/:会话投影能力家族 [English](README.md) | 中文 -会话投影能力家族:领域 host 插件经由此 seam,把日志派生的按会话状态的当前全量值供给客户端载体。 +本家族向客户端载体提供从日志派生的当前逐会话状态。 -| 包 | ctx 键 | 职责 | +| 包 | 职责 | ctx 键 | |---|---|---| -| [`session-projection`](session-projection/README.md) | `sessionProjections` | 接口包:merge-extensible 的 `SessionProjectionMap` 类型表、`ProjectionDefinition` 单元契约,以及供载体同步读取的主动驱动注册表 | -| [`session-projection-cache`](session-projection-cache/README.md) | `sessionProjectionCache` | 持久投影缓存:基于域数据形态的按会话单元检查点持久化、带 turn/end + detach 两个必写点的节流后写,以及冷读阶梯(缓存行 + 持久化尾部回放) | +| [`session-projection/`](session-projection/README.md) | 定义并驱动会话投影单元 | `ctx.sessionProjections` | +| [`session-projection-cache/`](session-projection-cache/README.md) | 持久化并恢复投影检查点 | `ctx.sessionProjectionCache` | diff --git a/packages/session-projection/session-projection/README.i18n.yaml b/packages/session-projection/session-projection/README.i18n.yaml index 92935e92c0..34c1910b16 100644 --- a/packages/session-projection/session-projection/README.i18n.yaml +++ b/packages/session-projection/session-projection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-projection/session-projection/README.md -README.md: f4898b8e567fa5998c18111c5f4e27a8a350a42e -README.zh.md: 520205e616cdc1421c8f33f00d2013a6e0947d3d +README.md: a42e88c262915fc5e53cd72205079cbc8029a8df +README.zh.md: f60a48bd41f0c33edb85a495ada9b6818ec46ee5 diff --git a/packages/session-projection/session-projection/README.md b/packages/session-projection/session-projection/README.md index f4898b8e56..a42e88c262 100644 --- a/packages/session-projection/session-projection/README.md +++ b/packages/session-projection/session-projection/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Session-projection seam. It owns `ctx.sessionProjections`, the registry that DRIVES every registered projection unit forward over committed session events and serves finished whole values to carriers (the api-proxy history tail page and `session/projection` push frame today; TUI/ACP/headless consumers later). A domain registers pure mathematics; the framework owns the drive. Design authority: the [session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md). +Session-projection seam. It owns `ctx.sessionProjections`, the registry that drives every registered projection unit over committed session events and serves finished whole values to carriers, currently the api-proxy history tail page and `session/projection` push frame. A domain registers pure mathematics; the framework owns the drive. The [session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md) records the design rationale. ## Service: `SessionProjectionRegistry` (ctx key: `sessionProjections`) diff --git a/packages/session-projection/session-projection/README.zh.md b/packages/session-projection/session-projection/README.zh.md index 520205e616..f60a48bd41 100644 --- a/packages/session-projection/session-projection/README.zh.md +++ b/packages/session-projection/session-projection/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -会话投影 seam。它拥有 `ctx.sessionProjections`——该注册表驱动每个已注册的投影单元在已提交会话事件上前进,并向载体供给成品全量值(今天是 api-proxy 历史尾页与 `session/projection` 推送帧;日后是 TUI、ACP(Agent Client Protocol)、headless 消费方)。领域注册的只是纯数学;驱动权归框架。设计权威:[session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md)。 +会话投影 seam。它拥有 `ctx.sessionProjections`:该注册表在已提交的会话事件上驱动每个已注册的投影单元,并向载体提供完整的最终值,目前包括 api-proxy 历史尾页和 `session/projection` 推送帧。领域注册的只是纯数学;驱动权归框架。[session-projection RFC](../../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md)记录了设计理由。 ## 服务:`SessionProjectionRegistry`(ctx 键:`sessionProjections`) diff --git a/packages/session-projection/session-projection/src/index.ts b/packages/session-projection/session-projection/src/index.ts index dbf0c5ee6e..90c36ed579 100644 --- a/packages/session-projection/session-projection/src/index.ts +++ b/packages/session-projection/session-projection/src/index.ts @@ -5,8 +5,8 @@ * forward eagerly over committed session events. Domain host plugins * contribute pure mathematics (init/apply/view); the framework owns the * subscription, the per-session watermark cache, and change notification; - * carriers (api-proxy today, TUI/ACP/headless later) consume the snapshot - * read face and the change feed. Neither side knows the other + * carriers consume the snapshot read face and the change feed. Neither side + * knows the other * (capability-seam three-way split). Design authority: the session-projection * RFC (.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md). * diff --git a/packages/session-query/README.i18n.yaml b/packages/session-query/README.i18n.yaml index d55821c109..3911bf76a0 100644 --- a/packages/session-query/README.i18n.yaml +++ b/packages/session-query/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/session-query/README.md -README.md: 38edbf6b0303e3d5a7bd0dc1d180cd127c60f9dc -README.zh.md: 784cb79b01f178db68e0ae8d5f4ba892da02f852 +README.md: a9c9f33e2dc67e837c2be728d2b1a937ce50f13d +README.zh.md: a29dedba09271baa5836cb2d9d57c753ee41c660 diff --git a/packages/session-query/README.zh.md b/packages/session-query/README.zh.md index 784cb79b01..a29dedba09 100644 --- a/packages/session-query/README.zh.md +++ b/packages/session-query/README.zh.md @@ -2,12 +2,10 @@ [English](README.md) | 中文 -针对实时和持久会话日志提供可信的精确读取、关系跟踪、与提供方无关的语义过滤和 SQLite 全文搜索。 +本家族提供经过授权的实时与持久会话日志检索,且独立于压缩(compaction)。 | 包 | 职责 | ctx 键 | |---|---|---| -| [`session-query/`](session-query/README.md) | 组合式服务契约:提供具体的逻辑语料库读取、跟踪和语义过滤,以及抽象全文方法 | `ctx.sessionQuery` | -| [`session-query-sqlite/`](session-query-sqlite/README.md) | 具体服务后端:使用 SQLite FTS5 持久基库和实时覆盖层 | `ctx.sessionQuery` | -| [`tool-session-query/`](tool-session-query/README.md) | 工作区授权的面向模型搜索、血缘、关系和精确事件工具 | 无 | - -查询服务与压缩(compaction)无关:它读取规范血缘、接口操作、已记录来源信息和语义事件文本,但不参与压缩策略或执行。一个抽象服务组合全部查询操作;一个具体后端负责全文生命周期,无需提供方注册表或协调器;消费方将过大的纯文本结果交给通用执行后 spill 策略。 +| [`session-query/`](session-query/README.md) | 定义可信读取、关系查询和搜索操作 | `ctx.sessionQuery` | +| [`session-query-sqlite/`](session-query-sqlite/README.md) | 使用 SQLite 全文搜索实现会话查询 | `ctx.sessionQuery` | +| [`tool-session-query/`](tool-session-query/README.md) | 向模型公开经过工作区授权的会话查询 | 注册到 `ctx.tools` | diff --git a/packages/settings/settings-local/README.i18n.yaml b/packages/settings/settings-local/README.i18n.yaml index 0b1c7e9064..ec26e4241f 100644 --- a/packages/settings/settings-local/README.i18n.yaml +++ b/packages/settings/settings-local/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/settings/settings-local/README.md README.md: d1f3d755f9073acdf6fcfc5d1de883d74cc023c4 -README.zh.md: 3a290c2795e6d1944c5bce6edb99aab53f2728ee +README.zh.md: 03c7ef7c35312d9e1e4a86b0df71412875af7d6c diff --git a/packages/settings/settings-local/README.zh.md b/packages/settings/settings-local/README.zh.md index 3a290c2795..03c7ef7c35 100644 --- a/packages/settings/settings-local/README.zh.md +++ b/packages/settings/settings-local/README.zh.md @@ -28,15 +28,15 @@ - **按内容抑制自写。** provider 缓存最后可用文本;watcher 事件内容与缓存相同(含自己的写入)即为 no-op。 - **Host 配置适配器会收到解析后的路径。** `ctx.settings.documentPath` 是 `resolveSpec()` 得出的绝对文件名,包括自定义 YAML/JSON 路径;`prepareDocument()` 会保留现有文件,或在 Host 打开文档前,以仅属主可访问的权限独占创建缺失的空文件。浏览器只收到可用性标志,绝不重建 `$DSH_HOME`,也绝不提交文件系统目标。 -## Model Experience +## 模型体验 间接生效:本 provider 只存储并发布 namespace 分节,模型效果经由 `ctx.settings` 的消费插件产生,由各消费者自己的文档描述。 -#### KV Cache effect +#### KV Cache 影响 无直接失效;请求前缀的变更由消费插件拥有。 -## Known Limitations and Deferred Work +## 已知限制与暂缓事项 - **同 namespace 冲突仍是后写胜出** — 写锁加读-改-写让并发写入者不会丢掉彼此的 namespace,但两个写入者编辑同一个 namespace 时仍以较后的写入为准;没有按值合并,也没有修订检查。 - **漏掉的 watcher 事件在下一个信号前保持不可见** — 读取从不重新 stat 文件,因此 watcher 漏报的变更只会在下一个事件、下一次写入或重启时被并入。 diff --git a/packages/settings/settings/README.i18n.yaml b/packages/settings/settings/README.i18n.yaml index d0cfacf720..b2549e6be1 100644 --- a/packages/settings/settings/README.i18n.yaml +++ b/packages/settings/settings/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/settings/settings/README.md README.md: 0841624e553364fa03f7a1f1209aa72ab13d97e4 -README.zh.md: fa84ca198ba7383f8fbd7b29a53d74f2c375bd5e +README.zh.md: e8fe4485c2cf6cb6b8710347ebb8dcd1e8424b38 diff --git a/packages/settings/settings/README.zh.md b/packages/settings/settings/README.zh.md index fa84ca198b..e8fe4485c2 100644 --- a/packages/settings/settings/README.zh.md +++ b/packages/settings/settings/README.zh.md @@ -18,7 +18,7 @@ - 解析值是深冻结快照。每次提交后观察者收到 `(next, prev)`:同一回调的调用异步、逐次、按提交顺序执行(慢的旧调用绝不会覆盖更新的结果),异常——同步抛出与异步拒绝——均被隔离。watch 的 disposer 返回后不再启动新的调用(已排队的那一次会被跳过);已启动的调用仍会结算。`settings/updated` 事件逐 listener 扇出,一个抛错的 listener 不会饿死其余 listener;异步 listener 的拒绝会被隔离并记入日志,这正是 `INVARIANT` 编码的失败只从同步 listener 重新抛出的原因。 - 服务卸载先拒绝新写入与观察者调用的启动,再排干全部排队写入与已启动的观察者调用后才完成;registrant fiber 在写入途中被 dispose 时,该写入仍到达存储,但不向任何人提交或通知。 -## Provider 契约 +## 提供方契约 子类实现 `writable`、`load()`、`persist(ns, section)`,可选择为一个本地用户可编辑文件重写 `documentPath` 与 `prepareDocument()`,并通过受保护的 `publish(doc)` 推入外部观察到的文档。基类 service init 在服务可注入前加载并发布一次文档;自有 init(watcher、连接)的 provider 先经 `yield* super[Service.init]()` 委托。publish 时每个已注册 namespace 独立重解析:非法分节保留该 namespace 的最后可用值并告警——热重载绝不拖垮进程;启动期与注册期校验则立即报错。 @@ -28,15 +28,15 @@ `settings/document-updated (ns, revision)` 在**原始**用户分节发生变化时触发,无论解析值是否随之改变。配置界面需要的是这一个:存入一个与组合 `base` 相同的覆盖值不会改变解析值,却改变了文档的说法(该字段从继承变成了覆盖),也推进了每个已打开编辑器所持有的 revision。监听器的收容方式与 `settings/updated` 相同。 -## Model Experience +## 模型体验 间接生效:消费插件从各自 namespace 解析影响模型的值(例如默认模型路由);效果由各消费者自己的文档描述。 -#### KV Cache effect +#### KV Cache 影响 无直接失效;把设置值折叠进请求前缀的消费者拥有该变更。 -## Known Limitations and Deferred Work +## 已知限制与暂缓事项 - **单一用户层** — 解析只认识 schema 默认值、一个组合 `base` 与一个用户文档;尚无 project/managed 分层或按值溯源。 - **`redactSecrets` 并非一条可被证明的协议边界**:walker 只跟随 `object`/`dict`/`array`,因此只能经由 union、intersection 或 transform 抵达的 `role('secret')` 会被**原样**返回,且 `secrets` 列表为空;而 `schema.toJSON()` 会把 secret 字段的 `.default(...)` 一并带给每个客户端。这两种情况都不会被拒绝;机密无法经由被遍历的容器抵达的 schema,绝不可注册到暴露于协议的 namespace 上。真正的答案是一个 fail-closed 的 `describeForWire()`——它拒绝自己无法证明安全的 schema,并对序列化信封与错误文本做净化——此项暂缓。 diff --git a/packages/skill/README.i18n.yaml b/packages/skill/README.i18n.yaml index 88c51eca62..2d424c61dd 100644 --- a/packages/skill/README.i18n.yaml +++ b/packages/skill/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/skill/README.md -README.md: 4fb41dda5d9f001f5d7c47a29f7743291c0b0822 -README.zh.md: ebe9ca40a5967d3167bee9f30ba3d3e9c417f636 +README.md: d10049ac3e741350fddb42f430b70f063da1d12d +README.zh.md: 67f2da6f75edecd180ff861122c6392684ed9bdb diff --git a/packages/skill/README.zh.md b/packages/skill/README.zh.md index ebe9ca40a5..67f2da6f75 100644 --- a/packages/skill/README.zh.md +++ b/packages/skill/README.zh.md @@ -1,13 +1,13 @@ -# skill/ - skill(技能)能力家族 +# skill/:skill(技能)能力家族 [English](README.md) | 中文 -可复用 agent(智能体)指令的规范能力 seam 由三个包组成:提供方注册表、本地实现,以及面向模型的目录/loader 消费方。全部均为**产品**包。 +本家族发现可复用的 agent(智能体)指令,并通过与提供方无关的目录和 loader 将其公开给模型。 | 包 | 职责 | ctx 键 | |---|---|---| -| `skill/` | 提供方注册表、优先级解析、完整/不完整目录快照和完整定义查找 | `ctx.skills` | -| `skill-local/` | 带目录成员关系监视的项目/自定义/用户文件系统提供方 | (注册到 `ctx.skills`) | -| `tool-skill/` | 初始目录和替换目录,以及面向模型的 `skill` loader | (注册到 `ctx.tools`) | +| [`skill/`](skill/README.md) | 定义 skill 提供方注册和查找 | `ctx.skills` | +| [`skill-local/`](skill-local/README.md) | 从本地文件系统发现 skill | 注册到 `ctx.skills` | +| [`tool-skill/`](tool-skill/README.md) | 发布 skill 目录和面向模型的 loader | 注册到 `ctx.tools` | -接口位于 `skill/skill/`。提供方同步注册,并通过 `ctx.skills` 执行异步发现;`tool-skill` 只消费该接口,因此嵌入式或远程提供方可替换或补充 `skill-local`,无需改变面向模型的契约。`agent-core` 默认加载该家族,但它仍然是核心控制主干之外的能力,与 [`bash/`](../bash/README.md)、[`fs/`](../fs/README.md)、[`web/`](../web/README.md) 和 [`subagent/`](../subagent/README.md) 并列。 +该能力位于核心控制主干之外,可以使用本地、嵌入式或远程提供方,而无需更改面向模型的契约。 diff --git a/packages/spill/README.i18n.yaml b/packages/spill/README.i18n.yaml index 78759f933d..95800ac03e 100644 --- a/packages/spill/README.i18n.yaml +++ b/packages/spill/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/spill/README.md -README.md: cb6f1e9af94ee5b0d0c06a8c4293e37e2d1eb856 -README.zh.md: e43d30e1038877467a8c42c12c878de19cfc24e2 +README.md: b96375b1a332cd6e3e4da9c8b1c98011aa97c8bc +README.zh.md: c8495068edf1035937c99198dfdb5db65c9a9e59 diff --git a/packages/spill/README.zh.md b/packages/spill/README.zh.md index e43d30e103..c8495068ed 100644 --- a/packages/spill/README.zh.md +++ b/packages/spill/README.zh.md @@ -1,15 +1,13 @@ -# spill/ - spill 存储能力家族 +# spill/:工具输出 spill 能力家族 [English](README.md) | 中文 -工具输出 spill 的能力 seam:一个抽象存储接口、一个本地文件系统实现,以及一个使用该实现的工具结果策略。全部均为**产品**包。 +本家族持久化过大的工具输出,并以有界预览和取回定位信息替换内联结果。 | 包 | 职责 | ctx 键 | |---|---|---| -| `spill/` | 抽象 spill 存储 seam(`saveText`:持久化过大的工具文本,返回定位信息与取回指引) | `ctx.spillStore` | -| `spill-local/` | 本地文件系统后端:名称可防止路径遍历的私有会话级文件 | (注册到 `ctx.spillStore`) | -| `spill-policy/` | `tools/post-execute` 策略:将过大的纯文本结果替换为预览和 spill 定位信息 | (无服务接口) | +| [`spill/`](spill/README.md) | 定义 spill 存储 | `ctx.spillStore` | +| [`spill-local/`](spill-local/README.md) | 在会话范围的本地文件中存储 spill 文本 | 注册到 `ctx.spillStore` | +| [`spill-policy/`](spill-policy/README.md) | 应用执行后 spill 策略 | 监听 `ctx.tools` | -接口位于 `spill/spill/`。这种拆分方式与 bash/fs 相同:seam 只负责存储,`spill-local` 负责文件系统机制,`spill-policy` 负责决定何时 spill 以及面向模型的通知。预览机制位于 [`util/retention`](../util/README.md);策略只组合两者,不会让任何一方承担对方的职责。 - -设计原理见[工具输出 spill Agent Note](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md),其中说明了为什么最终结果 spill 要与工具自行提前 spill(bash 流、subagent rollout)分离,以及为什么创建操作应由运行时 spill seam 而非面向模型的 `write` 工具承担。 +参见[工具输出 spill 决策](../../.agents/notes/implemented/architecture/2026-07-08-tool-output-spill-files.md),其中记录了存储、保留和工具自有输出处理之间的边界。 diff --git a/packages/storage/README.i18n.yaml b/packages/storage/README.i18n.yaml index 49c1028a10..58fc19a4f8 100644 --- a/packages/storage/README.i18n.yaml +++ b/packages/storage/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/storage/README.md -README.md: c9eca7355fd63e1023e9723486c86673e4c3574b -README.zh.md: 43449f0a7a72e48dbd8a6b5d731f130f04f2d780 +README.md: a5f9d0204b93445699d28b1285a4a18f0408930e +README.zh.md: 0710dfb57f96011755b253ad95a0f9d2c0923a70 diff --git a/packages/storage/README.zh.md b/packages/storage/README.zh.md index 43449f0a7a..0710dfb57f 100644 --- a/packages/storage/README.zh.md +++ b/packages/storage/README.zh.md @@ -2,13 +2,13 @@ [English](README.md) | 中文 -存储家族持久化会话事件日志之外的一切数据:命名后端与类型化数据形式在一个中心相接。设计记录:[领域 KV 存储 Agent Note](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md)。 +本家族通过具名后端和类型化数据形式,持久化会话事件日志以外的应用数据。 | 包 | 职责 | ctx key | |---|---|---| -| `storage/` | 中心:命名后端注册表 + 可通过合并扩展的数据形式挂载、后端分面词汇、共享一致性测试套件 | `ctx.storage` | -| `storage-json/` | JSON 后端:每个单元一个人类可读文件,以原子方式重写整个文件 | 注册后端 `json` | -| `storage-sqlite/` | SQLite 后端:一个数据库承载所有已路由单元,每行一个文档 | 注册后端 `sqlite` | -| `domain/` | 领域数据形式:经 zod 验证的记录、逐领域写入链、`domain/changed` 事件、按配置路由后端 | `ctx.storageDomain` + `ctx.storage.domain` | +| [`storage/`](storage/README.md) | 将已注册后端与类型化数据形式连接起来 | `ctx.storage` | +| [`storage-json/`](storage-json/README.md) | 在 JSON 文件中存储数据 | 注册后端 `json` | +| [`storage-sqlite/`](storage-sqlite/README.md) | 在 SQLite 中存储数据 | 注册后端 `sqlite` | +| [`storage-domain/`](storage-domain/README.md) | 提供经过验证的领域记录存储 | `ctx.storageDomain` | -每个后端拥有一种介质,并公开数据形状**分面**(目前为 `kv`;为未来的会话后端迁移预留追加日志分面)。每个后端插件都会在注册后发布内部生命周期服务;领域插件在公开自身服务前注入每个已配置的后端 key,因此配置树中的条目顺序不影响启动顺序。消费方绝不直接接触后端,而是注入 `storageDomain` 并通过它打开已声明的领域。 +消费方使用数据形式,而不是直接访问后端。[领域存储决策](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md)记录了该家族的设计。 diff --git a/packages/storage/storage/README.i18n.yaml b/packages/storage/storage/README.i18n.yaml index 44493d3f84..4378ae55ad 100644 --- a/packages/storage/storage/README.i18n.yaml +++ b/packages/storage/storage/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/storage/storage/README.md -README.md: 994285842925539e73f8f11780f19495f71d0280 -README.zh.md: 9e03b5cf09350d3af134e2c7b099f9379fa917f1 +README.md: bf827220afe0d8b1cbc53b3a12e6d0034004e1d2 +README.zh.md: 6c1a5b4c05f665cdedd16fa67b47c87a04737e7b diff --git a/packages/storage/storage/README.md b/packages/storage/storage/README.md index 9942858429..bf827220af 100644 --- a/packages/storage/storage/README.md +++ b/packages/storage/storage/README.md @@ -2,22 +2,13 @@ English | [中文](README.zh.md) -Storage hub (`ctx.storage`) for non-session data: a named backend registry plus mounted data-form facilities. The hub performs no IO itself — backends own media, data forms own semantics. Design and trade-offs: [domain KV storage Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md). +Storage hub (`ctx.storage`) for non-session data: a named backend registry plus mounted data-form facilities. The hub performs no IO itself — backends own media, and data forms own semantics. The [storage family overview](../README.md) maps those packages; the [domain KV storage Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md) records the design rationale. ## Shape - `ctx.storage.backend` — name → backend table. Multiple backends stay mounted side by side (`json`, `sqlite`); which backend serves a consumer is that consumer's configuration (the domain layer's route table), never a hub-global choice. `register()` returns the disposer; duplicate names and unknown lookups fail loud. - `ctx.storage.mount(form, facility)` / `ctx.storage.form(form)` — data-form mounting. `StorageForms` is merge-extensible; the domain layer merges `domain` and is reached as `ctx.storage.domain`. -- A backend owns one medium (file-tree root, database file) and exposes optional data-shape **facets** — `kv` today; an append-log facet is reserved for the future session-backend migration. `src/backend.ts` is the normative contract text; `tests/contract.ts` exports the shared conformance suite every backend runs. - -## Packages in this group - -| Package | Role | -| --- | --- | -| `dsh-storage` | The hub service + backend vocabulary + shared conformance suite | -| `dsh-storage-json` | JSON backend: one unit per human-readable file, atomic whole-file rewrite | -| `dsh-storage-sqlite` | SQLite backend: one database hosting all routed units, document-per-row | -| `dsh-storage-domain` | Domain data form (`ctx.storage.domain`): typed schemas, write chain, change events | +- A backend owns one medium and exposes the data-shape facets it supports. `kv` is the current facet; `src/backend.ts` owns its exact contract. ## Model Experience @@ -37,5 +28,5 @@ Independent of live requests: the hub never touches a request prefix, so it cann ## Known Limitations and Deferred Work -- **`kv` is the only data shape** — the append-log facet the future session-backend migration needs is reserved in the design note but not yet defined; backends currently have exactly one facet to implement. +- **`kv` is the only data shape** — backends currently have one facet to implement. - **Forms resolve lazily** — reading `ctx.storage.domain` before the domain plugin mounts throws `form-not-mounted`; assemblies order plugins accordingly (misconfiguration fails loud rather than silently deferring). diff --git a/packages/storage/storage/README.zh.md b/packages/storage/storage/README.zh.md index 9e03b5cf09..6c1a5b4c05 100644 --- a/packages/storage/storage/README.zh.md +++ b/packages/storage/storage/README.zh.md @@ -2,22 +2,13 @@ [English](README.md) | 中文 -非会话数据的存储中心(`ctx.storage`):命名后端注册表加已挂载的数据形式设施。中心自身不执行 IO:后端拥有介质,数据形式拥有语义。设计与取舍见[领域 KV 存储 Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md)。 +非会话数据的存储中心(`ctx.storage`):具名后端注册表加已挂载的数据形式设施。中心自身不执行 IO:后端拥有介质,数据形式拥有语义。[存储家族概述](../README.md)列出了这些包;[领域 KV 存储 Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md)记录了设计理由。 ## 结构 - `ctx.storage.backend`:名称 → 后端表。多个后端并排保持挂载(`json`、`sqlite`);为消费方提供服务的后端由该消费方自身的配置决定(领域层的路由表),绝非中心的全局选择。`register()` 返回资源释放函数;注册重复名称或查找未知名称时都会明确报错。 - `ctx.storage.mount(form, facility)`/`ctx.storage.form(form)`:数据形式挂载。`StorageForms` 可通过合并扩展;领域层合并 `domain`,并通过 `ctx.storage.domain` 访问。 -- 后端拥有一种介质(文件树根、数据库文件),并公开可选的数据形状**分面**:目前为 `kv`;为未来的会话后端迁移预留追加日志分面。`src/backend.ts` 是规范契约文本;`tests/contract.ts` 导出每个后端都会运行的共享一致性测试套件。 - -## 该分组中的包 - -| 包 | 职责 | -| --- | --- | -| `dsh-storage` | 中心服务 + 后端词汇 + 共享一致性测试套件 | -| `dsh-storage-json` | JSON 后端:每个单元一个人类可读文件,以原子方式重写整个文件 | -| `dsh-storage-sqlite` | SQLite 后端:一个数据库承载所有已路由单元,每行一个文档 | -| `dsh-storage-domain` | 领域数据形式(`ctx.storage.domain`):类型化 schema、写入链、变更事件 | +- 后端拥有一种介质,并公开其支持的数据形状**分面**。当前分面为 `kv`;其确切契约由 `src/backend.ts` 负责。 ## 模型体验 @@ -37,5 +28,5 @@ ## 已知限制与暂缓事项 -- **`kv` 是唯一的数据形状**:设计记录为未来的会话后端迁移预留了 append-log facet,但尚未定义;后端目前恰好只有一个 facet 需要实现。 +- **`kv` 是唯一的数据形状**:后端目前只有一个分面需要实现。 - **数据形式按需解析**:在领域插件挂载前读取 `ctx.storage.domain` 会抛出 `form-not-mounted`;组装会按相应顺序排列插件(错误配置会明确报错,而不是静默推迟处理)。 diff --git a/packages/subagent/README.i18n.yaml b/packages/subagent/README.i18n.yaml index e5dde20e9d..9cde7517ab 100644 --- a/packages/subagent/README.i18n.yaml +++ b/packages/subagent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/README.md -README.md: f9b04b4aa80b6feacf5d0d1fa4cf6b3b2aebc211 -README.zh.md: cbac36517e31573a19f3e5631f0068df430ea761 +README.md: 6aeb7fb1eaa9341dd72df614ca11d114f321fb83 +README.zh.md: a78cb365a8e96ad44c0c930c072372f88930906c diff --git a/packages/subagent/README.zh.md b/packages/subagent/README.zh.md index cbac36517e..a78cb365a8 100644 --- a/packages/subagent/README.zh.md +++ b/packages/subagent/README.zh.md @@ -2,20 +2,18 @@ [English](README.md) | 中文 -subagent seam 允许 agent(智能体)把工作委派给子 agent。与 [bash](../bash/README.md) 和 [llm](../llm/README.md) 能力家族一样,这也是一种能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)),但有一个关键差异:**多个提供方实现在同一上下文中共存,并按名称注册**,而不是采用 bash 的单实现形态。该注册表仿照 LLM(大语言模型)适配器注册表。 +本家族允许一个 agent(智能体)将工作委派给子 agent。多个具名提供方可在同一上下文中共存。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx 键 | |---|---|---| -| `subagent/` | subagent 服务:具名提供方注册表、词汇、持久化描述符与可继续子 agent 编排 | `ctx.subagents` | -| `subagent-inprocess/` | 共享进程内运行驱动器(不含提供方;每次运行使用一个清理 effect) | 无 | -| `subagent-spawn/` | 进程内后端:支持冷恢复的全新子 agent | (注册到 `ctx.subagents`) | -| `subagent-fork/` | 进程内后端:以父 agent 已完成轮次的前缀作为初始内容、支持冷恢复的子 agent | (注册到 `ctx.subagents`) | -| `subagent-acp/` | 进程外后端:在 spawn 出的子进程中运行并通过 ACP(Agent Client Protocol)驱动的一次性子 agent | (注册到 `ctx.subagents`) | -| `subagent-dsh-sdk/` | 进程外后端:在 spawn 出的子进程中运行的子 harness 运行时,经 TypeScript SDK 客户端走 stdio JSON-RPC 驱动 | (注册到 `ctx.subagents`) | -| `tool-subagent/` | 面向模型的 `subagent` 委派工具,基于 `ctx.subagents` | (注册到 `ctx.tools`) | -| `tool-subagent-control/` | 基于 `ctx.subagents`、可选且全局名称唯一的 `send_message` 与 `list_agents` 工具 | (注册到 `ctx.tools`) | -| `tool-subagent-report/` | 子级作用域的 `report` 返回通道,用于可继续的进程内子级 | (注册到每个子级作用域) | +| [`subagent/`](subagent/README.md) | 定义提供方注册、委派和继续执行 | `ctx.subagents` | +| [`subagent-inprocess/`](subagent-inprocess/README.md) | 提供共享的进程内运行驱动器 | 无 | +| [`subagent-spawn/`](subagent-spawn/README.md) | 启动全新的进程内子 agent | 注册到 `ctx.subagents` | +| [`subagent-fork/`](subagent-fork/README.md) | 从父 agent 已完成的历史记录启动进程内子 agent | 注册到 `ctx.subagents` | +| [`subagent-acp/`](subagent-acp/README.md) | 通过 ACP(Agent Client Protocol)启动进程外子 agent | 注册到 `ctx.subagents` | +| [`subagent-dsh-sdk/`](subagent-dsh-sdk/README.md) | 通过 TypeScript SDK 启动进程外 Harness 子 agent | 注册到 `ctx.subagents` | +| [`tool-subagent/`](tool-subagent/README.md) | 向模型公开委派操作 | 注册到 `ctx.tools` | +| [`tool-subagent-control/`](tool-subagent-control/README.md) | 向模型公开子级消息发送和列举操作 | 注册到 `ctx.tools` | +| [`tool-subagent-report/`](tool-subagent-report/README.md) | 提供从子级到父级的报告通道 | 注册到子级作用域 | -接口和继续执行编排位于 `subagent/subagent/`。一次性提供方的 `start` 分发不依赖持久化;内部继续执行管理器以一个 Session 和至多一个进程内 Activation 的形式拥有每个持久的可继续子 agent。该管理器不绑定任何 Task,仅在 Agent 服务存在期间存在,并为每次继续执行操作分别解析持久化服务。进程内 `subagent-spawn` / `subagent-fork` 后端共享 `subagent-inprocess` 驱动器(一个自身不含提供方的库:两者都依赖它,彼此不依赖),进程外 `subagent-acp` / `subagent-dsh-sdk` 后端则经由 [`subprocess/`](../subprocess/README.md) seam spawn 其子进程(共享的凭据清除、以进程树为范围的拆卸、dispose(资源释放)阶梯)。测试只用包内 fixture(测试前置数据)替换子 agent 边界。 - -设计理由见 [.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)、[.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md) 和 [.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)。 +参见[能力家族](../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md)、[可继续执行的子级](../../.agents/notes/implemented/feature/2026-07-21-continuable-background-subagents.md)和[控制工具](../../.agents/notes/implemented/simplification/2026-07-26-merge-subagent-control-service.md)决策。 diff --git a/packages/subagent/subagent-acp/README.i18n.yaml b/packages/subagent/subagent-acp/README.i18n.yaml index 4e20ab3cc0..2f5532e9f4 100644 --- a/packages/subagent/subagent-acp/README.i18n.yaml +++ b/packages/subagent/subagent-acp/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent-acp/README.md -README.md: efcc77c442714a83631d009efb712fa7b8f5dfa0 -README.zh.md: baeecb623d000778a6beb3fdf90dc7ef3ba365bc +README.md: 83a5f60414528bdb768ffccd29f3091793f44b6b +README.zh.md: 4ea8daef9341897463f3dbedca86fd2c83b45514 diff --git a/packages/subagent/subagent-dsh-sdk/README.i18n.yaml b/packages/subagent/subagent-dsh-sdk/README.i18n.yaml index 311cd0c12a..297c86d78d 100644 --- a/packages/subagent/subagent-dsh-sdk/README.i18n.yaml +++ b/packages/subagent/subagent-dsh-sdk/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent-dsh-sdk/README.md -README.md: a0c0811b585b69a634e49a2c838137655f316585 -README.zh.md: 2d699dec90f3098b799da912ed184ae2429df0bd +README.md: 266825daaa951f3207a3c1b4a21f525fc5fb451a +README.zh.md: d68d3ea7a9bac5e658ddd52a9e00fa674ac2c45d diff --git a/packages/subagent/subagent/README.i18n.yaml b/packages/subagent/subagent/README.i18n.yaml index 9a455a6ce8..c8aefc7533 100644 --- a/packages/subagent/subagent/README.i18n.yaml +++ b/packages/subagent/subagent/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subagent/subagent/README.md -README.md: e54f0b98ec3649cec428a47026e6657a9749608b -README.zh.md: 074117cdbad52d754a449a0b6adc10daffa0c2f1 +README.md: a21aa6ae2822d68d513fd9409d77b3f3bf74a7a3 +README.zh.md: 3caa612aefcdac4f1dcdbcf4a3c1b81adc52c3d3 diff --git a/packages/subagent/subagent/README.md b/packages/subagent/subagent/README.md index e54f0b98ec..a21aa6ae28 100644 --- a/packages/subagent/subagent/README.md +++ b/packages/subagent/subagent/README.md @@ -4,21 +4,7 @@ English | [中文](README.zh.md) The subagent seam lets one agent delegate work to a child through a named provider. Callers use one service API (`ctx.subagents`); providers decide whether the child runs in this process, in another process, or through a future transport. -## Package roles - -The family separates the stable interface from implementations and model-facing tools: - -| Package | Role | -|---|---| -| `@deepseek-ai/dsh-subagent` | Provider registry, request/result/descriptor types, lifecycle events, and continuable-child orchestration. | -| `@deepseek-ai/dsh-subagent-spawn` | Fresh in-process child; supports continuable children. | -| `@deepseek-ai/dsh-subagent-fork` | In-process child seeded with completed parent turns; supports continuable children. | -| `@deepseek-ai/dsh-subagent-acp` | Fresh out-of-process ACP child (one-shot). | -| `@deepseek-ai/dsh-tool-subagent` | Model-facing delegation tool over one configured provider. | -| `@deepseek-ai/dsh-tool-subagent-control` | The globally named `send_message` follow-up tool. | -| `@deepseek-ai/dsh-tool-subagent-report` | Child-scoped return channel to the direct parent. | - -Multiple providers may coexist under different names. This lets a deployment expose, for example, a cheap in-process child and an isolated ACP child without changing the service contract. +The [subagent family overview](../README.md) maps implementations and model-facing consumers. This package owns the provider registry, shared request and result contracts, durable descriptors, and continuable-child orchestration. Multiple named providers may coexist behind that contract. ## Service API diff --git a/packages/subagent/subagent/README.zh.md b/packages/subagent/subagent/README.zh.md index 074117cdba..3caa612aef 100644 --- a/packages/subagent/subagent/README.zh.md +++ b/packages/subagent/subagent/README.zh.md @@ -4,21 +4,7 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委派给子 agent。调用方使用统一的服务 API(`ctx.subagents`);提供方决定子 agent 在当前进程、另一进程还是未来的传输之上运行。 -## 包角色 - -该系列包把稳定接口与实现、面向模型的工具分开: - -| 包 | 角色 | -|---|---| -| `@deepseek-ai/dsh-subagent` | 提供方注册表、请求/结果/描述符类型、生命周期事件和可继续子 agent 编排。 | -| `@deepseek-ai/dsh-subagent-spawn` | 全新的进程内子 agent;支持可继续子 agent。 | -| `@deepseek-ai/dsh-subagent-fork` | 以父 agent 已完成轮次作为初始内容的进程内子 agent;支持可继续子 agent。 | -| `@deepseek-ai/dsh-subagent-acp` | 全新的进程外 ACP(Agent Client Protocol)子 agent(一次性)。 | -| `@deepseek-ai/dsh-tool-subagent` | 基于一个已配置提供方、面向模型的委派工具。 | -| `@deepseek-ai/dsh-tool-subagent-control` | 全局具名 `send_message` 后续操作工具。 | -| `@deepseek-ai/dsh-tool-subagent-report` | 子级作用域的返回通道,指向直接父级。 | - -多个提供方可以使用不同名称共存。因此,部署可以同时公开低成本的进程内子 agent 和隔离的 ACP 子 agent,而无需改变服务契约。 +[subagent 家族概述](../README.md)列出了实现和面向模型的消费方。本包负责提供方注册表、共享请求和结果契约、持久描述符以及可继续子级编排。多个具名提供方可以在该契约背后共存。 ## 服务 API @@ -62,7 +48,7 @@ subagent seam 允许一个 agent(智能体)通过具名提供方把工作委 该 seam 拥有实现和消费方共享的深度词汇:`AgentOptions.subagentDepth` 声明、`assertSubagentMaxDepth` 和 `delegationDepthOf(agent)`。持久化的 `SessionHeader.delegationDepth` 具有权威性且单调:运行时选项可以加深计数,但绝不能降低它,因此恢复后的子 agent 不会被重新计为顶层。 -`inheritsParentContext` 只用于描述,不能强制执行。它仅说明子 agent 是否能看到父级已完成的对话历史(`fork` 可以;`spawn` 和 ACP 不可以),不表示是否继承工具、服务或权限。 +`inheritsParentContext` 只用于描述,不能强制执行。它仅说明子 agent 是否能看到父级已完成的对话历史(`fork` 可以;`spawn` 和 ACP(Agent Client Protocol)不可以),不表示是否继承工具、服务或权限。 ## 一次性所有权与生命周期 diff --git a/packages/subprocess/README.i18n.yaml b/packages/subprocess/README.i18n.yaml index 83491c5d82..9c0cb0c25d 100644 --- a/packages/subprocess/README.i18n.yaml +++ b/packages/subprocess/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/subprocess/README.md -README.md: 64e4740c7ac2706e45bb3517891504bf31a6109b -README.zh.md: e614441268d34e1e58960cc6ed86bb4495810743 +README.md: 187ea5b778a4bc1f9c3c9121adb18bda11cc7b58 +README.zh.md: dd3a975daec014131877d7b1523810bd932619d8 diff --git a/packages/subprocess/README.zh.md b/packages/subprocess/README.zh.md index e614441268..dd3a975dae 100644 --- a/packages/subprocess/README.zh.md +++ b/packages/subprocess/README.zh.md @@ -2,11 +2,11 @@ [English](README.md) | 中文 -这里集中提供受管子进程树的 spawn 能力:完整指定的 spawn spec,采用 Node 风格、按流划分的 stdio 处置方式(disposition),包括原始管道、inherit、附带 spill 文件的有界尾部保留收集;harness 中所有 spawn 调用方共用的凭据清除机制;基于偏移量的增量读取;以进程树为范围、带 SIGTERM→宽限期→SIGKILL 升级的信号发送;以及协作式 dispose(资源释放)阶梯。命令默认值补全、shell 语义、时限、协议分帧与呈现留在消费方:[bash 执行器](../bash/README.md)、[LSP 主机](../lsp/README.md)与 [ACP(Agent Client Protocol)subagent 后端](../subagent/README.md)。参见[subprocess seam Agent Note](../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md)。 +本家族通过显式的进程生命周期服务运行宿主子进程。 -| 包 | ctx 键 | 角色 | +| 包 | 职责 | 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 | +| [`subprocess/`](subprocess/README.md) | 定义子进程启动、流、终止和 dispose(资源释放)契约 | `ctx.subprocess` | +| [`subprocess-local/`](subprocess-local/README.md) | 实现本地进程树执行 | 注册到 `ctx.subprocess` | -即使消费方重载,进程生命周期仍由服务负责管理;消费方负责定义进程的含义(一条 bash 命令、未来的非 shell 运行器),以及决定塑造该进程的每一项默认值。 +服务负责进程生命周期;每个消费方负责进程执行的工作以及所应用的默认值。 diff --git a/packages/support/README.i18n.yaml b/packages/support/README.i18n.yaml index f6cacbfe54..1b392a1729 100644 --- a/packages/support/README.i18n.yaml +++ b/packages/support/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/support/README.md -README.md: b9550fd54feb36448227faae8485fe8b6dbf4fb0 -README.zh.md: 1b7464e0cab4460e587f2b5c8afa58d28116bab1 +README.md: 15cb82d3d76a1241af8b40e1c8536292618409a9 +README.zh.md: ff8595b47f1a1220912dc77a072463adf74f7bf9 diff --git a/packages/support/README.zh.md b/packages/support/README.zh.md index 1b7464e0ca..ff8595b47f 100644 --- a/packages/support/README.zh.md +++ b/packages/support/README.zh.md @@ -1,16 +1,16 @@ -# support/:开发/测试/示例基础设施 +# support/:开发和测试基础设施 [English](README.md) | 中文 -这些包用于开发、测试和示例,而非作为产品 API 发布。它们是实际的工作区包(具备类型、经过测试,并受覆盖率门禁约束),但具有**较低的兼容性预期**:当其背后的开发需求变化时,它们可以改变或被移除,无需像产品包那样谨慎执行弃用流程。 +这些包为仓库开发、测试和示例提供支持,而不是产品 API。其兼容性取决于所服务的开发需求。 -| 包 | 职责 | ctx 键 | -|---|---|---| -| `acp-snapshot/` | ACP(Agent 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(Server-Sent Events)故障服务器与 CLI(命令行界面),用于 LLM(大语言模型)恢复测试 | (独立服务器和测试库) | -| `llm-replay/` | 录制/回放适配器:通过已记录的会话 JSONL 对 `llm/stream` 进行短路处理(无密钥快照测试) | (监听 `llm/stream`) | +| 包 | 职责 | +|---|---| +| [`acp-snapshot/`](acp-snapshot/README.md) | 提供 ACP(Agent Client Protocol)快照测试工具包 | +| [`agent-loop-testkit/`](agent-loop-testkit/README.md) | 为 AgentLoop 测试挂载共享先决条件 | +| [`invariants/`](invariants/README.md) | 运行开发期运行时契约断言 | +| [`loader-smoke/`](loader-smoke/README.md) | 启动由 Loader 组合的应用以执行冒烟测试 | +| [`llm-mock-server/`](llm-mock-server/README.md) | 提供确定性的 OpenAI 兼容故障服务器 | +| [`llm-replay/`](llm-replay/README.md) | 为无密钥测试和演示回放已记录的模型响应 | -`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/` 转入产品分组。 +当一个包获得产品契约和产品消费方时,它会移出 `support/`。 diff --git a/packages/support/acp-snapshot/README.i18n.yaml b/packages/support/acp-snapshot/README.i18n.yaml index 362de9fbd1..9f70a479ca 100644 --- a/packages/support/acp-snapshot/README.i18n.yaml +++ b/packages/support/acp-snapshot/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/support/acp-snapshot/README.md -README.md: c8fe6907848a7b661c4bfb60c871f077753fda8b -README.zh.md: 0aa28f4b2c3df1a30e5fd33a80401712f81d6614 +README.md: ff8b89437703e0d63542f2a004f9b0929171010e +README.zh.md: 582285363f6556fcc68d783e2ceedc7eb007dafd diff --git a/packages/support/acp-snapshot/README.md b/packages/support/acp-snapshot/README.md index c8fe690784..ff8b894377 100644 --- a/packages/support/acp-snapshot/README.md +++ b/packages/support/acp-snapshot/README.md @@ -59,7 +59,7 @@ Every scenario compares `stdout.expected.jsonl` with cwd-rooted separators canon The example also ships a `cordis.snapshot.yml` replay overlay next to its `cordis.yml` (the bin swaps them under `DSH_SNAPSHOT=replay` — [single-source replay config Agent Note](../../../.agents/notes/archived/testing/2026-07-04-single-source-acp-replay-config.md)); replay fixtures are served by [`dsh-llm-replay`](../llm-replay/README.md), which this package points at via the `DSH_SNAPSHOT_*` env vars it sets on the child. `pnpm run test:snapshot:record` calls the live LLM and rewrites the recorded scenarios' model fixtures; `pnpm run test:snapshot:refresh` stays keyless, runs the replay overlay, and rewrites stdout, comparable session-log expected outputs, and owned prompt and tool-schema sidecars from the committed model scripts. Fixture roles, record/replay/refresh semantics, and scenario-table fields are documented on `Scenario` and in the [snapshot Agent Note](../../../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md). -Constraints: `suite.ts` and `harness.ts` import vitest (the harness polls its durable-boundary waits through `vi.waitFor`), so the package entry is importable only inside a vitest run (the launcher and normalizers have no such dependency but ship from the same entry). The launcher and suite factory are ACP-specific by design — the launcher speaks the SDK's `ClientSideConnection` — while the normalizers are transport-neutral session-log/text helpers also consumed by the TUI snapshot suite and the web browser e2e lane. Input scripts cover initialization, fresh-session creation, text prompting, cancellation, expected RPC failures, and durable turn-boundary waits. Permission round-trips are a FIFO queue of option-kind selections (`allow_once`, `reject_once`, …) mapped to the agent-issued `optionId`; an absent or exhausted queue answers `cancelled`, and an unoffered kind rejects the run. +Constraints: `suite.ts` and `harness.ts` import vitest (the harness polls its durable-boundary waits through `vi.waitFor`), so the package entry is importable only inside a vitest run (the launcher and normalizers have no such dependency but ship from the same entry). The launcher and suite factory are ACP-specific by design — the launcher speaks the SDK's `ClientSideConnection` — while the normalizers are transport-neutral session-log/text helpers also consumed by the Web browser e2e lane. Input scripts cover initialization, fresh-session creation, text prompting, cancellation, expected RPC failures, and durable turn-boundary waits. Permission round-trips are a FIFO queue of option-kind selections (`allow_once`, `reject_once`, …) mapped to the agent-issued `optionId`; an absent or exhausted queue answers `cancelled`, and an unoffered kind rejects the run. ## Model Experience diff --git a/packages/support/acp-snapshot/README.zh.md b/packages/support/acp-snapshot/README.zh.md index 0aa28f4b2c..582285363f 100644 --- a/packages/support/acp-snapshot/README.zh.md +++ b/packages/support/acp-snapshot/README.zh.md @@ -59,7 +59,7 @@ defineAcpSnapshotSuite({ 示例还发布 `cordis.snapshot.yml` 回放 overlay,位于 `cordis.yml` 旁边(bin 在 `DSH_SNAPSHOT=replay` 下交换它们,见[单源回放配置 Agent Note](../../../.agents/notes/archived/testing/2026-07-04-single-source-acp-replay-config.md));回放 fixture 由 [`dsh-llm-replay`](../llm-replay/README.md) 提供,该包通过对子级设置的 `DSH_SNAPSHOT_*` env var 指向它。`pnpm run test:snapshot:record` 调用实时 LLM,并重写已记录场景的模型 fixture;`pnpm run test:snapshot:refresh` 保持无密钥,运行回放 overlay,并从已提交模型脚本重写 stdout、可比较会话日志预期输出,以及各 pin 自有的提示词与工具 schema sidecar。Fixture 角色、录制/回放/刷新语义和场景表字段记录在 `Scenario` 以及[快照 Agent Note](../../../.agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md) 中。 -约束:`suite.ts` 与 `harness.ts` 导入 vitest(harness 通过 `vi.waitFor` 轮询其持久边界等待),因此包入口只能在 vitest 运行中导入(启动器和规范化器没有此依赖,但从同一入口发布)。启动器和套件工厂按设计专用于 ACP,启动器使用 SDK 的 `ClientSideConnection`;规范化器是与传输无关的会话日志/文本辅助工具,还由 TUI 快照套件和 web 浏览器 e2e lane 消费。输入脚本覆盖初始化、新建会话、文本提示、取消、预期 RPC 失败和持久轮次边界等待。权限往返是选项类别选择(`allow_once`、`reject_once`等)的 FIFO 队列,映射到 agent 发出的 `optionId`;缺少或耗尽的队列回答 `cancelled`,未提供类别会拒绝运行。 +约束:`suite.ts` 与 `harness.ts` 导入 vitest(harness 通过 `vi.waitFor` 轮询其持久边界等待),因此包入口只能在 vitest 运行中导入(启动器和规范化器没有此依赖,但从同一入口发布)。启动器和套件工厂按设计专用于 ACP,启动器使用 SDK 的 `ClientSideConnection`;规范化器是与传输无关的会话日志/文本辅助工具,还由 Web 浏览器 e2e lane 消费。输入脚本覆盖初始化、新建会话、文本提示、取消、预期 RPC 失败和持久轮次边界等待。权限往返是选项类别选择(`allow_once`、`reject_once` 等)的 FIFO 队列,映射到 agent 发出的 `optionId`;缺少或耗尽的队列回答 `cancelled`,未提供类别会拒绝运行。 ## 模型体验 diff --git a/packages/support/llm-replay/README.i18n.yaml b/packages/support/llm-replay/README.i18n.yaml index 9382698c66..a4729b2e69 100644 --- a/packages/support/llm-replay/README.i18n.yaml +++ b/packages/support/llm-replay/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/support/llm-replay/README.md -README.md: 85aa56705929e7630e4cfb6c2a3c9cbbd0d843a6 -README.zh.md: dfa43cf78c2b584fe4868603feb3fbcfb909e099 +README.md: ee062d0c2804905f33f1ff476d12bb6dd57666e5 +README.zh.md: ab3420d9500a6ca77f04a2ad96095f8883aeb874 diff --git a/packages/support/llm-replay/README.md b/packages/support/llm-replay/README.md index 85aa567059..ee062d0c28 100644 --- a/packages/support/llm-replay/README.md +++ b/packages/support/llm-replay/README.md @@ -4,7 +4,7 @@ English | [中文](README.zh.md) A replay LLM plugin for keyless snapshot tests. It yields model streams reconstructed from a recorded **session JSONL** fixture, so a test can boot the real agent against a fixed model transcript with no API key. With `providers` configured it registers a replay-only adapter whose catalog is available to scenarios that exercise model discovery; without `providers` it installs the catch-all `llm/stream` waterfall used by tests that do not need discovery. -Its consumers are the ACP, headless `stream-json`, and TUI snapshot suites plus the web browser e2e lane. Loader-driven suites mount this plugin in place of a real LLM adapter; the web lane installs it directly to retain the teardown consumption handle. Keeping derivation and replay here places that logic under the per-file 100% coverage gate on `packages/*/src`. +Its consumers are the ACP and headless `stream-json` snapshot suites plus the Web browser e2e lane. Loader-driven suites mount this plugin in place of a real LLM adapter; the Web lane installs it directly to retain the teardown consumption handle. ## How the fixture works diff --git a/packages/support/llm-replay/README.zh.md b/packages/support/llm-replay/README.zh.md index dfa43cf78c..ab3420d950 100644 --- a/packages/support/llm-replay/README.zh.md +++ b/packages/support/llm-replay/README.zh.md @@ -4,7 +4,7 @@ 用于无密钥快照测试的 LLM(大语言模型)回放插件。它根据已记录的**会话 JSONL** fixture(测试前置数据)重建模型流,使测试无需 API 密钥即可针对固定的模型 transcript(文本记录)启动真实 agent(智能体)。配置 `providers` 后,它会注册仅用于回放的适配器,其模型目录可供测试模型发现功能的场景使用;未配置 `providers` 时,它会安装无需模型发现功能的测试所用 catch-all `llm/stream` waterfall(瀑布式事件)。 -其消费方包括 ACP(Agent Client Protocol)、headless `stream-json` 和 TUI 快照套件,以及 Web 浏览器 e2e 流水线。Loader 驱动的套件使用此插件替代真实 LLM 适配器;Web 流水线直接安装它,以保留清理阶段的消费检查句柄。将派生和回放逻辑放在此处,可使其受 `packages/*/src` 的逐文件 100% 覆盖率门禁约束。 +其消费方包括 ACP(Agent Client Protocol)与 headless `stream-json` 快照套件,以及 Web 浏览器 e2e 流水线。Loader 驱动的套件使用此插件替代真实 LLM 适配器;Web 流水线直接安装它,以保留清理阶段的消费检查句柄。 ## fixture 的工作方式 diff --git a/packages/tasks/README.i18n.yaml b/packages/tasks/README.i18n.yaml index a8de1542ee..38cd47299c 100644 --- a/packages/tasks/README.i18n.yaml +++ b/packages/tasks/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/tasks/README.md -README.md: 9bafe5633bb7e57a5404ffb41fad04b621832b6d -README.zh.md: 37b0aeeddcaafd872f906eb2c35e75345d904b99 +README.md: 05ed9c439684337c45abe11f03c861a1b795b2ac +README.zh.md: 16fe6c0a2b183b719cb6aa8ac068e34d51832a2b diff --git a/packages/tasks/README.zh.md b/packages/tasks/README.zh.md index 37b0aeeddc..16fe6c0a2b 100644 --- a/packages/tasks/README.zh.md +++ b/packages/tasks/README.zh.md @@ -2,12 +2,12 @@ [English](README.md) | 中文 -这是后台任务 id、所有者隔离、读取、取消、等待和完成通知的共用归属位置。Bash、subagent(子 agent)及未来的长时间运行工具共用一套面向模型的协议。参见[后台任务运行时 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)。 +本家族为长时间运行的工具提供一套按所有者隔离的后台任务协议,用于观察、取消、等待和完成通知。 -| 包 | ctx 键 | 角色 | +| 包 | 职责 | ctx 键 | |---|---|---| -| [`tasks`](tasks/README.md)(`@deepseek-ai/dsh-tasks`) | `ctx.tasks` | 注册表 seam:带品牌类型的 `-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`、完成通知注入和后台工作习惯提示词段落 | +| [`tasks/`](tasks/README.md) | 定义任务注册表和生命周期契约 | `ctx.tasks` | +| [`tasks-local/`](tasks-local/README.md) | 实现进程本地任务注册表 | 注册到 `ctx.tasks` | +| [`tool-tasks/`](tool-tasks/README.md) | 向模型公开任务控制和完成通知 | 注册到 `ctx.tools` | -生产方或接口重载时,状态仍由注册表持有;工具包负责呈现。生产方通过 `ctx.tasks.start` 注册执行钩子,并自行决定其配置是否公开 `run_in_background`。 +参见[后台任务运行时](../../.agents/notes/implemented/architecture/2026-06-20-generic-long-running-tool-runtime.md)和[任务注册表](../../.agents/notes/implemented/architecture/2026-07-26-task-registry-seam.md)决策。 diff --git a/packages/telemetry/README.i18n.yaml b/packages/telemetry/README.i18n.yaml index 1ca80fbd0c..0d2a11ab23 100644 --- a/packages/telemetry/README.i18n.yaml +++ b/packages/telemetry/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/telemetry/README.md -README.md: 944cb3f9bac6169feddf8b49bc481cfbe7c6fa9d -README.zh.md: dc764799db6398d8e56d2635414c036c8cae8a6a +README.md: c390493d4053f9f532c30c3c2291d833554d6a99 +README.zh.md: 846a3e276aeaeda7e3456f4e4d4bea577d224a89 diff --git a/packages/telemetry/README.zh.md b/packages/telemetry/README.zh.md index dc764799db..846a3e276a 100644 --- a/packages/telemetry/README.zh.md +++ b/packages/telemetry/README.zh.md @@ -1,10 +1,12 @@ -# telemetry/ +# telemetry/:会话遥测能力家族 [English](README.md) | 中文 -面向外部的会话上报:遥测(telemetry)seam 及其 OpenTelemetry 后端。整套设计固定在[复活 Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)中:边界公理(harness 的职责止于 `emit()`,投递由上报 SDK 负责)、`telemetry/record` waterfall(瀑布式事件;脱敏规则由部署方挂载,seam 自身不带任何规则)、固定分片投影、handoff 游标,以及运维记录通道。 +本家族将会话活动投影为外发遥测,并将投递委派给配置的上报后端。 | 包 | 职责 | |---|---| -| [`@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)原样配置。 | +| [`session-telemetry/`](session-telemetry/README.md) | 定义捕获、脱敏、投影和后端投递 | +| [`session-telemetry-otel/`](session-telemetry-otel/README.md) | 通过 OpenTelemetry 日志投递遥测 | + +[遥测决策](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)记录了上报边界。 diff --git a/packages/timeout/README.i18n.yaml b/packages/timeout/README.i18n.yaml index 45143c1014..33a7a5b26d 100644 --- a/packages/timeout/README.i18n.yaml +++ b/packages/timeout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/timeout/README.md -README.md: 2a75e4d54aa1f518858d97e2f4c7d09a23a80a70 -README.zh.md: 40704d9c9c9ef659fe3922876b1de2cf020fe4be +README.md: d9d8e8a829d8d466d1cc3e42a6c32ac4b3db428b +README.zh.md: c5920258ade7022fb5b28a351ba060eb56e8bc3c diff --git a/packages/timeout/README.zh.md b/packages/timeout/README.zh.md index 40704d9c9c..c5920258ad 100644 --- a/packages/timeout/README.zh.md +++ b/packages/timeout/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -工具调用超时策略插件。它是单一 **产品**包:它是 `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/`](timeout-policy/README.md) | 强制执行配置的逐工具调用截止时间 | -超时被拆分为三层:[`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` 后端超时,不经过此策略。 +纯计时原语位于 [`util/timeout`](../util/timeout/README.md)。参见[超时库决策](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md)。 diff --git a/packages/todo/README.i18n.yaml b/packages/todo/README.i18n.yaml index 859de5e095..c75e09350a 100644 --- a/packages/todo/README.i18n.yaml +++ b/packages/todo/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/todo/README.md -README.md: da85a5573507cd8bb1ac52f9614225819a479baf -README.zh.md: 5b83fe4d2d59ff6401b6a455d8d5977d6a5e5122 +README.md: 66abf18131ee53d87e933757c40117841e94b07f +README.zh.md: 38a6b3c22653b8e22cc0b693eb666620513a3d8c diff --git a/packages/todo/README.md b/packages/todo/README.md index da85a55735..66abf18131 100644 --- a/packages/todo/README.md +++ b/packages/todo/README.md @@ -2,10 +2,10 @@ English | [中文](README.zh.md) -The model-facing todo tool. A single **product** package — there is no interface/implementation seam here, because the list is single-owner session state (one agent session owns its own list), not a swappable capability. +The model-facing todo capability. It is a single **product** package because one agent session owns the list; there is no replaceable provider seam. | Package | Role | ctx key | |---|---|---| -| `tool-todo/` | Model-facing `todo_write` tool; writes the whole list to the session log (`todo/write`) | (registers on `ctx.tools`) | +| [`tool-todo/`](tool-todo/README.md) | Stores and exposes the session's todo list. | (registers on `ctx.tools`) | -The list lives on the event-sourced session log (`SessionEventMap['todo/write']`, owned by [`dsh-session`](../core/session)); this package is the thin consumer that appends the snapshot. Host/client runtimes render the durable list from session events. +The child README owns the tool, persistence, and rendering contract. diff --git a/packages/todo/README.zh.md b/packages/todo/README.zh.md index 5b83fe4d2d..38a6b3c226 100644 --- a/packages/todo/README.zh.md +++ b/packages/todo/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -面向模型的 todo 工具。它是单一 **产品**包(package):这里没有接口/实现 seam,因为该列表是由单一所有者管理的会话状态(每个 agent(智能体)会话拥有自己的列表),而非可替换能力。 +面向模型的 todo 能力。它是单一**产品**包,因为一个 agent(智能体)会话拥有该列表;不存在可替换的提供方 seam。 | 包 | 职责 | ctx 键 | |---|---|---| -| `tool-todo/` | 面向模型的 `todo_write` 工具;将完整列表写入会话日志(`todo/write`) | (注册到 `ctx.tools`) | +| [`tool-todo/`](tool-todo/README.md) | 存储并公开会话的 todo 列表。 | (注册到 `ctx.tools`) | -列表存在于事件溯源会话日志中(`SessionEventMap['todo/write']`,由 [`dsh-session`](../core/session) 拥有);本包是追加快照的轻量消费方。宿主/客户端运行时会根据会话事件渲染该持久化列表。 +子级 README 负责工具、持久化和渲染契约。 diff --git a/packages/typert/README.i18n.yaml b/packages/typert/README.i18n.yaml index 0f3581331e..cf44d9aace 100644 --- a/packages/typert/README.i18n.yaml +++ b/packages/typert/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/typert/README.md -README.md: d11fd8f57245379d67d2a1cdcca334f0032db469 -README.zh.md: 696f572985f6cfe09c8855455b6b45ddb1efd4fe +README.md: ad9f843e48be0e3be85921ed8fd3ca4e2c327160 +README.zh.md: 0d4be016b766178b54f7e269583fa4200e3eb24b diff --git a/packages/typert/README.zh.md b/packages/typert/README.zh.md index 696f572985..0d4be016b7 100644 --- a/packages/typert/README.zh.md +++ b/packages/typert/README.zh.md @@ -2,10 +2,10 @@ [English](README.md) | 中文 -Typert 将源代码分析、运行时存储和 Loader 发现机制拆分为彼此独立的包。 +Typert 将源代码分析、运行时存储和 Loader 发现机制分离。 | 包 | 职责 | Cordis 键 | |---|---|---| -| [`registry/`](registry/README.md) | 运行时包反射和实时 Zod schema 注册表 | `ctx.typert` | -| [`loader/`](loader/README.md) | 发现 Loader 条目并注册所生成的宿主产物 | 使用 `ctx.loader`、`ctx.typert` | -| [`generator/`](generator/README.md) | 与编译器无关的类型分析和产物生成 | 构建时库 | +| [`registry/`](registry/README.md) | 存储运行时包反射和 schema | `ctx.typert` | +| [`loader/`](loader/README.md) | 发现 Loader 条目并注册生成的宿主产物 | 消费 `ctx.loader` 和 `ctx.typert` | +| [`generator/`](generator/README.md) | 从源代码类型生成运行时产物 | 构建时库 | diff --git a/packages/ui/README.i18n.yaml b/packages/ui/README.i18n.yaml index 541de5b199..fba1f95c52 100644 --- a/packages/ui/README.i18n.yaml +++ b/packages/ui/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/README.md -README.md: 76ca80e5685f70e73a6c46fe8d980f951b965ed3 -README.zh.md: 3958b0bdfb5d8cbab82f9fecfe54d12d462738ea +README.md: 15754410a4a81eb3fc898dd55269ddd1637e1dab +README.zh.md: 4023b80085998f57ed321bfda3a0abdd08b70a28 diff --git a/packages/ui/README.md b/packages/ui/README.md index 76ca80e568..15754410a4 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -6,16 +6,12 @@ Human-facing channels and the out-of-process SDK server. These are **product** p | Package | Role | ctx key | |---|---|---| -| `commands/` | Human-command registry: shared discovery metadata, scoped shadowing, cancellation, and direct UI dispatch | `ctx.commands` | -| `user-approval/` | One-shot user-approval mechanism, closed outcome vocabulary, audit events, and per-session approval policy | `ctx.approval` | -| `permission/` | User-facing permission presets (`workspace-write`/`danger-full-access`): one product-level select bundling the sandbox-mode and approval-policy knobs, written through to their session events | `ctx.permission` | -| `user-interaction/` | Abstract human question/answer seam used by UI-backed confirmation tools | `ctx.userInteraction` | -| `tool-ask-user/` | Model-facing `ask_user_question` tool over `ctx.userInteraction` | (registers on `ctx.tools`) | -| `jsonrpc/` | Stdio JSON-RPC server for out-of-process SDK clients | (drives `ctx.agents`) | -| `app-boot/` | Shared boot glue for the app bins: `.env` loading, fail-loud Loader guards, snapshot-aware config resolution, the settle-the-tree boot sequence | (library for the bins) | +| [`commands/`](commands/README.md) | Registers and dispatches human commands for interactive adapters. | `ctx.commands` | +| [`user-approval/`](user-approval/README.md) | Coordinates one-shot approval decisions. | `ctx.approval` | +| [`permission/`](permission/README.md) | Presents and persists user-facing permission presets. | `ctx.permission` | +| [`user-interaction/`](user-interaction/README.md) | Defines the provider-neutral human question/answer seam. | `ctx.userInteraction` | +| [`tool-ask-user/`](tool-ask-user/README.md) | Exposes human questions to the model. | (registers on `ctx.tools`) | +| [`jsonrpc/`](jsonrpc/README.md) | Serves out-of-process SDK clients over stdio JSON-RPC. | (drives `ctx.agents`) | +| [`app-boot/`](app-boot/README.md) | Provides shared boot support for application launchers. | (library for the bins) | -A UI integration is a client-driver plugin, not a loop change: it consumes the existing `agent/*` event taxonomy and the `dsh-agent` factory. [`jsonrpc`](jsonrpc/README.md) serves out-of-process SDK clients, while non-interactive one-shot tasks use `cli-demo`. [`commands`](commands/README.md) is the human-only discovery and dispatch plane for interactive adapters; command input and output do not become model messages. - -`user-approval`, `user-interaction`, and `tool-ask-user` live here because asking a human is a UI-backed product affordance, not part of the providerless core spine. `user-approval` owns the one-shot `ctx.approval` decision mechanism and its policy tier; answerers remain with the channel or automation transport that owns the agent. `user-interaction` remains provider-neutral (`ctx.userInteraction`), while `tool-ask-user` is its model-facing consumer and interactive app packages provide concrete providers. - -The runnable app bundles composed over [`agent-spine-demo`](../examples/agent-spine-demo/README.md) live in [`examples/`](../examples/README.md) (`cli-demo`, `acp-demo`, `jsonrpc-demo`), each with its own entry contract. The product [`dsh`](../../apps/cli/README.md) CLI uses no demo bundle. `ui/` keeps the reusable human/SDK channel plugins and shared `app-boot` glue; the automation-only ACP transport lives in [`acp/`](../acp/README.md). Each front door owns its stdout policy, and a leaf `cordis.yml` supplies backends and optional tools. +These packages integrate through existing agent and session contracts rather than changing the loop. Interactive applications provide the concrete command, approval, and question adapters; automation uses [`acp/`](../acp/README.md), and runnable demo bundles live under [`examples/`](../examples/README.md). The product [`dsh`](../../apps/cli/README.md) CLI composes these packages directly. diff --git a/packages/ui/README.zh.md b/packages/ui/README.zh.md index 3958b0bdfb..4023b80085 100644 --- a/packages/ui/README.zh.md +++ b/packages/ui/README.zh.md @@ -2,20 +2,16 @@ [English](README.md) | 中文 -面向用户的交互通道和进程外 SDK 服务器。这些是**产品**包(package):由用户或 SDK 客户端直接操作的真实接口。 +面向用户的通道和进程外 SDK 服务器。这些是**产品**包:由用户或 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`) | -| `jsonrpc/` | 面向进程外 SDK 客户端的 stdio JSON-RPC 服务器 | (驱动 `ctx.agents`) | -| `app-boot/` | app bin 的共享启动粘合层:加载 `.env`、会明确报错的 Loader 保护机制、感知快照的配置解析,以及等待整棵树停稳的启动序列 | (供各 bin 使用的库) | +| [`commands/`](commands/README.md) | 为交互式适配器注册并分派用户命令。 | `ctx.commands` | +| [`user-approval/`](user-approval/README.md) | 协调一次性审批决策。 | `ctx.approval` | +| [`permission/`](permission/README.md) | 呈现并持久化面向用户的权限预设。 | `ctx.permission` | +| [`user-interaction/`](user-interaction/README.md) | 定义与提供方无关的用户问答 seam。 | `ctx.userInteraction` | +| [`tool-ask-user/`](tool-ask-user/README.md) | 向模型公开用户问题。 | (注册到 `ctx.tools`) | +| [`jsonrpc/`](jsonrpc/README.md) | 通过 stdio JSON-RPC 为进程外 SDK 客户端提供服务。 | (驱动 `ctx.agents`) | +| [`app-boot/`](app-boot/README.md) | 为应用启动器提供共享启动支持。 | (供各 bin 使用的库) | -UI 集成属于由客户端驱动的插件,而非对循环的修改:它使用现有的 `agent/*` 事件分类和 `dsh-agent` 工厂。[`jsonrpc`](jsonrpc/README.md) 为进程外 SDK 客户端提供服务,非交互式的一次性任务则使用 `cli-demo`。[`commands`](commands/README.md) 是面向交互式适配器的仅面向用户的发现与分派通道;命令输入和输出不会成为模型消息。 - -`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)(`cli-demo`、`acp-demo`、`jsonrpc-demo`),各自拥有入口契约。产品 [`dsh`](../../apps/cli/README.md) CLI(命令行界面)不使用 demo bundle。`ui/` 保留可复用的用户/SDK 通道插件和共享 `app-boot` 粘合层;仅供自动化使用的 ACP(Agent Client Protocol)传输层位于 [`acp/`](../acp/README.md)。每个入口都负责自己的 stdout 策略,叶子 `cordis.yml` 则提供后端与可选工具。 +这些包通过现有的 agent(智能体)和会话契约集成,而不改变循环。交互式应用提供具体的命令、审批和提问适配器;自动化使用 [`acp/`](../acp/README.md),可运行的演示组合包位于 [`examples/`](../examples/README.md)。产品 [`dsh`](../../apps/cli/README.md) CLI(命令行界面)直接组合这些包。 diff --git a/packages/ui/app-boot/src/index.ts b/packages/ui/app-boot/src/index.ts index 7f3579cda1..2e5a133f00 100644 --- a/packages/ui/app-boot/src/index.ts +++ b/packages/ui/app-boot/src/index.ts @@ -340,10 +340,10 @@ export async function watchPersonalPatches( try { return await register } catch (error) { - // A surface can dispose the whole tree while the watcher is still opening - // (a TUI `/exit` typed during startup): the HMR effect registration then - // fails with INACTIVE_EFFECT. That is the app exiting exactly as asked, - // not a watch failure — return a no-op disposer instead of crashing. + // A surface can dispose the whole tree while the watcher is still opening; + // the HMR effect registration then fails with INACTIVE_EFFECT. That is the + // app exiting exactly as asked, not a watch failure, so return a no-op + // disposer instead of crashing. if ((error as { code?: string } | null)?.code === 'INACTIVE_EFFECT') return async () => {} throw error } @@ -625,11 +625,10 @@ export async function boot( stage = 'plugin tree failed to load' await mountRootInclude(ctx, absoluteConfigPath, patches) // A surface can finish and dispose the whole tree while startup is still - // in flight: the TUI renders as soon as its own fiber starts, so an `/exit` - // typed before the last entry settles tears the context down under us. The - // Loader service goes with it, and the activation audit describes a live - // tree — reading `ctx.loader` past this point would throw a TypeError over - // an app that exited exactly as asked. Transactional group updates settle + // in flight, before the last entry settles. The Loader service goes with + // it, and the activation audit describes a live tree — reading `ctx.loader` + // past this point would throw a TypeError over an app that exited exactly + // as asked. Transactional group updates settle // lifecycle inside the mount, so the teardown can land before it returns; // re-check after every await. await ctx.get('loader')?.await() diff --git a/packages/ui/app-boot/tests/app-boot.spec.ts b/packages/ui/app-boot/tests/app-boot.spec.ts index 96cad31ea3..24a24b4c58 100644 --- a/packages/ui/app-boot/tests/app-boot.spec.ts +++ b/packages/ui/app-boot/tests/app-boot.spec.ts @@ -435,11 +435,10 @@ describe('boot', () => { }) it('returns instead of asserting over a tree a surface disposed mid-startup', async () => { - // What a TUI `/exit` does (ui-tui's disposeRootAndExit): dispose the root - // fiber, which lands while boot() is still awaiting the Loader whenever the - // surface renders before the last entry settles. The Loader service goes - // with the tree, so reading it for the post-boot assertions would crash an - // app that exited exactly as the user asked. + // A surface can dispose the root fiber while boot() is still awaiting the + // Loader, before the last entry settles. The Loader service goes with the + // tree, so reading it for the post-boot assertions would crash an app that + // exited exactly as the user asked. const dir = tmp() writeFileSync(join(dir, 'exiting.mjs'), [ 'export const name = "exiting"', diff --git a/packages/ui/app-boot/tests/config-reload.spec.ts b/packages/ui/app-boot/tests/config-reload.spec.ts index d9f4ffa830..1e88954f69 100644 --- a/packages/ui/app-boot/tests/config-reload.spec.ts +++ b/packages/ui/app-boot/tests/config-reload.spec.ts @@ -344,9 +344,8 @@ describe('include patches layered over one base', () => { // The surface/`--config`/personal composition: `dsh` includes one shared // base and applies each source as its own patch list at the SAME include // level, because patches never cross an include boundary. A later layer - // must therefore be able to reach a row an earlier layer inserted — - // otherwise every surface-only row (the whole TUI front door) would be - // invisible to the user's `~/.dsh/config.yaml`. + // must therefore be able to reach a row an earlier layer inserted, or + // surface-only rows would be invisible to the user's personal config. const dir = mkdtempSync(join(tmpdir(), 'dsh-config-layered-')) writeFileSync(join(dir, 'noop.mjs'), NOOP_PLUGIN) writeFileSync(join(dir, 'base.yml'), '- id: shared\n name: ./noop.mjs\n config:\n value: base\n') diff --git a/packages/ui/app-boot/tests/personal-config.spec.ts b/packages/ui/app-boot/tests/personal-config.spec.ts index 53df1d84b7..7c92d53e56 100644 --- a/packages/ui/app-boot/tests/personal-config.spec.ts +++ b/packages/ui/app-boot/tests/personal-config.spec.ts @@ -235,11 +235,11 @@ describe('boot with personal patches', () => { }) it('returns a no-op disposer when the tree is disposed while the watcher opens', async () => { - // A TUI `/exit` typed during startup disposes the whole tree while - // registerConfig's effect registration is still in flight (the HMR effect - // then fails with INACTIVE_EFFECT); the app is exiting exactly as asked, - // so the watcher must not crash the process. The stub makes the race - // deterministic — the live-teardown ordering itself is not stageable. + // A surface can dispose the whole tree while registerConfig's effect + // registration is still in flight (the HMR effect then fails with + // INACTIVE_EFFECT); the app is exiting exactly as asked, so the watcher + // must not crash the process. The stub makes the race deterministic — the + // live-teardown ordering itself is not stageable. const dir = tmp() const ctx = await boot(NAME, writeTree(dir)) try { diff --git a/packages/ui/commands/README.i18n.yaml b/packages/ui/commands/README.i18n.yaml index 4b4254481a..83cb7e4883 100644 --- a/packages/ui/commands/README.i18n.yaml +++ b/packages/ui/commands/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/commands/README.md -README.md: 4ad72cf9e232c8d41e525f42eecde5637032a391 -README.zh.md: ba214cb7a4c0370128b188db0a08fcd0bc4fa127 +README.md: a931628df28cb11ffb8e91068f7602a4a30cff94 +README.zh.md: e1ef7288ca7dca2945ec93d0ba23a3d1d6edf90c diff --git a/packages/ui/commands/README.md b/packages/ui/commands/README.md index 4ad72cf9e2..a931628df2 100644 --- a/packages/ui/commands/README.md +++ b/packages/ui/commands/README.md @@ -16,7 +16,7 @@ Handlers return `success` or `error` plus optional UI text. Results are rendered ## Composition -The terminal app bundle mounts this service with `dsh-tui`; the UI-less agent spine and ACP automation app do not. Custom interactive compositions and command producers mount `@deepseek-ai/dsh-commands` explicitly. +The shipped `dsh` base mounts this service and the Web client dispatches through it. UI-less demo spines and ACP automation do not provide a command adapter. Custom interactive compositions and command producers mount `@deepseek-ai/dsh-commands` explicitly. ## Model Experience diff --git a/packages/ui/commands/README.zh.md b/packages/ui/commands/README.zh.md index ba214cb7a4..e1ef7288ca 100644 --- a/packages/ui/commands/README.zh.md +++ b/packages/ui/commands/README.zh.md @@ -16,7 +16,7 @@ ## 组合 -终端应用组合包会将此服务与 `dsh-tui` 一起挂载;无 UI 的 agent 主干和 ACP(Agent Client Protocol)自动化应用不会挂载它。自定义交互式组合与命令生产方会显式挂载 `@deepseek-ai/dsh-commands`。 +随产品交付的 `dsh` 基础组合会挂载此服务,Web 客户端通过它分派命令。无 UI 的演示主干和 ACP(Agent Client Protocol)自动化不提供命令适配器。自定义交互式组合与命令生产方会显式挂载 `@deepseek-ai/dsh-commands`。 ## 模型体验 diff --git a/packages/ui/user-interaction/README.i18n.yaml b/packages/ui/user-interaction/README.i18n.yaml index dabfab02f3..2fd2733c62 100644 --- a/packages/ui/user-interaction/README.i18n.yaml +++ b/packages/ui/user-interaction/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/user-interaction/README.md -README.md: c7fec590d6e44a13b94cc682f5e069b2d3c5e416 -README.zh.md: 094cd2bf81eafc7d4f24385cfb30e97478cbb58d +README.md: 7d3c4e87c018e42794d319b540fd0a94abc9b43c +README.zh.md: 0e5d15a673c124abab4b13e869623df1a5c63acd diff --git a/packages/ui/user-interaction/README.md b/packages/ui/user-interaction/README.md index c7fec590d6..7d3c4e87c0 100644 --- a/packages/ui/user-interaction/README.md +++ b/packages/ui/user-interaction/README.md @@ -28,7 +28,7 @@ For a single-select question, `custom` overrides the selected choice and `select ## Role -This is the interface package. Model-facing consumers such as `@deepseek-ai/dsh-tool-ask-user` depend on this seam; `dsh-tui` and the host runtime provide interactive implementations. The loop stays unchanged: a tool call awaits a promise, and the tool result resumes the normal agent loop. +This is the interface package. Model-facing consumers such as `@deepseek-ai/dsh-tool-ask-user` depend on this seam; the Web host runtime provides the shipped interactive implementation. The loop stays unchanged: a tool call awaits a promise, and the tool result resumes the normal agent loop. ## Model Experience diff --git a/packages/ui/user-interaction/README.zh.md b/packages/ui/user-interaction/README.zh.md index 094cd2bf81..0e5d15a673 100644 --- a/packages/ui/user-interaction/README.zh.md +++ b/packages/ui/user-interaction/README.zh.md @@ -28,7 +28,7 @@ ## 职责 -这是接口包。`@deepseek-ai/dsh-tool-ask-user` 等面向模型的消费方依赖此 seam;`dsh-tui` 和宿主运行时提供交互式实现。循环保持不变:工具调用等待 Promise,工具结果随后恢复正常的 agent loop(智能体循环)。 +这是接口包。`@deepseek-ai/dsh-tool-ask-user` 等面向模型的消费方依赖此 seam;Web 宿主运行时提供随产品交付的交互式实现。循环保持不变:工具调用等待 Promise,工具结果随后恢复正常的 agent loop(智能体循环)。 ## 模型体验 diff --git a/packages/util/README.i18n.yaml b/packages/util/README.i18n.yaml index c1ab4a3b74..82e5f323d8 100644 --- a/packages/util/README.i18n.yaml +++ b/packages/util/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/README.md -README.md: 46904aba70c7cf0f98bb75cce79d97bb12b950a9 -README.zh.md: 78a4c3038ef31bb1ec5fb10751b7c148ea20b716 +README.md: b84f2cbb88981aa2edbde65d8a711b28cc5b48e9 +README.zh.md: 44834041aad8d891b5dcaf63e02be5641e878cdc diff --git a/packages/util/README.zh.md b/packages/util/README.zh.md index 78a4c3038e..44834041aa 100644 --- a/packages/util/README.zh.md +++ b/packages/util/README.zh.md @@ -2,21 +2,13 @@ [English](README.md) | 中文 -其他分组共享的零依赖原语。当某个包拥有某个微小的基础类型或辅助工具,而该类型或辅助工具为多个功能家族所需,却不属于其中任何一个家族时,该包就归入此处。这样可避免一个功能包仅为使用共享原语而依赖不相关的功能包。这些都是**支持** 包:规模小、稳定,且不依赖 harness。 +这些零依赖包提供由多个能力家族共享的小型原语。业务语义仍归各个消费这些原语的能力所有。 | 包 | 职责 | |---|---| -| `brand/` | 仅包含类型的 `Branded` 名义类型原语(无运行时代码,无 harness 依赖) | -| `paths/` | 规范的单根 `DSH_HOME` 解析,以及 harness 用户数据的共享文件系统路径常量和辅助工具(无 harness 依赖) | -| `timeout/` | 超时的时序/分类部分:`clampTimeout`/`deadline`/`timeoutOf`/`TimeoutReason`(纯函数,无 harness 依赖);终止机制保留在各个功能中 | -| `retention/` | 有界的面向模型输出:`ItemRetainer`/`TextRetainer` 加上中性通知辅助工具(纯工具,无 harness 依赖);业务语义保留在各个工具中 | -| `atomic-write/` | 原子文件替换:`writeFileAtomic`(独占创建临时文件 + 携带调用方所声明 mode 的 rename);由设置与凭据存储共用 | -| `native-command/` | 宿主原生 OS 集成的免 shell `execFile` 运行器——utf8 捕获、abort 传播、Windows 窗口隐藏(无 harness 依赖);命令选择保留在各调用方 | - -`dsh-brand` 是规范示例:它只负责 `Branded` 辅助工具,因此功能包可以为自己拥有的 id 添加品牌(`dsh-tasks` 的 `TaskId`、`dsh-session` 的 `SessionId` 等),而只需依赖 `dsh-brand`,无需仅为使用 `Branded` 而引入不相关的包。 - -`dsh-paths` 为每个包提供同一个可配置的 Harness 主目录,而不将这项横切事实归属给 bash、skill(技能)、telemetry 或组合包。它优先解析显式值,其次是 `$DSH_HOME`,最后回退到 `~/.dsh`;返回绝对路径,但不缓存、创建或修改任何内容。harness 将所有用户数据保存在同一根目录下。 - -`dsh-timeout` 对超时家族采用相同结构:`dsh-bash` 和 `dsh-web-fetch-local` 都只依赖 `dsh-timeout`,便可将调用方的取消与 deadline 融合,然后区分「已超时」和「已取消」。它刻意只负责时序/分类部分,*终止*机制(对进程组发送 SIGKILL、关闭 fetch socket)保留在各个功能中,因为没有任何共享层可以负责每个功能的终止操作(见[超时库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-timeout-deadline-library.md))。 - -`dsh-retention` 对有界工具输出采用同样的拆分方式:工具(`glob`/`grep`/`bash`/`web_fetch`/`web_search`)将项或文本送入 retainer,取回保留的内容以及精确的省略量;分组、退出码、提供方错误和恢复文案则仍由工具负责。它刻意只负责保留机制;`truncated` 只表示因预算而发生了省略,绝不表示「检查不完整」状态(见[保留库 Agent Note](../../.agents/notes/implemented/architecture/2026-07-06-tool-result-retention-library.md))。 +| [`brand/`](brand/README.md) | 提供带名义品牌的类型 | +| [`paths/`](paths/README.md) | 解析 Harness 数据根目录和共享路径 | +| [`timeout/`](timeout/README.md) | 提供截止时间和超时分类原语 | +| [`retention/`](retention/README.md) | 限制保留文本和项目集合的大小 | +| [`atomic-write/`](atomic-write/README.md) | 以原子方式替换文件 | +| [`native-command/`](native-command/README.md) | 不经 shell 运行宿主原生命令 | diff --git a/packages/util/atomic-write/README.i18n.yaml b/packages/util/atomic-write/README.i18n.yaml index 822a0952ca..33e1f6014b 100644 --- a/packages/util/atomic-write/README.i18n.yaml +++ b/packages/util/atomic-write/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/atomic-write/README.md README.md: 2ff4abb6ac10d8b592ccd2056b4f1f92cc8518b0 -README.zh.md: bd5d3f1f2583ff1b97a80ce2c1c6ca7989e13d15 +README.zh.md: 4284d06422564268bb9e31d1a1ed06ab5271e562 diff --git a/packages/util/atomic-write/README.zh.md b/packages/util/atomic-write/README.zh.md index bd5d3f1f25..4284d06422 100644 --- a/packages/util/atomic-write/README.zh.md +++ b/packages/util/atomic-write/README.zh.md @@ -30,15 +30,15 @@ await withFileLock('/home/u/.dsh/settings.yaml', async () => { `withFileLock` 跨进程串行化同一文件的写入方,服务于单靠原子提交无法保证安全的读-渲染-提交循环。锁是以 `wx` 创建的同目录 `.lock`,因此读取方从不参与竞争;等待方按指数退避,超时即失败而非无限阻塞。竞争者绝不移除现有锁:锁龄无法区分已经崩溃的所有者与被暂停但仍存活的写入方。 -## Model Experience +## 模型体验 无:本包是纯文件系统原语,此处没有任何内容会到达模型请求。 -#### KV Cache effect +#### KV Cache 影响 无;此处没有任何内容会进入请求前缀。 -## Known Limitations and Deferred Work +## 已知限制与暂缓事项 - **原子但不保证持久**——不对文件或其所在目录做 `fsync`,因此崩溃后可能观察到 rename 被回退。此处的文件型存储在启动时重新读取并重新发布,把持久性留作调用方的策略。 - **仅支持字符串内容**——在有消费方需要之前,不提供 `Buffer` 或流式形态。 diff --git a/packages/util/native-command/README.i18n.yaml b/packages/util/native-command/README.i18n.yaml index efffaa41cb..7c984a67ad 100644 --- a/packages/util/native-command/README.i18n.yaml +++ b/packages/util/native-command/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/native-command/README.md README.md: 7fc8b1f4640ef87ada62b6656854feb37080e4e6 -README.zh.md: 94db732afbb77eb90faf3c5710a8a9c4a8630f95 +README.zh.md: b91af6fe4787c35c56fbd4523d5a7e10673e32cb diff --git a/packages/util/native-command/README.zh.md b/packages/util/native-command/README.zh.md index 94db732afb..b91af6fe47 100644 --- a/packages/util/native-command/README.zh.md +++ b/packages/util/native-command/README.zh.md @@ -8,7 +8,7 @@ 它是**库,不是服务或插件**:没有 `ctx`、不注册任何东西、不持有状态、不发事件。 -## Surface +## 接口面 ```ts import { runNativeCommand, type NativeCommandRunner } from '@deepseek-ai/dsh-native-command' diff --git a/packages/util/retention/README.i18n.yaml b/packages/util/retention/README.i18n.yaml index aa87f67e37..b9ba96e765 100644 --- a/packages/util/retention/README.i18n.yaml +++ b/packages/util/retention/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/retention/README.md -README.md: d257075a67b35e92bce53a88fc6d002f4f4d5d9b -README.zh.md: 1a0761b4744b7f090cfbf94f847bb99fc2d41a96 +README.md: 45c88e9fc2d8df98f935fd1c3b43c4622802bd2a +README.zh.md: 0fcb76a8ae381d3e3da54e90640a38cba9de12e8 diff --git a/packages/util/retention/README.md b/packages/util/retention/README.md index d257075a67..45c88e9fc2 100644 --- a/packages/util/retention/README.md +++ b/packages/util/retention/README.md @@ -47,7 +47,7 @@ Text caps and `omittedBytes` count **bytes**, for process/body safety (a child's ## Tool mappings -Every current retention consumer maps to the library below. A broad migration is out of scope for the library's first landing — these are the intended shapes. +Current retention consumers use these mappings: | Tool | Retainer & strategy | Notes | |---|---|---| @@ -57,7 +57,7 @@ Every current retention consumer maps to the library below. A broad migration is | `web_fetch` | `TextRetainer`, `head` or `headTail` | Provider/resource caps stay provider facts; the retainer supplies only retained text and omission metadata. | | `web_search` | `ItemRetainer`, `head` | Standardizes the "sources capped" notice when providers return more sources than the model-facing result should include. | -`read` is **intentionally out of scope for v1.** Its `read-render` helper owns a file-specific pagination contract — `offset`/`limit`, line numbers, `totalLines`, offset-out-of-range errors, per-line preview truncation, a byte cap over the selected window — which is a line-window renderer, not generic retention. A single `Omitted` count cannot represent both sides of a line window. +`read` remains outside this generic library. Its `read-render` helper owns a file-specific pagination contract — `offset`/`limit`, line numbers, `totalLines`, offset-out-of-range errors, per-line preview truncation, and a byte cap over the selected window — which is a line-window renderer. A single `Omitted` count cannot represent both sides of that window. ## Usage shape diff --git a/packages/util/retention/README.zh.md b/packages/util/retention/README.zh.md index 1a0761b474..0fcb76a8ae 100644 --- a/packages/util/retention/README.zh.md +++ b/packages/util/retention/README.zh.md @@ -47,7 +47,7 @@ import type { ## 工具映射 -当前每个保留消费方都按下表映射到该库。广泛迁移不属于该库首次落地的范围;下表是预期形态。 +当前的保留机制消费方采用以下映射: | 工具 | Retainer 与策略 | 说明 | |---|---|---| @@ -57,7 +57,7 @@ import type { | `web_fetch` | `TextRetainer`,`head` 或 `headTail` | 提供方/资源上限保留为提供方事实;retainer 只提供保留文本和省略元数据。 | | `web_search` | `ItemRetainer`,`head` | 当提供方返回的来源超过面向模型的结果应包含的数量时,标准化「来源已达上限」通知。 | -`read` **刻意不在 v1 范围内**。其 `read-render` 辅助工具负责文件专用的分页契约:`offset`/`limit`、行号、`totalLines`、偏移越界错误、每行预览截断、针对已选窗口的字节上限。这是行窗口渲染器,而非通用保留机制。单个 `Omitted` 数量无法表示行窗口两侧。 +`read` 仍不属于这个通用库。其 `read-render` 辅助工具负责文件专用的分页契约:`offset`/`limit`、行号、`totalLines`、偏移越界错误、每行预览截断,以及所选窗口的字节上限。这是行窗口渲染器。单个 `Omitted` 数量无法表示该窗口两侧。 ## 使用形态 diff --git a/packages/util/timeout/README.i18n.yaml b/packages/util/timeout/README.i18n.yaml index 45df2b407f..de4a85e9c9 100644 --- a/packages/util/timeout/README.i18n.yaml +++ b/packages/util/timeout/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/util/timeout/README.md -README.md: 11c55a45a1255e14fb551e42ba3965453dbd94ae -README.zh.md: 078c36a20b4ba7d08f67cc394b2f178f6c0a9ab8 +README.md: 0ff5550ef7ea6b8315a6a529a4b8b503162a8f12 +README.zh.md: 79d7ee674209b0324ae9428c1a04fda8a2547db5 diff --git a/packages/util/timeout/README.md b/packages/util/timeout/README.md index 11c55a45a1..0ff5550ef7 100644 --- a/packages/util/timeout/README.md +++ b/packages/util/timeout/README.md @@ -46,7 +46,7 @@ export async function runWithDeadline(upstream: AbortSignal | undefined, timeout The signal only *notifies* — the caller MUST attach its own termination (`d.signal.addEventListener('abort', kill)`, or hand `d.signal` to `fetch`). Racing a promise against a timer would resolve the tool-call while the child process or socket leaks on; handing out a signal forces a real termination path to exist. -Pass your own `code` to `timeoutOf` so classification composes under nesting: when the `upstream` you were handed is *itself* a deadline signal (a future `tools/execute` middleware arming a per-call deadline), `AbortSignal.any` preserves the outer `TimeoutReason` if the outer timer fires first. Scoping to your `code` makes a foreign timeout read as an ordinary upstream cancel — the correct classification from your capability's view — instead of your own timeout firing when your local timer never expired. +Pass your own `code` to `timeoutOf` so classification composes under nesting. When `upstream` is itself a deadline signal, `AbortSignal.any` preserves its `TimeoutReason` if that timer fires first. Scoping to your code makes a foreign timeout read as an ordinary upstream cancel instead of claiming that the local timer expired. For a streamed transport, create one `idleWatchdog`, pass its stable `signal` into the transport, and call `watchdog.next(iterator)` for each provider read. The interval must be positive, finite, and no greater than `MAX_TIMER_DELAY_MS`; Node otherwise clamps it to one millisecond. It measures only outstanding demand, so no timer runs while downstream code renders or otherwise waits before asking for the next chunk. The primitive still only notifies, so the transport must observe the stable signal; the DeepSeek and pi-ai adapters prove that timeout closes their real response body or SDK request. diff --git a/packages/util/timeout/README.zh.md b/packages/util/timeout/README.zh.md index 078c36a20b..79d7ee6742 100644 --- a/packages/util/timeout/README.zh.md +++ b/packages/util/timeout/README.zh.md @@ -46,7 +46,7 @@ export async function runWithDeadline(upstream: AbortSignal | undefined, timeout 该信号只会*通知*;调用方必须接入自己的终止机制(`d.signal.addEventListener('abort', kill)`,或将 `d.signal` 传给 `fetch`)。让 promise 与 timer 竞速,会在子进程或套接字仍在泄漏时就让工具调用完成;发出信号则会强制要求存在真正的终止路径。 -将你自己的 `code` 传给 `timeoutOf`,以便分类可在嵌套中组合:当你收到的 `upstream` *本身*就是 deadline 信号时(未来启动每次调用 deadline 的 `tools/execute` 中间件),如果外层 timer 首先触发,`AbortSignal.any` 会保留外层 `TimeoutReason`。将范围限定为你的 `code`,可将外部超时视为普通 upstream 取消,这才是你所属功能视角下的正确分类,而不会在本地 timer 尚未到期时就声称自己超时。 +将你自己的 `code` 传给 `timeoutOf`,使分类可在嵌套场景中正确组合。当 `upstream` 本身是 deadline 信号时,如果该 timer 先触发,`AbortSignal.any` 会保留它的 `TimeoutReason`。将匹配范围限定为你的 code,会把外部超时视为普通的 upstream 取消,而不会声称本地 timer 已到期。 对于流式传输,创建一个 `idleWatchdog`,将其稳定的 `signal` 传给传输层,并为提供方的每次读取调用 `watchdog.next(iterator)`。间隔必须为正有限数,且不得超过 `MAX_TIMER_DELAY_MS`;否则 Node 会将其限制为 1 毫秒。它只对尚未完成的读取请求计时,因此当下游代码进行渲染或在请求下一个分片前以其他方式等待时,timer 不会运行。该原语仍然只会通知,因此传输层必须观察稳定信号;DeepSeek 和 pi-ai 适配器证明,超时会关闭它们的真实响应正文或 SDK 请求。 diff --git a/packages/web/README.i18n.yaml b/packages/web/README.i18n.yaml index 612114305c..cd412d1a99 100644 --- a/packages/web/README.i18n.yaml +++ b/packages/web/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/web/README.md -README.md: 8cd173b922ead32219ffab2b2d6b6428b3ce375c -README.zh.md: 0b07514ace8cfc9bd92126274c68363b3e942f4d +README.md: 65b3dde1e2f9c35c308a10ca80245064f7a0361e +README.zh.md: 43b8b7d9c53336e984d9c61fa9b612fc1733e31f diff --git a/packages/web/README.zh.md b/packages/web/README.zh.md index 0b07514ace..43b8b7d9c5 100644 --- a/packages/web/README.zh.md +++ b/packages/web/README.zh.md @@ -1,18 +1,16 @@ -# web/ - web 能力家族 +# web/:web 能力家族 [English](README.md) | 中文 -web 访问能力 seam:抽象 web 接口、搜索/抓取提供方实现,以及面向模型的 web 工具。这些全是**产品**包。 +本家族提供与提供方无关的 web 搜索和抓取操作,以及消费这些操作的面向模型工具。 | 包 | 职责 | ctx key | |---|---|---| -| `web/` | 抽象 web seam(搜索/抓取提供方注册表 + 选择 + 词汇 + `WebError`) | `ctx.web` | -| `web-search-exa/` | Exa 支持的 `WebSearchProvider` | (注册到 `ctx.web`) | -| `web-search-perplexity/` | Perplexity 支持的 `WebSearchProvider` | (注册到 `ctx.web`) | -| `web-search-deepseek/` | DeepSeek 支持的 `WebSearchProvider`,通过 Anthropic 兼容 API 使用原生 `web_search` | (注册到 `ctx.web`) | -| `web-fetch-local/` | 用于匿名访问公共 HTTP(S) 的 `WebFetchProvider` | (注册到 `ctx.web`) | -| `tool-web/` | 面向模型的 `web_search`/`web_fetch` 工具 schema | (注册到 `ctx.tools`) | +| [`web/`](web/README.md) | 定义 web 提供方注册、选择和共享错误 | `ctx.web` | +| [`web-search-exa/`](web-search-exa/README.md) | 通过 Exa 提供 web 搜索 | 注册到 `ctx.web` | +| [`web-search-perplexity/`](web-search-perplexity/README.md) | 通过 Perplexity 提供 web 搜索 | 注册到 `ctx.web` | +| [`web-search-deepseek/`](web-search-deepseek/README.md) | 提供 DeepSeek 原生 web 搜索 | 注册到 `ctx.web` | +| [`web-fetch-local/`](web-fetch-local/README.md) | 抓取公共 HTTP 和 HTTPS 资源 | 注册到 `ctx.web` | +| [`tool-web/`](tool-web/README.md) | 向模型公开 web 搜索和抓取 | 注册到 `ctx.tools` | -接口位于 `web/web/`。与 bash/fs 不同,该 seam 跨越**两种能力**(搜索和抓取),每种能力都可能有多个提供方:`ctx.web` 是单一的 web 访问中间层,拥有一项提供方选择策略、一套中止/错误词汇,以及一个面向产品的「该 harness 如何访问 web」配置接口。提供方注册的是**能力**而非工具;`tool-web` 是面向模型名称、schema、提示词指引和呈现的唯一负责方。替换搜索提供方不会改变模型提出查询的方式,替换抓取实现也不会改变模型请求 URL 的方式。 - -设计原理见 [web 能力 seam Agent Note](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md),其中也解释了搜索与抓取为何有意合并为一个 seam,以及为何暂缓实现 `web_fetch` 的 SSRF 防护。 +[web 能力决策](../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md)记录了搜索和抓取共用一项提供方选择服务的原因。 diff --git a/packages/workflow/README.i18n.yaml b/packages/workflow/README.i18n.yaml index 450461c540..f02915a31d 100644 --- a/packages/workflow/README.i18n.yaml +++ b/packages/workflow/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/workflow/README.md -README.md: 17da3397e4fa64837b271d89e9062e3621434338 -README.zh.md: 1609d1e961f9b21f859eda66ed84d7f8bdbbe978 +README.md: 2416e26e73340a6d8624b8e77681dfc9f55fe7c5 +README.zh.md: a5c94a0a9799d78e5cca72394385b38d2fb2ba70 diff --git a/packages/workflow/README.zh.md b/packages/workflow/README.zh.md index 1609d1e961..a5c94a0a97 100644 --- a/packages/workflow/README.zh.md +++ b/packages/workflow/README.zh.md @@ -1,16 +1,14 @@ -# workflow/:动态工作流能力族 +# workflow/:动态工作流能力家族 [English](README.md) | 中文 -工作流 seam:由模型编写 JavaScript 编排脚本,大规模扇出 subagent(分阶段、每个 agent(智能体)的结构化结果、并发上限),其设计参考 Claude Code 动态工作流。这是 bash 形态的能力 seam(见[能力 seam](../../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)):每个上下文只有一个引擎实现注册为 `ctx.workflows`;面向模型的工具使用它。 +本家族通过 subagent 运行由模型编写的编排工作流,并将通用工具与固定策略工具公开给模型。 -| 包 | 角色 | ctx 键 | +| 包 | 职责 | ctx 键 | |---|---|---| -| `workflow/` | 抽象工作流 seam:服务基类、运行词汇和 `workflow/*` 事件 | `ctx.workflows` | -| `workflow-workerthread/` | `node:worker_threads` 引擎:每次运行使用一个 worker;脚本的 vm 上下文位于 worker 内,`agent()` 通过消息端口桥接到 `ctx.subagents` | (提供 `ctx.workflows`) | -| `tool-workflow/` | 面向模型的 `workflow` 工具,基于 `ctx.workflows` | (注册到 `ctx.tools`) | -| `tool-ralph/` | 基于 `ctx.workflows` 和全新结构化输出 subagent 提供方的固定全新 agent Ralph 策略 | (注册到 `ctx.tools`) | +| [`workflow/`](workflow/README.md) | 定义工作流执行和生命周期事件 | `ctx.workflows` | +| [`workflow-workerthread/`](workflow-workerthread/README.md) | 在线程中运行工作流脚本 | 注册到 `ctx.workflows` | +| [`tool-workflow/`](tool-workflow/README.md) | 向模型公开通用工作流执行 | 注册到 `ctx.tools` | +| [`tool-ralph/`](tool-ralph/README.md) | 公开使用全新 agent(智能体)的固定 Ralph 工作流 | 注册到 `ctx.tools` | -接口位于 `workflow/workflow/`。引擎的 `agent()` 钩子使用 [subagent seam](../subagent/README.md)(任何已注册提供方;随产品交付的示例使用 `spawn`),`agent({ schema })` 则使用进程内后端实现的结构化输出支持。worker thread 隔离的是脚本:宿主绝不会被它阻塞,已取消运行经过宽限时间后的终止也会实际生效;但它不是安全边界。如果将来确有需要,可以在同一接口背后换用 isolated-vm/独立进程引擎,以实现真正的沙箱隔离。 - -通用脚本引擎的决策和暂缓事项见[动态工作流 Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。独立的 [Ralph 消费方](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)会固定脚本和全新提供方策略,而不是再添加一个引擎或 agent loop(智能体循环)模式。 +worker thread 将工作流执行与宿主事件循环隔离,但不构成安全边界。参见[动态工作流](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)和 [Ralph 工具](../../.agents/notes/implemented/feature/2026-07-19-fresh-agent-ralph-workflow-tool.md)决策。 diff --git a/packages/workspace/README.i18n.yaml b/packages/workspace/README.i18n.yaml index ba76f98d80..93dea4b3f0 100644 --- a/packages/workspace/README.i18n.yaml +++ b/packages/workspace/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/workspace/README.md -README.md: ba92e95d3cde0a95eaaeae5a9b4384c3b8c9c4b8 -README.zh.md: 96fd4b9790dcb5cc69e068aa14e2348dfa86a31e +README.md: 9f6ead776c1e5d36af5b6917ad8f27725b11e6a0 +README.zh.md: aa691ec681112f4c25a6391c33eabd2711c7abfc diff --git a/packages/workspace/README.md b/packages/workspace/README.md index 4b3b19b282..9f6ead776c 100644 --- a/packages/workspace/README.md +++ b/packages/workspace/README.md @@ -8,4 +8,4 @@ This family owns persistent workspaces: user directories with titles and ordered |---|---|---| | [`workspace/`](workspace/README.md) | Registers workspaces and accounts for their sessions | `ctx.workspace` | -Workspace deletion removes the registry record, not user files or session logs. See the [workspace lifecycle decision](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md) and [storage design](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md). +The [workspace package reference](workspace/README.md) owns lifecycle, persistence, and deletion semantics. diff --git a/packages/workspace/README.zh.md b/packages/workspace/README.zh.md index 96fd4b9790..aa691ec681 100644 --- a/packages/workspace/README.zh.md +++ b/packages/workspace/README.zh.md @@ -1,11 +1,11 @@ -# workspace/:Workspace 实体 +# workspace/:workspace 实体家族 [English](README.md) | 中文 -Workspace 系列负责持久 workspace 概念:用户工作所在的目录,包含标题以及属于它的有序会话列表。设计记录:[领域 KV 存储 Agent Note](../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md)。 +本家族拥有持久 workspace:带标题和有序会话成员关系的用户目录。 | 包 | 职责 | ctx 键 | |---|---|---| -| `workspace/` | 基于存储领域数据形式的 `WorkspaceRegistry` 服务:按 realpath 去重的路径、会话归属记账、实体缓存 | `ctx.workspace` | +| [`workspace/`](workspace/README.md) | 注册 workspace 并记录其会话归属 | `ctx.workspace` | -所有权信息以 workspace 记录中的 `sessionIds`(有序)为准,绝不从会话 cwd 派生;`attachSession` 会验证会话头的 cwd 解析到 workspace 路径,因此一个会话在结构上最多属于一个 workspace。删除 Workspace 只会移除该注册表记录及会话归属记录:目录、用户文件和会话日志都会保留,相关会话则进入 Ungrouped(参见[决策记录](../../.agents/notes/implemented/feature/2026-07-27-workspace-registration-deletion.md))。 +[workspace 包参考](workspace/README.md)负责生命周期、持久化和删除语义。 diff --git a/python/README.i18n.yaml b/python/README.i18n.yaml index 29806c7c41..8dadf07570 100644 --- a/python/README.i18n.yaml +++ b/python/README.i18n.yaml @@ -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 python/README.md -README.md: 010d922b52eae0be184baec063e307b5c8e55819 -README.zh.md: ebf773d943e3ad3248fb41c58ca7e44950bb9d7f +README.md: 27637edb9d4d5e8714fe379a00b6aae3af1a541f +README.zh.md: dd020aa507de965815fc05f8423b87e766a2fb42 diff --git a/python/README.md b/python/README.md index 010d922b52..27637edb9d 100644 --- a/python/README.md +++ b/python/README.md @@ -17,4 +17,4 @@ The SDK starts the matching bundled runtime unless the caller selects an explici ## Contributor workflows -The [Python contributor reference](development.md) covers building runtime artifacts, validating the packages, source-mode development, and distribution. +The [Python contributor workflows](development.md) cover building runtime artifacts, validating the packages, source-mode development, and distribution. diff --git a/python/README.zh.md b/python/README.zh.md index ebf773d943..dd020aa507 100644 --- a/python/README.zh.md +++ b/python/README.zh.md @@ -17,4 +17,4 @@ ## 贡献者工作流 -[Python 贡献者参考](development.md)介绍运行时产物构建、包验证、源码模式开发和分发。 +[Python 贡献者工作流](development.md)介绍运行时产物构建、包验证、源码模式开发和分发。 diff --git a/python/development.i18n.yaml b/python/development.i18n.yaml index ec6e970c2c..b7792acf27 100644 --- a/python/development.i18n.yaml +++ b/python/development.i18n.yaml @@ -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 python/development.md -development.md: f7cf69f798c08f8e2b558bb68431bc77ab220378 -development.zh.md: 53e31bc1455367f95260dccf5a2bcc7d2ed6e55d +development.md: b0d4875f0d5b7c8fd2b4b480ac67793741640710 +development.zh.md: 4fda7efbeaa0359bd4999aca148ca21a63c4fd70 diff --git a/python/development.md b/python/development.md index f7cf69f798..b0d4875f0d 100644 --- a/python/development.md +++ b/python/development.md @@ -2,7 +2,7 @@ English | [中文](development.zh.md) -Reference for building, validating, and distributing the Python SDK and bundled runtime. Package behavior belongs in the [SDK reference](sdk/README.md) and [runtime carrier reference](sdk-runtime/README.md). +Follow the workflow for the contributor outcome you need: build runtime artifacts, validate the SDK, run against source, or build distributions. Package behavior belongs in the [SDK reference](sdk/README.md) and [runtime carrier reference](sdk-runtime/README.md). ## Build runtime artifacts diff --git a/python/development.zh.md b/python/development.zh.md index 53e31bc145..4fda7efbea 100644 --- a/python/development.zh.md +++ b/python/development.zh.md @@ -2,7 +2,7 @@ [English](development.md) | 中文 -构建、验证和分发 Python SDK 与内置运行时的参考。包行为分别见 [SDK 参考](sdk/README.md)和[运行时载体参考](sdk-runtime/README.md)。 +根据所需的贡献者成果选择工作流:构建运行时产物、验证 SDK、从源码运行或构建分发包。包行为分别见 [SDK 参考](sdk/README.md)和[运行时载体参考](sdk-runtime/README.md)。 ## 构建运行时产物 diff --git a/python/sdk/README.i18n.yaml b/python/sdk/README.i18n.yaml index a96ccd30b8..a57fee928d 100644 --- a/python/sdk/README.i18n.yaml +++ b/python/sdk/README.i18n.yaml @@ -3,4 +3,4 @@ # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write python/sdk/README.md README.md: bb3420f1a1bd461facbd0eb1312a255df1da4412 -README.zh.md: adc8e377ca9f70838b6164301a9052e2247c8eff +README.zh.md: 2cb48ac9aaae115bbcce5943449bca9e42f2592c diff --git a/python/sdk/README.zh.md b/python/sdk/README.zh.md index adc8e377ca..2cb48ac9aa 100644 --- a/python/sdk/README.zh.md +++ b/python/sdk/README.zh.md @@ -29,7 +29,7 @@ with DeepSeekHarness( result = harness.run("Make the requested code change.") ``` -`provider` 用于选择当前 Cordis 组合已注册的提供方路由;`model` 是该适配器解析的模型 ID。`max_tokens` 是可选的正整数,用于限制根 agent 及其进程内后代每次请求的输出 token;省略时由提供方默认值控制。压缩摘要继续使用压缩插件单独配置的上限。内置默认组合注册 `deepseek-official`。自定义组合可以挂载 `llm-pi-ai`,在其中配置各提供方的凭据与端点,再选择 pi-ai 已安装目录中的任意提供方/模型组合。 +`provider` 用于选择当前 Cordis 组合已注册的提供方路由;`model` 是该适配器解析的模型 ID。`max_tokens` 是可选的正整数,用于限制根 agent(智能体)及其进程内后代每次请求的输出 token;省略时由提供方默认值控制。压缩摘要继续使用压缩插件单独配置的上限。内置默认组合注册 `deepseek-official`。自定义组合可以挂载 `llm-pi-ai`,在其中配置各提供方的凭据与端点,再选择 pi-ai 已安装目录中的任意提供方/模型组合。 `HarnessClient` 会在运行时进程的生命周期内保留已发现的 subagent(子 agent)祖先关系。每次执行 `Session.run()` 时,`TurnResult.notifications` 与 `on_notification` 会按协议传输顺序收到根会话及所有已知后代的通知,其中包括嵌套 subagent 的生命周期事件与会话事件。`TurnResult.events` 仍只保存根会话的完整事件流,`TurnResult.final_response` 则取该会话最后一个 `assistant/message` 的文本内容,因此后代消息不会覆盖根会话回复。 diff --git a/scripts/gen-third-party-notices.ts b/scripts/gen-third-party-notices.ts index 6b790829d5..0d41953e4b 100644 --- a/scripts/gen-third-party-notices.ts +++ b/scripts/gen-third-party-notices.ts @@ -562,7 +562,7 @@ ${vendored.map(row => `| \`${row.npmName}\` | [${row.upstream.replace('https://' ## Runtime npm dependencies -External packages that a workspace package resolves at runtime. \`scripts/install.sh\` installs this repository itself, so the tier covers every plugin a user can mount from \`cordis.yml\` — not only what the \`dsh\` CLI/TUI, the Web UI, and the Python SDK runtime load by default. +External packages that a workspace package resolves at runtime. \`scripts/install.sh\` installs this repository itself, so the tier covers every plugin a user can mount from \`cordis.yml\` — not only what the \`dsh\` CLI, Web UI, and Python SDK runtime load by default. ${renderNpmTable(runtimeDeps)} diff --git a/skills/dsh-customize/SKILL.md b/skills/dsh-customize/SKILL.md index bde4c2eb00..5d67af1e51 100644 --- a/skills/dsh-customize/SKILL.md +++ b/skills/dsh-customize/SKILL.md @@ -22,7 +22,7 @@ Do not assume a path or branch name. DSH is usually installed from source with a 1. Create a fresh task branch and worktree from the recorded staging tip, using the repository-required worktree location — default to `.worktrees/` under the repository root unless the repository requires otherwise. Never implement or commit directly on staging. 2. Implement the change, then select and run the repository-required review and checks. If a check fails, fix the cause and rerun it before integration. -3. Test assembled TUI behavior interactively in a dedicated tmux session. Test other interactive UI behavior in a browser; unit tests and snapshots alone are insufficient. +3. Test assembled interactive behavior in the Web UI; unit tests and snapshots alone are insufficient. 4. Record the task tip and confirm the task worktree is clean before integration. ## Integrate under the lock diff --git a/skills/dsh-upstream-customization/SKILL.md b/skills/dsh-upstream-customization/SKILL.md index 944f4b083c..7ec1618db3 100644 --- a/skills/dsh-upstream-customization/SKILL.md +++ b/skills/dsh-upstream-customization/SKILL.md @@ -23,5 +23,5 @@ Classification and a recommendation are not publishing approval. Obtain explicit 3. Review the outgoing commits and diff against upstream. Confirm they contain only the approved feature, no credentials or personal data, and a clean worktree. 4. Reconfirm the approved feature name and publishing target before the first push. Do not infer authorization from earlier local work. 5. Push only that branch and open only a draft PR. Keep its description synchronized with later changes. -6. For a TUI feature, preferably attach a screenshot from the assembled application after removing credentials and personal data. +6. For a Web UI feature, attach a screenshot or GIF from the assembled application after removing credentials and personal data. 7. Report the upstream base and branch commits, commands and checks run, pushed branch, and draft PR URL.