Merge remote-tracking branch 'origin/master' into fix/stats

This commit is contained in:
07akioni
2026-08-12 21:29:00 +08:00
1021 changed files with 3194 additions and 3311 deletions

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/README.md
README.md: 236531281c17ef982982e97caad99491584bd0b5
README.zh.md: e619ffaa6341f509537342bde90344141d4c8f64
README.zh.md: 73bc3e31c90a4f12c4c5f11e9fd0552601dcc7a6

View File

@@ -1,8 +1,8 @@
# client/ — web GUI 浏览器半侧
# client/ — web GUI 浏览器
[English](README.md) | 中文
dsh web GUI 的浏览器侧shell 启动、浏览器与宿主通信、共享 UI 服务和功能插件。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.md)。除 `test-runtime` 外,均为命名成 `@deepseek-ai/dsh-client-<name>` 的**产品**包。
dsh web GUI 的浏览器侧shell 启动、浏览器与宿主通信、共享 UI 服务和功能插件。编写规则见 [AGENTS.md](AGENTS.md);宿主半侧是 [`host/`](../host/README.md)。除 `test-runtime` 外,均为名为 `@deepseek-ai/dsh-client-<name>` 的**产品**包。
| 包 | 目的 |
|---|---|
@@ -10,7 +10,7 @@ dsh web GUI 的浏览器侧shell 启动、浏览器与宿主通信、共享 U
| [`modules/`](modules/README.md) | 加载浏览器侧客户端模块。 |
| [`web-react/`](web-react/README.md) | 连接 shell 运行时与 React 渲染。 |
| [`connection/`](connection/README.md) | 维护浏览器与宿主之间的 RPC 通信和事件传递。 |
| [`runtime/`](runtime/README.md) | 为会话、Workspace 和 UI 组合提供共享客户端服务。 |
| [`runtime/`](runtime/README.md) | 为会话、工作区和 UI 组合提供共享客户端服务。 |
| [`hmr/`](hmr/README.md) | 在开发期间刷新客户端插件。 |
| [`locale/`](locale/README.md) | 提供本地化偏好与消息词典。 |
| [`schema-form/`](schema-form/README.md) | 为设置编辑器提供 schema 驱动的草稿处理。 |
@@ -20,9 +20,9 @@ dsh web GUI 的浏览器侧shell 启动、浏览器与宿主通信、共享 U
| [`ui-primitives/`](ui-primitives/README.md) | 提供共享 React 控件、图标和内容渲染器。 |
| [`ui-attachment/`](ui-attachment/README.md) | 提供附件展示原子组件:草稿图片栏、消息画廊与灯箱。 |
| [`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-sidebar/`](ui-sidebar/README.md) | 展示工作区与会话导航。 |
| [`ui-workspace/`](ui-workspace/README.md) | 提供工作区选择与创建界面。 |
| [`ui-conversation/`](ui-conversation/README.md) | 展示当前话及其输入界面。 |
| [`ui-tool/`](ui-tool/README.md) | 编排工具调用树和按工具键控的视图。 |
| [`ui-workflow-run/`](ui-workflow-run/README.md) | 把持久工作流运行回放为 Chat 嵌套折叠项,并只为实时子 Session 提供导航。 |
| [`ui-goal/`](ui-goal/README.md) | 展示和管理当前目标。 |
@@ -30,14 +30,14 @@ dsh web GUI 的浏览器侧shell 启动、浏览器与宿主通信、共享 U
| [`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-subagent/`](ui-subagent/README.md) | 提供 subagent(子 agent导航、子级 transcript文本记录状态和内联引用。 |
| [`ui-task/`](ui-task/README.md) | 在会话标题栏列出当前会话的后台任务。 |
| [`ui-model/`](ui-model/README.md) | 在话界面中提供模型选择。 |
| [`ui-model/`](ui-model/README.md) | 在话界面中提供模型选择。 |
| [`ui-permission/`](ui-permission/README.md) | 配置默认权限并切换当前会话的访问模式。 |
| [`ui-plan/`](ui-plan/README.md) | 展示生效中的 plan mode 状态及其退出控件。 |
| [`ui-plugin-config/`](ui-plugin-config/README.md) | 插件设置分区:把宿主平面的插件配置呈现为可展开卡片。 |
| [`ui-question/`](ui-question/README.md) | 展示 agent 请求的交互式问题。 |
| [`ui-agent-preset/`](ui-agent-preset/README.md) | 选择会话的 agent 预设,并创作预设组。 |
| [`ui-agent-preset/`](ui-agent-preset/README.md) | 选择会话的 agent 预设,并编写预设组。 |
| [`ui-settings/`](ui-settings/README.md) | 承载设置界面及其扩展区域。 |
| [`ui-settings-general/`](ui-settings-general/README.md) | 提供常规设置分区。 |
| [`ui-models/`](ui-models/README.md) | 提供模型提供方配置与 DeepSeek 配置引导。 |
@@ -45,4 +45,4 @@ dsh web GUI 的浏览器侧shell 启动、浏览器与宿主通信、共享 U
每个子文档负责自身的约定和详细行为。[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)负责跨包组合与加载决策。
子系统参考是 [client-modules.md](../../docs/subsystems/client-modules.md)[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)是权威 slot 模型,[web 客户端架构说明](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md)拥有加载链与对象层。
子系统参考是 [client-modules.md](../../docs/subsystems/client-modules.md)[slot 系统标准](../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)是权威 slot 模型,[web 客户端架构 Agent Note](../../.agents/notes/implemented/architecture/2026-07-19-gui-web-client-architecture.md)拥有加载链与对象层。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/connection/README.md
README.md: d3727df981ecbc022345def48b38fbc879e29cb2
README.zh.md: 69af4cdba553399c0db0e0587e540c2a7f5375d2
README.zh.md: 5e632bf6d7135bda60178f0699691aff2d5623db

View File

@@ -2,15 +2,15 @@
[English](README.md) | 中文
协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 当前页面的 loopback 状态 + 可观察且按 generation 生效的 `hostDescription` + 单消费方流循环启动器);导出表层携带协议约定类型、`AbstractApiClient` 抽象,以及循环的 sink配置类型。每次就绪握手成功后都会在 `onConnected` 之前发布完整的 `host.describe`generation 失效或显式 stop 会清空它,因此原生能力消费者不会保留已经断线的判断。浏览器载体以 HTTP POST 发送 unaryrespond并为 `events.mux``events.host` 各开一条只下行的 WebSocket进程内载体满足同一双流抽象。Host half 持有唯一 `/api` route 及其 Fetch bridge已注册的 TypeRT interceptor 会先认领自己的 Remote endpoint未认领请求再回退 API Proxy。Loopback hostname 判定逻辑留在包内部:`/api` Host fence 与 WebSocket upgrade 会直接使用它,其他客户端插件则消费派生的 `ctx.connection.isLoopback` 状态。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory``host.openPath`,以及整个配置面——`settings.describe`/`openDocument`/`update`/`replace`/`mutate``credentials.describe`/`set`/`unset`;读取与原生操作也在内,因为 describe 会返回已暴露的配置、打开操作会作用于 Host 桌面,而探测任意引用会报出某条凭据来自何处——以及 agent preset 的创作面 `agentPreset.read`/`copy`/`openDocument`/`remove`,因为组装指明了一个会话所运行的插件,读取它是侦察,而 copy/remove/openDocument 管理名单并驱动宿主桌面(创作只有复制一种写入,因此这些方法都不接收组装文本或路径);`agentPreset.list``agentPreset.select` 不在其中——名单只携带 id 与信任级别,而选择一个 preset 并不比 `session.create` 自带的 `agentPreset` 多给任何能力,何况默认 preset 本就带着 bash以空信任表过信任 fence从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台载体与 ConnectionController 循环属于包内部apply 负责选择并驱动它们。下行边界见 [WebSocket 下行载体 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md)。
协议消费层:客户端插件的 apply 会挂载 `ctx.connection`(共享 API 客户端 + 当前页面的 loopback 状态 + 可观察且按 generation 生效的 `hostDescription` + 单消费方流循环启动器);导出表层携带协议约定类型、`AbstractApiClient` 抽象,以及循环的 sink配置类型。每次就绪握手成功后都会在 `onConnected` 之前发布完整的 `host.describe`generation 失效或显式 stop 会清空它,因此原生能力消费者不会保留已经断线的判断。浏览器载体以 HTTP POST 发送 unaryrespond并为 `events.mux``events.host` 各开一条只下行的 WebSocket进程内载体满足同一双流抽象。Host half 持有唯一 `/api` route 及其 Fetch bridge已注册的 TypeRT interceptor 会先认领自己的 Remote endpoint未认领请求再回退 API Proxy。Loopback hostname 判定逻辑留在包内部:`/api` Host fence 与 WebSocket upgrade 会直接使用它,其他客户端插件则消费派生的 `ctx.connection.isLoopback` 状态。node 半侧的 `/api` 路由让特权方法集(`host.pickDirectory``host.openPath`,以及整个配置面——`settings.describe`/`openDocument`/`update`/`replace`/`mutate``credentials.describe`/`set`/`unset`;读取与原生操作也在内,因为 describe 会返回已暴露的配置、打开操作会作用于 Host 桌面,而探测任意引用会报出某条凭据来自何处——以及 agent(智能体) preset 的创作面 `agentPreset.read`/`copy`/`openDocument`/`remove`,因为组装指明了一个会话所运行的插件,读取它是侦察,而 copy/remove/openDocument 管理名单并驱动宿主桌面(创作只有复制一种写入,因此这些方法都不接收组装文本或路径);`agentPreset.list``agentPreset.select` 不在其中——名单只携带 id 与信任级别,而选择一个 preset 并不比 `session.create` 自带的 `agentPreset` 多给任何能力,何况默认 preset 本就带着 bash以空信任表过信任 fence从而钉在回环——已声明的 `trustedHosts` 授权可达其余全部方法,而这些方法在真正的认证层出现之前仍只限回环本机。平台载体与 ConnectionController 循环属于包内部apply 负责选择并驱动它们。下行边界见 [WebSocket 下行载体 Agent Note](../../../.agents/notes/implemented/architecture/2026-08-04-websocket-downlink-carrier.md)。
## /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 不参与引导的组合。这道栅栏是可达性策略而不是认证Web 载体不提供认证层。决策记录:[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 失败在启动任何事件流前拒绝握手。因此非回环(`--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 下行
`/api/events.mux``/api/events.host` 各接受一条 WebSocket upgrade并只向浏览器发送对应的 `ServerRequest` text message;客户端不会在这些 socket 上发送业务数据。任一 socket 结束都会使当前 connection generation 失败并重建两条流,连接就绪仍要求两条 socket open `host.describe` HTTP 调用成功。Host teardown 会终止两条 socket、中止各自的 source并等待 source 清理完成后再返回。普通网络 GET 这些路径会返回 426不保留 SSE 回退;`toFetchHandler` 的 SSE 编解码只服务进程内同构载体。
`/api/events.mux``/api/events.host` 各接受一条 WebSocket upgrade并只向浏览器发送对应的 `ServerRequest` 文本消息;客户端不会在这些 socket 上发送业务数据。任一 socket 结束都会使当前 connection generation 失败并重建两条流,连接就绪仍要求两条 socket 均已打开`host.describe` HTTP 调用成功。Host teardown 会终止两条 socket、中止各自的 source并等待 source 清理完成后再返回。普通网络 GET 这些路径会返回 426不保留 SSEServer-Sent Events回退;`toFetchHandler` 的 SSE 编解码只服务进程内同构载体。
## 模型体验

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/modules/README.md
README.md: efaff699839b977cc45f89f3c164402241b90dc2
README.zh.md: 772a4870f7ef6730d9d3d4db434ed771d97984f0
README.zh.md: 6ab3c8494d7ea902e8b891008f236aec41301a35

View File

@@ -2,13 +2,13 @@
[English](README.md) | 中文
客户端模块系统Node 内部 ESM loader 的浏览器端对等实现,以惰性 CJS 表实现。web 外壳挂载 vendored cordis Loader 来治理配置项fiber 生命周期、inject 等待、update/refresh并通过其 `internal` 约定注入该包package`ClientModuleLoader`vendored 一侧唯一的消费点是 `EntryTree.import`,因此替换 `internal` 恰好只会替换「插件代码如何到达」,不会改变其他内容。
客户端模块系统Node 内部 ESM loader 的浏览器端对等实现,以惰性 CJS 表实现。web 外壳挂载 vendored cordis Loader 来治理配置项fiber 生命周期、inject 等待、update/refresh并通过其 `internal` 约定注入该包的 `ClientModuleLoader`vendored 一侧唯一的消费点是 `EntryTree.import`,因此替换 `internal` 恰好只会替换「插件代码如何到达」,不会改变其他内容。
惰性 CJS 模型web2执行插件组合包只会注册其 factory`window.__ModuleLoader__.load({id, factory})`);每个模块主体的副作用(包括 CSS 注入)都位于 factory 闭包中,在物化时运行(`factory(require)` → 导出表层,并在 `loadCache` 中记忆化),不会在脚本执行时运行。如果 factory 依赖另一个已注册但尚未物化的模块系统会递归物化它因此加载顺序无需外部编排require 循环会抛出异常factory 形式的 CJS 无法提供部分导出)。`<id>/client` 与裸 id 指向同一表层(一个插件组合包就是其包的客户端侧)。
惰性 CJS 模型web2执行插件 bundle 只会注册其 factory`window.__ModuleLoader__.load({id, factory})`);每个模块主体的副作用(包括 CSS 注入)都位于 factory 闭包中,在物化时运行(`factory(require)` → 导出表层,并在 `loadCache` 中记忆化),不会在脚本执行时运行。如果 factory 依赖另一个已注册但尚未物化的模块系统会递归物化它因此加载顺序无需外部编排require 循环会抛出异常factory 形式的 CJS 无法提供部分导出)。`<id>/client` 与裸 id 指向同一表层(一个插件 bundle 就是其包的客户端侧)。
解析分支顺序(`import(specifier)`):平台种子词 → 外壳实例;记忆化记录 → 表层;外壳自身的静态注册表(`registerStatic`app-shell→ 模块;已注册 factory → 物化;模块图记录(`window.__DSH_BOOT__`)→ 加载外部 classic script + 物化;其他情况一律抛出异常。这是构建时组合包纯度门禁的运行时镜像。交给 factory 的同步 `require` 采用相同顺序,但不含异步加载分支,并把观察到的边记录到模块记录中。`prefetch` 是第一阶段到达钩子(只加载脚本并注册 factory并发调用共享一个进行中的任务`invalidate` 会丢弃 factory 与物化记录,使下一次 prefetch/import 重新加载脚本;它是 HMR热模块替换钩子。
解析分支顺序(`import(specifier)`):平台种子词 → 外壳实例;记忆化记录 → 表层;外壳自身的静态注册表(`registerStatic`app-shell→ 模块;已注册 factory → 物化;模块图记录(`window.__DSH_BOOT__`)→ 加载外部 classic script + 物化;其他情况一律抛出异常。这是构建时 bundle 纯度门禁的运行时镜像。交给 factory 的同步 `require` 采用相同顺序,但不含异步加载分支,并把观察到的边记录到模块记录中。`prefetch` 是第一阶段到达钩子(只加载脚本并注册 factory并发调用共享一个进行中的任务`invalidate` 会丢弃 factory 与物化记录,使下一次 prefetch/import 重新加载脚本;它是 HMR热模块替换钩子。
Node 侧会扫描已启用的 Loader 配置项以发现 web `dsh.client` 包,解析每个 `exports["./client"]`,把构建后的组合包哈希写入启动图,并通过 `/plugins` 提供该文件及其 sourcemap。源码启动会把宿主侧导入映射到 TypeScript 源码,但仍消费这一构建后的客户端导出;缺失文件共享一条构建说明,随后以 package/path list 列出各项,而无关的文件系统错误仍是独立故障。
Node 侧会扫描已启用的 Loader 配置项以发现 web `dsh.client` 包,解析每个 `exports["./client"]`,把构建后的 bundle 哈希写入启动图,并通过 `/plugins` 提供该文件及其 sourcemap。源码启动会把宿主侧导入映射到 TypeScript 源码,但仍消费这一构建后的客户端导出;缺失文件共享一条构建说明,随后以包/路径列表列出各项,而无关的文件系统错误仍是独立故障。
## 模型体验
@@ -20,5 +20,5 @@ Node 侧会扫描已启用的 Loader 配置项以发现 web `dsh.client` 包,
## 已知限制与暂缓事项
- **有意采用扁平模块图**:每个组合包是一个模块节点,其边只指向表中的叶节点;接口(`loadCache`/`edges`/`invalidate`)已经支持通用模块图,因此可以改变 externalization 粒度而不更改接口。
- **自身不记录卸载账目**:样式移除与 fiber 拆卸顺序属于 HMR 驱动器(`@deepseek-ai/dsh-client-hmr`loader 只在每条记录中登记其拥有的样式标签 id。
- **有意采用扁平模块图**:每个 bundle 是一个模块节点,其边只指向表中的叶节点;接口(`loadCache`/`edges`/`invalidate`)已经支持通用模块图,因此可以改变 externalization 粒度而不更改接口。
- **自身不维护卸载记录**:样式移除与 fiber 拆卸顺序属于 HMR 驱动器(`@deepseek-ai/dsh-client-hmr`loader 只在每条记录中登记其拥有的样式标签 id。

View File

@@ -3,4 +3,4 @@
# 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: 441f5462d98c8a9e92ebc9f08cdd7568e89fd427
README.zh.md: 9e31d51410639cdd314ddd37c2c6287f26f8c484
README.zh.md: f4ec9ac53c10f8f375c837556db7623c95d11c17

View File

@@ -24,7 +24,7 @@ Workspace 和 Session 列表各自具有单调的 `pending` → `ready` 基线
`WorkspaceListState.archivedSessionIds` 镜像 Host 的注册表级全局归档集合(一个按 Host 顺序的 `readonly SessionId[]`,仅在成员变化时才替换;需要 O(1) 查询的消费方自建临时 Set。它是全快照状态`workspace.list` 基线、`archiveSession` 一元回声和 `host/archived-sessions-changed` 帧各自安装完整集合。`WorkspacesService.archiveSession(sessionId)` 通过 wire 归档;投影层在当前 selection 落入归档集合时统一清空为 New Session 视图状态——一条规则同时覆盖本地回声、其他标签页的帧、以及重连基线恢复出一个离线期间被归档的 selection。在 `workspace.list` 请求进行中安装的集合还会取代该过期基线携带的集合。各分组视图在所有位置隐藏集合成员,而会话行本身仍留在列表 store 中。
SlotsService 分别为 renderer 提供 `useSessions``useWorkspaces` 的裸 observableweb-react 创建钩子。Workspace 业务状态不会进入 `SessionListState`配置项 store。
SlotsService 分别为 renderer 提供 `useSessions``useWorkspaces` 的裸 observableweb-react 创建钩子。Workspace 业务状态不会进入 `SessionListState`条目 store。
`indexSubagentDescendants()` 从保留的列表镜像中派生每个 parent 的后代总数与运行中后代数。它只沿不间断的 `origin: 'subagent'` 祖先链追踪,因此普通 fork 会开启独立的归属子树;遇到环时,追踪会停止但不会抛出异常,缺失的 parent 则会保留为无害的键,直至其摘要到达。
@@ -40,7 +40,7 @@ SlotsService 分别为 renderer 提供 `useSessions` 与 `useWorkspaces` 的裸
## 待处理队列投影
`ConversationSnapshot.queue` 是 Host 提供的 `agent.inbox.nextTurn` 权威瞬态快照;待处理的 next-step steering中途引导不进入此投影。每行携带其 `MessageId`、所有内容块均为文本时的完整可编辑文本以及扁平化预览。Host 根据持久 `agent/inbox/spliced` 变更派生完整 `session/queue` 快照,并在重连时发送基线;面向单条消息的 `agent/inbox/inserted``claimed``discarded` 通知不用于重建该投影。`Session.updateQueue()` 经 Host 侧 `Inbox.splice()` 发送编辑/移除操作,客户端不做乐观变更,因此下一份 Host 快照是唯一可见的提交结果claim 竞态则会返回 `queue-item-not-found`
`ConversationSnapshot.queue` 是 Host 提供的 `agent.inbox.nextTurn` 权威瞬态快照;待处理的 next-step steering中途引导不进入此投影。每行携带其 `MessageId`、所有内容块均为文本时的完整可编辑文本以及扁平化预览。Host 根据持久 `agent/inbox/spliced` 变更派生完整 `session/queue` 快照,并在重连时发送基线;面向单条消息的 `agent/inbox/inserted``claimed``discarded` 通知不用于重建该投影。`Session.updateQueue()` 经 Host 侧 `Inbox.splice()` 发送编辑/移除操作,客户端不做乐观变更,因此下一份 Host 快照是唯一可见的提交结果claim 竞态则可能呈现 `queue-item-not-found`
## Conversation 组装
@@ -66,28 +66,28 @@ Trajectory Definition 组装出一条按时间顺序排列、以用途为判别
## 模型重试投影
Host 所属的 LLM retry invariant 会在持久追加边界验证按提供方路由的 `llm/retry``llm/retry-started` 记录,包括标识、顺序、计时器、整数、状态、提供方延迟和非空诊断字段约定。客户端的 Retry、Assistant 与 Turn Error Definition 把这些记录和 Assistant、TurnStep 事件一起折叠:失败步骤的流式输出片段会被移除,并在 retry 事件的序列位置插入一条持久重试提示。该提示在匹配的 started 记录到达前为 `scheduled`;如果所属 Step 或 Turn 先关闭,则标记为 `cancelled`started 记录到达后则标记为 `started`。normal mode 提示携带其有限上限always mode 提示保持显式无界。没有重试的终态 `turn/end` 错误会从持久消息与可选错误码投影出一个 `turn-error` 节点AUTH 投影会把可能回显凭据片段的提供方文案替换为 `API key is invalid`,原始诊断仍保留在会话日志中。进入重试的失败只保留该次尝试的重试提示。窗口重建与历史回放使用同一组 Definition因此刷新既不会让已丢弃的分片重新出现也不会丢失终态失败反馈。可见但尚未定稿的输出会在终态错误旁冻结为中断的 Assistant 节点。
Host 所属的 LLM(大语言模型)retry invariant 会在持久追加边界验证按提供方路由的 `llm/retry``llm/retry-started` 记录,包括标识、顺序、计时器、整数、状态、提供方延迟和非空诊断字段约定。客户端的 Retry、Assistant 与 Turn Error Definition 把这些记录和 Assistant、TurnStep 事件一起折叠:失败步骤的流式输出片段会被移除,并在 retry 事件的序列位置插入一条持久重试提示。该提示在匹配的 started 记录到达前为 `scheduled`;如果所属 Step 或 Turn 先关闭,则标记为 `cancelled`started 记录到达后则标记为 `started`。normal mode 提示携带其有限上限always mode 提示保持显式无界。没有重试的终态 `turn/end` 错误会从持久消息与可选错误码投影出一个 `turn-error` 节点AUTH 投影会把可能回显凭据片段的提供方文案替换为 `API key is invalid`,原始诊断仍保留在会话日志中。进入重试的失败只保留该次尝试的重试提示。窗口重建与历史回放使用同一组 Definition因此刷新既不会让已丢弃的分片重新出现也不会丢失终态失败反馈。可见但尚未定稿的输出会在终态错误旁冻结为中断的 Assistant 节点。
reason 为 `max-tokens``turn/end` 会在该轮位置投影出一个 `turn-max-tokens` 节点:一条 warning 样式的本地化提示,说明回答在单次请求的输出 token 上限处停止,已截断的输出保留在对话流中,并提示发送“继续”可在新一轮接着输出。事件本身不携带 token 数量,提示因此不显示任何数字。窗口重建与历史回放使用同一 Definition 重建该节点,刷新和恢复后结束原因保持一致。
## 会话 fork
`ISessions.fork({sessionId, atSeq?, increaseTitle?})` 只在子会话摘要已能在本地寻址后才完成;该摘要携带源会话的谱系和 cwd`blank: false`,由调用方决定是否打开。`increaseTitle: true` 会在 client 端源会话的持久化标题改名到子会话:尾部 `(N)``N` 递增并保留括号样式,其余标题追加 ` (1)`;源会话没有持久化标题时跳过改名,改名失败时拒绝 promise 但保留已创建的子会话。该选项不会进入 Host fork 请求。即使响应为 `workspace-attach-failed`,其中仍会标识 Host 已发布的子会话,因此 `SessionManager` 会先将这一部分成功对账,再让 `SessionForkError` 到达调用方,避免重试创建重复的子会话。
`ISessions.fork({sessionId, atSeq?, increaseTitle?})` 只在子会话摘要已能在本地寻址后才完成;该摘要携带源会话的谱系和 cwd`blank: false`,由调用方决定是否打开。`increaseTitle: true` 会在 client 端根据源会话的持久化标题重命名子会话:尾部 `(N)``N` 递增并保留括号样式,其余标题追加 ` (1)`;源会话没有持久化标题时跳过改名,改名失败时拒绝 promise 但保留已创建的子会话。该选项不会进入 Host fork 请求。即使响应为 `workspace-attach-failed`,其中仍会标识 Host 已发布的子会话,因此 `SessionManager` 会先将这一部分成功对账,再让 `SessionForkError` 到达调用方,避免重试创建重复的子会话。
## 会话模型选择
每个常驻 `Session` 都拥有一个 `modelSelection` 快照,其中包含当前提供方/模型目标、按提供方分组的目录、逐提供方失败记录,以及 `idle``loading``ready``selecting``error` 状态。历史记录会建立或刷新当前目标,打开选择器会刷新目录;选择失败会保留上一个目标和可用分组。目录与选择操作共用单调递增的代次,因此较旧响应无法覆盖较新的选择。重连重建会恢复 Host 报告的目标,同时不替换未变化的选择子结构。
每个常驻 `Session` 都拥有一个 `modelSelection` 快照,其中包含当前模型选择、按提供方分组的目录、逐提供方失败记录,以及 `idle``loading``ready``selecting``error` 状态。历史记录会建立或刷新当前模型选择,打开选择器会刷新目录;选择失败会保留上一次模型选择和可用分组。目录与选择操作共用单调递增的代次,因此较旧响应无法覆盖较新的模型选择。重连重建会恢复 Host 报告的模型选择,同时不替换未变化的选择子结构。
## 模型体验
无,因为会话对象层会选择后续 Host 请求使用的提供方/模型路由,但不添加任何模型可见内容。
无,因为会话对象层会选择后续 Host 请求使用的提供方模型路由,但不添加任何模型可见内容。
#### KV Cache 影响
更改目标可能改变提供方侧的缓存复用,或使其失效;该包本身不会改变提示词前缀。
更改模型选择可能改变提供方侧的缓存复用,或使其失效;该包本身不会改变提示词前缀。
## 已知限制与暂缓事项
- **`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 永远无法匹配。
- **插件 bundle 从该包导入值时必须使用 `/client` 子路径**:裸包名不在 loader externals 表中,会内联第二个模块实例;其私有 scope-tag Symbol 永远无法匹配。

View File

@@ -3,4 +3,4 @@
# 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: ef1d2f9d8ce936fe60d38849f975dc8c0a08ded4
README.zh.md: aff77bc4c31a9aaa13977480d32551536ddceadd
README.zh.md: 315e508ab1a2837acf4d159795cdcc93dedd72fa

View File

@@ -2,22 +2,22 @@
[English](README.md) | 中文
面向 settings 编辑器的 schema草稿模型层。wire 侧的 `settings.describe` 携带每个 namespace 的序列化 schemastery schema`schema.toJSON()` 的 ref 封);`rehydrateSchema``new Schema(json)` 将其还原rehydrate为活的校验器——在宿主上校验分节的那份 schema 对象,就是在浏览器里校验草稿的那份对象,因此客户端校验绝不会偏离 Service Definition 的校验。编辑器各自渲染自己的控件Models 页围绕它在此探测到的字段手写自己的卡片);该包不含任何 React也不做任何渲染。
面向 settings 编辑器的 schema草稿模型层。wire 侧的 `settings.describe` 携带每个 namespace 的序列化 schemastery schema`schema.toJSON()` 的 ref 封`rehydrateSchema``new Schema(json)` 将其还原rehydrate为活的校验器——在宿主上校验分节的那份 schema 对象,就是在浏览器里校验草稿的那份对象,因此客户端校验绝不会偏离 Service Definition 的校验。编辑器各自渲染自己的控件Models 页围绕它在此探测到的字段手写自己的卡片);该包不含任何 React也不做任何渲染。
## 约定
编辑的单元是**用户分节草稿**:一个以不可变方式编辑的普通对象(`setPath` 会物化中间对象,`deletePath` 即逐字段重置——去掉该键,解析值便回退到组合 base 与 schema 默认值)。字段只要出现在草稿中就被标记为**已覆盖**`hasPath`)——判定采用存在性语义而非值比较,与 settings seam 的分层方式严格对应。`nodeAtPath` 解析可配置提供方目录 `settingsPath` 所寻址的 schema 节点object 属性按名称解析dict 条目经由 `inner`),编辑器因此可以在决定渲染什么之前,先探测某提供方的 profile 携带哪些字段(及其 `meta.role`);无法解析的路径返回 `undefined`,调用方因此会大声降级,而不是渲染出错误的子树。`validateDraft(schema, draft)` 运行还原出的校验器并返回其失败消息,页面因此可以在写入前拒绝无效草稿。
编辑的单元是**用户分节草稿**:一个以不可变方式编辑的普通对象(`setPath` 会物化中间对象,`deletePath` 即逐字段重置——去掉该键,解析值便回退到组合 base 与 schema 默认值)。字段只要出现在草稿中就被标记为**已覆盖**`hasPath`)——判定采用存在性语义而非值比较,与 settings seam 的分层方式严格对应。`nodeAtPath` 解析可配置提供方目录 `settingsPath` 所寻址的 schema 节点object 属性按名称解析dict 条目经由 `inner`),编辑器因此可以在决定渲染什么之前,先探测某提供方的 profile 携带哪些字段(及其 `meta.role`);无法解析的路径返回 `undefined`,调用方因此会明确进入降级路径,而不是渲染出错误的子树。`validateDraft(schema, draft)` 运行还原出的校验器并返回其失败消息,页面因此可以在写入前拒绝无效草稿。
## Model Experience
## 模型体验
无。该包支撑的是浏览器配置编辑器;这里没有任何内容进入模型请求。
#### KV Cache effect
#### KV Cache 影响
无;该包既不组装也不发送提供方请求。
## Known Limitations and Deferred Work
## 已知限制与暂缓事项
- **重建 schema 会执行所收到的封**——`rehydrateSchema` 会重建一个活的 schemastery 校验器,而 schemastery 通过 `new Function` 复活序列化过的 callback,因此 schema 信封是可执行内容,而非惰性数据。只有信封来自提供该页面的同一受信任 host 时才安全;该协议没有跨信任边界使用的惰性表示。
- **重建 schema 会执行所收到的封**——`rehydrateSchema` 会重建一个活的 schemastery 校验器,而 schemastery 通过 `new Function` 复活序列化过的回调函数,因此 schema 信封是可执行内容,而不是不可执行数据。只有该封装来自提供该页面的同一受信任宿主时才安全;该协议没有跨信任边界使用的不可执行表示。
- **校验是草稿级的,而非逐字段**——`validateDraft` 报告 schemastery 的第一条失败消息及其 `$.path`;它不会把错误映射到各个控件。
- **没有通用渲染器**——消费方在这些辅助函数上构建功能专用表单。[Web 配置面 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-30-web-config-plane.md) 记录该权衡。

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-agent-preset/README.md
README.md: f0fdff4b1f2453be9e8b4c1d20f7ca0fee506d55
README.zh.md: c391a4548f2bd2fc4bfe168e9d494bc0073a8cb2
README.zh.md: 7d78d50c9198363c7a04cc2eb1c9292126feb9e8

View File

@@ -56,7 +56,7 @@ preset 自行发布描述,长度不限,而网格让每一行卡片等高—
未组装任何 preset 的部署返回空名单本行、chip、标签与分区都不渲染任何内容——此时每个会话共用宿主组装也就无从选择或管理。未配置可写根目录的部署返回 `authorable: false`,分区随之退化为只读浏览:随附组装仍可在查看器中打开,但每个复制操作都被禁用并以原因作提示,而不是给出一个创建必然失败的对话框。
## Model Experience
## 模型体验
Indirectly, through the preset a later session is composed from; [`dsh-agent-presets`](../../preset/agent-presets/README.md) owns what that composition puts in front of the model.
@@ -64,7 +64,7 @@ Indirectly, through the preset a later session is composed from; [`dsh-agent-pre
没有直接的失效影响。更改默认值绝不触及运行中会话的前缀;此后创建的会话依据它自己的组装建立自己的前缀。
## Known Limitations and Deferred Work
## 已知限制与暂缓事项
- **没有元数据的 preset 按 id 列出** —— 展示文本是可选的,未取名的副本刻意回退到目录名,而不是与其来源呈现得一模一样。
- **展示的路径是文本,不是链接** —— 宿主没有桌面打开器时,卡片显示目录供手工复制;浏览器自身无法打开宿主文件系统上的位置。

View File

@@ -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-command/README.md
README.md: 0281df76fe601eaad86cefc0dcaecc6d8999df60
README.zh.md: ace51230d6d250f4a3b09a5212182ccf434f9249
README.zh.md: 87c98cc92b80fe62a7522bf95a5612c388e07a1a

View File

@@ -2,17 +2,17 @@
[English](README.md) | 中文
客户端命令 API`ctx.command`):以会话为 key 的命令目录缓存、带 `matchSpace``matchEnter` 决策钩子的 `/` 命令 source、三类派发`execute``popupSelect``leadingInput`),以及面向业务包的 popupSelect 注册。[Web 命令 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-25-web-command-surfaces-and-assembly.zh.md)记录了这项决策。
客户端命令 API`ctx.command`):以会话为 key 的命令目录缓存、带 `matchSpace``matchEnter` 决策钩子的 `/` 命令 source、三类派发`execute``popupSelect``leadingInput`),以及面向业务包的 popupSelect 注册。[Web 命令 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-25-web-command-surfaces-and-assembly.zh.md) 记录了这项决策。
`src/client/contract.ts` 是固定的业务 API 约定:`CommandServiceContract.register(name, spec)``decorate(name, spec)` 是业务包消费的全部内容;`CommandUiSpec{options, onSelect}` 自己提供 popup 数据——外层组件归本包所有,业务包永远见不到它。contribution 是 client 自有命令(与 host 同名碰撞即 fail-louddecoration装饰则为**已存在的** host 命令添加裸调用 popup。host 保留目录行、带参 claimspace / 带参 Enter与生命周期记账被装饰的名字若在会话目录中无 host 行,则永不触发。命令类型按每次派发派生,绝不在注册时定型:带 `input` 的 host descriptor 是 `leadingInput`,注册了 `CommandUiSpec` 的是 `popupSelect`,其余全部是 `execute`
`src/client/contract.ts` 是固定的业务 API 约定:`CommandServiceContract.register(name, spec)``decorate(name, spec)` 是业务包消费的全部内容;`CommandUiSpec{options, onSelect}` 自己提供 popup 数据——外层组件归本包所有,业务包永远见不到它。贡献项是客户端自有命令(与 host 命令同名时会明确报错);装饰则为**已存在的** host 命令添加裸调用 popup。host 保留目录行、带参 claim空格/带参数的 Enter与生命周期记账被装饰的名字若在会话目录中无 host 行,则永不触发。命令类型按每次派发派生,绝不在注册时定型:带 `input` 的 host descriptor 是 `leadingInput`,注册了 `CommandUiSpec` 的是 `popupSelect`,其余全部是 `execute`
`CommandDirectory``src/client/directory.ts`)是唯一的 wire 派生缓存,以会话为 key。普通会话通过 `command.list({sessionId})` 拉取source 的 scope 出生 `warm` 钩子会预热该会话的缓存项。由目录寻址的可继续子代理会在客户端解析为空命令目录:`command.list` 绑定 Agent若预热它就会仅因查看持久化历史而激活子代理。缓存项由转发的 owner 事件 `commands/change` 软失效(重拉在途期间旧快照继续服务),也由转发的 `agent-preset/selected` 对该会话单独软失效(重组 agent 不产生任何注册,注册表级信号不会为它触发),由 `connection/reset` 硬失效,并以 epoch 把关,被取代的旧拉取永远无法覆盖更新的结果。`matchSpace` 只凭该缓存同步应答;`matchEnter` 在 SubmitAttempt 信号上强等缓存,预热失败即拒绝——`/` 开头的一行绝不会被静默降级为普通提示词。
`command.execute` 返回已匹配的命令结果后,当前浏览器会发布本地 `command/executed(sessionId, name, result)`。其他客户端只会通过 Host 事件流收到持久命令节点,不会收到这条确认,因此浏览器专属副作用可以筛选由实际提交命令的客户端收到的成功结果,而不会把 Session 回放当成操作请求。监听器失败会逐项记录并隔离,不会改变已经准入的命令结果,也不会阻止后续监听器运行。
菜单查询会按顺序且不区分大小写地模糊匹配命令名的子序列。前缀排名最高;其余匹配项按分隔符边界优先、相邻字符优先、间隔越短越优先的规则排序,若仍同分,则以目录顺序和 contribution 顺序打破平局。此行为只影响命令发现space 和 Enter 仍要求命令名精确匹配。原理:[Web 斜杠命令模糊发现](../../../.agents/notes/implemented/feature/2026-08-04-web-slash-command-fuzzy-discovery.md)。
菜单查询会按顺序且不区分大小写地模糊匹配命令名的子序列。前缀排名最高;其余匹配项按分隔符边界优先、相邻字符优先、间隔越短越优先的规则排序,若仍同分,则以目录顺序和贡献项顺序打破平局。此行为只影响命令发现space 和 Enter 仍要求命令名精确匹配。原理:[Web 斜杠命令模糊发现](../../../.agents/notes/implemented/feature/2026-08-04-web-slash-command-fuzzy-discovery.md)。
`PopupSelectController``src/client/popup.ts`)是无头的壳状态:`PopupSelectView` 自行注册进 `conversation.input.overlay`SlotMap key 归 ui-conversation 所有;本包只以 type-only 导入引入该声明——没有运行时依赖边。壳是打开期间持有焦点的瞬态层onSelect 之后的 token 片段消费在两条分支上都经 `consumeTokenSegment` 执行(菜单路径做 span CAS回车路径做裸 token 相等比较),作用于接线层经 `bindDraft` 绑定的草稿表层。
`PopupSelectController``src/client/popup.ts`)是不含界面的外壳状态:`PopupSelectView` 自行注册进 `conversation.input.overlay`SlotMap key 归 ui-conversation 所有;本包只以 type-only 导入引入该声明——没有运行时依赖边。壳是打开期间持有焦点的瞬态层onSelect 之后的 token 片段消费在两条分支上都经 `consumeTokenSegment` 执行(菜单路径做 span CAS回车路径做裸 token 相等比较),作用于接线层经 `bindDraft` 绑定的草稿表层。
`/client` 入口导出插件主体(`apply``inject`)、`CommandService`、目录类和 popup 类及其状态类型,以及固定的约定类型;外层组件本身是 overlay 注册的内部实现。
@@ -26,4 +26,4 @@
## 已知限制与暂缓事项
- **脱离会话后detached result 的 notice 回退到 console**fire-and-forget 路径经 `SessionInput.notify` 把结果送到触发会话的编辑器;会话拆除console 输出行是仅剩的呈现面。
- **脱离会话后detached result 的 notice 回退到 console**fire-and-forget 路径经 `SessionInput.notify` 把结果送到触发会话的 composer;会话销毁console 输出行是仅剩的呈现面。

View File

@@ -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-conversation/README.md
README.md: 131f76fc8bc7b63449e022ad91fd8453c10d5f01
README.zh.md: 55f49f01c6cddd6b02382df742f923c8e159c1ac
README.zh.md: 8d0cd4e82b8bf4256db9b0f3cb868630f57ce68e

View File

@@ -2,31 +2,31 @@
[English](README.md) | 中文
会话领域:骨架(标题栏/标签页/编辑器/空状态)、聊天视图(分组步骤摘要流、流式尾部隔离与轮次状态)、编辑器 dock与输入区一同 sticky 的会话统计行)、输入区 dock队列行加 todo 计划条)、详情壳层,以及按 scope 寻址的 ConversationService。Tool 展示属于 [`ui-tool`](../ui-tool/README.md)。
会话领域:骨架(标题栏/标签页/编辑器/空状态)、聊天视图(分组步骤摘要流、流式尾部隔离与轮次状态)、编辑器 dock与输入区一同 sticky 的会话统计行)、输入区 dock队列行加 todo 计划条)、详情壳层,以及按 scope 寻址的 ConversationService。工具展示属于 [`ui-tool`](../ui-tool/README.md)。
压缩compaction在检查点自身的消息流位置渲染为一行折叠标记不替换其上方的 transcript文本记录。自动压缩使用「上下文已压缩」标题。每个已加载对应 `compact/summary` 事件的完成标记都会显示被替换条目数量和估算 token 数量,并可点击展开摘要。手动 `/compact` 开始时显示为运行中的 `compact` 行;成功结算后,其显式摘要事件引用会在保持同一 React key 的前提下把该命令折叠进检查点行。完成的检查点静止时保留上下文压缩图标,仅在悬停或键盘聚焦时将其替换为收起/展开指示图标。输入被拒绝、没有可压缩历史、取消和失败时仍使用通用命令行及处理器撰写的文本。配对绝不依赖相邻关系,因为压缩运行期间可能注入持久上下文。面向模型的带框检查点载荷绝不渲染;被引用的 `compact/summary` 事件位于已加载窗口之外时,检查点仍然可见但不可展开。
压缩compaction在检查点自身的消息流位置渲染为一行折叠标记不替换其上方的 transcript文本记录。自动压缩使用「上下文已压缩」标题。每个已加载对应 `compact/summary` 事件的完成标记都会显示被替换条目数量和估算 token 数量,并可点击展开摘要。手动 `/compact` 开始时显示为运行中的 `compact` 行;成功结算后,其显式摘要事件引用会在保持同一 React key 的前提下把该命令折叠进检查点行。完成的检查点静止时保留上下文压缩context compaction图标,仅在悬停或键盘聚焦时将其替换为收起/展开指示图标。输入被拒绝、没有可压缩历史、取消和失败时仍使用通用命令行及处理器撰写的文本。配对绝不依赖相邻关系,因为压缩运行期间可能注入持久上下文。面向模型的带框检查点载荷绝不渲染;被引用的 `compact/summary` 事件位于已加载窗口之外时,检查点仍然可见但不可展开。
常驻会话壳会跨无会话与会话状态切换而保留。没有当前会话时,它会锁定消息操作,并让整张虚线编辑器卡片成为根作用域 `conversation.hero.workspace` Workspace picker 的入口textarea 保持只读且支持键盘操作。选择 Workspace 会连接或复用由 Host 拥有的空白会话,并在不替换会话壳的情况下打开该会话。根组件始终拥有同一个滚动容器与 Hero编辑器子树首个会话到达时彼此独立的严格会话页头和主体 outlet 只填入各自区域,因此 Workspace picker、滚动主体、编辑器 seat 与 textarea 都保留原有 React 和 DOM identity。空白会话与活跃会话渲染相同的输入区主体InputHub 则在 Workspace 切换间携带草稿,并将草稿镜像到会话 store。活跃阶段会话标题栏作为普通列 chrome仅显示当前会话标题和视图标签fork 谱系仍保留为会话数据,不投影到标题栏。其下滚动容器(`data-conversation-scroll`)承载流动排版的各视图与 sticky 编辑器栈(统计 dock输入区 dock输入栏。该滚动容器无条件预留自己的滚动条槽选用编辑器 overlay 的视图也仍把它保留为滚动容器,因此无论对话记录是否滚动、无论展示哪个视图标签,输入卡片都保持同一个横向位置([决策](../../../.agents/notes/implemented/bug-fix/2026-08-04-composer-tab-gutter-reservation.md)。textarea 上的滚轮会链式处理:限高草稿先在本地滚动,到达边缘后再转交给该宿主。
别的插件可以经 `ctx.conversation.blocks` 让某个会话的编辑器变为惰性:它设置一个携带自己本地化理由的 block输入栏就渲染同一个禁用的 textarea并把该理由作为 placeholder——复用无 Workspace 时的那套姿态。推送方向是约束而非偏好知道某会话发不出消息的插件ui-model在没有适配器服务其路由时本就依赖本包因此本包读不到它们。模型 seat 是 block 唯一保留可用的控件——这份约定里的每个 block 都靠选模型来解除把它一起锁上会让编辑器索要它自己拦下的那件事。block 只是提示性设计;无论客户端禁用了什么,宿主都会拒绝一个它路由不了的 prompt。两者同时成立时以无 Workspace 姿态为准,因为选 Workspace 是更靠前的前提。
别的插件可以经 `ctx.conversation.blocks` 让某个会话的编辑器变为惰性:它设置一个携带自己本地化理由的 block输入栏就渲染同一个禁用的 textarea并把该理由作为 placeholder——复用无 Workspace 时的那套姿态。推送方向是约束而非偏好知道某会话发不出消息的插件ui-model在没有适配器服务其路由时本就依赖本包因此本包读不到它们。模型 seat 是 block 唯一保留可用的控件——这份约定里的每个 block 都靠选模型来解除把它一起锁上会让编辑器索要它自己拦下的那件事。block 只是提示性设计;无论客户端禁用了什么,宿主都会拒绝一个它无法路由的提示词。两者同时成立时以无 Workspace 姿态为准,因为选 Workspace 是更靠前的前提。
视图环是一个 slot严格会话主体注册在 `children` 表中声明 Session scope `'conversation.view'` 列表,并通过自身的 renderSlot share 渲染活跃配置项(`only: <active id>`);视图标签页则从注册选项(`id``order``label`投影而来。聊天视图是该包自身的配置项ui-trajectory 等插件通过 `ctx.slots.register` 贡献标签页,每个视图负责自己的 chrome。
视图环是一个 slot严格会话主体注册在 `children` 表中声明会话作用域`'conversation.view'` 列表,并通过自身的 renderSlot share 渲染活跃配置项(`only: <active id>`);视图标签页则从注册选项(`id``order``label`投影而来。聊天视图是该包自身的配置项ui-trajectory 等插件通过 `ctx.slots.register` 贡献标签页,每个视图负责自己的 chrome。
Chat 业务行是彼此独立的注册表贡献不是封闭的内建联合。Client 插件通过 declaration merging 增加类型化 `ChatNodeDataMap` key`ctx.conversationEvents` 上注册 `ConversationNodeDefinition`,再向 `conversation.chat.node` 注册匹配的 keyed renderer它无须修改 Session fold 或中央 renderer switch。稳定事件 id、append/prepend 回放、Location data 与 renderer 约束见 [Conversation Node 实操手册](../../../docs/cookbook/adding-a-conversation-node.md)。
Chat 业务行是彼此独立的注册表贡献不是封闭的内建联合。Client 插件通过 declaration merging 增加类型化 `ChatNodeDataMap` key`ctx.conversationEvents` 上注册 `ConversationNodeDefinition`,再向 `conversation.chat.node` 注册匹配的 keyed renderer它无须修改会话 fold 或中央 renderer switch。稳定事件 id、append/prepend 回放、Location data 与 renderer 约束见 [Conversation Node 实操手册](../../../docs/cookbook/adding-a-conversation-node.md)。
会话页头会在标题旁渲染 Session scope `'conversation.session.header.actions'` 列表,并在最右侧渲染独立的 `'conversation.session.header.utilities'` 列表。Session 上下文和谱系控件保留在 `actions`可选的 Session 工具不会改变它们的顺序或位置。编辑器链的 currency 包含当前对话 `session`ui-subagent 会选取 one-shot 或 parent 不可用的已寻址会话,并按原因显示只读文案,而普通 InputBar 会让所有已寻址 child 仅保留 Send因为继续执行服务不公开逐 Activation 取消操作,`session.cancel` 也会绕过其所有权。
会话页头会在标题旁渲染会话作用域`'conversation.session.header.actions'` 列表,并在最右侧渲染独立的 `'conversation.session.header.utilities'` 列表。会话上下文和谱系控件保留在 `actions`可选的会话工具不会改变它们的顺序或位置。编辑器链的 currency 包含当前对话 `session`ui-subagent 会选取 one-shot 或 parent 不可用的已寻址会话,并按原因显示只读文案,而普通 InputBar 会让所有已寻址 child 仅保留 Send因为继续执行服务不公开逐 Activation 取消操作,`session.cancel` 也会绕过其所有权。
已记录的非用户消息渲染为默认折叠的展开项,标题栏先给出运行时为该消息投影出的角色——注入为 `上下文注入`,召回为 `跨会话召回`——其后是该投影从持久来源读出的生产者名称,因此读者无需展开即可区分 skill技能目录、工作区指令文件与被召回的会话。来源未提供生产者名称时只显示角色。共享的 `DisclosureRow` 原子组件让该上下文界面与消息流中的其他紧凑行保持相同几何,同时保留上下文语义:展开内容区的高度会随内容自适应,最大为 141px超出后滚动且不会合成工具状态或摘要[历史展开项决策](../../../.agents/notes/archived/feature/2026-07-30-web-context-injection-disclosure.md)、[生产者标签决策](../../../.agents/notes/implemented/feature/2026-08-04-web-context-source-and-steer-marks.md))。该内容区按生产方在持久来源上声明的形态渲染:`instructions` 在正文之上列出它对账过的文件,`catalog` 列出来源记录的条目而非面向模型的文,其余取值——未声明、本版本不认识、或字段不可用——一律渲染 opaque 内容区即按真实换行展示面向模型的文本并把剩余来源字段列出。opaque 不是兜底剩余物而是有文档的默认恢复的、fork 的、外部写入的日志,无论其生产方是否挂载在此处,都必须渲染得出来。持久或待处理的 steering中途引导气泡沿用用户气泡的呈现不加任何装饰transcript 中唯一的 steering 信号是它出现在轮次中途的位置。
已记录的非用户消息渲染为默认折叠的展开项,标题栏先给出运行时为该消息投影出的角色——注入为 `上下文注入`,召回为 `跨会话召回`——其后是该投影从持久来源读出的生产者名称,因此读者无需展开即可区分 skill技能目录、工作区指令文件与被召回的会话。来源未提供生产者名称时只显示角色。共享的 `DisclosureRow` 原子组件让该上下文界面与消息流中的其他紧凑行保持相同几何,同时保留上下文语义:展开内容区的高度会随内容自适应,最大为 141px超出后滚动且不会合成工具状态或摘要[历史展开项决策](../../../.agents/notes/archived/feature/2026-07-30-web-context-injection-disclosure.md)、[生产者标签决策](../../../.agents/notes/implemented/feature/2026-08-04-web-context-source-and-steer-marks.md))。该内容区按生产方在持久来源上声明的形态渲染:`instructions` 在正文之上列出它对账过的文件,`catalog` 列出来源记录的条目而非面向模型的文,其余取值——未声明、本版本不认识、或字段不可用——一律渲染 opaque 内容区即按真实换行展示面向模型的文本并把剩余来源字段列出。opaque 不是兜底剩余物而是有文档的默认恢复的、fork 的、外部写入的日志,无论其生产方是否挂载在此处,都必须渲染得出来。持久或待处理的 steering中途引导气泡沿用用户气泡的呈现不加任何装饰transcript 中唯一的 steering 信号是它出现在轮次中途的位置。
Think 行默认保持折叠并在不展开思维链的情况下暴露实时推理reasoning吞吐当推理块是流式输出尾部时摘要从结算后的首行切换到最新的非空行其单行滚动区会随每个 delta 追到行内末端。展开该行会移除移动摘要,让完整推理进入普通页面流,因此页面阅读不会与内部跟随器争夺滚动;结算后恢复左对齐的稳定首行摘要([决策](../../../.agents/notes/implemented/feature/2026-08-02-web-thinking-tail-scroll.md))。
聊天视图保留 Tool 的消息流位置,但委托其展示。每个已排序的 `tool-call` Conversation Node 都通过 `conversation.chat.node` 的同名 key 分发;详情壳层则通过 `conversation.details.tool` 传递当前选中的调用。组装后的 Web bundle 为该 Chat Node key 注册 [`ui-tool`](../ui-tool/README.md),由后者渲染 Runtime 已投影的递归 root/child 树,并负责按名称分发、通用展示和 render-intent 卡片;只有详情席位会在该 renderer 缺席时保留 raw-result fallback。
聊天视图保留工具的消息流位置,但委托其展示。每个已排序的 `tool-call` Conversation Node 都通过 `conversation.chat.node` 的同名 key 分发;详情壳层则通过 `conversation.details.tool` 传递当前选中的调用。组装后的 Web bundle 为该 Chat Node key 注册 [`ui-tool`](../ui-tool/README.md),由后者渲染运行时已投影的递归 root/child 树,并负责按名称分发、通用展示和 render-intent 卡片;只有详情席位会在该 renderer 缺席时保留 raw-result fallback。
聊天流会将跨重试轮次连续出现的模型重试节点投影为一个稳定的弱化状态行,并用最新一次尝试更新该行;每个重试事件仍保留在运行时快照与会话日志中。前端倒计时以客户端收到事件的时刻为计划延迟的起点,避免 Host 与浏览器的时钟偏差;剩余时间向上取整到秒,且下限为 1 秒。最近一次尚未完成的重试会显示从左到右的文字渐变动画。后续轮次事实用于区分已开始的尝试与在退避期间取消的尝试Host 的 running 位只控制实时动画随后该行会显示静态的已完成或已取消标签。normal 策略行显示有限重试上限always 策略行显示 `∞`。激活该行会显示最近一次重试的精确延迟和失败消息。客户端运行时会在相应重试节点到达前移除每个失败步骤的流式输出尾部;后续某次尝试成功后,该状态仍保持可见。未进入重试的终态失败会在其轮次边界渲染为持久的内联状态,展示适合显示的持久消息与可选错误码,但不会提供 Host 无法兑现的操作AUTH 文案绝不会回显提供方给出的凭据片段。
审批经由本包声明的链接管编辑器:`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 缺席即隐藏 chipchip 打开 Menu 原语下拉,其中 kebab-case 预设名渲染为 Title Case 标签;普通安全预设会立即经输入栏注入的 `command` 回调提交 `/permission <preset>`,而 `danger-full-access` 在界面中显示为 `Full access`,选择后先打开页面内的 Modal 风险确认。用户勾选确认项前启用按钮始终不可用取消、Escape、关闭按钮与点击遮罩都不会提交命令。
审批通过本包声明的链接管编辑器:`ApprovalPanel` 注册为按选择器路由的 `'conversation.composer'` 配置项ui-question 模式),在审批等待未决期间取代 InputBar 占据编辑器(琥珀色条、理由标题、来自运行中调用参数的配对命令行、一次性的拒绝/允许)。`contract/slots.ts` 中的 `PendingApproval` 领域面在运行时 `PendingWait` 载体之上拥有 wire 编码——带审计关联的 `ApprovalResponsePayload` 值;广播的 `approval/resolved` 帧使等待落定并恢复编辑器。运行时 manager 会将所有审批或问题等待通过 `SessionSummary.pendingInteraction` 投影出来,未实例化的会话也不例外;`ui-workspace` 负责其侧边栏呈现。未决等待完全离开消息流问题ui-question与审批ApprovalPanel都经编辑器接管作答不再保留只读占位卡。编辑器底行的 Access 席位挂载 `PermissionSelect`,由 host 计算的 `permissions` 投影经标准工具包 `useProjection` 供数key 缺席即隐藏 chipchip 打开 Menu 原语下拉,其中 kebab-case 预设名渲染为 Title Case 标签;普通安全预设会立即经输入栏注入的 `command` 回调提交 `/permission <preset>`,而 `danger-full-access` 在界面中显示为 `Full access`,选择后先打开页面内的 Modal 风险确认。用户勾选确认项前启用按钮始终不可用取消、Escape、关闭按钮与点击遮罩都不会提交命令。
`TodoDock``order: 0` 占用 `'conversation.input.dock'` 列表 slot位于 Goal 与 Queue 之前),作为计划条读取 host 计算的 `todos` 投影(站立计划:其后没有更晚 `turn/start` 的最近一次 `todo/write`)并渲染 `TodoPanel`。面板接收纯列表,列表为空时自我隐藏;列表非空时默认折叠,表头显示标题及以 `·` 连接的各状态计数(如 `1 已完成 · 2 进行中 · 1 待处理`省略零计数。dock adapter 拥有 selection因此面板保持为 props 的纯函数。输入区 composer 链隐藏的一切也会隐藏整个 dock。`todo_write` Tool 行属于 [`ui-tool`](../ui-tool/README.md)。
`TodoDock``order: 0` 占用 `'conversation.input.dock'` 列表 slot位于 Goal 与 Queue 之前),作为计划条读取 host 计算的 `todos` 投影(当前计划:其后没有更晚 `turn/start` 的最近一次 `todo/write`)并渲染 `TodoPanel`。面板接收纯列表,列表为空时自我隐藏;列表非空时默认折叠,表头显示标题及以 `·` 连接的各状态计数(如 `1 已完成 · 2 进行中 · 1 待处理`省略零计数。dock adapter 拥有 selection因此面板保持为 props 的纯函数。输入区 composer 链隐藏的一切也会隐藏整个 dock。`todo_write` 工具行属于 [`ui-tool`](../ui-tool/README.md)。
`QueueDock``order: 20` 的末端 input-dock 条目。队列为空时隐藏;只有一个待处理项时直接渲染该行;存在两个或更多待处理项时,默认收起为 `"<n> 条排队消息"` 表头,其按钮可展开或收起完整列表。表头暴露 `aria-expanded``aria-controls`;展开后的列表以 180px 为高度上限,并可滚动。存在进行中的编辑或变更时,列表行会保持可见;队列清空后,下一次出现队列时会恢复默认收起状态。普通会话中的每条可见行仍是单行预览,并提供针对精确单次入队项的编辑、删除和严格 steering 操作;已寻址 subagent 则保留只读行,因为其继续执行传输不提供 Queue 变更。如果严格 steering 输给已关闭的窗口,原单次入队项会留在 Queue 中正常投递;如果驱动器已经认领该项,正常投递就已开始。这两种已收敛的竞态都不显示失败,传输和未知错误仍会显示。
@@ -34,7 +34,7 @@ Host 带 placement 的 `session/queue` 快照也会携带待处理 steering。Qu
键盘消息提交会根据所寻址会话的运行状态和 steering 能力解析投递方式。空闲时Enter 和 Cmd/Ctrl+Enter 都执行普通 Queue 发送。主会话运行期间,由 Host settings 支撑的 `ui-conversation.busyEnter` General Settings 偏好会把普通 Enter 分配为 `Queue`(默认值)或 `Steer`Cmd/Ctrl+Enter 则执行另一种行为;本地 settings 提供方将其存入 `$DSH_HOME/settings.yaml`,因此该选择会跟随同一个用户 home 跨越 Web 端口。Shift+Enter 仍然换行。草稿为空时Cmd/Ctrl+Enter 改为按 FIFO 顺序把仍在排队的消息全部插话进运行中的轮次(把 dock 的逐条严格 steer 操作应用于整个队列);空草稿 + 普通 Enter 仍是无操作。这个整队列手势可用时,文本框 placeholder 会提示该手势owner 提供的 placeholder 仍然优先。已寻址 subagent 即使正在运行,也会让这两个手势都使用其仅支持 Queue 的继续执行传输。该偏好只影响支持 steering 的繁忙态手势对,发送按钮与非键盘提交操作仍使用 Queue。Composer Steer 复用现有尽力而为的 `session.prompt(mode: 'steer')` 约定:如果当前 next-step 窗口在接纳前关闭AgentLoop 会把消息接纳为下一条唤醒 Queue 轮次,不显示失败,也不会丢失草稿事务。该持久化边界由[Host settings 支撑的偏好决策](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.md)拥有。
Session UI 状态中的选择与活跃视图位于已声明的聊天 store`stores.ts` `createChatStore`InputHub 拥有输入区状态机,并将草稿镜像到该 store 以便持久化。apply 将同一个 store handle 传给严格限定于会话的子树、聊天视图和详情注册,因此每个会话内共享一个实例,框架拥有其生命周期。组件保持纯粹:框架标准工具包提供 `useSession``sessionId`、全局 `useSessions``useWorkspaces`,以及输入状态机的 `useInput``inputActions`store 表层与 inject factory 提供其余状态和回调。
会话 UI 状态中的选择与活跃视图位于已声明的聊天 store`stores.ts` `createChatStore`InputHub 拥有输入区状态机,并将草稿镜像到该 store 以便持久化。apply 将同一个 store handle 传给严格限定于会话的子树、聊天视图和详情注册,因此每个会话内共享一个实例,框架拥有其生命周期。组件保持纯粹:框架标准工具包提供 `useSession``sessionId`、全局 `useSessions``useWorkspaces`,以及输入状态机的 `useInput``inputActions`store 表层与 inject factory 提供其余状态和回调。
图片经粘贴与整页拖放进入:输入栏绑定 document 级拖拽监听composer-bar slot 为 `kind: 'single'`,同一时刻至多一个 bar 绑定),文件拖拽悬停窗口时显示 `DropOverlay` 原子组件——纯文本拖拽不受影响,锁定或忙碌的 composer 显示禁用遮罩并拒绝 drop。两种手势共用一条对宿主 `imageLimits` 投影的加入预检(数量、单图字节、总字节):会突破上限的加入整批拒收,立刻弹出点名上限的横幅,完全不进入附件栏。仍然到达的宿主侧拒绝按 `attachment-error` 原因映射为产品文案(`image-labels.ts``attachmentErrorText`);用户无法解决的原因折叠为一条带原因码的发送失败文案,非附件错误码保留开发者可读的原文加错误码。
@@ -63,5 +63,5 @@ Host 带 placement 的 `session/queue` 快照也会携带待处理 steering。Qu
- **others 工具行的闪光图标是手绘近似版本**:无法在本地导出设计字形的矢量几何;等到存在精确导出后再将其提升到 ui-primitives。
- **审批面板的「始终允许此类」暂缓**:持久授权需要授权存储设计;今天只能回答允许一次/拒绝。
- **TodoPanel 将过长条目截成单行省略号**figma 条没有换行或展开入口,完整文本无法在行内读完。
- **Queue 编辑仅支持文本**:包含非文本块的行仍显示扁平化预览,但由于内联编辑器无法保留这些块,其编辑控件会被禁用。文本行进入编辑模式后,删除和严格 steering(中途引导)操作会被保存和取消取代Enter 保存Escape 取消。
- **Queue 严格 steering 会保留完整消息**Agent 运行期间steering 操作会以原子方式把所寻址的 Queue 单次入队项转移到当前 next-step 窗口。包含混合内容的行仍可使用此操作,因为它会转发不可变消息,而非文本投影。带 placement 的 Host 快照会在会话流末尾渲染待处理 steering直到已消费的 `user/message` 折叠进持久 transcript文本记录因此立即展示、重连和回放共享同一个线性权威。
- **Queue 编辑仅支持文本**:包含非文本块的行仍显示扁平化预览,但由于内联编辑器无法保留这些块,其编辑控件会被禁用。文本行进入编辑模式后,删除和严格 steering 操作会被保存和取消取代Enter 保存Escape 取消。
- **Queue 严格 steering 会保留完整消息**agent 运行期间steering 操作会以原子方式把所寻址的 Queue 单次入队项转移到当前 next-step 窗口。包含混合内容的行仍可使用此操作,因为它会转发不可变消息,而非文本投影。带 placement 的 Host 快照会在会话流末尾渲染待处理 steering直到已消费的 `user/message` 折叠进持久 transcript文本记录因此立即展示、重连和回放共享同一个线性权威。

View File

@@ -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-deliverables/README.md
README.md: 9cfcde9510c5d6814d5b42fd58a0520ea37b8679
README.zh.md: 94dba9bb38725ddb3ef39d55a5f8a185a5008d18
README.zh.md: 4e09524e269df31cdabce0d5f6e6900d23305265

View File

@@ -4,11 +4,11 @@
产出文件功能的属主:把已完成轮次末尾的产出文件行注册到 chat 视图的 `conversation.chat.turnTail` slot 中。全部策略都在本包内;从 cordis.yml 中删去本插件那一行即可整体移除该界面,属主视图无需额外开销即可渲染空 slot。
`deliverablesDefinition` 把每个 Turn 中成功的修改调用折叠进引擎发布的 `DeliverablesTurnData``producedForClosing` 结合收尾 Assistant 的 seq 读取这份数据。依据的是修改工具自身附带的 `locations`而不是收尾正文无论模型是否记得点名产出文件都会被列出。修改操作按渲染意图而非工具名识别diff 卡片,或 `kind``edit` 的通用卡片(即 `str_replace_editor` 的 insert 操作所呈现的形态);因此新的修改工具只需声明自身行为即可加入。读取、删除和失败的调用不贡献任何条目;同一路径在一个 Turn 内按首见顺序只出现一次。Conversation Location 索引拥有 Turn 成员关系,因此一个 Turn 即使先修改文件、随后没有正文内容就结束,也不会溢进下一个 Turn 的行里。
`deliverablesDefinition` 把每个轮次中成功的修改调用折叠进引擎发布的 `DeliverablesTurnData``producedForClosing` 结合收尾 Assistant 的 seq 读取这份数据。依据的是修改工具自身附带的 `locations`而不是收尾正文无论模型是否记得点名产出文件都会被列出。修改操作按渲染意图而非工具名识别diff 卡片,或 `kind``edit` 的通用卡片(即 `str_replace_editor` 的 insert 操作所呈现的形态);因此新的修改工具只需声明自身行为即可加入。读取、删除和失败的调用不贡献任何条目;同一路径在一个轮次内按首见顺序只出现一次。Conversation Location 索引负责维护轮次归属关系,因此一个轮次即使先修改文件、随后没有正文内容就结束,也不会溢进下一个轮次的行里。
`ProducedFiles` 在收尾消息正文与其 IconActions 之间渲染该行:一个低调的标签和一条经过测量的单行文件 lane。它展示能够放下的最大前缀至多六个标签项文本为文件名完整路径作为 `title`),并为本地化后的精确 `+ N 个文件` 宽度预留空间,因此剩余计数始终可见,既不换行也不横向滚动。每个标签项经由属主提供的 `openFile` 打开——与工具行相同的 Host 打开器chat 视图会把相对路径按会话 cwd 解析。存在隐藏文件时,第二行的**在文件夹中显示**也经由同一属主路径打开会话 workspace它只在页面使用 loopback 且当前 Host 握手报告 `canOpenPath` 时出现,直接远程 Web 与 headless容器 Linux Host 默认均省略该操作。设计原理:[workspace 文件链接 Agent Note](../../../.agents/notes/implemented/feature/2026-07-31-web-workspace-file-links.md)。
收尾正文承载同一份词表。本插件提供供 chat 视图按收尾消息查询的 `chatFileMentions` 服务:`producedFileMentions` 按精确路径解析行内代码 token或当 token 恰好是且仅是一条产出路径的 basename 时解析——两条路径共享同一 basename 时,文本保持不可点击而不作猜测,因此提及链接永远不会打开错误的文件 404。解析成功的提及保留代码标签并采用 Markdown 样式表的链接样式:静止时为链接蓝色,悬停时显示下划线,与 URL 提升的行内代码完全一致——完整路径作为其 `title`;提及绝不会渲染在链接内部或流式文本中。决策记录:[行内文件提及 Agent Note](../../../.agents/notes/implemented/feature/2026-08-07-web-inline-file-mentions.md)。
收尾正文承载同一份词表。本插件提供供 chat 视图按收尾消息查询的 `chatFileMentions` 服务:`producedFileMentions` 按精确路径解析行内代码 token或当 token 恰好等于某条产出路径的 basename,且这样的路径仅有一条时解析——两条路径共享同一 basename 时,文本保持不可点击而不作猜测,因此提及链接永远不会打开错误的文件,也不会导致 404。解析成功的提及保留代码标签并采用 Markdown 样式表的链接样式:静止时为链接蓝色,悬停时显示下划线,与 URL 提升的行内代码完全一致——完整路径作为其 `title`;提及绝不会渲染在链接内部或流式文本中。决策记录:[行内文件提及 Agent Note](../../../.agents/notes/implemented/feature/2026-08-07-web-inline-file-mentions.md)。
## 模型体验

View File

@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-directory-picker-native/README.md
README.md: feb944b38a17086e0fcc6e4da52e1f16a4055166
README.zh.md: 72296b18af69749428157302744063056a6a50de
README.zh.md: 0420fb4d5edc0c05ce72d68243c7377b144ee639

View File

@@ -10,7 +10,7 @@ node 半边是一个空 `apply`:它的存在只为让插件出现在 host 的
## 模型体验
None, as the directory chooser is browser chrome; nothing here reaches a model request.
无,因为目录选择器属于浏览器界面;本包中的任何内容都不会进入模型请求。
#### KV Cache 影响

View File

@@ -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-directory-picker/README.md
README.md: 3ab8ca07f88b61727badf97669ed52b349669efd
README.zh.md: 8e2dea6a92771ab3f40fccf2ffb3359427627fd4
README.zh.md: dcc5b4820d38bdeee0eefa0fe9b8518fae995544

View File

@@ -4,7 +4,7 @@
应用内目录浏览界面:浏览式选取交互的浏览器半边。它通过 ui-workspace 的两个 directory-flow 洞(`conversation.hero.workspace.directoryFlow``sidebar.workspaces.directoryFlow`)装入「选择工作区目录」对话框,经 `ctx.workspaces` 驱动本地 Host 的 `host.listDirectory``host.createDirectory` 原语。它的 node 对侧是 [`dsh-host-directory-picker-browse`](../../host/directory-picker-browse/README.md);挂载本包即用一行 cordis.yml 把界面与该后端组合起来,因此没有任何客户端代码按能力种类分支。与 [`-native`](../ui-directory-picker-native/README.md) 界面不同,本对话框不需要本地操作系统选择框,因此也服务于进程内与远程浏览器部署。
对话框是 680×500 的 Miller 分栏视图(在窄屏或矮屏上收窄):头部承载标题、选中路径面包屑和可点击编辑的路径区;下方在未选中行时是一整栏层级,选中后该行均分为「层级 | 选中文件夹的子项」两栏。导航落地是选择锚定且安静的——面包屑跳转或提交路径被扫描期间仍渲染旧视图,目标腿与父腿作为一帧落地——因此回退时离显示根之外始终保持两栏,且不会闪过中间帧。**新建文件夹**打开一个嵌套创建对话框,目标为选中的文件夹,并选中它创建出来的那个;**打开**采纳选中的文件夹没有选中时回落到当前层级。Host 标记的隐藏条目默认不显示,直到页脚开关将其揭开——那只是客户端过滤。
对话框是 680×500 的 Miller 分栏视图(在较矮或较窄的视口中限制尺寸):头部承载标题、选中路径面包屑和可点击编辑的路径区;下方在未选中行时是一整栏层级,选中后该行均分为「层级 | 选中文件夹的子项」两栏。导航落地是选择锚定且安静的——面包屑跳转或提交路径被扫描期间仍渲染旧视图,目标目录与父目录两段导航在同一帧完成——因此回退时,只要尚未到达显示根目录,就会始终保持两栏,且不会闪过中间帧。**新建文件夹**打开一个嵌套创建对话框,目标为选中的文件夹,并选中它创建出来的那个;**打开**采纳选中的文件夹没有选中时回落到当前层级。Host 标记的隐藏条目默认不显示,直到页脚开关将其揭开——那只是客户端过滤。
确认一个目录即为选中的路径,关闭对话框即为取消。浏览类失败——不可读的目标、创建冲突——都留在对话框自己的提示区内,因此本占位者从不驱动 owner 的 `onError` 分支;工作区创建的错误界面仍由 owner 持有。两处注册通过嵌套的 `slots.inject()` 安装,因为任一声明方条目都可能稍后激活或替换其声明;对话框文案注册在本包自己的 locale 命名空间下,两份字典作为一个单元落地,因此激活失败不会占住该命名空间的其中一种语言。
@@ -12,7 +12,7 @@ node 半边是一个空 `apply`:它的存在只为让插件出现在 host 的
## 模型体验
None, as the directory browser is browser chrome; nothing here reaches a model request.
无,因为目录浏览器属于浏览器界面;本包中的任何内容都不会进入模型请求。
#### KV Cache 影响

View File

@@ -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-model/README.md
README.md: e49fdee52caa99cd249db7d7fbec0064c47e2dc7
README.zh.md: 366c96c3e37619297075ad495142416ffd3baea6
README.zh.md: e9568d00db471abf13f0efc391f73981190395b7

View File

@@ -8,7 +8,7 @@ Host 报告的 `ModelSelection` 是唯一的选择事实,其中包含提供方
当宿主报告没有适配器服务该会话的路由(`session.models.routable`)时,本插件经 `ctx.conversation.blocks` 注册一个 composer 阻塞块,输入框随之停用并显示本插件自己的文案;恢复后无需重新加载即自动清除。它只跟随 `routable``null`(首次加载之前,或加载失败之后)绝不阻断,否则一个慢的宿主就会锁死一个本来可用的 composer目录成员关系同样不阻断因为一条仍在服务、只是不再公布该模型的路由不在分组里却完全可用。触发器自己的 `Select model` 回退仍然覆盖那种情形——那是显示,不是闸门。
目录按会话惰性解析(`ctx.models.directoryFor(sessionId)`),随会话作用域一并释放。已寻址 subagent 会话不公开任一入口,其目录会拒绝加载、选择与重新连接刷新,因为绑定到 agent智能体的普通模型 RPC 会在直接 parent 继续执行路径之外激活持久化 child 历史。
目录按会话惰性解析(`ctx.models.directoryFor(sessionId)`),随会话作用域一并 dispose资源释放。已寻址 subagent 会话不公开任一入口,其目录会拒绝加载、选择与重新连接刷新,因为绑定到 agent智能体的普通模型 RPC 会在直接 parent 继续执行路径之外激活持久化 child 历史。
每一份常驻目录都会直接在转发的 owner 事件 `llm/adapters-updated``settings/document-updated` 上重拉。因此提供方拓扑、提供方目录与默认选择都能收敛Host 与 client runtime 无需再派生一个单独的模型变更别名。

View File

@@ -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-models/README.md
README.md: a8d030b7676e87709fb36b87a6599decc43e0b4b
README.zh.md: 63fb1b486acc2bca34792f485ffd89fb32749e43
README.zh.md: a01b5a6f25fc881b2a9bc7a3799214550d0fc7d3

View File

@@ -2,9 +2,9 @@
[English](README.md) | 中文
模型设置插件:提供方配置页和按条件显示的 DeepSeek 官方首次使用引导步骤。它把三个协议领域汇聚为一个共享快照:`llm.providers`(可配置提供方目录,含每条路由的存活/休眠状态)、`settings.describe`(序列化 schema、分层脱敏值、secret 槽位)与 `credentials.describe`(不含值的 configured/source/writable 徽标);页面据此渲染提供方行,一次只展开一张编辑卡片,且不把路由存活状态呈现为提供方状态。
模型设置插件:提供方配置页和按条件显示的 DeepSeek 官方首次使用引导步骤。它把三个协议领域汇聚为一个共享快照:`llm.providers`(可配置提供方目录,含每条路由的存活/休眠状态)、`settings.describe`(序列化 schema、分层脱敏值、secret slot)与 `credentials.describe`(不含值的 configured/source/writable 徽标);页面据此渲染提供方行,一次只展开一张编辑卡片,且不把路由存活状态呈现为提供方状态。
行是*已配置*的提供方(其 profile 在所属 namespace 中解析得出);密钥未在任何地方配置的整分节提供方会渲染为其展开的设置卡片而非一行,但仅限首次运行姿态——即尚无任何提供方已注册且备齐其 profile 所指名的凭据——且仅持续到用户关闭该卡片为止,此后它就是一行带缺失密钥点的普通行。每一类卡片各自持有自己的展开状态,因此关掉其中一张绝不会丢弃另一张里的草稿。「新增」流程则是一张承载休眠目录提供方选择框的卡片——裸挂载的 `llm-pi-ai` 在任何路由存在之前就能提供其完整的已安装 catalog。pi-ai 卡片还会编辑该路由的**模型列表**,并可查询提供方所提供的模型。只有确认引用的凭据已配置时,行才会以绿色实心点标示 API 密钥状态;只有确认具名引用缺失时,才会以红色实心点标示。无引用的提供方原生认证以及无法取得凭据补充信息时都不显示状态点。编辑器是每个适配器家族各一张的手写卡片:主字段是单独一个 **API 密钥**输入框——页面从不询问环境变量名;键入的密钥经 `credentials.set` 以**只写**方式存入 profile 的引用之下profile 没有引用时便派生 `<ROUTE>_API_KEY`pi-ai profile 会把这次派生记录为 `apiKeyEnv`,因此 `settings.yaml` 从不携带密钥值。为新的 pi-ai 提供方留空密钥会保存一个不带引用的 profile因此能保留提供方原生认证例如 Bedrock 凭据链或 Vertex ADC。「应用」成功后会发出本地无障碍状态消息且绝不回显任何机密内容。收起的「自定义设置」折叠区承载精选的额外字段——两个家族都有 `baseURL`deepseek 的占位符显示公共端点)、各适配器自己的模型目录,以及适配器未提供的那类 pi-ai 路由的**显示名称**与 **API 协议**。这两个字段是手工声明路由为自己命名的东西:创建卡片之所以索要它们,正因为没有东西能为它们兜底,因此编辑器也够得着这两个,而不是把它们留给 `settings.yaml`。清空名称即取消设置,路由退回自己的 id——占位符显示的就是它协议没有这样的兜底。内置目录路由两个都不给它的名称由目录条目兜底它的每个模型各自带着自己的协议路由级协议只可能把它们全部覆盖掉。Provider ID 保持固定:它是 settings 的键、是其他每个 namespace 与每一条已记录会话引用的名字,也是页面读不回、因而搬不走的凭据引用词干。推理等级刻意**不在**其中它是按模型的能力而同一提供方下各模型接受的档位并不一致因此提供方级的控件只可能被设成其中一些模型会拒绝的值——那会连支持该档位的模型也一并隐藏。输入框的模型选择器为每个模型提供它自己的档位在那里切换会把提供方、模型、推理等级一并记为下一个会话的默认值。profile 字段仍留在 `settings.yaml`,供清楚自己路由的部署使用。每条 DeepSeek 模型行可编辑 `id`、可选的显示名称 `name` 与可选的 `contextWindow`/`maxTokens`;精选集合以外的现有字段会在编辑后保留,其余每个 profile 字段仍归 `settings.yaml` 所有。只有当某行仅由用户层承载时它才可删除(删除会还原组合 base其本地化确认对话框会在标题、说明和最终操作中点名该提供方。当目录条目表明拥有该路由的适配器在这个键下什么都没有时该行会带上 **自定义** 标签。标签只跟随这个答案:存了 profile 并不使一条路由成为自定义——收窄一个内置提供方的模型同样会存下 profile——而什么都不回答的适配器其路由保持无标签不会被当成内置。
行是*已配置*的提供方(其 profile 在所属 namespace 中解析得出);其配置键未在任何位置配置的整分节提供方会渲染为其展开的设置卡片而非一行,但仅限首次运行姿态——即尚无任何提供方已注册且备齐其 profile 所指名的凭据——且仅持续到用户关闭该卡片为止,此后它就是一行带缺失密钥点的普通行。每一类卡片各自持有自己的展开状态,因此关掉其中一张绝不会丢弃另一张里的草稿。「新增」流程则是一张承载休眠目录提供方选择框的卡片——裸挂载的 `llm-pi-ai` 在任何路由存在之前就能提供其完整的已安装 catalog。pi-ai 卡片还会编辑该路由的**模型列表**,并可查询提供方所提供的模型。只有确认引用的凭据已配置时,行才会以绿色实心点标示 API 密钥状态;只有确认具名引用缺失时,才会以红色实心点标示。无引用的提供方原生认证以及无法取得凭据补充信息时都不显示状态点。编辑器是每个适配器家族各一张的手写卡片:主字段是单独一个 **API 密钥**输入框——页面从不询问环境变量名;键入的密钥经 `credentials.set` 以**只写**方式存入 profile 的引用之下profile 没有引用时便派生 `<ROUTE>_API_KEY`pi-ai profile 会把这次派生记录为 `apiKeyEnv`,因此 `settings.yaml` 从不携带密钥值。为新的 pi-ai 提供方留空密钥会保存一个不带引用的 profile因此能保留提供方原生认证例如 Bedrock 凭据链或 Vertex ADC。「应用」成功后会发出本地无障碍状态消息且绝不回显任何机密内容。收起的「自定义设置」折叠区承载精选的额外字段——两个家族都有 `baseURL`deepseek 的占位符显示公共端点)、各适配器自己的模型目录,以及适配器未提供的那类 pi-ai 路由的**显示名称**与 **API 协议**。这两个字段是手工声明路由为自己命名的东西:创建卡片之所以索要它们,正因为没有东西能为它们兜底,因此编辑器也够得着这两个,而不是把它们留给 `settings.yaml`。清空名称即取消设置,路由退回自己的 id——占位符显示的就是它协议没有这样的兜底。内置目录路由两个都不给它的名称由目录条目兜底它的每个模型各自带着自己的协议路由级协议只可能把它们全部覆盖掉。Provider ID 保持固定:它是 settings 的键、是其他每个 namespace 与每一条已记录会话引用的名字,也是页面读不回、因而搬不走的凭据引用词干。推理等级刻意**不在**其中它是按模型的能力而同一提供方下各模型接受的档位并不一致因此提供方级的控件只可能被设成其中一些模型会拒绝的值——那会连支持该档位的模型也一并隐藏。输入框的模型选择器为每个模型提供它自己的档位在那里切换会把提供方、模型、推理等级一并记为下一个会话的默认值。profile 字段仍留在 `settings.yaml`,供清楚自己路由的部署使用。每条 DeepSeek 模型行可编辑 `id`、可选的显示名称 `name` 与可选的 `contextWindow`/`maxTokens`;精选集合以外的现有字段会在编辑后保留,其余每个 profile 字段仍归 `settings.yaml` 所有。只有当某行仅由用户层承载时它才可删除(删除会还原组合 base其本地化确认对话框会在标题、说明和最终操作中点名该提供方。当目录条目表明拥有该路由的适配器在这个键下什么都没有时该行会带上 **自定义** 标签。标签只跟随这个答案:存了 profile 并不使一条路由成为自定义——收窄一个内置提供方的模型同样会存下 profile——而什么都不回答的适配器其路由保持无标签不会被当成内置。
前序首次使用引导页面完成后DeepSeek 步骤会从同一个联接快照得出首次运行就绪状态。该步骤的存在是为了让用户手上有一个可对话的模型,因此只要用户已经能触达**任何**一个提供方,它就直接完成而不渲染——已注册且其具名凭据引用已存储的路由(包括来自启动环境且只读的凭据),或 profile 根本不指名任何引用、因而走原生认证的路由。只有二者皆无的用户才会被问到 DeepSeek即这条提示唯一能为其提供密钥输入框的路由。它通过 `llm-deepseek` 的可配置提供方声明识别官方适配器,因此同 id 但未声明的存活路由不属于可修复配置。只有已挂载且活跃、引用可写但尚未配置的适配器才会显示前往「设置」Models 分区的页面;密钥输入和 `credentials.set` 仅由该分区已有的设置卡片负责,该步骤绝不持有 secret。适配器缺失、路由不活跃、联接失败、部署只读或设置凭据能力不可用时该步骤均不渲染并直接完成以免首次使用引导阻塞产品Models 页仍是诊断界面。

View File

@@ -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-primitives/README.md
README.md: f96e931472a0946bd023b97b034643f079a67e44
README.zh.md: 0db2ac1bccbdc4793c488e1ce621ee5c674d9895
README.zh.md: bf47fa2bca6f685fc52ca6ff786439a8d51230fc

View File

@@ -38,7 +38,7 @@
## 模型体验
无。该包package在浏览器中渲染纯 React 原子组件;这里没有任何内容进入模型请求。
无。该包在浏览器中渲染纯 React 原子组件;这里没有任何内容进入模型请求。
#### KV Cache 影响

View File

@@ -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-question/README.md
README.md: d31ceb62c46cb7a720b52d9e2a6c92e98d1c7e42
README.zh.md: 9f9ad01c3f1f661f60fe11ec072f487cb18c170a
README.zh.md: 4ef87b730862043323be022c7cb35553a9d4aebf

View File

@@ -2,15 +2,15 @@
[English](README.md) | 中文
Web 提问功能插件:其浏览器侧把 `question` 配置项注册到会话拥有的 `conversation.composer` 键控 slot 中。其主机侧刻意为空——在那里挂载 `dsh-tool-ask-user` 会把工具放进注册表的**全局层**,而全局层会并入每一个 agent无论它由哪个 preset 组装,于是一个"两工具"的 benchmark preset 实际会呈现三个。渲染提问是宿主的 UI 能力,拥有该工具则是 agent 的能力,因此 `tool-ask-user` 行属于需要它的各个 preset以及没有 preset 的 TUI 组装)。
Web 提问功能插件:其浏览器侧把 `question` 条目注册到会话拥有的 `conversation.composer` 键控 slot 中。其主机侧刻意为空——在那里挂载 `dsh-tool-ask-user` 会把工具放进注册表的**全局层**,而全局层会并入每一个 agent(智能体),无论它由哪个 preset 组装,于是一个两工具的 benchmark preset 实际会呈现三个。渲染提问是宿主的 UI 能力,拥有该工具则是 agent 的能力,因此 `tool-ask-user` 行属于需要它的各个 preset以及没有 preset 的 TUI 组装)。
组件每次渲染一个问题,提供进度导航、单选和多选选项、由标签后缀派生的推荐徽标,以及自定义答案。用户打开或编辑自定义答案时,多选题草稿会保留已选中的标签,因此提交项可以同时携带 `selected``custom`;单选题的自定义答案仍保持互斥。问题详情复用助手输出的 `MarkdownText` 原语,包括其 GFM 渲染与不受信任内容策略。限高卡片保持标题、导航与提交动作固定超长的详情与选项共享内部滚动区。单选选项会立即前进所有问题均已回答或跳过后Enter 会提交IME 输入法组合期间按 Enter 只会确认输入候选,不会前进。组件为整个请求提交一批结构化答案:「跳过此问题」会保留其他草稿,并为该项发出既有的空 `{ selected: [] }` 形状;关闭则以 `ASK_CANCELLED` 拒绝整个等待。
组件每次渲染一个问题,提供进度导航、单选和多选选项、由标签后缀派生的推荐徽标,以及自定义答案。用户打开或编辑自定义答案时,多选题草稿会保留已选中的标签,因此提交项可以同时携带 `selected``custom`;单选题的自定义答案仍保持互斥。问题详情复用助手输出的 `MarkdownText` 原语,包括其 GFM 渲染与不受信任内容策略。限高卡片保持标题、导航与提交动作固定,超长的详情与选项共享内部滚动区。选择单选选项会立即前进所有问题均已回答或跳过后Enter 会提交IME 组合输入期间按 Enter 只会确认输入候选,不会前进。组件为整个请求提交一批结构化答案:「跳过此问题」会保留其他草稿,并为该项发出既有的空 `{ selected: [] }` 形状;关闭则以 `ASK_CANCELLED` 拒绝整个等待。
若某个请求的唯一问题声明了呈现意图,则改为渲染该意图自己的界面。`plan-review`——由 `dsh-plan-mode``exit_plan_mode` 审阅上设置——采用等待审批卡片的形状:一条 `Plan review` 条带、计划作为可滚动的 markdown 主体、问题文本作为卡片的无障碍名称,以及一行 `Chat about it` / `Refuse` / `Approve` 的决定操作。Approve 与 Refuse 用提问方自己的选项标签回答(意图指名哪个标签表示批准,因此裁决绝不依赖选项顺序),并把提问方的描述保留为 tooltip`Chat about it``ASK_CANCELLED` 拒绝该等待,让编辑器归位,用户可以直接说出他想说的话。卡片只在能够发出该请求允许的每一个答案时才接管:只有一个问题、声明了意图、计划以 `detail` 存在、提供了被指名的批准标签,且是二元单选(除批准外最多一个选项,且非多选)。其他任何情形——没有意图、一批含多个问题、缺少计划、批准标签未命中任何选项、出现第三个选项、多选决定——都留在能够表达它的通用流程上。意图改变的只是布局,从不改变可达的答案。
选择状态只存在于以请求 rpcId 为 key 的组件本地。使用相同 id 回放时,只要组件仍挂载,就会保留草稿;主机发出的 `question/resolved` 则会移除编辑器。主机仍具有最终决定权HTTP 交付成功不会在本地移除待处理状态。
编辑器外框文案(翻页器、按钮、占位符、校验提示)是双语的:插件在 `dsh-client-locale``question` 命名空间下注册 zh/en 词典,并通过 inject face 把绑定的翻译函数和 locale 快照源交给该配置项,因此切换语言会重新渲染已挂载的编辑器。问题与选项文本来自模型并原样渲染;载体失败消息也不经翻译直接显示。
编辑器外框文案(翻页器、按钮、占位符、校验提示)是双语的:插件在 `dsh-client-locale``question` 命名空间下注册 zh/en 词典,并通过 inject face 把绑定的翻译函数和 locale 快照源交给该条目,因此切换语言会重新渲染已挂载的编辑器。问题与选项文本来自模型并原样渲染;载体失败消息也不经翻译直接显示。
## 模型体验
@@ -23,4 +23,4 @@ Web 提问功能插件:其浏览器侧把 `question` 配置项注册到会话
## 已知限制与暂缓事项
- **未提交的草稿不持久**:重新连接再同步或完整刷新页面时,会恢复主机拥有且 rpcId 相同的待处理请求,但编辑器卸载会重置本地选项和自定义文本草稿。
- **每次只有一个请求拥有编辑器**:后续待处理请求仍留在会话快照中,并在较早请求解决后显示。
- **每次只有一个请求拥有编辑器**:后续待处理请求仍留在会话快照中,并在较早请求落定后显示。

View File

@@ -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-skill/README.md
README.md: f6818ae9f44a7d0a484302fe8e294ee2159890ec
README.zh.md: 4b9d646fe39887e9d655b28c61c11876b55d1111
README.zh.md: 2def01566d6ab388f3501a4f8e24075f073d4e22

View File

@@ -4,15 +4,15 @@
skill技能调用 source 的浏览器端:把 `/` 触发的 `skill` source 注册进 `ctx.slash`。普通会话的候选来自 `skill.list` RPC以每次调用的 `ClientSessionContext` 投影中的 `{sessionId}` 寻址host 从会话 header 解析 `cwd`。宿主提供每一个用户可调用的 skill`modelInvocable: false` 的条目(即 `disable-model-invocation` skill此路径是其唯一入口会以当前语言把仅限用户标记作为描述前缀带上。由目录寻址的可继续 subagent 在客户端解析为没有 skill 候选,因为现有 skill RPC 要求会话已挂载;查看其持久化历史不得激活它。目录按普通会话缓存,拉取走 single-flightscope 创建时的 `warm` 钩子预热该会话的缓存项,转发的 owner 事件 `agent-preset/selected` 丢弃该会话这一项(目录属于 preset而空会话可能在预热之后才切换`connection/reset` 清空全部缓存。结果按 `startsWith(query)` 过滤。
pick 会落下字面文本 `/name `提示词发出的就是同一段字面文本([slash 流水线 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md))——本 source 不实现任何裁决钩子,也没有引用 codec。确定性在宿主侧pre-step 手势边界(`dsh-tool-skill`)识别用户消息中任意位置、以空白为界、指名用户可调用 skill 的 `/name` token并为每个入口注入渲染后的 `<skill_content>`,因此菜单 pick、手动键入的 token 与 TUI/ACPAgent Client Protocol提示词都以同一种方式加载 skill。与宿主命令同名的名称仍解析为命令裁决在客户端把该行认领走它根本不会成为提示词——这是有意的优先级与同产品一致。列表 RPC 使用插件注册时捕获的根上下文连接——source 绝不从每次调用的参数上读取服务;草稿 chip 视觉由 `lexicon` 扫描派生。
pick 会落下字面文本 `/name `发出的提示词中也是同一段字面文本([slash 流水线 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md))——本 source 不实现任何裁决钩子,也没有引用 codec。确定性在宿主侧pre-step 手势边界(`dsh-tool-skill`)识别用户消息中任意位置、以空白为界、指名用户可调用 skill 的 `/name` token并为每个入口注入渲染后的 `<skill_content>`,因此菜单 pick、手动键入的 token 与 TUI/ACPAgent Client Protocol提示词都以同一种方式加载 skill。与宿主命令同名的名称仍解析为命令裁决在客户端把该行认领走它根本不会成为提示词——这是有意的优先级与同产品一致。列表 RPC 使用插件注册时捕获的根上下文连接——source 绝不从每次调用的参数上读取服务;草稿 chip 视觉由 `lexicon` 扫描派生。
`skill.list` 失败时 `candidates` 抛出异常slash 壳层记录日志并折叠为静默的菜单组丢弃——菜单只显示 pendingready 状态。
`skill.list` 失败时 `candidates` 抛出异常slash 壳层记录日志,并静默丢弃该菜单组——菜单只显示 pendingready 状态。
`/client` 导出表层只有插件主体(`apply``inject`source 对象是注册 effect 的内部实现。
`/client` 导出接口只有插件主体(`apply``inject`source 对象是注册 effect 的内部实现。
## skill 工具行
浏览器插件还会把 `skill` wire 名称注册进 `ui-tool` 的 keyed `tool.call.toolview` slot。收起的行以与 Bash 行相同的中性层级显示 14 像素的 skill 文档与闪光组合图标、`Skill` 标题、分隔符和请求加载的 skill 名称;运行中的调用带有 transcript文本记录的扫光效果失败时用错误首行替换名称中断调用则使用警告状态。已结算的行以整行作为展开入口展开后显示一个尺寸受限的 `Instructions` 卡片,其中原样呈现持久化的工具输出;可用时还会提供标准执行轨迹的 `Inspect` 入口。该行的名称、生命周期和正文只派生自 `ui-tool` 提供的冻结 call/result slice,绝不读取当前 skill 目录,因此即使已安装的 skill 或其描述发生变化,回放仍保持稳定。
浏览器插件还会把 `skill` wire 名称注册进 `ui-tool` 的 keyed `tool.call.toolview` slot。收起的行以与 Bash 行相同的中性视觉层级显示 14 像素的 skill 文档与闪光组合图标、`Skill` 标题、分隔符和请求加载的 skill 名称;运行中的工具调用带有 transcript文本记录的扫光效果失败时用错误首行替换名称中断的工具调用则使用警告状态。已结算的行以整行作为展开入口,展开后显示一个尺寸受限的 `Instructions` 卡片,其中原样呈现持久化的工具输出;可用时还会提供标准执行轨迹的 `Inspect` 入口。该行的名称、生命周期和正文只派生自 `ui-tool` 提供的冻结的工具调用/工具结果切片,绝不读取当前 skill 目录,因此即使已安装的 skill 或其描述发生变化,回放仍保持稳定。
## 模型体验
@@ -32,6 +32,6 @@ pick 会落下字面文本 `/name `,提示词发出的就是同一段字面文
## 已知限制与暂缓事项
- **仅含结果的 history 页使用通用行**:键控分派要求配对调用位于运行时窗口内;分页将调用留在窗口外时,结果没有工具身份。这项客户端呈现功能不会为了恢复该身份而扩展 history 协议约定。
- **仅含工具结果的 history 页使用通用行**:键控分派要求配对的工具调用位于运行时窗口内;分页将工具调用留在窗口外时,工具结果没有工具身份。这项客户端呈现功能不会为了恢复该身份而扩展 history 协议约定。
- **文本是唯一依据**:引用是普通的草稿文本;手动键入的相同 token 就是同一个引用宿主手势边界评判的是发出的文本而不是菜单交互。chip 视觉由 lexicon 扫描派生;没有 occurrence 身份、位置跟踪,也没有提示词协议上的结构化引用载荷(两者都是台账事项)。
- **预热落定之前打开的菜单**:在那次击键下不显示 skill 候选;下一次击键会重新轮询已落定的缓存。

View File

@@ -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-slash/README.md
README.md: e6c9c3240d03a0cde57eda0bce2a995f91786f4e
README.zh.md: cf9e42d419cb472509a74ba227990b1502b38a75
README.zh.md: bbf000933536f25cad7f4488b85b1d9b0ffa92c0

View File

@@ -2,17 +2,17 @@
[English](README.md) | 中文
输入触发流水线插件:光标处的 `/``@` 检测(词边界 + guard tier 规则)、分组候选菜单,以及把 pick 路由到已注册 source。`ctx.slash` 拥有 source roster并按会话 scope`sessionOf`)各解析一个 `SlashController`;对话接线层在 controller 上驱动 `track``arbitrate``onSpace``adjudicate`。同一个 controller 还暴露 `toggleSource`,供 chrome launcher 在一段合成 selection span 上只打开一个已注册 source所得候选仍走通常的菜单、键盘仲裁、pick callback 与 scoped 输入改写。source 每次调用收到一个 `ClientSessionContext` 投影——会话始终由 agent智能体支撑因此投影只含会话身份。source 在它能触达的每个会话 controller 中都会被预热scope 出生时在场的 roster controller 构造预热,晚于此注册的 source 由注册动作本身预热进每个 controller。`lexicon` 名录在预热后仍会变化的 source 实现 `subscribeLexicon(session, listener)`controller 每收到通知就重拉,并把聚合结果经其 `lexicon` 快照 store 发布。流水线与命令无关:空格/回车裁决按注册序轮询可选的 `matchSpace``matchEnter` 钩子,第一个非 undefined 的应答胜出。
输入触发流水线插件:光标处的 `/``@` 检测(词边界 + guard tier 规则)、分组候选菜单,以及把 pick 路由到已注册 source。`ctx.slash` 拥有 source roster并按会话 scope`sessionOf`)各解析一个 `SlashController`;对话接线层在 controller 上驱动 `track``arbitrate``onSpace``adjudicate`。同一个 controller 还暴露 `toggleSource`,供 chrome launcher 在一段合成 selection span 上只打开一个已注册 source所得候选仍走通常的菜单、键盘仲裁、pick callback 与 scoped 输入改写。source 每次调用收到一个 `ClientSessionContext` 投影——会话始终由 agent智能体支撑因此投影只含会话身份。source 在它能触达的每个会话 controller 中都会被预热scope 创建时 roster 中已有的 source 会在 controller 构造期间预热,晚于此注册的 source 由注册动作本身预热进每个仍存续的 controller。`lexicon` 名录在预热后仍会变化的 source 实现 `subscribeLexicon(session, listener)`controller 每收到通知就重拉,并把聚合结果经其 `lexicon` 快照 store 发布。流水线与命令无关:空格/回车裁决按注册序轮询可选的 `matchSpace``matchEnter` 钩子,第一个非 undefined 的应答胜出。
分层:`src/core/` 是纯内核——`detectTrigger``menuReduce``seedGroups``MENU_CLOSED``exactMatch`,零 ReactDOMcordis`src/client/service.ts` 是壳层,把内核接到菜单快照 store、逐 hit 候选拉取(以 generation 把关、后继请求经 `AbortSignal` 取代旧请求、失败的 source 静默丢弃并留一条 console 记录)和三条 pick 路径上。`src/types.ts` 与两个 `contract.ts` 文件是冻结的跨包约定;变更需经主线程仲裁。
MenuView 把菜单 store 渲染进 `conversation.input.overlay` slot列表类会话 scope菜单关闭期间渲染 null。键入式 trigger 会 seed 为该 trigger 注册的所有 source程序化 launcher 只 seed 所请求的 source并在菜单关闭或重新开始键入式 tracking 前,通过 controller 的 `launcher` 快照 store 发布该 source 名称。分组按可选的 `SlashSource.order` 排序(越小越靠前,默认 0同值保持注册序组标题行经 `slash.menu` locale 命名空间本地化(未知 source 显示其原名);列表高度收敛到 composer 上方的可用空间,指针落在菜单与所在 composer 卡片之外即关闭菜单。该 slot 由 ui-conversation 的组合器条目拥有锚点、children 声明、生命周期);其 SlotMap 类型合并放在本包的 `src/client/slots.ts`因为依赖方向ui-conversation → ui-slash不允许反向的类型导入。combobox 模式:焦点始终留在 textarea行在 mousedown 时完成 pick高亮由 `aria-activedescendant` 承载。
MenuView 把菜单 store 渲染进 `conversation.input.overlay` slot列表类会话 scope菜单关闭期间渲染 null。键入式 trigger 会 seed 为该 trigger 注册的所有 source程序化 launcher 只 seed 所请求的 source并在菜单关闭或重新开始键入式 tracking 前,通过 controller 的 `launcher` 快照 store 发布该 source 名称。分组按可选的 `SlashSource.order` 排序(越小越靠前,默认 0同值保持注册序组标题行经 `slash.menu` locale 命名空间本地化(未知 source 显示其原名);列表高度受限于 composer 上方的可用空间,指针落在菜单与所在 composer 卡片之外即关闭菜单。该 slot 由 ui-conversation 的组合器条目拥有锚点、children 声明、生命周期);其 SlotMap 类型合并放在本包的 `src/client/slots.ts`因为依赖方向ui-conversation → ui-slash不允许反向的类型导入。combobox 模式:焦点始终留在 textarea行在 mousedown 时完成 pick高亮由 `aria-activedescendant` 承载。
`/client` 导出表层是插件主体(`apply``inject`)、`SlashService``MenuViewInjected` 与约定类型。MenuView 本身是内部实现——slot 注册以闭包持有它。
`/client` 导出接口是插件主体(`apply``inject`)、`SlashService``MenuViewInjected` 与约定类型。MenuView 本身是内部实现——slot 注册以闭包持有它。
## 模型体验
无。触发流水线只是浏览器呈现——pick 产出 `CommandClaim``ReferenceInsert` 数据,其模型可见后果(宿主命令执行;插入的引用文本随普通提示词发送)由消费方的 host 包与输入状态机包拥有
无。触发流水线只是浏览器呈现——pick 产出 `CommandClaim``ReferenceInsert` 数据,其模型可见后果(宿主命令执行;插入的引用文本随普通提示词发送)由负责消费这些数据的宿主包与输入状态机包负责
#### KV Cache 影响

View File

@@ -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-slots/README.md
README.md: 7ee2a3d22c2a41b0e5c356c52d383a047cf99029
README.zh.md: 793bb2d1f77e02e34a41fab16b2c2c64b78744dc
README.zh.md: 0a61ec43d575429616ad5658228d1e9c93ef8462

View File

@@ -4,22 +4,22 @@
Slot 注册表纯核心、slot 终端设计SlotMap 声明合并、SlotCore 上唯一的 `register` 组合 API、四 share 组件 props 类型家族、store seat 类型家族,以及 renderer 安装约定。只使用 React 类型;该包不依赖 React也不依赖 Cordis。
一次 `register({ name, children?, store?, inject?, ...kind }, Component)` 调用会向已声明 slot 贡献一个组件,同时声明子 slot声明 = 渲染授权 = 运行时规范三者共用一张表、store seat 以及注册方的业务表层。组件会在调用点依据 `ComposedProps` 接受检查;该类型是四个 share 的交集,每个 share 都从各自的唯一真源派生:
一次 `register({ name, children?, store?, inject?, ...kind }, Component)` 调用会向已声明 slot 贡献一个组件,同时声明子 slot声明 = 渲染授权 = 运行时规范三者共用一张表、store seat 以及注册方的业务表层。组件会在调用点依据 `ComposedProps` 接受类型检查;该类型是四个 share 的交集,每个 share 都从各自的唯一真源派生:
| share | 类型 | 来源 |
|---|---|---|
| runtime | `PropsRuntime<K>` | SlotMap 条目:`owner`(父级 renderSlot 调用点)+ Session 标准工具包 + 全局 seat |
| 运行时 | `PropsRuntime<K>` | SlotMap 条目:`owner`(父级 renderSlot 调用点)+ 会话标准工具包 + 全局 seat |
| child render | `PropsRenderSlots<S>` | register 调用的 `children` key 集合(静态缩窄的 `renderSlot` |
| store | `PropsStore<H>` | 已声明 handle`useStore` selector hook + 移除 draft 的 `actions` |
| store | `PropsStore<H>` | 已声明 handle`useStore` selector 钩子 + 移除 draft 的 `actions` |
| business | `I` | 从 `inject` factory 返回值推断 |
chain-kind slot 会反转键控路由:条目自行提名,而不是由分发点选择 `entryKey`。每次注册都携带一个纯 `ChainSelect` selector另有可选的升序 `priority`,相同值按注册顺序处理);第一个非 null 返回值选中其条目,并成为组件的 `matched` prop全部返回 null 时则使用 owner 的 `renderSlotChain` fallback`ChainRenderOpts`)。
标准工具包接口(`SessionStandardProps``GlobalStandardProps`)在这里声明为空,由 runtime 包合并(与 SlotMap key 相同的 declare-merge 模式。renderer 会把运行时 Session 和 Workspace observable source 绑定为 selector hook。Inject factory 参数从声明派生(`InjectParams`Session slot 获得 `sessionId`;声明 store 时追加 baked `actions`;没有其他参数,数据访问位于 apply 闭包的 ctx 中。
标准工具包接口(`SessionStandardProps``GlobalStandardProps`)在这里声明为空,由运行时包合并(与 SlotMap key 相同的 declare-merge 模式。renderer 会把运行时会话和 Workspace observable source 绑定为 selector 钩子。Inject factory 参数从声明派生(`InjectParams`会话 slot 获得 `sessionId`;声明 store 时追加 baked `actions`;没有其他参数,数据访问位于 apply 闭包的 ctx 中。
store 家族(输入 `defineStore` 规范/输出 `StoreHandle<T, A>`)为 store seat 建模:`init` 推断状态 schema`actions` 是完整的 draft-transform 写入集合;`BakedActions` 移除 draft 参数,成为组件和 inject factory 收到的回调。`defineStore` 值实现位于 runtime 包(引擎所属位置),并满足这里导出的 `DefineStore` 约定。引擎产物与 renderer host 约定携带裸快照 source`getSnapshot``subscribe`),绝不携带 React hookhook 绑定属于渲染机制,只有 props 约定 hook 类型(`SnapshotSelectorHook`)位于这里。
store 家族(输入 `defineStore` 规范/输出 `StoreHandle<T, A>`)为 store seat 建模:`init` 推断状态 schema`actions` 是完整的 draft-transform 写入集合;`BakedActions` 移除 draft 参数,成为组件和 inject factory 收到的回调。`defineStore` 值实现位于运行时包(引擎所属位置),并满足这里导出的 `DefineStore` 约定。引擎产物与 renderer host 约定携带裸快照 source`getSnapshot``subscribe`),绝不携带 React 钩子;钩子绑定属于渲染机制,只有 props 约定钩子类型(`SnapshotSelectorHook`)位于这里。
`SlotCore` 在构造时预置 `'root'` slot并强制执行加载时验证注册未声明 slot、重复声明子项、在两个 scope 下使用同一个共享 handle、chain 注册缺少 `select`,这些情况都在 register 时抛出)。条目的 disposer 会递归移除其声明的子 slot账本行、贡献和 store 挂载都会随同一生命周期结束而移除。每个 key 还携带一个 declaration epoch声明代次它只在声明与折叠时递增;运行时将其用于 [`ctx.slots.inject`](../runtime/README.md#slot-declaration-injection),且与普通条目版本相互独立。`renderer.ts` 携带安装约定(`SlotRenderer``SlotRendererHost`)以及 `StaleAuthorizationError``SlotOwnershipError`;实现在 web-react 中,安装则在外壳启动中完成。
`SlotCore` 在构造时预置 `'root'` slot并强制执行加载时验证注册未声明 slot、重复声明子项、在两个 scope 下使用同一个共享 handle、chain 注册缺少 `select`,这些情况都在 register 时抛出)。条目的 disposer 会递归移除其声明的子 slot账本行、贡献和 store 挂载都会随同一生命周期结束而移除。每个 key 还携带一个 declaration epoch声明代次它只在声明与移除时递增;运行时将其用于 [`ctx.slots.inject`](../runtime/README.md#slot-declaration-injection),且与普通条目版本相互独立。`renderer.ts` 携带安装约定(`SlotRenderer``SlotRendererHost`)以及 `StaleAuthorizationError``SlotOwnershipError`;实现在 web-react 中,安装则在外壳启动中完成。
## 模型体验

View File

@@ -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-task/README.md
README.md: a1430db55c7519c612e5d39de4627c750976d2d5
README.zh.md: 5e29c939806f9a6500e78324c5a3322b6fd94539
README.zh.md: 46af64003f40f138875fdbcaf4fee5e92debbf17

View File

@@ -10,7 +10,7 @@ Web 后台任务特性的归属方:向 `conversation.session.header.actions`
Escape 关闭列表并把焦点交还触发器,在其外部按下指针同理。最后一个任务消失时先关闭列表再卸载控件,焦点因此不会从一个被移除的节点上凭空消失。样式只用 token文案走本包自己的 `task` locale 命名空间。行为由 [Web 后台任务展示 Agent Note](../../../.agents/notes/implemented/feature/2026-08-08-web-background-task-display.md) 规定。
## Model Experience
## 模型体验
无,因为本包为人类渲染宿主计算出的注册表状态,不触及 prompt、消息、schema、流或工具结果。模型对同一批任务的视角仍属于 [`dsh-tool-tasks`](../../tasks/tool-tasks/README.md)。
@@ -18,7 +18,7 @@ Escape 关闭列表并把焦点交还触发器,在其外部按下指针同理
无;本包从不组装或发送 provider 请求。
## Known Limitations and Deferred Work
## 已知限制与暂缓事项
- **行是只读的** —— 任务的流式输出与人类发起的中断是各自独立的阶段。中断还额外欠一个 seam 目前没有回答的、面向模型的决策:`kill()` 会把终态投递标为已上报,所以照当前契约写出来的中断会让模型一直以为它的任务还在跑。
- **列表不等于注册表自己的集合** —— 它展示的是「一个会话通过线路视图能看到什么」所以别的会话拥有的任务在这里永远不出现而进程重启会清空列表transcript 里启动这些任务的 `run_in_background` 卡片却还在。无主任务(在没有活体 `Agent` 时启动的)是反过来的情形:它会进入每一个会话的列表,与 `list(caller)` 对每个调用方的报告一致。

View File

@@ -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: df4d5e0370962bf6f2a8ac0a7b88d669225dc5c5
README.zh.md: e0f614645a16b44e374e450bb8dd1e3c5805ea42
README.zh.md: fb8e937979a5168803be78a49e1bc1e7eacd7a47

View File

@@ -10,7 +10,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](../../../.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)。
## 模型体验
@@ -18,7 +18,7 @@
#### KV Cache 影响
无;该包package既不组装也不发送提供方请求。
无;该包既不组装也不发送提供方请求。
## 已知限制与暂缓事项

View File

@@ -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-tool/README.md
README.md: 8255aee4292ea821962477a7108b40cf3b937981
README.zh.md: 2bc5859f592efb6ac4b67157be09a3c09afb691c
README.zh.md: 1925398bb376aa28a512084e81a0dd9c2de14649

View File

@@ -2,23 +2,23 @@
[English](README.md) | 中文
Client Tool 展示插件。`ui-conversation` 通过 `conversation.chat.node`同名 key 分发每个已排序的 `tool-call` Conversation Node本包渲染其中的 root 及其 Code Dispatch 子调用,并把每个原子调用通过 keyed slot `tool.call.toolview` 分发。没有注册的 Tool 名称使用通用卡片。
Client 工具展示插件。`ui-conversation` 通过 `conversation.chat.node`匹配 key 分发每个已排序的 `tool-call` Conversation Node本包渲染其中的 root 及其 Code Dispatch 子调用,并把每个原子调用通过 keyed slot `tool.call.toolview` 分发。没有注册的工具名称使用通用卡片。
业务 UI 包只注册 wire Tool 名称和原子视图,不配对 Session Event、不重建 transcript也不拥有 root/subcall 拓扑。Runtime 继续负责 call/result 配对、生命周期和递归 `subCalls` 投影conversation view 继续负责 ChatFlow 位置。
业务 UI 包只注册 wire 工具名称和原子视图,不配对会话事件、不重建 transcript(文本记录),也不拥有 root/subcall 拓扑。运行时仍对 call/result 配对、生命周期和递归 `subCalls` 投影拥有最终决定权conversation view 仍对 ChatFlow 位置拥有最终决定权
## 渲染约定
`ToolCallTree` 接收一个已经包含递归 `subCalls` 的 root `ToolCallBlock`、selection 状态、会话 `cwd`,以及用于打开文件和检查调用的 Host 回调。它递归遍历标准 call block,让 root 与任意深度的 child 经过同一条原子分发路径,不订阅独立的 parent-to-children map。
`ToolCallTree` 接收一个已经包含递归 `subCalls` 的 root `ToolCallBlock`、selection 状态、会话 `cwd`,以及用于打开文件和检查调用的 Host 回调。它递归遍历标准调用块,让 root 与任意深度的 child 经过同一条原子分发路径,不订阅独立的 parent-to-children map。
每个 root 和 child wrapper 都保留 `data-chat-anchor-key="call:<id>"``data-chat-call-id` DOM 约定,供分页和 selection 使用。
每个 root 和 child 包装层都保留 `data-chat-anchor-key="call:<id>"``data-chat-call-id` DOM 约定,供分页和 selection 使用。
本包还通过 `ToolDetails` 填充 `conversation.details.tool`。行 renderer 与详情 renderer `terminal``read``diff``search``web` render intent 共用同一组纯 card model。本版本不认识的 intent 标签和格式错误的 wire card 数据都会回退为压平的 Tool result 文本。
本包还通过 `ToolDetails` 填充 `conversation.details.tool`。行 renderer 与详情 renderer 共用同一组面向 `terminal``read``diff``search``web` render intent 纯 card model。未知的 intent 标签和格式错误的 wire card 数据都会回退为压平的工具结果文本。
通用行把已知 Tool 名称归类为 search、read、shell、write、edit、code 或 generic 变体。运行中、成功、失败和中断状态只来自冻结的 call/result slice。只有用户调用 Host 打开文件回调时,文件路径才相对会话 `cwd` 解析;展示代码不读取 Session service
通用行把已知工具名称归类为 search、read、shell、write、edit、code 或 generic 变体。运行中、成功、失败和中断状态只来自冻结的 call/result slice。只有用户调用 Host 打开文件回调时,文件路径才相对会话 `cwd` 解析;展示代码不读取会话服务
## 原子 Tool 视图
## 原子工具视图
业务所有方把自己的 wire Tool 名称注册进 `tool.call.toolview`
拥有该视图的业务包将其 wire 工具名称注册进 `tool.call.toolview`
```ts ignore-check
ctx.slots.inject('tool.call.toolview', () =>
@@ -28,15 +28,15 @@ ctx.slots.inject('tool.call.toolview', () =>
}, BusinessToolRow))
```
owner 载荷为 `ToolCallOwnerProps``callId`、`toolName`、冻结的 `block`、可选 `cwd`,以及普通的 `openFile``inspect` 回调。注册项会收到正常的 Session slot runtime share,但不会收到 React node、Runtime service 或 root/subcall 知识。
owner 载荷为 `ToolCallOwnerProps``callId`、`toolName`、冻结的 `block`、可选 `cwd`,以及普通的 `openFile``inspect` 回调。注册项会收到常规的会话 slot 运行时共享数据,但不会收到 React node、运行时服务或 root/subcall 知识。
本包当前拥有 generic fallback以及 bash/pwsh、read、write/edit、grep/glob、web、todo、question 和 Code Dispatch 的内置展示。`ui-skill` 展示了业务包如何拥有 `skill` 注册。
本包当前拥有 generic fallback以及 bash/pwsh、read、write/edit、grep/glob、web、todo、question 和 Code Dispatch 的内置展示。`ui-skill` 展示了业务包自行拥有 `skill` 注册
各类卡片的上限与 fallback 规则仍由对应的 [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)、[diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md)、[read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.md)、[search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md) 和 [web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md) Note 负责。
各类卡片的上限与 fallback 规则仍由对应的 [terminal](../../../.agents/notes/implemented/feature/2026-07-28-web-terminal-card.md)、[diff](../../../.agents/notes/implemented/feature/2026-07-30-web-diff-card.md)、[read](../../../.agents/notes/implemented/feature/2026-07-30-web-read-card-frontend.md)、[search](../../../.agents/notes/implemented/feature/2026-07-30-web-search-card.md) 和 [web](../../../.agents/notes/implemented/feature/2026-07-30-web-result-card-frontend.md) Agent Note 负责。
## 模型体验
无,因为本包只渲染已经记录的 Tool 调用和结果,不改变模型请求、Tool 执行或 Session Event
无,因为本包只渲染已经记录的工具调用和结果,不改变模型请求、工具执行或会话事件
#### KV Cache 影响
@@ -45,5 +45,5 @@ owner 载荷为 `ToolCallOwnerProps``callId`、`toolName`、冻结的 `block`
## 已知限制与后续工作
- Host 不把 `run_code` 暴露为 Code Mode 程序 binding因此生产事件只产生一层分发递归的运行时/UI 约定支持嵌套。
- 第一方 Tool 视图集中在本包,可以通过 keyed slot 独立迁回各自业务包。
- Tool 文案复用 `ui-conversation` locale namespace。
- 第一方工具视图集中在本包,可以通过 keyed slot 独立迁移到各自所属的业务包。
- 工具文案复用 `ui-conversation` locale namespace。

View File

@@ -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-trajectory/README.md
README.md: d3786b6460c5df7eaa6d24e68c80025e7fb29ae4
README.zh.md: 5eb1451b9a3a9896d5486fcf5c8d9cf30d6159a0
README.zh.md: b3bc88d8ab73ccc0da9a49d1867fe2c66be5ca43

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
Trajectory 渲染按轮次组织的事件记录表,其中可选择用户、助手、工具和嵌套子工具记录。较粗的分割线标示轮次边界,紧凑的行内标记标识步骤,主记录表仅保留索引、事件和内容;选择记录则会打开局部检查器,查看 token 用量、耗时、输入、输出和计时。可滚动的概述区域默认保持滚动条滑块透明直到鼠标悬停该区域或其中包含键盘焦点时才显示同时不改变滚动条预留的几何空间。独立运行的压缩compaction请求会按时间顺序显示在自己的 `Between turns` 区段中,而带编号的压缩仍位于其所属轮次内。长记录表打开时定位于当前尾部,用户到达已加载范围顶部时加载一页更早的历史,并且只挂载可见行窗口和少量额外缓冲行;仅含请求的分隔行并入下一个具备可测高度的虚拟项,语义行键和 ARIA 索引在向前补页后保持不变。选择、时间线导航、折叠、搜索和请求汇总只覆盖当前已加载的窗口。初始尾部完成定位前以及更早页面仍在等待时,记录表会用明确的加载行遮住真实记录。固定在记录表上方的 Overview 区域从左到右投影记录的真实开始时间与耗时;仍有更早记录未加载且 viewport 包含已加载时间域起点时,中性的省略号控件会标识被省略的前缀,并可加载一页更早历史,而不会为未知部分虚构耗时。助手时间条会区分记录到的 TTFT 与解码时间,悬停 500 ms 后可查看精确时刻和耗时详情。拖选一个区间会将记录表聚焦到活动区间与该闭区间有重叠的所有记录,清除选择则恢复完整的已加载记录表。滚轮手势用于缩放时间域。右键单击会清除所选区间;在已放大的 viewport 上按住右键拖动则只会平移视图,不会改变该区间。初始视图和流式更新都会停留在尾部;向上滚动会暂停跟随,因此新记录不会打断对旧记录的检查。仅含内容更新的流式帧会保持虚拟行的键和高度不变、复用测量结果,并且不会重复写入末尾滚动位置。已完成的回复会在 Trajectory target State 中保留组装后的 blocks、计时与用量共享 Session 窗口则保留原始 Event。Trajectory 要求会话壳将 composer 作为浮层置于全高记录表上方;其响应式纵向滚动容器会预留 composer 的实时高度确保仍可滚动到最后几行。Trajectory 自有的 Definition 从共享 Session 窗口组装业务记录,其中包括因取消而冻结的助手和工具记录,因此 Trajectory 既不读取也不改变 Chat 会话快照。该包不提供 service也不声明 Context 合并;它会注册 target 专属 Event Definition、Trajectory view builder以及会话 `'conversation.view'` slot 环中的一个视图标签页。约定api-contracts v3 §8。
Trajectory 渲染按轮次组织的事件记录表,其中可选择用户、助手、工具和嵌套子工具记录。较粗的分割线标示轮次边界,紧凑的行内标记标识步骤,主记录表仅保留索引、事件和内容;选择记录则会打开局部检查器,查看 token 用量、耗时、输入、输出和计时。可滚动的概述区域默认保持滚动条滑块透明直到鼠标悬停该区域或其中包含键盘焦点时才显示同时不改变滚动条预留的几何空间。独立运行的压缩compaction请求会按时间顺序显示在自己的 `Between turns` 区段中,而带编号的压缩仍位于其所属轮次内。长记录表打开时定位于当前尾部,用户到达已加载范围顶部时加载一页更早的历史,并且只挂载可见行窗口和少量额外缓冲行;仅含请求的分隔行并入下一个具备可测高度的虚拟项,语义行键和 ARIA 索引在向前补页后保持不变。选择、时间线导航、折叠、搜索和请求汇总只覆盖当前已加载的窗口。初始尾部完成定位前以及更早页面的加载尚未完成时,记录表会用明确的加载行遮住真实记录。固定在记录表上方的 Overview 区域从左到右投影记录的真实开始时间与耗时;仍有更早记录未加载且 viewport 包含已加载时间域起点时,中性的省略号控件会标识被省略的前缀,并可加载一页更早历史,而不会为未知部分虚构耗时。助手时间条会区分记录到的 TTFT 与解码时间,悬停 500 ms 后可查看精确时刻和耗时详情。拖选一个区间会将记录表聚焦到活动区间与该闭区间有重叠的所有记录,清除选择则恢复完整的已加载记录表。滚轮手势用于缩放时间域。右键单击会清除所选区间;在已放大的 viewport 上按住右键拖动则只会平移视图,不会改变该区间。初始视图和流式更新都会停留在尾部;向上滚动会暂停跟随,因此新记录不会打断对旧记录的检查。仅含内容更新的流式帧会保持虚拟行的键和高度不变、复用测量结果,并且不会重复写入末尾滚动位置。已完成的回复会在 Trajectory target State 中保留组装后的 blocks、计时与用量共享 Session 窗口则保留原始 Event。Trajectory 要求会话壳将 composer 作为浮层置于全高记录表上方;其响应式纵向滚动容器会预留 composer 的实时高度确保仍可滚动到最后几行。Trajectory 自有的 Definition 从共享 Session 窗口组装业务记录,其中包括因取消而冻结的助手和工具记录,因此 Trajectory 既不读取也不改变 Chat 会话快照。该包不提供 service也不声明 Context 合并;它会注册 target 专属 Event Definition、Trajectory view builder以及会话 `'conversation.view'` slot 环中的一个视图标签页。约定api-contracts v3 §8。
## 模型体验

View File

@@ -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-workflow-run/README.md
README.md: 489715c51759b1efd2da68d3bd3e0f7788ce7ecd
README.zh.md: 326a7ae4e4b8eaad43ca7ad0d22145452af6a734
README.zh.md: e4fffd3f627bb2742d28db961e0f2870b13f0b59

View File

@@ -20,7 +20,7 @@
本包把 Definition、locale 字典和 `workflow-run` renderer 都注册为 Cordis effect移除客户端 entry 会撤销三者。shipped Web bundle 在 `ui-conversation``ui-tool` 之后装配该插件。
## Model Experience
## 模型体验
无,因为本包只为人类展示持久 Session 事实,不增加 prompt、工具 schema、请求内容或模型可见结果。
@@ -28,7 +28,7 @@
无。
## Known Limitations and Deferred Work
## 已知限制与暂缓事项
- 只有经 `dsh-tool-workflow` 发起的顶层调用会生成这些记录;嵌套 Code Mode 调用和直接 `WorkflowService` 消费方不会生成。
- 导航刻意只面向实时运行。终态成员继续保留供复盘,但本节点永不为其提供冷 Session 入口。

View File

@@ -3,4 +3,4 @@
# 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: 4b44e0e2c2e192363ce32ac2decc218f5ea17bd2
README.zh.md: f0b567569849c698e8a1a29323e7088b49996522
README.zh.md: d9cd3574fc1f1940b7dcd5e7722a3dccd1e75600

View File

@@ -4,9 +4,9 @@
Web 外壳内核:`new AppWebEntry(el, seams?).run()` 通过两阶段启动web2挂载整个客户端。第一阶段模块侧构建客户端模块系统`@deepseek-ai/dsh-client-modules`),以主机推送的配置项图(`window.__DSH_BOOT__`)为基础,并行预取 `immediately` 层级;执行组合包只会注册 factory。第二阶段插件侧挂载仓库内置的 Cordis Loader并通过其 `internal` 约定注入模块系统;为每一行图数据创建一个 loader 配置项,另创建外壳自身的 app-shell 组装配置项tree.import 会物化各模块);以 settle 作为 AppRoot 的门禁loader 完全停稳 + 每个配置项 fiber 都为 ACTIVE → 一次切换显示完整 UI。组合完全由主机图决定花名册和 immediately 层级都位于负责组合的应用中;外壳不作任何组合决策。
外壳自给自足web2 硬性规则):内核不对任何插件包package执行值导入;启动状态 store 与信号在这里手写(`loader-status.ts`因此即使插件失败加载页面仍能工作而此时这一点尤其重要。app-shell 组装(`@deepseek-ai/dsh-client-app-shell`,由外壳拥有、背后没有 npm 包的伪配置项)是唯一通过 `registerStatic` 注册的模块;它与任何插件一样,通过 inject 等待 slots/sessions/layout。
外壳自给自足web2 硬性规则):内核不对任何插件包执行值导入;启动状态 store 与信号在这里手写(`loader-status.ts`因此即使插件失败加载页面仍能工作而此时这一点尤其重要。app-shell 组装(`@deepseek-ai/dsh-client-app-shell`,由外壳拥有、背后没有 npm 包的伪配置项)是唯一通过 `registerStatic` 注册的模块;它与任何插件一样,通过 inject 等待 slots/sessions/layout。
`PLATFORM_MODULES`src/platform.ts是共享模块表层的唯一真源:种子表 key、tsdown 客户端 external 和 vite alias 集都是它的投影。
`PLATFORM_MODULES`src/platform.ts是共享模块接口的唯一真源:种子表 key、tsdown 客户端 external 和 vite alias 集都是它的投影。
可选的覆盖参数 `seams` 会为外部 `<script>` 执行无法到达页面上下文的环境转发模块系统的 `loadBundle` 传输覆盖(`BootSeams`);普通浏览器调用方省略此参数。