Merge remote-tracking branch 'origin/master' into feat/persistent-ptc

This commit is contained in:
creatixchu committed 2026-09-26 11:30:25 +08:00
commit 683e42a3b0
216 files changed
+2959 -727

No files matched your search

+2 -2
View File
@@ -12,8 +12,8 @@
en: 055a5e49f9af3dc2
zh: 9f7c78409cccfded
/agent-notes/archiving-and-deletion:
en: 6c393c7058c787d7
zh: 70ce9ca3bd9c2133
en: 57ccc5fec461ebb0
zh: 69e8f9e79abc53aa
/agent-notes/when-to-write-one:
en: 2beb1b94d8fb633c
zh: 889b5542bb771205
+1 -1
View File
@@ -39,7 +39,7 @@ Delete an implemented Agent Note that only describes small UI adjustments or pur
The archive is path-encoded as `archived/{class}/yyyy-mm-dd-topic-title.md`; `implemented` is deliberately absent because only implemented notes can enter it. An archival change moves the complete English/Chinese/sidecar triplet, retains `Status: implemented`, inserts the same `Archived: YYYY-MM-DD` line immediately below that status in both language files, re-records the sidecar, and repairs or deletes inbound links. These are the only permitted content changes during archival. Existing title punctuation, blank-line layout, and language-switcher wording do not block archival; preserve them with the body.
Once sealed, every archived triplet is permanently frozen. Do not edit, translate, reformat, update, move, or delete it, and do not treat it as authority for current behavior. Documentation gates skip archived sources, including their outbound links; active prose may still link into an archived note when it intentionally cites history. [`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) enforces the closed class tree, complete triplets, archive metadata, sidecar hashes, and the append-only frozen-content manifest. The [archive-policy Agent Note](implemented/process/2026-07-26-frozen-agent-note-archive.md) owns the rationale.
Once sealed, every archived triplet is permanently frozen. Do not edit, translate, reformat, update, move, or delete it, and do not treat it as authority for current behavior. Documentation gates skip archived sources, including their outbound links; active prose may still link into an archived note when it intentionally cites history. [`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) enforces the closed class tree, complete triplets, archive metadata, sidecar hashes, and the append-only frozen-content manifest, except for the exact seal transitions documented in [archive instructions](archived/AGENTS.md). The [archive-policy Agent Note](implemented/process/2026-07-26-frozen-agent-note-archive.md) owns the rationale.
## When to write one
+1 -1
View File
@@ -41,7 +41,7 @@
归档路径编码为 `archived/{class}/yyyy-mm-dd-topic-title.md`;其中有意省略 `implemented`,因为只有 implemented Agent Note 可以进入归档。归档变更会移动完整的英文、中文和伴随记录三个文件,保留 `Status: implemented`,在两种语言的文件中紧接该状态行插入相同的 `Archived: YYYY-MM-DD` 行,重新记录伴随记录,并修复或删除入站链接。归档时只允许对内容做这些更改。 既有标题标点、空行布局和语言切换栏措辞不阻止归档;与正文一起保留。
封存后,每组归档文件都永久冻结。禁止编辑、翻译、重新格式化、更新、移动或删除,也不得将其视为当前行为的权威依据。文档门禁会跳过归档源文件,包括其中的出站链接;当活跃文档有意引用历史时,仍可链接到归档 Agent Note。[`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) 强制执行封闭的类别目录树、完整的三文件配对、归档元数据、伴随记录 hash,以及仅追加的冻结内容 manifest(元数据清单)。[归档政策 Agent Note](implemented/process/2026-07-26-frozen-agent-note-archive.zh.md) 记录了设计依据。
封存后,每组归档文件都永久冻结。禁止编辑、翻译、重新格式化、更新、移动或删除,也不得将其视为当前行为的权威依据。文档门禁会跳过归档源文件,包括其中的出站链接;当活跃文档有意引用历史时,仍可链接到归档 Agent Note。[`verify-archived-agent-notes`](../../scripts/verify-archived-agent-notes.ts) 强制执行封闭的类别目录树、完整的三文件配对、归档元数据、伴随记录 hash,以及仅追加的冻结内容 manifest(元数据清单),但[归档说明](archived/AGENTS.md)中记录的精确封存哈希转换除外。[归档政策 Agent Note](implemented/process/2026-07-26-frozen-agent-note-archive.zh.md) 记录了设计依据。
<a id="when-to-write-one"></a>
+2
View File
@@ -5,3 +5,5 @@ Archived Agent Note triplets under the kind directories are frozen historical sn
The archival change may only relocate a complete English/Chinese/sidecar triplet, insert the identical `Archived: YYYY-MM-DD` line below both `Status: implemented` lines, re-record the sidecar, and repair or delete inbound links. Do not inspect, verify, or repair links out of archived notes.
Run the [`dsh-archive-agent-notes`](../../skills/dsh-archive-agent-notes/SKILL.md) workflow and append new artifact hashes with `pnpm run verify-archived-agent-notes --write`. The normal verifier rejects changed or missing sealed artifacts, incomplete triplets, unknown kind folders, and invalid archive metadata.
The authorized Figma-link removal from `feature/2026-08-10-durable-workflow-runs-in-chat` has three exact old-to-new seal exceptions in `scripts/archived-agent-notes.ts` for its English, Chinese, and pairing files. These exceptions permit no other content change, reversal, or deletion.
@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write
2026-09-24-web-default-schedule-composition.md: 3f27bb09ebc00d42c32563e17a1de82c8e525be1
2026-09-24-web-default-schedule-composition.zh.md: 9e0e1fb77ce89b579e1bc8b0d4d0a1b9f4e9fea5
@@ -1,6 +1,7 @@
# Agent Note: Schedule in the shipped Web composition
Status: implemented
Archived: 2026-09-25
English | [中文](2026-09-24-web-default-schedule-composition.zh.md)
@@ -1,6 +1,7 @@
# Agent Note: 默认 Web 组合中的 Schedule
Status: implemented
Archived: 2026-09-25
[English](2026-09-24-web-default-schedule-composition.md) | 中文
@@ -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 .agents/notes/archived/feature/2026-08-10-durable-workflow-runs-in-chat.md
2026-08-10-durable-workflow-runs-in-chat.md: 01c59ef25996d6349ab2904f148bf4e72fdd5f09
2026-08-10-durable-workflow-runs-in-chat.zh.md: 5f6b39b59beca5e2363261605ec58fbdcf71ea6f
2026-08-10-durable-workflow-runs-in-chat.md: a49cb75e44bd65ef694679eeb295cfef564c7545
2026-08-10-durable-workflow-runs-in-chat.zh.md: 753e02238cd8c00d0494cb673ba9896d61ff4cd8
@@ -25,7 +25,7 @@ The renderer gives each level a distinct visual responsibility. The run uses a 3
Navigation is derived from two current authorities rather than persisted. A member row is interactive only while its durable member state is running and the current ordinary Session list contains the same id with `origin: 'subagent'`, `parentId` equal to the displayed parent, and `running: true`. Underlined member text is the only visible affordance; keyboard focus draws a two-pixel business-primary ring around the name area, and the fixed status label remains the lifecycle word rather than an action instruction. The renderer invokes only the injected ordinary `sessions.open(id)` callback. Addressed-only, remote, wrong-parent, and terminal members remain visible but static.
The [seven-state Figma reference](https://www.figma.com/design/tguwzZRmHCjbq58mfsqT0M?node-id=5-2) fixes the information hierarchy for running expanded/collapsed, completed history/expanded, failed plus cancelled, interrupted recovery, and dark narrow presentation. Repository `DisclosureRow`, `StateDot`, icons, semantic tokens, and keyed-node behavior remain the implementation authority; the reference introduces no runtime field or state owner.
The seven-state Figma reference fixes the information hierarchy for running expanded/collapsed, completed history/expanded, failed plus cancelled, interrupted recovery, and dark narrow presentation. Repository `DisclosureRow`, `StateDot`, icons, semantic tokens, and keyed-node behavior remain the implementation authority; the reference introduces no runtime field or state owner.
## Verification
@@ -25,7 +25,7 @@ renderer 为每一层分配不同视觉职责。运行使用 32 像素 module-pl
导航从两个当前权威派生,不写入持久记录。只有持久成员状态仍为运行中,且当前普通 Session 列表包含同一 id、`origin: 'subagent'`、`parentId` 等于当前父 Session、`running: true` 时,成员行才可交互。带下划线的成员文字是唯一可见提示;键盘聚焦时,名称区显示 2 像素 business-primary 焦点环,固定状态列继续只表达生命周期,而不写动作说明。renderer 只调用注入的普通 `sessions.open(id)` 回调。仅地址化、远程、父级不符或终态成员继续可见,但保持静态。
[七状态 Figma 参考](https://www.figma.com/design/tguwzZRmHCjbq58mfsqT0M?node-id=5-2)固定运行展开/收起、完成历史/展开、失败与取消、恢复后中断以及暗色窄列的信息层级。仓库的 `DisclosureRow`、`StateDot`、图标、语义 token 和 keyed-node 行为仍是实现权威;参考稿不引入运行时字段或状态 owner。
七状态 Figma 参考固定运行展开/收起、完成历史/展开、失败与取消、恢复后中断以及暗色窄列的信息层级。仓库的 `DisclosureRow`、`StateDot`、图标、语义 token 和 keyed-node 行为仍是实现权威;参考稿不引入运行时字段或状态 owner。
## 验证
+6 -3
View File
@@ -376,6 +376,9 @@
"architecture/2026-09-11-wasm-preview-font-and-image-budgets.i18n.yaml": "sha256:af027c7dae53a181e4d73fd2309f69e9ae3c48df0d3b1dfc53cae274aa60d69e",
"architecture/2026-09-11-wasm-preview-font-and-image-budgets.md": "sha256:44dd52839cdb67644c0dbcbc4beb6f36802628c4a8d17a0aad11db1f9be649ed",
"architecture/2026-09-11-wasm-preview-font-and-image-budgets.zh.md": "sha256:8285432ee1676be8b79a05953c4b6ac4a7ca5d8e701173b3a2b533608b584d0a",
"architecture/2026-09-24-web-default-schedule-composition.i18n.yaml": "sha256:8c3d5c614924d0e81f361a299427ac697aefc6189c41eb3f4151f5c42e371ca0",
"architecture/2026-09-24-web-default-schedule-composition.md": "sha256:3a139593ade13f95cbb67094c1eec66cb0a2c14bcc154512799803b2a7d22ab4",
"architecture/2026-09-24-web-default-schedule-composition.zh.md": "sha256:59908f7987058742d1226dc8a801d03c265c84d51172249ba51716b91f084f0c",
"bug-fix/2026-07-19-windows-atomic-write-dacl-preservation.i18n.yaml": "sha256:8be5b0afd8820c593e2ec254d35fb8c384267814104f6234bb9af824c5f974ba",
"bug-fix/2026-07-19-windows-atomic-write-dacl-preservation.md": "sha256:861e6130e893489271478def5f54f212a106a1580b744e6e40f56356f73c7e10",
"bug-fix/2026-07-19-windows-atomic-write-dacl-preservation.zh.md": "sha256:3549257e06f074591de580ebfaa71284c7c384fb0ffb1963c30258d7a7c26db7",
@@ -1264,9 +1267,9 @@
"feature/2026-08-10-creator-guidance-introduce-cue.i18n.yaml": "sha256:74f519839f0cf82c7304bdeae41ae1cab8bb930bfb94f79ab44708acd3b72128",
"feature/2026-08-10-creator-guidance-introduce-cue.md": "sha256:3e25409dda498de150de18943ee332e1760963e377a40a365902b66f667fdc9f",
"feature/2026-08-10-creator-guidance-introduce-cue.zh.md": "sha256:203847010cab9e9d13c3969f17921d3a5aa0d69377eccfef555c7ff96572f162",
"feature/2026-08-10-durable-workflow-runs-in-chat.i18n.yaml": "sha256:0db3c21a3c8785e56d5d7b90df990b158069cd1cea664e62fc4786c6d7588450",
"feature/2026-08-10-durable-workflow-runs-in-chat.md": "sha256:f9f5290cd880908d17b1080ae5776f22e182f5253471f90cf8bead418b33b502",
"feature/2026-08-10-durable-workflow-runs-in-chat.zh.md": "sha256:f4ffd2700bc9c2a0cc4b9d90f84e1d0a576939e2e6f0b130c5b142fd8cd518b0",
"feature/2026-08-10-durable-workflow-runs-in-chat.i18n.yaml": "sha256:03eadb60c8ba4fde262cd61e3f911d8cd68ea4a58f57adeb8dd7bb9a97d08ca2",
"feature/2026-08-10-durable-workflow-runs-in-chat.md": "sha256:6018de4a89ca99d6cbfd618aff1c8136b23d8e4854a30f7b12a602f0417cbdc2",
"feature/2026-08-10-durable-workflow-runs-in-chat.zh.md": "sha256:0080859221773122022d5880efa3d52f29c0872b52d63e170250d94f214ff37a",
"feature/2026-08-10-minimal-read-image-tool.i18n.yaml": "sha256:2311b6453b90a1f81d4fcbdaa723ab233cc0998f34393956364c8b92bc4d1213",
"feature/2026-08-10-minimal-read-image-tool.md": "sha256:debe6b570d11634f57a80eaa5891e4fd1e7a5aac19f10fb7dd1683c316515150",
"feature/2026-08-10-minimal-read-image-tool.zh.md": "sha256:572d080340682b354cb1f8a0f74af153d6542bd3dffbe3b72b32591e5d5d3baf",
@@ -15,5 +15,5 @@
en: 21d7ec9a9ff3cbf2
zh: 93d03b8b3012aa7e
/agent-note-host-owned-scheduled-messages/consequences:
en: 0358aab730e4bf56
zh: 61e89e0374891f8a
en: a48f13f89a1944b1
zh: f8fa677122ce2e12
@@ -58,7 +58,7 @@ Session inbox persistence and the task put occur in separate persistence systems
**Keep the mock's hourly choice out of the stored vocabulary.** The approved mock lists 每小时整点 beside the daily, weekly and one-shot choices. Hourly and fixed-rate intervals are the same elapsed-time rule, so the card maps that menu slot onto the existing interval kind instead of adding a persisted kind. A dedicated hourly kind would need a new durable variant, its decoder, its target resolution, and its tool schema, all to express what an interval already expresses.
Task discovery and timers do not depend on loaded Sessions. The after, absolute-time, and fixed-rate selectors retain their semantics alongside Daily, Weekly, and Cron; reading existing saved tasks does not rewrite them. The Host must have its ordinary Session persistence and recovery services available, and the Web bundle mounts Schedule in its host row list. Explicit deletion stops future delivery and removes the task from `list` and `catalog` without deleting conversation messages; the removed row takes its saved receipts with it. History is pruned to the configured window and record cap when an acknowledgment is appended, loaded into Host memory, and rewritten with the task on each acknowledgment. Pagination limits the returned record count; the plugin configuration (`deliveryHistoryDays`, default 30, and `deliveryHistoryRecords`, default 200) limits storage growth. Earlier overwritten receipts and deliveries whose task write failed cannot be reconstructed. Pause and execution-history operations remain unsupported.
Task discovery and timers do not depend on loaded Sessions. The after, absolute-time, and fixed-rate selectors retain their semantics alongside Daily, Weekly, and Cron; reading existing saved tasks does not rewrite them. The Host must have its ordinary Session persistence and recovery services available, and enabling the optional experimental bundle `@deepseek-ai/dsh-experimental-schedule-bundle` mounts Schedule; the Web composition carries its row disabled. Explicit deletion stops future delivery and removes the task from `list` and `catalog` without deleting conversation messages; the removed row takes its saved receipts with it. History is pruned to the configured window and record cap when an acknowledgment is appended, loaded into Host memory, and rewritten with the task on each acknowledgment. Pagination limits the returned record count; the plugin configuration (`deliveryHistoryDays`, default 30, and `deliveryHistoryRecords`, default 200) limits storage growth. Earlier overwritten receipts and deliveries whose task write failed cannot be reconstructed. Pause and execution-history operations remain unsupported.
Required coverage includes startup from stored tasks, missing-status normalization, cold Session restoration, latest-only catch-up, inactive-task retention without repeat dispatch, real message receipts, deletion across restart, removal of a deleted task's saved deliveries, queue/persistence failures, and disposal. Wall-clock obligations include gaps and skipped dates, earlier-only overlaps, fractional seconds, UTC-range edges, committed-target and alias preservation, and post-restoration clock rollback. Weekly adds weekday normalization and validation, next-target selection across a gap and an overlap, and stored set preservation. Historical decoders must reject Daily, Weekly, and Cron while Host storage accepts them. Client coverage includes every recurring kind, exact rule time, zone, and weekday set, All/Enabled/Inactive status filters, retained inactive details and original-Session navigation, authoritative refresh after confirmed deletion and notification, empty lists, reconnect, and late responses. Presentation coverage includes the page's New action starting a Session, the transcript card's narrowing of persisted result JSON including an incomplete or replayed result, the header clock's direct open for exactly one task and its count label, the idle-only row mark with the hover-card two-task limit and omission line. History coverage includes legacy byte preservation, append and restart, immutable sent instructions, strict latest-receipt consistency, stable cursor pagination, binding checks, failed persistence, and late page replies during refresh or task changes. Cross-store crash duplication remains an explicit limitation rather than a completion claim.
@@ -58,7 +58,7 @@ Session 收件箱持久化和任务 put 发生在两个持久化系统中。收
**不把 mock 的「每小时整点」引入持久词表。** 设计稿把它与每天、每周、仅一次并列。每小时整点与固定间隔在语义上是同一种按经过时间计量的规则,因此详情里的该项直接映射到既有的间隔类型,而不是新增一个持久种类。若真要新增,需要新的持久变体、解码器、目标解析与工具 schema,而它表达的能力现有间隔已经覆盖。
任务发现和 timer 不依赖已加载的 Session。延迟、绝对时间和固定速率选择器在与 Daily、Weekly、Cron 并存时保留自身语义;读取已有任务不会重写它们。Host 必须提供普通 Session 持久化和恢复服务,Web bundle 在其宿主行列表中挂载 Schedule。显式删除会阻止后续投递,并将任务从 `list` 和 `catalog` 中移除,不删除对话消息;被移除的行会带走其已保存回执。历史在追加确认时按配置的窗口与条数上限裁剪,随后加载到 Host 内存,并在每次确认时随任务一起重写。分页限制返回记录数量,不限制存储增长或指令字节数。此前被覆盖的回执,以及任务写入失败的投递记录,无法重建。暂停和执行历史操作仍不受支持。
任务发现和 timer 不依赖已加载的 Session。延迟、绝对时间和固定速率选择器在与 Daily、Weekly、Cron 并存时保留自身语义;读取已有任务不会重写它们。Host 必须提供普通 Session 持久化和恢复服务,而挂载 Schedule 需要启用可选实验 bundle `@deepseek-ai/dsh-experimental-schedule-bundle`;Web 组合以禁用状态携带其条目。显式删除会阻止后续投递,并将任务从 `list` 和 `catalog` 中移除,不删除对话消息;被移除的行会带走其已保存回执。历史在追加确认时按配置的窗口与条数上限裁剪,随后加载到 Host 内存,并在每次确认时随任务一起重写。分页限制返回记录数量,不限制存储增长或指令字节数。此前被覆盖的回执,以及任务写入失败的投递记录,无法重建。暂停和执行历史操作仍不受支持。
必要覆盖范围包括从已存储任务启动、缺失状态规范化、冷 Session 恢复、仅补发最新发生时点、保留未运行任务且不重复投递、真实消息回执、重启后的删除结果、删除会移除已保存发送记录、排队或持久化失败,以及销毁。钟表规则的验证要求包括缺口和跳过的日期、仅选择较早时点的重叠、小数秒、UTC 范围边缘、已提交目标和别名保留,以及恢复后的时钟回拨。Weekly 另需覆盖星期集合的规范化与校验、跨缺口与重叠的下一目标选择,以及已存集合的保留。历史解码器必须拒绝 Daily、Weekly 与 Cron,而宿主存储接受它们。客户端覆盖范围包括每种重复规则、精确规则时间、时区和星期集合、全部/已开启/未运行状态筛选、保留未运行任务详情和原 Session 导航、确认删除和通知后的权威刷新、空列表、重新连接和晚到响应。呈现覆盖范围包括页面“新建”操作打开新会话、transcript 卡片从不完整或回放的结果 JSON 中收窄任务、页头时钟在恰好一个任务时直接打开并给出数量标签、仅空闲行标记及其悬浮卡片两个任务上限与遗漏行。历史覆盖范围包括旧文件字节保留、追加与重启、不可变的发送指令、最近回执一致性严格校验、稳定游标分页、绑定校验、持久化失败,以及刷新或切换任务期间晚到的分页响应。跨存储崩溃导致的重复投递仍是明确限制,不能被表述为完成保证。
@@ -9,11 +9,11 @@
en: 2ba6c43b20a3d347
zh: 5647179ddaa38ad9
/agent-note-ship-experimental-capabilities-as-optional-bundles/decision:
en: 1c5a43b3ce2d3056
zh: b59f549a12cf67d1
en: fdd54ede661f7a83
zh: a41ed1a872bb8ba3
/agent-note-ship-experimental-capabilities-as-optional-bundles/alternatives-considered:
en: a65d84004e48b3ef
zh: 17221f08e7a8a89b
/agent-note-ship-experimental-capabilities-as-optional-bundles/consequences:
en: d52ed0a248131a37
zh: 779b8d415281c5da
en: e7fb2a93a89ba9b6
zh: 37ed9f0a3ca44583
@@ -10,7 +10,7 @@ The Web plugin page offered two optional bundles, Agent Teams and voice input. A
## Decision
`OPTIONAL_BUNDLES` lists Agent Teams, voice input, and Auto review. Every optional bundle declares `icon` and exports `./locale/*.json` with `meta.title` and `meta.description`, so the Official group renders a localized title, description, and image; `verify-default-product-isolation` rejects an optional bundle without them. The Inspector's `cordis.patch.yml` names the package instead of a built-artifact path, so the same file is the bundle patch and the `--patch` overlay for a built Web launch.
`OPTIONAL_BUNDLES` lists Agent Teams, voice input, Auto review, and the Schedule bundle. Every optional bundle declares `icon` and exports `./locale/*.json` with `meta.title` and `meta.description`, so the Official group renders a localized title, description, and image; `verify-default-product-isolation` rejects an optional bundle without them. The Inspector's `cordis.patch.yml` names the package instead of a built-artifact path, so the same file is the bundle patch and the `--patch` overlay for a built Web launch.
An optional bundle is a runtime dependency of the installation, so its dependency graph is downloaded by every `dsh` install. The list therefore admits only packages whose graph is already in the installation's closure; Auto review adds nothing. The Inspector stays available through explicit installation and does not appear in the default plugin list. The browser-use and computer-use providers stay explicit compositions: Playwright MCP, Chrome DevTools MCP, and the native Cua Driver runtime binaries add about 85 MB and 21 packages to every install whether or not the bundle is enabled, and a shipped Cua Driver MCP switch would offer a capability whose executable the installation does not carry. Those provider packages keep locale display metadata for their component rows. Two more packages stay out for other reasons: `ptc-runtime-python` replaces the PTC runtime, and `workflow-ptc` rejects a non-TypeScript runtime at load while the Web presets carry `workflow-ptc` rows a bundle patch cannot reach; `browser-use-stagehand-native` requires a native model name and API key at schema validation with no configuration form on the page.
@@ -22,4 +22,4 @@ An optional bundle is a runtime dependency of the installation, so its dependenc
## Consequences
The Official group grows from two to three entries, each tagged experimental by its experimental name. The runtime dependency closure of the installation is unchanged. A browser-use or computer-use provider still needs its Service Definition and the provider in a profile patch or composition.
The Official group carries four optional-bundle entries, each tagged experimental by its experimental name. The runtime dependency closure of the installation is unchanged. A browser-use or computer-use provider still needs its Service Definition and the provider in a profile patch or composition.
@@ -10,7 +10,7 @@ Web 插件页只提供两个可选 bundle:Agent Teams 与语音输入。Auto r
## 决策
`OPTIONAL_BUNDLES` 列出 Agent Teams、语音输入与 Auto review。每个可选 bundle 都声明 `icon` 并导出带 `meta.title` 与 `meta.description` 的 `./locale/*.json`,官方分组因此能渲染本地化标题、描述和图片;`verify-default-product-isolation` 会拒绝缺少这些声明的可选 bundle。Inspector 的 `cordis.patch.yml` 直接写包名而非构建产物路径,所以同一文件既是 bundle patch,也是已构建 Web 启动的 `--patch` overlay。
`OPTIONAL_BUNDLES` 列出 Agent Teams、语音输入、Auto review 与 Schedule bundle。每个可选 bundle 都声明 `icon` 并导出带 `meta.title` 与 `meta.description` 的 `./locale/*.json`,官方分组因此能渲染本地化标题、描述和图片;`verify-default-product-isolation` 会拒绝缺少这些声明的可选 bundle。Inspector 的 `cordis.patch.yml` 直接写包名而非构建产物路径,所以同一文件既是 bundle patch,也是已构建 Web 启动的 `--patch` overlay。
可选 bundle 是安装的运行时依赖,其依赖图会随每次 `dsh` 安装一起下载。因此名单只接纳依赖图已在安装闭包内的包;Auto review 不增加任何依赖。Inspector 保留显式安装方式,不在默认插件列表中显示。浏览器操作与电脑操作提供方仍保持显式组合:Playwright MCP、Chrome DevTools MCP 与原生 Cua Driver 运行时二进制会让每次安装多出约 85 MB、21 个包,无论 bundle 是否启用;而随附的 Cua Driver MCP 开关会提供一个安装本身并不携带其可执行文件的能力。这些提供方包保留 locale 显示元数据,供组件行使用。另有两个包因其他原因不纳入:`ptc-runtime-python` 会替换 PTC 运行时,而 `workflow-ptc` 在加载时拒绝非 TypeScript 运行时,且 Web preset 内含 bundle patch 无法触及的 `workflow-ptc` 行;`browser-use-stagehand-native` 在 schema 校验时就要求原生模型名称与 API 密钥,而插件页没有对应的配置表单。
@@ -22,4 +22,4 @@ Web 插件页只提供两个可选 bundle:Agent Teams 与语音输入。Auto r
## 后果
官方分组从两项增至三项,每项都因实验包名而带实验性标签。安装的运行时依赖闭包保持不变。浏览器操作或电脑操作提供方仍需在 profile patch 或组合中同时挂载其 Service Definition 与提供方。
官方分组包含四个可选 bundle 条目,每项都因实验包名而带实验性标签。安装的运行时依赖闭包保持不变。浏览器操作或电脑操作提供方仍需在 profile patch 或组合中同时挂载其 Service Definition 与提供方。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-24-schedule-opt-in-optional-bundle.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-24-schedule-opt-in-optional-bundle.md
/agent-note-schedule-as-an-opt-in-optional-bundle:
en: d234bf064c5990f1
zh: c54b941429b4658c
/agent-note-schedule-as-an-opt-in-optional-bundle/problem:
en: 2409ea3a006ea5d9
zh: dbcda58a4c6b5b9f
/agent-note-schedule-as-an-opt-in-optional-bundle/decision:
en: 5aa944749a231645
zh: c14651be6367ee13
/agent-note-schedule-as-an-opt-in-optional-bundle/alternatives-considered:
en: 1782c8ecdbe6bf87
zh: dd6997d067257562
/agent-note-schedule-as-an-opt-in-optional-bundle/consequences:
en: 8ea1d9aaf25f1702
zh: 32e721347ea55475
@@ -0,0 +1,34 @@
# Agent Note: Schedule as an opt-in optional bundle
Status: implemented
English | [中文](2026-09-24-schedule-opt-in-optional-bundle.zh.md)
> Supersedes [Schedule in the shipped Web composition](../../archived/architecture/2026-09-24-web-default-schedule-composition.md): the three rows now ship disabled, and switching them on is this bundle's job.
## Problem
`packages/bundle/web-app/cordis.patch.yml` inserts `time-context`, `schedule`, and `ui-schedule` with `disabled: true`, so the shipped Web composition exposes no Schedule surface: no `schedule_*` tools, no Session reminder catalog, no Automation tasks page, and no per-step clock reading. The rows have no switch on the product surface; a deployment that wants them edits a profile patch layer or passes a `--patch` overlay, and both are configuration files a person using the shipped Web profile cannot reach.
## Decision
The rows stay in the composition because an id-targeted patch overrides an existing row: a profile patch or a bundle sets a row's `disabled` to `false` without inserting it again.
`@deepseek-ai/dsh-experimental-schedule-bundle` (`packages/experimental/schedule-bundle/`) carries three id-targeted patches in its `cordis.patch.yml`, one per row, each setting `disabled: false`; it inserts no row. `OPTIONAL_BUNDLES` in `packages/boot/app-boot/src/profile.ts` names the package, and `apps/cli` declares it as a runtime dependency, so every installation ships it switched off. The [experimental-as-optional-bundles decision](2026-09-21-experimental-capabilities-as-optional-bundles.md) owns the `OPTIONAL_BUNDLES` conventions and the localized `icon` and `meta.title` / `meta.description` metadata the Web Plugins page renders in its Official group.
Enabling the bundle turns on `time-context` (a per-step clock reading with the sampled instant, the browser zone attached to the open request, and the elapsed time since the preceding model-visible message), `schedule` (durable reminders plus `schedule_create`, `schedule_list`, `schedule_update`, and `schedule_delete` on live root Agents), and `ui-schedule` (the Session reminder catalog and the Automation tasks page). The bundle switches `time-context` on with `schedule` because a reminder request states a wall-clock target: the sampled instant is what lets the model turn a request such as "tomorrow at nine" into an offset-bearing `at` value. The [Schedule subsystem](../../../../docs/subsystems/schedule.md) owns the durable records, delivery, and management operations; the [Web bundle](../../../../packages/bundle/web-app/README.md) owns the composition that carries the disabled rows. Disabling the bundle restores the shipped composition, and the Host stops scheduling while the `schedule` row stays disabled; stored task records remain in the Schedule domain.
## Alternatives considered
**Ship Schedule on by default.** Every Web session then pays four tool schemas in each request header and one durable user message per eligible step, and a conversation that never creates a reminder pays both costs. The arrangement also left the capability without an off switch on the product surface.
**Keep a `--patch` overlay file.** An overlay is a launch-time argument rather than a product-surface switch, so a person using the shipped Web profile cannot reach it. Re-declaring the rows through its `insert` list does not override them: `applyEntryPatches` appends that list without de-duplicating ids, and the Loader collapses the composed list to one entry per id with the last declaration winning, so the overlay replaces the Web bundle's rows instead of setting fields on them.
**Extract the three rows out of the Web composition into the bundle.** The bundle would insert the rows instead of overriding them, so a profile that also declares one of those ids would have two declarations for it, of which the Loader keeps only the last. The disabled rows are what an override-style switch targets.
## Consequences
- A default `dsh web` session carries no Schedule tool schemas, no Session reminder catalog, no Automation tasks page, and no per-step clock reading. An installation that wants them enables the optional bundle from the Plugins page or lists the package in a profile's `dsh.profile.bundles`.
- An enabled installation adds four tool schemas to every live root Agent and one durable clock message per eligible step; the reading is model-visible and durable, so it replays, compacts, and appears in exported Session logs like any other user message.
- A composition that does not carry the three Web rows reports one `patch: entry <id> not found` warning per patch and mounts nothing from this bundle, and its package declares no plugin dependency, so enabling it cannot mount a Host service twice.
- The switch is configuration-only: `src/index.ts` is an empty module, the patch carries the runtime content, and the package owns no mutable runtime state, so it publishes no invariant companion.
@@ -0,0 +1,34 @@
# Agent Note: Schedule 作为按需开启的可选 bundle
Status: implemented
[English](2026-09-24-schedule-opt-in-optional-bundle.md) | 中文
> 取代[默认 Web 组合中的 Schedule](../../archived/architecture/2026-09-24-web-default-schedule-composition.md):这三行现在默认禁用,打开它们是此 bundle 的职责。
## Problem
`packages/bundle/web-app/cordis.patch.yml` 以 `disabled: true` 插入 `time-context`、`schedule` 与 `ui-schedule`,因此随发行版交付的 Web 组合不提供任何定时能力:没有 `schedule_*` 工具、没有 Session 提醒目录、没有自动化任务页面,也没有逐步时钟读数。这些条目在产品界面上没有开关;想要它们的部署要改 profile patch 层或传 `--patch` overlay,而两者都是使用随发行版 Web profile 的人无法触及的配置文件。
## Decision
这三行仍留在组合中,因为按 id 定位的 patch 覆盖的是已存在的行:profile patch 或 bundle 把某行的 `disabled` 设为 `false`,无需再次插入它。
`@deepseek-ai/dsh-experimental-schedule-bundle`(`packages/experimental/schedule-bundle/`)在其 `cordis.patch.yml` 中携带三个按 id 定位的 patch,每行一个,各自把 `disabled` 设为 `false`;它不插入任何行。`packages/boot/app-boot/src/profile.ts` 的 `OPTIONAL_BUNDLES` 列出该包,`apps/cli` 将其声明为运行时依赖,因此每次安装都随包携带且默认关闭。[实验性能力作为可选 bundle 的决策](2026-09-21-experimental-capabilities-as-optional-bundles.zh.md)负责 `OPTIONAL_BUNDLES` 的约定,以及 Web 插件管理页在 Official 分组中渲染的本地化 `icon` 与 `meta.title` / `meta.description` 元数据。
启用该 bundle 会打开 `time-context`(每个步骤的时钟读数,包含采样瞬时、附加到当前开放请求的浏览器时区,以及自前一条模型可见消息以来的经过时长)、`schedule`(持久提醒,以及每个活跃根 Agent 上的 `schedule_create`、`schedule_list`、`schedule_update` 与 `schedule_delete`),以及 `ui-schedule`(Session 提醒目录与自动化任务页面)。该 bundle 把 `time-context` 与 `schedule` 一起打开,因为提醒请求给出的是挂钟时间目标:采样瞬时正是让模型把“明天九点”这类请求转换成带偏移量的 `at` 值的依据。持久记录、投递与管理操作由 [Schedule 子系统](../../../../docs/subsystems/schedule.zh.md)负责;承载这些禁用行的组合由 [Web bundle](../../../../packages/bundle/web-app/README.zh.md)负责。禁用该 bundle 会恢复随发行版交付的组合,`schedule` 行保持禁用期间 Host 不再调度;已存储的任务记录保留在 Schedule domain 中。
## Alternatives considered
**默认发布 Schedule。** 那样每个 Web 会话都要在每个请求头中带上四个工具 schema,并在每个符合条件的步骤追加一条持久 user 消息,从不创建提醒的对话也要承担这两项成本。这种安排还让该能力在产品界面上没有关闭开关。
**保留一个 `--patch` overlay 文件。** overlay 是启动时的参数,而不是产品界面上的开关,因此使用随发行版 `web` profile 的人无法触及它。通过它的 `insert` 列表重新声明这些条目并不能覆盖它们:`applyEntryPatches` 追加该列表时不对 id 去重,而 Loader 会把组合出的列表按 id 收敛为一个条目、最后一个声明生效,因此该 overlay 会替换 Web Bundle 的条目,而不是给它们设值。
**把这三行从 Web 组合中抽出、移入 bundle。** 这样 bundle 会插入这些行而不是覆盖它们;某个 profile 若也声明了这些 id,同一个 id 就会有两份声明,而 Loader 只保留最后一份。按需开关针对的正是这些禁用行。
## Consequences
- 默认的 `dsh web` 会话不带 Schedule 工具 schema、Session 提醒目录、自动化任务页面与逐步时钟读数。需要它们的安装可从插件管理页启用该可选 bundle,或把该包列进 profile 的 `dsh.profile.bundles`。
- 启用后的安装会给每个活跃根 Agent 增加四个工具 schema,并在每个符合条件的步骤追加一条持久时钟消息;该读数对模型可见且持久,因此它与其他 user 消息一样参与回放、压缩,并出现在导出的 Session 日志中。
- 在不携带这三行 Web 行的组合中,该 bundle 的每个补丁都会报告一条 `patch: entry <id> not found` 警告且不挂载任何东西;其包不声明插件依赖,因此启用它不会重复挂载同一个 Host 服务。
- 该开关只涉及配置:`src/index.ts` 是空模块,运行时内容由 patch 承载,包本身不拥有可变的运行时状态,因此不发布不变量伴随模块。
@@ -1,22 +0,0 @@
# Bilingual-pair consistency record for 2026-09-24-web-default-schedule-composition.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-24-web-default-schedule-composition.md
/agent-note-schedule-in-the-shipped-web-composition:
en: 39229d7ab6ed433b
zh: 54287471d371a11c
/agent-note-schedule-in-the-shipped-web-composition/problem:
en: 2871fd5ed881c007
zh: d03c78d402d186da
/agent-note-schedule-in-the-shipped-web-composition/decision:
en: bb19ad12b2ab0ffc
zh: 971f33e95474a36c
/agent-note-schedule-in-the-shipped-web-composition/recorded-sessions:
en: 5539a42052f4ddb7
zh: 98cc190ae63b4549
/agent-note-schedule-in-the-shipped-web-composition/alternatives-considered:
en: ca032a407c2ee8cb
zh: 748a4fcf2485a3da
/agent-note-schedule-in-the-shipped-web-composition/consequences:
en: 4f23bda8fda37987
zh: 9f7c4a3538bd2979
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-09-25-session-log-otel-byte-limits.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-25-session-log-otel-byte-limits.md
/agent-note-session-log-otlp-strings-and-bounded-requests:
en: 041f7e2b55202ea3
zh: e01834ae6c9fc7bc
/agent-note-session-log-otlp-strings-and-bounded-requests/problem:
en: 20f659a15fb55352
zh: 49d39c95da1b8aa5
/agent-note-session-log-otlp-strings-and-bounded-requests/decision:
en: 0f0866232326658b
zh: aa608d17ba712d8a
/agent-note-session-log-otlp-strings-and-bounded-requests/alternatives-considered:
en: 0dc933b18a1a3573
zh: 77655740322c4e45
/agent-note-session-log-otlp-strings-and-bounded-requests/consequences:
en: 3859569c9cdea30c
zh: 517449b8c49807f6
@@ -0,0 +1,29 @@
# Agent Note: Session-log OTLP strings and bounded requests
Status: implemented
English | [中文](2026-09-25-session-log-otel-byte-limits.zh.md)
## Problem
Session logs contain nested JSON and large tool results. The product collector accepts named records with string content and limits request size; count-only SDK batching cannot bound bytes after UTF-8 encoding and JSON escaping.
## Decision
The shared `otel` Cordis plugin owns ordinary-event and Session-log transport implementations. Business adapters inject it and create independent caller-owned channels; their existing UI/RPC entry points retain authorization, redaction, identity, configuration, and bounded shutdown. Mounting the shared service creates no unused queues. Each canonical event becomes `eventName: "session-log"`, with `sessionId` and JSON-stringified event values in `content`; serialization does not preserve JSONL key ordering. Redaction runs before serialization and the copied envelope excludes the payload.
Session logs use a separate provider and byte/count queue. Each record is measured once with the SDK serializer including a full envelope; the sum conservatively bounds a combined request. Greedy packing avoids full-candidate serialization and recursive re-encoding. Single oversized records receive one diagnostic and do not block later events. One HTTP request owns the transport slot until settlement, even after its watchdog fires; shutdown expiry stops the remaining queue. Scope name/version and explicit transport options remain owned by the backend.
The [telemetry revival](../feature/2026-07-23-session-telemetry-otel-revival.md) remains authoritative for redaction, capture, and best-effort handoff. This decision replaces its SDK-only queue/batching rule: SDK batch deadlines cannot own an operation split into multiple HTTP requests. The shared Session-log channel schedules complete requests; the SDK still owns transport/retry. Feedback authorization and the separate DeepSeek model-request contribution remain unchanged.
## Alternatives considered
**One string for the entire Session.** Rejected because one large Session would exceed the request ceiling even when each event fits. One event per record preserves event identity and permits batching.
**Truncate oversized records or add client-only fragments.** Rejected because truncation loses content and fragments require a collector reassembly protocol. Explicit rejection retains the one-event-per-record meaning.
**Count compressed bytes or characters.** Rejected because collector limits can apply after decompression, and characters omit UTF-8 and escaping overhead. Measuring full uncompressed requests gives a conservative bound.
## Consequences
Product and Session records never share a request or queue, and neither package imports the other. Conservative per-record envelope accounting can produce smaller batches than exact combined accounting. Queue overflow, oversize rejection, exit, and transport failure can lose data; no durable outbox or acknowledgement is added. Tests cover byte limits, linear measurement, slow-request serialization, metadata, redaction-copy isolation, and feedback-authorized replay.
@@ -0,0 +1,29 @@
# Agent Note: Session 日志的 OTLP 字符串与有界请求
Status: implemented
[English](2026-09-25-session-log-otel-byte-limits.md) | 中文
## 问题
Session 日志包含嵌套 JSON 和大型工具结果。产品采集端接收带名称和字符串内容的记录,并限制请求大小;仅按数量分批的 SDK 无法约束 UTF-8 编码与 JSON 转义后的字节数。
## 决策
共享 `otel` Cordis 插件负责普通事件和 Session 日志的传输实现。业务适配器注入它并创建独立、由调用方持有的通道;现有 UI/RPC 入口保留授权、脱敏、身份、配置和有期限的关闭。只挂载共享服务不会创建无用队列。每条规范事件转换为 `eventName: "session-log"`,带有 `sessionId` 和在 `content` 中 JSON 字符串化的事件值;序列化不保留 JSONL 键顺序。脱敏先于序列化,复制的 envelope 不包含 payload。
Session 日志使用独立 provider 和字节/条数队列。每条记录使用 SDK 序列化器计量一次,包含完整 envelope;其总和保守约束合并请求大小。顺序组包避免候选批次的完整序列化和递归重复编码。单条超限记录只产生一次诊断,不阻塞后续事件。每个 HTTP 请求在结束前始终占有传输槽位,即使其监测期限已经触发;关闭期限到期后停止剩余队列。scope 名称/版本和显式传输选项仍由后端拥有。
[遥测恢复决策](../feature/2026-07-23-session-telemetry-otel-revival.zh.md) 继续约束脱敏、捕获和尽力交接。本决策替代其仅使用 SDK 队列/聚合的规则:SDK 批次期限无法拥有拆成多个 HTTP 请求的操作。共享 Session 日志通道调度完整请求,SDK 仍负责传输和重试。反馈授权和独立的 DeepSeek 模型请求附加路径不变。
## 考虑过的替代方案
**整个 Session 合为一个字符串。** 拒绝,因为即使每条事件都能放入请求,大型 Session 仍会超过请求上限。每条事件对应一条记录,既保留事件身份又允许分批。
**截断超限记录或仅在客户端分片。** 拒绝,因为截断会丢失内容,而分片需要采集端重组协议。显式拒绝保留一条事件对应一条记录的含义。
**计算压缩字节数或字符数。** 拒绝,因为采集端限制可能作用于解压后内容,而字符数不包含 UTF-8 和转义开销。计量完整未压缩请求提供保守上限。
## 影响
产品事件和 Session 记录不共享请求或队列,两个包也不相互导入。保守的逐记录 envelope 计量可能比精确合并计量产生更小批次。队列溢出、超限拒绝、退出和传输失败可能丢失数据;不新增持久 outbox 或确认机制。测试覆盖字节上限、线性计量、慢请求串行调度、元数据、脱敏复制隔离和反馈授权回放。
@@ -9,8 +9,8 @@
en: d3383b495424d118
zh: 4e6ffc8ebfa7f741
/agent-note-session-telemetry-seam-with-mandatory-redaction-and-the-otel-backend/decision:
en: 85b950a67315023c
zh: cb4c08338c30b226
en: 739b871ae583dcbf
zh: d17053e3b0a35942
/agent-note-session-telemetry-seam-with-mandatory-redaction-and-the-otel-backend/alternatives-considered:
en: 01838eefca2bc117
zh: fc71f5449d095b27
@@ -16,6 +16,8 @@ Every deployment that wants harness sessions in an observability stack must hand
- **The `session-telemetry/record` waterfall** — the delta over the branch version and the seam's redaction extension point. Every record passes it before reaching any backend; the seam ships NO rules of its own — the innermost `next()` is a pass-through, deployments mount their rules as listeners (stacking by transforming `next()`'s return value), and a throwing rule withholds the record fail-closed. Redaction applies to the exported copy only; the canonical log is never rewritten.
- **`@deepseek-ai/dsh-session-telemetry-otel`** — the reference backend: OTel JS SDK log pipeline (`LoggerProvider` → `BatchLogRecordProcessor` → OTLP/HTTP exporter), configured verbatim through `exporter`/`processor` passthroughs. `DISABLED` is the plugin default and constructs no transport. `FEEDBACK_ONLY` requires `exporter.url`; the [explicit-feedback policy](../architecture/2026-09-05-nonofficial-feedback-otel.md) requires a new submission for every bounded capture, for all providers. [Buffer-free feedback replay](../../archived/simplification/2026-08-06-buffer-free-feedback-telemetry.md) avoids a second in-memory copy of the session prefix.
The [Session-log request limits](../architecture/2026-09-25-session-log-otel-byte-limits.md) supersede the SDK-only batching rule below for the OTel Session sender; capture, redaction, and handoff rationale remain applicable.
The boundary axiom holds: the harness's aspect ends at `emit()`. Batching, retry, queueing, and loss policy are the reporting SDK's, configured through passthroughs. Delivery is best-effort: a crash can lose queued records. Backend retry or losing the same live object's module-scope cursor can duplicate lifecycle-local rows, so receivers deduplicate on `(session.id, session.format_version, event.seq)`.
## Alternatives considered
@@ -16,6 +16,8 @@ Status: implemented
- **`session-telemetry/record` waterfall(瀑布式事件)** —— 相对分支版本的增量,也是该 seam 的脱敏扩展点。每条记录抵达任何后端前必经此处;seam 自身不带任何规则——最内层 `next()` 原样透传,部署方以监听器挂载自己的规则(通过变换 `next()` 的返回值堆叠),抛异常的规则将该记录 fail-closed 扣下。脱敏只作用于导出副本;canonical log 永不改写。
- **`@deepseek-ai/dsh-session-telemetry-otel`** —— 参考后端:OTel JS SDK 日志流水线(`LoggerProvider` → `BatchLogRecordProcessor` → OTLP/HTTP exporter),经 `exporter`/`processor` passthrough 原样配置。`DISABLED` 是插件默认值,且不构造任何传输。`FEEDBACK_ONLY` 要求 `exporter.url`;[显式反馈策略](../architecture/2026-09-05-nonofficial-feedback-otel.zh.md)要求每次有界捕获都由新提交触发,适用于所有提供方。[无缓冲反馈回放](../../archived/simplification/2026-08-06-buffer-free-feedback-telemetry.md)避免在内存中创建会话前缀的第二份副本。
[Session 日志请求上限](../architecture/2026-09-25-session-log-otel-byte-limits.zh.md) 在 OTel Session 发送器中替代下述仅由 SDK 分批的规则;采集、脱敏和交接的理由仍然适用。
边界公理保持不变:harness 的职责止于 `emit()`。批处理、重试、排队与丢失策略属于 reporting SDK,并经 passthrough 配置。投递是尽力而为:崩溃可能丢失已排队记录。后端重试或丢失同一 live 对象的模块作用域游标可能重复生命周期本地 row,因此接收端基于 `(session.id, session.format_version, event.seq)` 去重。
## 考虑过的替代方案
@@ -12,8 +12,8 @@
en: 8892eea7f62d45ed
zh: 18f64966ff53e38b
/agent-note-web-subagent-catalog-and-human-continuation/design-context:
en: 3c44fc5af90bc807
zh: 478538d028a52858
en: da3858c86cf685ff
zh: b13ee4f45e3dff7d
/agent-note-web-subagent-catalog-and-human-continuation/product-contract:
en: 2060d607d1cd1ef3
zh: e7100887358625c4
@@ -28,7 +28,7 @@ This decision covers Web discovery, transcript viewing, and parent-authorized hu
## Design context
The Figma [subagent list](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=383-14602&p=f), [hierarchical expansion](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=383-15917&p=f), and [child conversation](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=388-18584&p=f) frames are non-normative interaction and visual references. This note owns lifecycle, wire, and failure semantics.
The Figma subagent list, hierarchical expansion, and child conversation frames are non-normative interaction and visual references. This note owns lifecycle, wire, and failure semantics.
| Design intent | Shipped contract |
| --- | --- |
@@ -28,7 +28,7 @@ Web 产品通过页头的当前 title 谱系区域公开选中会话中由会话
## 设计上下文
Figma 中的 [subagent 列表](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=383-14602&p=f)、[层级展开](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=383-15917&p=f)与 [child 对话](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=388-18584&p=f)画框是非规范性的交互与视觉参考。本记录负责生命周期、协议与失败语义。
Figma 中的 subagent 列表、层级展开与 child 对话画框是非规范性的交互与视觉参考。本记录负责生命周期、协议与失败语义。
| 设计意图 | 已交付约定 |
| --- | --- |
@@ -9,8 +9,8 @@
en: cef0f05fd8dfd6a1
zh: 228107eb6c00a91a
/agent-note-freeze-low-future-value-agent-notes-outside-the-active-corpus/decision:
en: 453cf66046816477
zh: af5bb77e849416a1
en: f0d9c292c3b8dd3b
zh: fd928084dfd333e2
/agent-note-freeze-low-future-value-agent-notes-outside-the-active-corpus/alternatives-considered:
en: 7637deef67f3ec14
zh: 7f43606e487fab67
@@ -18,7 +18,7 @@ The root `.rgignore` excludes the archive from searches that traverse a parent d
After archival, the triplet is permanently frozen and is historical context rather than current authority. It is not updated for renamed packages, changed behavior, translation standards, formatting rules, broken outbound links, or later documentation contracts. Active prose may intentionally link into an archived note, redirect that link to current authority, or delete it. Repository gates therefore validate links into archived files but never treat archived files as link sources.
[`verify-archived-agent-notes`](../../../../scripts/verify-archived-agent-notes.ts) owns the frozen boundary. It accepts only the closed set of Agent Note kinds, requires a complete triplet with implemented status and matching valid archive dates, verifies the sidecar against both current Git blob hashes, and seals every artifact by path and SHA-256 content hash in an append-only manifest. Its `--write` mode first proves every existing seal unchanged and then appends only newly archived artifacts. Pull-request CI supplies the trusted base SHA and checks out complete history before running the verifier, so a reused runner's shallow checkout cannot omit the baseline manifest. The ordinary Agent Note format, translation-pairing, wrapping, Markdown-link, package-path, Mermaid, documentation-TypeScript, and type-equivalence gates exclude archive sources; their evolving standards cannot create pressure to edit history.
[`verify-archived-agent-notes`](../../../../scripts/verify-archived-agent-notes.ts) owns the frozen boundary. It accepts only the closed set of Agent Note kinds, requires a complete triplet with implemented status and matching valid archive dates, verifies the sidecar against both current Git blob hashes, and seals every artifact by path and SHA-256 content hash in an append-only manifest, with only the exact seal-transition exceptions recorded in [archive instructions](../../archived/AGENTS.md). Its `--write` mode first proves every existing seal unchanged and then appends only newly archived artifacts. Pull-request CI supplies the trusted base SHA and checks out complete history before running the verifier, so a reused runner's shallow checkout cannot omit the baseline manifest. The ordinary Agent Note format, translation-pairing, wrapping, Markdown-link, package-path, Mermaid, documentation-TypeScript, and type-equivalence gates exclude archive sources; their evolving standards cannot create pressure to edit history.
The [`dsh-archive-agent-notes`](../../../skills/dsh-archive-agent-notes/SKILL.md) workflow owns classification. It requires a semantic note-by-note audit, uses code and current documentation to identify present authority, treats word count only as triage, carries calibrated keep/archive/delete examples, and reports genuinely borderline outcomes for review.
@@ -18,7 +18,7 @@ implemented Agent Note 作为当前决策记录持续维护,因此活跃记录
归档后,这三个文件永久冻结,只作为历史背景,不再是当前权威依据。不得因为包重命名、行为变化、翻译标准、格式规则、出站链接失效或后续文档约定而更新归档文件。活跃文档可以有意链接到归档 Agent Note,也可以把该链接重定向到当前权威依据,或直接删除。仓库门禁因此会校验指向归档文件的链接,但绝不把归档文件作为链接源来校验。
[`verify-archived-agent-notes`](../../../../scripts/verify-archived-agent-notes.ts) 负责维护冻结边界。它只接受封闭集合中的 Agent Note 类别,要求三个配对文件完整、状态为 implemented,且归档日期有效并互相匹配;它还会用双方当前的 Git blob hash 校验伴随记录,并在仅追加的 manifest(元数据清单)中按路径和 SHA-256 内容 hash 封存每项产物。其 `--write` 模式会先证明每条现有封存记录对应的内容都未改变,再仅追加新归档的产物。拉取请求 CI 会提供可信的基准 SHA,并在运行校验器前检出完整历史,因此复用运行器上的浅克隆检出无法漏掉基线 manifest。普通的 Agent Note 格式、翻译配对、换行、Markdown 链接、包路径、Mermaid、文档 TypeScript 和类型等价门禁都排除归档源文件,因此这些门禁持续演进的标准不会产生修改历史记录的压力。
[`verify-archived-agent-notes`](../../../../scripts/verify-archived-agent-notes.ts) 负责维护冻结边界。它只接受封闭集合中的 Agent Note 类别,要求三个配对文件完整、状态为 implemented,且归档日期有效并互相匹配;它还会用双方当前的 Git blob hash 校验伴随记录,并在仅追加的 manifest(元数据清单)中按路径和 SHA-256 内容 hash 封存每项产物,仅允许[归档说明](../../archived/AGENTS.md)中记录的精确封存哈希转换例外。其 `--write` 模式会先证明每条现有封存记录对应的内容都未改变,再仅追加新归档的产物。拉取请求 CI 会提供可信的基准 SHA,并在运行校验器前检出完整历史,因此复用运行器上的浅克隆检出无法漏掉基线 manifest。普通的 Agent Note 格式、翻译配对、换行、Markdown 链接、包路径、Mermaid、文档 TypeScript 和类型等价门禁都排除归档源文件,因此这些门禁持续演进的标准不会产生修改历史记录的压力。
[`dsh-archive-agent-notes`](../../../skills/dsh-archive-agent-notes/SKILL.md) 工作流负责分类判断。它要求逐份 Agent Note 做语义审计,使用代码和当前文档识别现行权威依据,仅把字数作为初步筛选手段,收录经过校准的保留、归档和删除示例,并报告真正处于边界的结果,以供评审。
@@ -9,8 +9,8 @@
en: b3ec2245644b46e6
zh: 83de1cb620f9ddcf
/agent-note-make-lefthook-installation-worktree-local/decision:
en: 577d90ff7e03ebaf
zh: dc680155c0c3a2a1
en: b53a2425f88a6031
zh: 41f459b00803b3f9
/agent-note-make-lefthook-installation-worktree-local/alternatives-considered:
en: d7100b0d2c95a3f9
zh: 070c38c4d0d1331a
@@ -14,7 +14,7 @@ Lefthook-generated hooks prefer an absolute binary path captured from the instal
Hook installation is worktree-scoped. With `CI=true` or `GITHUB_ACTIONS=true`, the installer returns before Git discovery or mutation because automated jobs do not consume contributor hooks. Otherwise, it requires Git 2.26 or newer so `git config --show-scope` can report which scope supplied a value, upgrades a format-0 repository to format 1, enables `extensions.worktreeConfig`, and assigns the current worktree an absolute `core.hooksPath` at `$GIT_DIR/dsh-hooks`.
Before upgrading format 0, the installer refuses direct common-config `extensions.*`; it also refuses direct `core.worktree` or `core.bare=true` and non-empty dormant worktree configs that enabling the extension would activate. The migration removes direct `core.bare=false` because false is Git's default. The common repository config and every existing `config.worktree` must be regular files. These checks disable include expansion because Git's repository-format parser also ignores included targets. A repository-scoped lock serializes migration and hook writes; its process ID, random token, file identity, and exact contents must still match at release. Dead or invalid locks require manual recovery rather than automatic breaking.
Before upgrading format 0, the installer refuses direct common-config `extensions.*`; it also refuses direct `core.worktree` or `core.bare=true` and non-empty dormant worktree configs that enabling the extension would activate. The migration removes direct `core.bare=false` because false is Git's default. The common repository config and every existing `config.worktree` must be regular files. These checks disable include expansion because Git's repository-format parser also ignores included targets. A repository-scoped lock serializes migration and hook writes; its process ID, random token, file identity, and exact contents must still match at release. Dead or invalid locks require manual recovery rather than automatic breaking. On Windows, acquiring or inspecting a lock retries EPERM within the existing 30-second acquisition deadline because deleting a lock can temporarily deny access while readers close their handles; other errors and expired deadlines still fail.
Each hook directory carries a JSON ownership marker containing the absolute path last published to worktree config. After a checkout moves, that marker permits replacement of only the exact stale owned value. Git seeds a new linked worktree's `config.worktree` from the main worktree; when that seed contains the marker-backed reserved hook path of a registered worktree, the installer replaces only the new worktree's config with its own path. Before Lefthook runs, the marker and every existing generated hook must be unaliased regular files. The installer resolves the effective scope, origin, and value of `core.hooksPath`, including active `config.worktree` includes; it refuses command-scoped paths, unowned worktree-scoped paths, and unowned reserved directories. An inherited system, global, or common-repository path requires `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`, which opts only the current worktree into Lefthook. Inactive `includeIf` targets are not recursively inspected because they do not affect the current configuration. Command-scoped Git configuration is removed from the Lefthook subprocess environment after validation.
@@ -14,7 +14,7 @@ Lefthook 生成的钩子会优先使用安装时从对应 worktree 记录的绝
钩子安装以 worktree 为作用域。当 `CI=true` 或 `GITHUB_ACTIONS=true` 时,安装程序会在探测 Git 或做出任何变更之前返回,因为自动化任务不会使用贡献者钩子。否则,安装程序要求 Git 2.26 或更高版本,使 `git config --show-scope` 可以报告由哪个作用域提供配置值;它会将格式版本为 0 的仓库升级到格式版本 1,启用 `extensions.worktreeConfig`,并将当前 worktree 的 `core.hooksPath` 设为指向 `$GIT_DIR/dsh-hooks` 的绝对路径。
升级格式 0 之前,安装程序会拒绝共用配置中直接设置的 `extensions.*`;它还会拒绝直接设置的 `core.worktree` 或 `core.bare=true`,以及启用扩展后将被激活的非空休眠 worktree 配置。迁移会移除直接设置的 `core.bare=false`,因为 false 是 Git 的默认值。共用仓库配置和每个已有的 `config.worktree` 都必须是常规文件。这些检查会禁用 include 展开,因为 Git 的仓库格式解析器也会忽略 include 目标。仓库级锁会串行化迁移和钩子写入;释放时,锁的进程 ID、随机令牌、文件身份和完整内容必须仍然匹配。所属进程已结束或内容无效的锁必须人工介入恢复,而不能自动强制解除。
升级格式 0 之前,安装程序会拒绝共用配置中直接设置的 `extensions.*`;它还会拒绝直接设置的 `core.worktree` 或 `core.bare=true`,以及启用扩展后将被激活的非空休眠 worktree 配置。迁移会移除直接设置的 `core.bare=false`,因为 false 是 Git 的默认值。共用仓库配置和每个已有的 `config.worktree` 都必须是常规文件。这些检查会禁用 include 展开,因为 Git 的仓库格式解析器也会忽略 include 目标。仓库级锁会串行化迁移和钩子写入;释放时,锁的进程 ID、随机令牌、文件身份和完整内容必须仍然匹配。所属进程已结束或内容无效的锁必须人工介入恢复,而不能自动强制解除。在 Windows 上,获取或检查锁时遇到 EPERM 会在原有的 30 秒获取期限内重试,因为删除锁时,读取者关闭句柄前可能暂时无法访问该文件;其他错误及超时仍会导致失败。
每个钩子目录都有一个 JSON 所有权标记,其中包含上次写入 worktree 配置的绝对路径。检出目录移动后,该标记只允许替换确切的陈旧自有值。Git 会以主 worktree 的配置为新链接 worktree 初始化 `config.worktree`;当该初始配置包含某个已注册 worktree 中由所有权标记佐证的保留钩子路径时,安装程序只会在新 worktree 的配置中将其替换为新 worktree 自有的路径。Lefthook 运行前,所有权标记和每个已有的生成钩子都必须是不带别名的常规文件。安装程序会解析 `core.hooksPath` 的生效作用域、来源和值,包括通过当前生效的 `config.worktree` include 加载的值;它会拒绝命令作用域路径、非自有的 worktree 作用域路径以及非自有的保留目录。继承自系统、全局或共用仓库配置的路径必须设置 `DSH_LEFTHOOK_ALLOW_HOOKS_PATH_OVERRIDE=1`,从而只让当前 worktree 显式启用 Lefthook。未生效的 `includeIf` 目标不会被递归检查,因为它们不影响当前配置。完成验证后,Lefthook 子进程的环境会移除命令作用域的 Git 配置。
-1
View File
@@ -56,7 +56,6 @@ External packages installed for runtime use or distributed inside the prebuilt b
| [`@opentelemetry/api`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
| [`@opentelemetry/api-logs`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
| [`@opentelemetry/core`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
| [`@opentelemetry/exporter-logs-otlp-http`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
| [`@opentelemetry/otlp-exporter-base`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
| [`@opentelemetry/otlp-transformer`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
| [`@opentelemetry/resources`](https://github.com/open-telemetry/opentelemetry-js) | Apache-2.0 |
+3
View File
@@ -74,6 +74,8 @@ flowchart LR
cfg --> plugin_dsh_base_storage_domain
plugin_dsh_base_session_projection_cache["session-projection-cache<br/>@deepseek-ai/dsh-session-projection-cache"]
cfg --> plugin_dsh_base_session_projection_cache
plugin_dsh_base_otel["otel<br/>@deepseek-ai/dsh-otel"]
cfg --> plugin_dsh_base_otel
plugin_dsh_base_session_telemetry_otel["session-telemetry-otel<br/>@deepseek-ai/dsh-session-telemetry-otel"]
cfg --> plugin_dsh_base_session_telemetry_otel
plugin_dsh_base_subprocess["subprocess<br/>@deepseek-ai/dsh-subprocess-local"]
@@ -231,6 +233,7 @@ flowchart LR
| `storage-json` | `@deepseek-ai/dsh-storage-json` |
| `storage-domain` | `@deepseek-ai/dsh-storage-domain` |
| `session-projection-cache` | `@deepseek-ai/dsh-session-projection-cache` |
| `otel` | `@deepseek-ai/dsh-otel` |
| `session-telemetry-otel` | `@deepseek-ai/dsh-session-telemetry-otel` |
| `subprocess` | `@deepseek-ai/dsh-subprocess-local` |
| `sandbox` | `@deepseek-ai/dsh-sandbox-local` |
+1
View File
@@ -39,6 +39,7 @@
"@deepseek-ai/dsh-compaction-tool-result-pruner": "workspace:*",
"@deepseek-ai/dsh-cordis-client-runner": "workspace:*",
"@deepseek-ai/dsh-experimental-agent-team-profile": "workspace:*",
"@deepseek-ai/dsh-experimental-schedule-bundle": "workspace:*",
"@deepseek-ai/dsh-fs-local": "workspace:*",
"@deepseek-ai/dsh-goal": "workspace:*",
"@deepseek-ai/dsh-goal-round-driver": "workspace:*",
@@ -1,2 +0,0 @@
- id: schedule
disabled: false
@@ -8,7 +8,8 @@ import type { DailyScheduleRecord, ScheduleCatalogEntry } from '@deepseek-ai/dsh
import { expect, it } from 'vitest'
import { withDefaultWeb, webGet, webRequest } from './default-web-process.ts'
const patches = [fileURLToPath(new URL('./fixtures/schedule.patch.yml', import.meta.url))]
/** The optional Schedule bundle is the switch this built-Web scenario turns on. */
const patches = [fileURLToPath(new URL('../../../../../../packages/experimental/schedule-bundle/cordis.patch.yml', import.meta.url))]
const id = 'retained-remote-task' as DailyScheduleRecord['id']
const sessionId = 'unloaded-owner' as ScheduleCatalogEntry['sessionId']
+8 -8
View File
@@ -6,8 +6,8 @@
en: 45561fc0dd3a1b20
zh: f4b03355d4bd0693
/deepseek-harness-desktop/closing-the-window-and-quitting:
en: fb852f3bd70d8602
zh: 3d2b0a8d580d7f0a
en: c323e02e3f0305fe
zh: 3c730db632136443
/deepseek-harness-desktop/key-technical-decisions:
en: 069fdfd72d2ea26a
zh: 76ae549d43c99bb4
@@ -15,8 +15,8 @@
en: 31cbb7452be8e1ad
zh: 502ec556e2ec301e
/deepseek-harness-desktop/installation-ownership:
en: 8307baf6c5b691d0
zh: 86e46b9552017ff6
en: d186d3cfc9597688
zh: 5f7094355c5f562c
/deepseek-harness-desktop/installation-ownership/runtime-and-plugin-activation:
en: 0d11af8639977a6d
zh: 86b086b12d2210d3
@@ -24,11 +24,11 @@
en: 9b9b362a9865e763
zh: c748901168e2db8c
/deepseek-harness-desktop/develop/startup-onboarding:
en: 5e2d62207a603d81
zh: 5fb863d4906f4a67
en: a51346b8d880ddba
zh: 7913214c1c66063c
/deepseek-harness-desktop/develop/welcome-window-appearance:
en: 7220801ccc3b2e6b
zh: 042dc97976aef979
en: ae319a496639b38d
zh: ef044103240ac9da
/deepseek-harness-desktop/package:
en: 4cd8a8da6977928d
zh: 9f624855a2edac37
+6 -2
View File
@@ -28,6 +28,8 @@ Windows keeps a tray icon for the whole run. Its tooltip is the product name, a
Every ordinary quit entry — ⌘Q, the application menu, the Dock menu, the Windows tray and caption Application menu, and the quits caused by closing the mandatory-update or welcome window — asks the Host what it would interrupt. The Host answers over the private IPC channel with two facts: active tasks under the same rule as the update restart check (running agents including subagents and approval waits, queued messages, running or stopping jobs), and armed scheduled reminders reported by the `schedule` family of `workspace/session-activity` for the sessions loaded during this run. Neither fact, the quit proceeds silently. Otherwise a native message box without an owner window — hidden windows stay hidden — shows **Quit DeepSeek Harness?** with one of three localized explanations: running tasks will be interrupted, scheduled tasks will not run while the application is closed, or both. Quit is the default button and Cancel answers Esc; macOS places Cancel left of Quit, Windows places Quit left of Cancel, and the Windows task dialog shows the application icon and stays light regardless of the application theme. A Host that has not reached ready or has failed cannot run tasks, so the quit proceeds without asking. An inspection failure or a Host that misses the two-second deadline is treated as running tasks. While the box is open, another quit request joins it instead of stacking a second one (macOS also raises it; Electron exposes no handle to the Windows task dialog), the explanation does not change when work starts or ends, Quit stops the application without re-inspecting, and Cancel changes nothing. Cancelling a quit that closing the welcome window started shows the welcome window again.
The mandatory update confirmation includes the installation wait notice on Windows; macOS uses the shorter restart explanation.
The confirmation is skipped when the installer restart already confirmed task interruption, when the fatal-recovery dialog exits or restarts, when the development Restart App and Host command runs, and during operating-system shutdown, restart, or log-off: Windows sets this on the definitive session-end message; macOS sets it on the power-off notification, and because another application can still cancel that shutdown, the next focus or show of the main window clears it. Installer-owned quit cancels any pending ordinary quit decision; late inspection results and dialog answers cannot open another confirmation or repeat cleanup. A user-initiated update download that finishes while the window is hidden defers its Install and Restart confirmation until the window is shown again; the mandatory flow keeps its taskbar and Dock attention. The Windows installer and uninstaller tell a user whose application is still running to quit it from the system tray. Desktop does not enable scheduled tasks by default, so the scheduled-task explanations appear only once that feature is on; reminders fire only in loaded sessions, and unloaded sessions are neither counted nor resumed until they are opened.
The tray renderer enlarges the whale by 20% around the tile center while preserving the background and aspect ratio; application and installer icons retain their original proportions.
@@ -84,6 +86,8 @@ The `dsh-app://shell/` origin serves packaged update documents, scripts, and sty
The product UI retains Web actions, including "Open In..." through the shared authenticated HTTP routes. Desktop uses Web's automatic directory-picker selection and initializes new profiles with the shared Web template's bundles.
Update copy preserves the complete version, including prerelease suffixes, without adding v or V. When no update is available, the dialog title reports the result and the body shows the current version.
Electron chooses typed English or Chinese shell copy from its application locale and falls back to English. The macOS application bundle declares English and Simplified Chinese in `CFBundleLocalizations`, allowing macOS to match the initial application language to the user’s preferred languages. Saved Client UI language preferences still take precedence for the main interface. On Windows, the main document's language updates desktop menus, recovery and update prompts. The repository Client UI i18n gate checks desktop sources.
Windows uses a 40-DIP caption with native window controls and colors synchronized from the application palette. Localized Application and Edit entries beside the sidebar toggle open native popup menus. They mount only after the application frame publishes its shell overlay seat, and remain absent during startup loading. Application provides Check for Updates and Exit; Edit provides undo, redo, cut, copy, paste, delete, and select all by sending the corresponding keys to the focused editor, independently of custom shortcut bindings. Plugin management uses the main application's Plugins page. No separate native menu row appears on Alt. Other platforms retain their native menus. Editable fields retain keyboard commands and a context menu without shortcut labels; Chromium supplies command availability, and selected read-only text offers Copy.
@@ -138,7 +142,7 @@ The API-key input starts empty and uses `autocomplete="new-password"` to ask Chr
Repeated launches and `dsh://open` keep the workspace hidden until the startup credential check or a welcome action permits entry. Entering from Welcome places keyboard focus on the document without selecting a sidebar control; Tab navigation remains available.
Desktop checks configured model API-key references after the Host starts and before opening the workspace. With no configured key, the welcome window offers the [API-key page](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=2138-44626). Save and continue writes through the existing credential service using the official DeepSeek provider's configured reference, then opens the workspace. Set up later opens the workspace without saving the draft or a completion setting; the next process launch checks credentials again. Back to sign in returns to the entry and clears the unsaved key and validation message. Buttons keep their labels and block competing actions while saving or opening the workspace. The Desktop preload marker suppresses the Web credential dialog, while retaining the Models settings page and the welcome notice.
Desktop checks configured model API-key references after the Host starts and before opening the workspace. With no configured key, the welcome window offers the API-key page. Save and continue writes through the existing credential service using the official DeepSeek provider's configured reference, then opens the workspace. Set up later opens the workspace without saving the draft or a completion setting; the next process launch checks credentials again. Back to sign in returns to the entry and clears the unsaved key and validation message. Buttons keep their labels and block competing actions while saving or opening the workspace. The Desktop preload marker suppresses the Web credential dialog, while retaining the Models settings page and the welcome notice.
The welcome window reads the shared `locale.preference` before it appears. An explicit English or Chinese choice wins; otherwise Desktop picks the first supported OS language and falls back to English. The main UI reads the same preference and OS language order through its isolated preload before mounting. Language changes in Settings update the shell’s current dictionary and menu; automatic selection writes no preference. The welcome window has no language selector.
@@ -146,7 +150,7 @@ The browser-login waiting page offers a copy-link action for the current pending
### Welcome window appearance
The welcome window follows system appearance with the design’s Platform light/dark colors and shows the 600 × 700 [entry layout](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=2121-39334) and the API-key form, with native window controls, a draggable title area, a local brand SVG, system sans-serif fallbacks, and locally bundled Montserrat Light for non-button text. The window uses macOS menu vibrancy or Windows acrylic with the onboarding window tint: 40% white in light mode and 50% rgb(24 25 28) in dark mode. The local React welcome entry bundles React and the shared `StateDot` loading indicator with its CSS; it uses the isolated preload without loading the main Web application. The entry, sign-in status and API-key pages share a fixed bottom action row; the back-to-sign-in link sits below it. Buttons share the platform motion timings, and Reduce Motion disables their transitions. The OS owns blur strength and outer corners. macOS Reduce Transparency suppresses translucency, and Increase Contrast forces that setting on. Save and continue writes to the development credential store; Set up later opens the real workspace without saving a key or completion flag. The generated project links the declared workspace dependency closure as well as pnpm’s hoisted packages, so unhoisted configured plugins remain resolvable. [The window note](../../.agents/notes/implemented/architecture/2026-09-08-desktop-welcome-window-material.md) owns the material and onboarding decisions.
The welcome window follows system appearance with the design’s Platform light/dark colors and shows the 600 × 700 entry layout and the API-key form, with native window controls, a draggable title area, a local brand SVG, system sans-serif fallbacks, and locally bundled Montserrat Light for non-button text. The window uses macOS menu vibrancy or Windows acrylic with the onboarding window tint: 40% white in light mode and 50% rgb(24 25 28) in dark mode. The local React welcome entry bundles React and the shared `StateDot` loading indicator with its CSS; it uses the isolated preload without loading the main Web application. The entry, sign-in status and API-key pages share a fixed bottom action row; the back-to-sign-in link sits below it. Buttons share the platform motion timings, and Reduce Motion disables their transitions. The OS owns blur strength and outer corners. macOS Reduce Transparency suppresses translucency, and Increase Contrast forces that setting on. Save and continue writes to the development credential store; Set up later opens the real workspace without saving a key or completion flag. The generated project links the declared workspace dependency closure as well as pnpm’s hoisted packages, so unhoisted configured plugins remain resolvable. [The window note](../../.agents/notes/implemented/architecture/2026-09-08-desktop-welcome-window-material.md) owns the material and onboarding decisions.
## Package
+6 -2
View File
@@ -28,6 +28,8 @@ Windows 在整个运行期间常驻托盘图标。悬停提示为产品名,单
所有普通退出入口——⌘Q、应用菜单、Dock 菜单、Windows 托盘和标题栏"应用程序"菜单,以及关闭强制更新窗口或欢迎窗口引起的退出——都先向 Host 查询退出会中断什么。Host 通过私有 IPC 通道回答两项事实:与更新重启检查同一口径的运行中任务(运行中的 agent,包括子代理和等待审批的回合、排队消息、运行中或停止中的后台任务),以及本次运行中已加载会话里由 `workspace/session-activity` 的 `schedule` family 报告的已挂定时器的提醒。两项都没有时直接退出,不弹框。否则弹出一个没有父窗口的原生消息框——隐藏的窗口保持隐藏——标题为**退出 DeepSeek Harness?**,正文为三种本地化说明之一:正在运行的任务将会中断、应用关闭期间定时任务不会运行,或两者兼有。"退出"是默认按钮,Esc 等同"取消";macOS 上"取消"在"退出"左侧,Windows 上"退出"在"取消"左侧,Windows 任务对话框显示应用图标且不跟随应用主题、始终为浅色。Host 尚未就绪或已失败时不可能有任务在跑,直接退出。查询失败或 Host 超过两秒截止时间未答复,按运行中任务处理。弹框打开期间,再次请求退出只会并入同一弹框而不叠加新弹框(macOS 上还会把它提到前面;Electron 不暴露 Windows 任务对话框的句柄);任务开始或结束不会改变文案;点"退出"不再重新查询即停止应用;点"取消"不发生任何变化。取消由关闭欢迎窗口引起的退出时,欢迎窗口会重新显示。
强制更新的安装确认在 Windows 上额外显示等待提示,macOS 只显示简短的重启说明。
以下情况跳过确认:安装更新的重启已确认过任务中断、致命错误恢复对话框中的退出或重启、开发版"重启应用与 Host"命令,以及操作系统关机、重启或注销:Windows 在确定性的会话结束消息上设置该状态;macOS 在关机通知上设置,而其他应用仍可能取消这次关机,因此主窗口下一次获得焦点或显示时会清除它。安装器接管退出时会取消尚未结束的普通退出决策;晚到的查询结果和弹框答复不会再次打开确认框或重复清理。窗口隐藏期间完成的用户主动发起的更新下载,把"安装并重启"确认推迟到窗口再次显示时;强制更新流程沿用其任务栏和 Dock 提醒。Windows 安装程序和卸载程序在应用仍在运行时提示用户先在系统托盘中退出。Desktop 默认未开启定时任务,定时任务的说明只在该功能开启后出现;提醒只在已加载的会话中触发,未加载的会话既不计入,也要等到打开后才会继续。
托盘渲染器以底板中心为基准将鲸鱼放大 20%,保留背景和宽高比;应用和安装器图标保持原有比例。
@@ -86,6 +88,8 @@ Electron 拥有 `$DSH_HOME/profiles/desktop`。其 `dependencies` 包含 pnpm
产品 UI 保留 Web 操作,包括通过共享认证 HTTP 路由执行的“打开方式…”。Desktop 使用 Web 的自动目录选择机制,并以共享 Web 模板的 bundle 列表初始化新 profile。
升级文案完整展示版本号(包括预发布后缀),不额外添加 v 或 V。无可用更新时,弹窗标题显示检查结果,正文显示当前版本。
Electron 根据应用语言选择类型化的英文或中文 shell 文案,并回退到英文。macOS 应用包通过 `CFBundleLocalizations` 声明支持英语和简体中文,让 macOS 根据用户的首选语言匹配初始应用语言。主界面仍优先使用已保存的 Client UI 语言偏好。在 Windows 上,主文档的语言会更新桌面菜单、恢复与更新提示。仓库 Client UI i18n 检查覆盖桌面端源码。
Windows 使用 40 DIP 顶栏,保留原生窗口按钮,颜色随应用调色板同步。侧栏开关旁的本地化“应用”和“编辑”入口打开原生弹出菜单。仅当应用框架发布 shell overlay 席位后才挂载菜单,启动加载期间不显示。“应用”提供检查更新和退出;“编辑”向当前编辑器发送对应按键,提供撤销、重做、剪切、复制、粘贴、删除和全选,不受自定义快捷键绑定影响。插件管理使用主应用的“插件”页面。按 Alt 不会出现额外的原生菜单行。其他平台保留原生菜单。可编辑区域保留快捷键和不带快捷键标注的右键菜单;命令可用状态由 Chromium 提供,选中的只读文本提供“复制”命令。
@@ -140,7 +144,7 @@ API Key 输入框初始为空,并通过 `autocomplete="new-password"` 请求 C
重复启动和 `dsh://open` 会保持工作区隐藏,直到启动凭据检查或欢迎页操作允许进入。从 Welcome 进入时,键盘焦点落在文档上,不选中侧边栏控件;Tab 导航仍可使用。
Desktop 在 Host 启动后、打开工作区前检查模型 API Key 引用是否已配置。没有已配置的密钥时,欢迎窗口提供 [API Key 页面](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=2138-44626)。“保存并继续”通过现有凭证服务写入 DeepSeek 官方提供方配置的引用,然后打开工作区。“稍后配置”打开工作区,但不保存草稿或完成标记;下次进程启动时会重新检查凭证。“返回登录”回到入口并清空未保存的密钥和校验提示。保存或打开工作区期间,按钮保持原文案并禁用竞争操作。Desktop preload 标记使 Web 凭证弹窗不再显示,同时保留模型设置页和欢迎须知。
Desktop 在 Host 启动后、打开工作区前检查模型 API Key 引用是否已配置。没有已配置的密钥时,欢迎窗口提供 API Key 页面。“保存并继续”通过现有凭证服务写入 DeepSeek 官方提供方配置的引用,然后打开工作区。“稍后配置”打开工作区,但不保存草稿或完成标记;下次进程启动时会重新检查凭证。“返回登录”回到入口并清空未保存的密钥和校验提示。保存或打开工作区期间,按钮保持原文案并禁用竞争操作。Desktop preload 标记使 Web 凭证弹窗不再显示,同时保留模型设置页和欢迎须知。
欢迎窗口在显示前读取共享的 `locale.preference`。用户明确选择的英文或中文优先;否则 Desktop 按系统语言顺序匹配支持的语言,并以英文兜底。主界面在挂载前通过隔离 preload 读取同一偏好和系统语言顺序。在设置中切换语言会更新桌面壳的当前词典和菜单;自动选择不会写入偏好。欢迎窗口不提供语言切换入口。
@@ -148,7 +152,7 @@ Desktop 在 Host 启动后、打开工作区前检查模型 API Key 引用是否
### 欢迎窗口外观
欢迎窗口使用设计稿的 Platform light/dark 颜色跟随系统外观,展示 600 × 700 的[入口布局](https://www.figma.com/design/jRBBK7zBgcszdVWQ0Fh5J8/Harness?node-id=2121-39334)和 API Key 表单,包含原生窗口控件、可拖动标题区域、本地品牌 SVG、系统无衬线字体回退,以及非按钮文字使用的本地 Montserrat Light 字体。窗口使用 macOS menu vibrancy 或 Windows acrylic,叠加 onboarding 的窗口背景色:浅色模式为 40% 白色,深色模式为 50% rgb(24 25 28)。本地 React 欢迎入口将 React、公共 `StateDot` 加载指示器及其 CSS 一起打包;它通过隔离 preload 工作,不加载主 Web 应用。入口、登录状态和 API Key 页面共用固定的底部操作行;“返回登录”链接位于操作行下方。按钮共用平台的过渡时序,开启“减少动态效果”会禁用过渡。操作系统控制模糊强度和外部圆角。macOS 的“降低透明度”会抑制半透明效果,“增强对比度”会强制开启该设置。“保存并继续”写入开发环境的凭证存储;“稍后配置”打开真实工作区,不保存密钥或完成标记。生成的开发项目同时链接已声明的 workspace 依赖闭包和 pnpm 提升的包,因此未提升的配置插件仍能解析。[窗口记录](../../.agents/notes/implemented/architecture/2026-09-08-desktop-welcome-window-material.zh.md)负责材质与引导决策。
欢迎窗口使用设计稿的 Platform light/dark 颜色跟随系统外观,展示 600 × 700 的入口布局和 API Key 表单,包含原生窗口控件、可拖动标题区域、本地品牌 SVG、系统无衬线字体回退,以及非按钮文字使用的本地 Montserrat Light 字体。窗口使用 macOS menu vibrancy 或 Windows acrylic,叠加 onboarding 的窗口背景色:浅色模式为 40% 白色,深色模式为 50% rgb(24 25 28)。本地 React 欢迎入口将 React、公共 `StateDot` 加载指示器及其 CSS 一起打包;它通过隔离 preload 工作,不加载主 Web 应用。入口、登录状态和 API Key 页面共用固定的底部操作行;“返回登录”链接位于操作行下方。按钮共用平台的过渡时序,开启“减少动态效果”会禁用过渡。操作系统控制模糊强度和外部圆角。macOS 的“降低透明度”会抑制半透明效果,“增强对比度”会强制开启该设置。“保存并继续”写入开发环境的凭证存储;“稍后配置”打开真实工作区,不保存密钥或完成标记。生成的开发项目同时链接已声明的 workspace 依赖闭包和 pnpm 提升的包,因此未提升的配置插件仍能解析。[窗口记录](../../.agents/notes/implemented/architecture/2026-09-08-desktop-welcome-window-material.zh.md)负责材质与引导决策。
## 打包
+5 -1
View File
@@ -9,4 +9,8 @@
#copy { min-height: 32px; padding: 3px 0; background: transparent; color: #4d6bfe; font-size: 12px; }
#copy-message { color: #d93025 !important; }
#manual-copy { display: block; margin-top: 8px; }
#progress { display: block; width: 100%; height: 4px; margin-top: 12px; border: 0; accent-color: #4d6bfe; }
#progress { display: block; width: 100%; height: 4px; margin-top: 12px; border: 0; appearance: none; -webkit-appearance: none; background: #e6e7eb; accent-color: #0f1115; }
#progress::-webkit-progress-bar { background: #e6e7eb; }
#progress::-webkit-progress-value { background: #0f1115; }
#progress::-moz-progress-bar { background: #0f1115; }
+65 -65
View File
@@ -77,17 +77,17 @@ export const en = {
updateCheckFailed: 'Could not check for updates. Please try again later.',
updateDownloadFailed: 'Could not download the update. Please try again.',
updateInstallFailed: 'Could not install the update. Please try again later.',
updateCheckNetworkFailed: 'Could not check for updates. Please try again later. The connection was interrupted. Check your network and try again.',
updateDownloadNetworkFailed: 'Could not download the update. Please try again. The connection was interrupted. Check your network and try again.',
updateInstallNetworkFailed: 'Could not install the update. Please try again later. The connection was interrupted. Check your network and try again.',
updateCheckNetworkFailed: 'Could not check for updates. Check your connection and try again.',
updateDownloadNetworkFailed: 'Could not download the update. Check your connection and try again.',
updateInstallNetworkFailed: 'Could not install the update. Check your connection and try again.',
unknownError: 'Unknown error',
updateCheckTitle: 'Check for Updates',
updateCurrent: 'No updates available. Current version: V{version}',
updateCurrentDetail: 'Current version: {version}',
updateCurrent: 'You’re up to date!',
updateChecking: 'Checking for updates…',
updateDownload: 'Download update',
updateDownloadedTitle: 'DeepSeek Harness v{version} downloaded',
updateDownloadedDetail: 'The update package has downloaded. Select “Install and Restart” to restart the app and begin installation.',
updateDownloadedTitleWindows: 'New version v{version} is ready',
updateDownloadedTitle: 'Version {version} is ready to install',
updateDownloadedDetail: 'The app will close during the update and reopen automatically when it is complete.',
updateDownloadedDetailWindows: 'The app will close temporarily during the update and reopen automatically when it is complete.\n\nThe update may take some time. Please wait and do not launch the app again during installation.',
updateClose: 'Close',
updateAcknowledge: 'OK',
@@ -97,47 +97,47 @@ export const en = {
updateInstalling: 'Preparing to restart…',
updateRetry: 'Retry update',
updateActiveTasks: 'Tasks are still in progress',
updateActiveTasksDetail: 'Restarting to update may interrupt these tasks. Continue updating?',
updateActiveTasksDetail: 'Updating will stop the tasks in progress and restart the app. Continue?',
updateStopTasks: 'Stop tasks and update',
updateTasksChanged: 'New tasks started. Review the update confirmation again.',
updateTasksChanged: 'New tasks have started. Confirm again to stop the tasks and update.',
updateTasksUnavailable: 'Task status is unavailable. Try updating again when the workspace is ready.',
updateStopFailed: 'Tasks could not be stopped safely. The update was not installed. Please try again later.',
updateStopFailed: 'Could not safely stop the tasks. The update has not been installed. Please try again later.',
updateTechnicalDetails: 'View technical details',
updateTitle: 'DeepSeek Harness Update',
updateAvailable: 'An update is available',
updateDetail: 'DeepSeek Harness {version}\n\nDeepSeek Harness will restart to complete the update.',
updateAvailable: 'New version available: {version}',
updateDetail: 'Once the download is complete, you can install the update and restart the app.',
installAndRestart: 'Install and Restart',
later: 'Later',
updateFailedTitle: 'Update Failed',
mandatoryTitle: 'Update required',
mandatoryDetail: 'This version is no longer supported. Update to continue. Existing tasks can keep running until you approve a restart.',
mandatoryUnavailable: 'The update requirement could not be checked. Retry when the connection is available.',
mandatoryTitle: 'Update to continue',
mandatoryDetail: 'This version is no longer supported. Update to continue. Tasks in progress will keep running until you confirm installation and restart.',
mandatoryUnavailable: 'Could not check update requirements. Please try again later.',
policyLoginTitle: 'Sign in to the test environment',
policyLoginRequired: 'This is a test build. Checking update requirements needs Feishu sign-in. Signing in does not download or install an update.',
policyLoginRequired: 'Sign in with Feishu to check update requirements for this test build. Signing in will not download or install an update.',
policyLogin: 'Sign in with Feishu',
policyLoginFailed: 'Test environment sign-in did not complete. Please check your connection and try again.',
policyLoginFailed: 'Feishu sign-in did not complete. Please try again.',
policyLoginLoading: 'Loading sign-in page…',
mandatoryNoRelease: 'No applicable update is available. Check again or contact support.',
mandatoryNoRelease: 'No compatible update was found. Check again or contact support.',
mandatoryRefresh: 'Check again',
mandatoryPage: 'Open download page',
mandatoryCopy: 'Copy download address',
mandatoryPageFailed: 'The download page could not be opened. Copy the address below and open it in your browser.',
mandatoryActionFailed: 'The update action failed. Retry; the update requirement remains active.',
mandatoryPage: 'Download from the official website',
mandatoryCopy: 'Copy download link',
mandatoryPageFailed: 'Could not open the official download page. Copy the link and open it in your browser.',
mandatoryActionFailed: 'The action failed. Please try again. You can use the app again after the update is complete.',
mandatoryReady: 'Update ready',
mandatoryVersion: 'V{version}',
mandatoryReadyDetail: 'Installing the update will restart the application.',
mandatoryDeferred: 'Existing tasks can keep running. Update to continue using the application.',
mandatoryContinue: 'Continue installing update',
mandatoryVersion: 'New version: {version}',
mandatoryReadyDetail: 'The app will close during the update and reopen automatically when it is complete.',
mandatoryDeferred: 'Tasks in progress will keep running. Complete the update before using the app again.',
mandatoryContinue: 'Continue update',
mandatoryInspecting: 'Checking tasks…',
mandatoryStopping: 'Safely stopping tasks in the application.',
mandatoryRestarting: 'The application will restart shortly. Please wait.',
mandatoryStopping: 'Stopping tasks…',
mandatoryRestarting: 'The app will restart shortly. Please wait.',
mandatoryDownloadFailed: 'The update files could not be downloaded or prepared. Please retry.',
mandatoryInstallFailed: 'The update was not installed. Check tasks again and retry.',
mandatoryOpenHelp: 'If the page did not open, you can',
mandatoryReopen: 'Open download page again',
mandatoryInstallFailed: 'The update has not been installed. Check the tasks again and retry.',
mandatoryOpenHelp: 'Page didn’t open?',
mandatoryReopen: 'Open the official download page again',
mandatoryCopied: 'Link copied',
mandatoryCopyFailed: 'Copy failed. Select and copy the address below manually.',
mandatoryAddress: 'Download address',
mandatoryCopyFailed: 'Could not copy the link. Select and copy it below.',
mandatoryAddress: 'Download link',
mandatoryNotification: 'Return to the application to confirm installation and restart.',
} as const
@@ -221,17 +221,17 @@ export const zh = {
updateCheckFailed: '检查更新失败,请稍后重试。',
updateDownloadFailed: '下载更新失败,请重试。',
updateInstallFailed: '安装更新失败,请稍后重试。',
updateCheckNetworkFailed: '检查更新失败,请稍后重试。网络连接异常,请检查网络后重试。',
updateDownloadNetworkFailed: '下载更新失败,请重试。网络连接异常,请检查网络后重试。',
updateInstallNetworkFailed: '安装更新失败,请稍后重试。网络连接异常,请检查网络后重试。',
updateCheckNetworkFailed: '检查更新失败,请检查网络连接后重试。',
updateDownloadNetworkFailed: '下载更新失败,请检查网络连接后重试。',
updateInstallNetworkFailed: '安装更新失败,请检查网络连接后重试。',
unknownError: '未知错误',
updateCheckTitle: '检查更新',
updateCurrent: '当前暂无可用更新。当前版本:V{version}',
updateCurrentDetail: '当前版本:{version}',
updateCurrent: '已是最新版本',
updateChecking: '正在检查更新…',
updateDownload: '下载更新',
updateDownloadedTitle: 'DeepSeek Harness v{version} 下载完成',
updateDownloadedDetail: '安装包已下载完毕,点击“安装并重启”,即刻重启客户端,开始部署。',
updateDownloadedTitleWindows: '新版本 v{version} 已准备就绪',
updateDownloadedTitle: '新版本 {version} 已准备就绪',
updateDownloadedDetail: '更新期间应用将暂时关闭,完成后会自动打开。',
updateDownloadedDetailWindows: '更新期间应用将暂时关闭,完成后会自动打开。\n\n更新可能需要一些时间,请耐心等待,期间请勿重复启动应用。',
updateClose: '关闭',
updateAcknowledge: '确定',
@@ -241,47 +241,47 @@ export const zh = {
updateInstalling: '正在准备重启…',
updateRetry: '重试更新',
updateActiveTasks: '仍有进行中的任务',
updateActiveTasksDetail: '重启更新可能中断这些任务,是否要继续更新?',
updateActiveTasksDetail: '更新将停止进行中的任务并重启应用,是否继续?',
updateStopTasks: '停止任务并更新',
updateTasksChanged: '有新任务开始,请重新确认更新。',
updateTasksChanged: '有新任务开始运行,请重新确认是否停止任务并更新。',
updateTasksUnavailable: '无法确认任务状态,请在工作区就绪后重试更新。',
updateStopFailed: '未能安全停止任务,更新未安装。请稍后重试。',
updateStopFailed: '未能安全停止任务,更新尚未安装,请稍后重试。',
updateTechnicalDetails: '查看技术详情',
updateTitle: 'DeepSeek Harness 更新',
updateAvailable: '发现可用更新',
updateDetail: 'DeepSeek Harness {version}\n\nDeepSeek Harness 将重启以完成更新。',
updateAvailable: '发现新版本 {version}',
updateDetail: '下载完成后,可安装并重启应用。',
installAndRestart: '安装并重启',
later: '稍后',
updateFailedTitle: '更新失败',
mandatoryTitle: '需要更新',
mandatoryDetail: '当前版本已停止支持,请更新后继续使用。在您确认重启之前,现有任务可以继续运行。',
mandatoryUnavailable: '暂时无法检查更新要求,请在网络恢复后重试。',
mandatoryTitle: '请更新后继续使用',
mandatoryDetail: '当前版本已停止支持,请更新后继续使用。确认安装并重启前,进行中的任务会继续运行。',
mandatoryUnavailable: '暂时无法检查更新要求,请稍后重试。',
policyLoginTitle: '登录测试环境',
policyLoginRequired: '这是测试版应用,检查更新要求需要先通过飞书登录。登录不会下载或安装更新。',
policyLoginRequired: '此测试版需要先通过飞书登录,才能检查更新要求。登录不会下载或安装更新。',
policyLogin: '通过飞书登录',
policyLoginFailed: '测试环境登录未完成,请检查网络后重试。',
policyLoginFailed: '飞书登录未完成,请重试。',
policyLoginLoading: '正在加载登录页面…',
mandatoryNoRelease: '暂时没有可用的更新,请重新检查或联系支持人员。',
mandatoryNoRelease: '暂未找到适用的更新,请重新检查或联系支持人员。',
mandatoryRefresh: '重新检查',
mandatoryPage: '前往官网下载',
mandatoryCopy: '复制下载链接',
mandatoryPageFailed: '无法打开浏览器,请复制下载链接后手动打开。',
mandatoryActionFailed: '更新操作失败,请重试;应用仍需更新后才能继续使用。',
mandatoryPageFailed: '无法打开官网下载页面,请复制链接后在浏览器中打开。',
mandatoryActionFailed: '操作失败,请重试。完成更新后才能继续使用应用。',
mandatoryReady: '更新已准备就绪',
mandatoryVersion: 'V{version}',
mandatoryReadyDetail: '安装后将重新启动应用。',
mandatoryDeferred: '现有任务可以继续运行。完成更新后才能继续操作应用。',
mandatoryContinue: '继续安装更新',
mandatoryVersion: '新版本:{version}',
mandatoryReadyDetail: '更新期间应用将暂时关闭,完成后会自动打开。',
mandatoryDeferred: '进行中的任务会继续运行。请完成更新后再操作应用。',
mandatoryContinue: '继续更新',
mandatoryInspecting: '正在检查任务状态…',
mandatoryStopping: '正在安全结束应用中的任务。',
mandatoryStopping: '正在停止任务…',
mandatoryRestarting: '应用即将重启,请稍候。',
mandatoryDownloadFailed: '更新文件下载或准备失败,请重试。',
mandatoryInstallFailed: '更新尚未安装,请重新检查任务后重试。',
mandatoryOpenHelp: '若页面未打开,可',
mandatoryReopen: '重新打开官网',
mandatoryCopied: '已复制链接',
mandatoryCopyFailed: '复制失败,请手动选择下方地址复制。',
mandatoryAddress: '下载地址',
mandatoryOpenHelp: '页面未打开?',
mandatoryReopen: '重新前往官网下载',
mandatoryCopied: '链接已复制',
mandatoryCopyFailed: '复制失败,请手动选择并复制下方链接。',
mandatoryAddress: '下载链接',
mandatoryNotification: '返回应用确认安装并重启。',
} as const satisfies DesktopMessages
@@ -325,7 +325,7 @@ export function formatDesktopMessage(
/**
* Select localized copy for an ordinary downloaded-update confirmation.
* @param messages - Selected Desktop dictionary.
* @param version - Prepared update version without a leading v.
* @param version - Prepared update version, including any prerelease suffix.
* @param platform - Operating system presenting the confirmation.
* @returns The versioned title and installation guidance.
*/
@@ -335,7 +335,7 @@ export function desktopUpdateReadyConfirmation(
platform: string,
): { message: string; detail: string } {
return {
message: formatDesktopMessage(platform === 'win32' ? messages.updateDownloadedTitleWindows : messages.updateDownloadedTitle, { version }),
message: formatDesktopMessage(messages.updateDownloadedTitle, { version }),
detail: platform === 'win32' ? messages.updateDownloadedDetailWindows : messages.updateDownloadedDetail,
}
}
+5 -3
View File
@@ -778,7 +778,8 @@ async function main(): Promise<void> {
if (state.phase === 'error' && state.failedOperation === 'check') { await showUpdateFailure(state); return }
if (state.phase === 'idle') {
await ordinaryMessageBox({ type: 'info', title: locale.messages.updateCheckTitle,
message: formatDesktopMessage(locale.messages.updateCurrent, { version: app.getVersion() }) })
message: locale.messages.updateCurrent,
detail: formatDesktopMessage(locale.messages.updateCurrentDetail, { version: app.getVersion() }) })
return
}
if (state.phase === 'ready' || (state.phase === 'error' && state.failedOperation === 'install')) {
@@ -790,8 +791,9 @@ async function main(): Promise<void> {
}
if (state.phase !== 'available' && !(state.phase === 'error' && state.failedOperation === 'download')) return
if (manual) {
const result = await ordinaryMessageBox({ title: locale.messages.updateCheckTitle, message: locale.messages.updateAvailable,
detail: formatDesktopMessage(locale.messages.updateDetail, { version: state.version ?? '' }),
const result = await ordinaryMessageBox({ title: locale.messages.updateCheckTitle,
message: formatDesktopMessage(locale.messages.updateAvailable, { version: state.version ?? '' }),
detail: locale.messages.updateDetail,
buttons: [locale.messages.updateDownload], cancelId: 1 })
if (result.response !== 0) return
}
+5 -1
View File
@@ -243,7 +243,11 @@ export class DesktopMandatoryUpdateWindow {
}
private view(): MandatoryUpdateView {
return { locale: this.options.locale, policy: this.options.policy(), update: this.options.update(),
const locale = process.platform === 'win32'
? { ...this.options.locale, messages: { ...this.options.locale.messages,
mandatoryReadyDetail: this.options.locale.messages.updateDownloadedDetailWindows } }
: this.options.locale
return { locale, policy: this.options.policy(), update: this.options.update(),
deferred: this.deferred,
...(this.confirmation === undefined ? {}
: { confirmation: { version: this.confirmation.version, active: this.confirmation.active, revision: this.confirmation.revision } }),
@@ -1,8 +1,8 @@
{
"title": "仍有进行中的任务",
"detail": "重启更新可能中断这些任务,是否要继续更新?",
"detail": "更新将停止进行中的任务并重启应用,是否继续?",
"status": "",
"version": "V1.0.1-nightly.1",
"version": "新版本:1.0.1-nightly.1",
"buttons": [
"停止任务并更新",
"稍后更新"
@@ -1,18 +1,18 @@
{
"returned": [
"Checking for updates…",
"No updates available. Current version: V1.0.0",
"This is a test build. Checking update requirements needs Feishu sign-in. Signing in does not download or install an update."
"You’re up to date!",
"Sign in with Feishu to check update requirements for this test build. Signing in will not download or install an update."
],
"cancelled": [
"Checking for updates…",
"No updates available. Current version: V1.0.0",
"This is a test build. Checking update requirements needs Feishu sign-in. Signing in does not download or install an update."
"You’re up to date!",
"Sign in with Feishu to check update requirements for this test build. Signing in will not download or install an update."
],
"failed": [
"Checking for updates…",
"No updates available. Current version: V1.0.0",
"This is a test build. Checking update requirements needs Feishu sign-in. Signing in does not download or install an update.",
"Test environment sign-in did not complete. Please check your connection and try again."
"You’re up to date!",
"Sign in with Feishu to check update requirements for this test build. Signing in will not download or install an update.",
"Feishu sign-in did not complete. Please try again."
]
}
@@ -0,0 +1,49 @@
[
{
"title": "Update to continue",
"detail": "This version is no longer supported. Update to continue. Tasks in progress will keep running until you confirm installation and restart.",
"version": "New version: 0.1.7-alpha.2",
"update": "Download update"
},
{
"title": "Update ready",
"detail": "The app will close during the update and reopen automatically when it is complete.",
"version": "New version: 0.1.7-alpha.2",
"update": "Install and Restart"
},
{
"title": "Tasks are still in progress",
"detail": "Updating will stop the tasks in progress and restart the app. Continue?",
"version": "New version: 0.1.7-alpha.2",
"update": "Stop tasks and update",
"later": "Update later"
},
{
"title": "Update ready",
"detail": "Tasks in progress will keep running. Complete the update before using the app again.",
"version": "New version: 0.1.7-alpha.2",
"update": "Continue update"
},
{
"title": "Preparing to restart…",
"detail": "Stopping tasks…",
"version": "New version: 0.1.7-alpha.2"
},
{
"title": "Update to continue",
"detail": "This version is no longer supported. Update to continue. Tasks in progress will keep running until you confirm installation and restart.",
"version": "New version: 0.1.7-alpha.2",
"error": "The update has not been installed. Check the tasks again and retry.",
"update": "Retry update",
"page": "Download from the official website"
},
{
"title": "Update to continue",
"detail": "This version is no longer supported. Update to continue. Tasks in progress will keep running until you confirm installation and restart.",
"version": "",
"error": "No compatible update was found. Check again or contact support.",
"page": "Open the official download page again",
"browser-message": "Could not open the official download page. Copy the link and open it in your browser.",
"copy-message": "Could not copy the link. Select and copy it below."
}
]
@@ -0,0 +1,49 @@
[
{
"title": "请更新后继续使用",
"detail": "当前版本已停止支持,请更新后继续使用。确认安装并重启前,进行中的任务会继续运行。",
"version": "新版本:0.1.7-alpha.2",
"update": "下载更新"
},
{
"title": "更新已准备就绪",
"detail": "更新期间应用将暂时关闭,完成后会自动打开。",
"version": "新版本:0.1.7-alpha.2",
"update": "安装并重启"
},
{
"title": "仍有进行中的任务",
"detail": "更新将停止进行中的任务并重启应用,是否继续?",
"version": "新版本:0.1.7-alpha.2",
"update": "停止任务并更新",
"later": "稍后更新"
},
{
"title": "更新已准备就绪",
"detail": "进行中的任务会继续运行。请完成更新后再操作应用。",
"version": "新版本:0.1.7-alpha.2",
"update": "继续更新"
},
{
"title": "正在准备重启…",
"detail": "正在停止任务…",
"version": "新版本:0.1.7-alpha.2"
},
{
"title": "请更新后继续使用",
"detail": "当前版本已停止支持,请更新后继续使用。确认安装并重启前,进行中的任务会继续运行。",
"version": "新版本:0.1.7-alpha.2",
"error": "更新尚未安装,请重新检查任务后重试。",
"update": "重试更新",
"page": "前往官网下载"
},
{
"title": "请更新后继续使用",
"detail": "当前版本已停止支持,请更新后继续使用。确认安装并重启前,进行中的任务会继续运行。",
"version": "",
"error": "暂未找到适用的更新,请重新检查或联系支持人员。",
"page": "重新前往官网下载",
"browser-message": "无法打开官网下载页面,请复制链接后在浏览器中打开。",
"copy-message": "复制失败,请手动选择并复制下方链接。"
}
]
@@ -1,6 +1,6 @@
{
"message": "DeepSeek Harness v0.1.99 downloaded",
"detail": "The update package has downloaded. Select “Install and Restart” to restart the app and begin installation.",
"message": "Version 0.1.7-alpha.2 is ready to install",
"detail": "The app will close during the update and reopen automatically when it is complete.",
"buttons": [
"Install and Restart"
]
@@ -1,6 +1,6 @@
{
"message": "DeepSeek Harness v0.1.99 下载完成",
"detail": "安装包已下载完毕,点击“安装并重启”,即刻重启客户端,开始部署。",
"message": "新版本 0.1.7-alpha.2 已准备就绪",
"detail": "更新期间应用将暂时关闭,完成后会自动打开。",
"buttons": [
"安装并重启"
]
@@ -1,5 +1,5 @@
{
"message": "New version v0.1.99 is ready",
"message": "Version 0.1.7-alpha.2 is ready to install",
"detail": "The app will close temporarily during the update and reopen automatically when it is complete.\n\nThe update may take some time. Please wait and do not launch the app again during installation.",
"buttons": [
"Install and Restart"
@@ -1,5 +1,5 @@
{
"message": "新版本 v0.1.99 已准备就绪",
"message": "新版本 0.1.7-alpha.2 已准备就绪",
"detail": "更新期间应用将暂时关闭,完成后会自动打开。\n\n更新可能需要一些时间,请耐心等待,期间请勿重复启动应用。",
"buttons": [
"安装并重启"
+2 -2
View File
@@ -218,7 +218,7 @@ async function qualify() {
const checking = await dialogWith(messages.updateChecking)
await screenshot(checking, 'checking.png')
server.release()
const current = await dialogWith(messages.updateCurrent.replace('{version}', app.getVersion()))
const current = await dialogWith(messages.updateCurrent)
assert.equal(current, checking)
await clickText(current, messages.updateAcknowledge)
assert.equal(server.requests.filter(path => path === '/payload.exe').length, 0)
@@ -227,7 +227,7 @@ async function qualify() {
server.select('healthy', '0.1.6-nightly.1')
checkMenu.click()
const available = await dialogWith(messages.updateAvailable)
const available = await dialogWith(messages.updateAvailable.replace('{version}', '0.1.6-nightly.1'))
server.select('corrupt', '0.1.6-nightly.1')
await clickText(available, messages.updateDownload)
await waitFor(() => fixture.coordinator.state.phase === 'error', 'checksum failure')
+5 -5
View File
@@ -543,7 +543,7 @@ describe('desktop main startup', () => {
expect(testAuth.login).toHaveBeenCalledOnce()
const messages = harness.dialog.showMessageBox.mock.calls.map(call => (call.at(-1) as { message: string }).message)
expect(messages.filter(message => message === en.policyLoginRequired)).toHaveLength(1)
expect(messages).toContain('No updates available. Current version: V1.0.0')
expect(messages).toContain('You’re up to date!')
} finally {
explanation.resolve({ response: 1 })
login.resolve('cancelled')
@@ -1062,7 +1062,7 @@ describe('desktop main startup', () => {
vi.stubGlobal('process', { ...process, platform })
vi.spyOn(harness.app, 'getPreferredSystemLanguages').mockReturnValue([language])
await readyForUpdate()
harness.updateState = { phase: 'ready', version: '0.1.99' }
harness.updateState = { phase: 'ready', version: '0.1.7-alpha.2' }
harness.dialog.showMessageBox.mockResolvedValueOnce({ response: 1 })
await expect(harness.prepareUpdate()).resolves.toBe(false)
const { message, detail, buttons } = harness.dialog.showMessageBox.mock.lastCall![0] as MessageBoxOptions
@@ -1441,7 +1441,7 @@ describe('desktop main startup', () => {
await prompt
expect(signal.aborted).toBe(true)
expect(harness.dialog.showMessageBox).toHaveBeenLastCalledWith(expect.objectContaining({
message: 'No updates available. Current version: V1.0.0',
message: 'You’re up to date!', detail: 'Current version: 1.0.0',
}))
})
@@ -1506,7 +1506,7 @@ describe('desktop main startup', () => {
vi.stubGlobal('fetch', vi.fn<typeof fetch>().mockImplementation(() => policy.promise))
harness.updateCheck.mockResolvedValue({ phase: 'available', version: '1.0.1-nightly.1' })
harness.dialog.showMessageBox.mockImplementation(({ signal, message }: { signal?: AbortSignal; message: string }) => {
if (message !== en.updateAvailable || signal === undefined) return Promise.resolve({ response: 1 })
if (message !== en.updateAvailable.replace('{version}', '1.0.1-nightly.1') || signal === undefined) return Promise.resolve({ response: 1 })
available.resolve(signal)
return new Promise((resolve) => { signal.addEventListener('abort', () => { resolve({ response: 0 }) }, { once: true }) })
})
@@ -1543,7 +1543,7 @@ describe('desktop main startup', () => {
const ordinaryShown = Promise.withResolvers<undefined>()
const policyShown = Promise.withResolvers<undefined>()
harness.dialog.showMessageBox.mockImplementation(({ message }: { message: string }) => {
if (message.startsWith('No updates available.')) {
if (message === en.updateCurrent) {
ordinaryShown.resolve(undefined)
return ordinaryResult.promise
}
@@ -205,3 +205,10 @@ it('rebinds policy updates and reloads to a replacement Windows main window', ()
sender: window.webContents, senderFrame: window.webContents.mainFrame,
})).not.toThrow()
})
it.each(['darwin', 'win32'] as const)('includes the installation wait notice only on Windows: %s', (platform) => {
const f = setup(platform)
expect(f.view().locale.messages.mandatoryReadyDetail).toBe(platform === 'win32'
? '更新期间应用将暂时关闭,完成后会自动打开。\n\n更新可能需要一些时间,请耐心等待,期间请勿重复启动应用。'
: '更新期间应用将暂时关闭,完成后会自动打开。')
})
@@ -95,7 +95,7 @@ it('keeps mandatory diagnostics expandable without clearing the block or authori
expect([p.element('error').textContent, p.element('technical-details-label').textContent,
p.element('update').textContent]).toMatchInlineSnapshot(`
[
"未能安全停止任务,更新未安装。请稍后重试。",
"未能安全停止任务,更新尚未安装,请稍后重试。",
"查看技术详情",
"重试更新",
]
@@ -182,7 +182,7 @@ it('uses the same modal for download, verification, inspected confirmation and t
p.publish({ ...p.initial, update: { phase: 'installing' }, restart: 'preparing' })
expect(p.element('detail').textContent).toBe('应用即将重启,请稍候。')
p.publish({ ...p.initial, update: { phase: 'installing' }, restart: 'stopping-tasks' })
expect(p.element('detail').textContent).toBe('正在安全结束应用中的任务。')
expect(p.element('detail').textContent).toBe('正在停止任务…')
expect(p.document.querySelector('main')).toBe(modal)
})
@@ -191,7 +191,7 @@ it('reveals the complete selectable address only after copy failure, without rep
await expect.poll(() => p.element('error').textContent).toBe('更新文件下载或准备失败,请重试。')
const originalError = p.element('error').textContent
p.publish({ ...p.initial, navigation: { page: 'requested' } })
expect(p.element('browser-message').textContent).toBe('若页面未打开,可')
expect(p.element('browser-message').textContent).toBe('页面未打开?')
expect(p.element('manual-copy').hidden).toBe(true)
p.publish({ ...p.initial, navigation: { page: 'failed', copy: 'failed' } })
expect(p.element('manual-copy').hidden).toBe(false)
@@ -199,11 +199,11 @@ it('reveals the complete selectable address only after copy failure, without rep
expect((p.element('address') as HTMLTextAreaElement).readOnly).toBe(true)
expect(p.element('error').textContent).toBe(originalError)
expect(p.element('technical-details-content').textContent).toBe('HASH_MISMATCH')
expect(p.element('copy-message').textContent).toBe('复制失败,请手动选择下方地址复制。')
expect(p.element('copy-message').textContent).toBe('复制失败,请手动选择并复制下方链接。')
p.publish({ ...p.initial, navigation: { page: 'failed', copy: 'copied' } })
expect(p.element('manual-copy').hidden).toBe(true)
expect((p.element('address') as HTMLTextAreaElement).value).toBe('')
expect(p.element('copy').textContent).toBe('已复制链接')
expect(p.element('copy').textContent).toBe('链接已复制')
expect(p.action).not.toHaveBeenCalled()
})
@@ -215,3 +215,31 @@ it('renders server markup literally in a dedicated safety case', async () => {
expect(p.element('title').childElementCount).toBe(0)
expect(p.element('detail').childElementCount).toBe(0)
})
it.each(['en', 'zh-CN'])('records mandatory update guidance and actions across its states: %s', async (language) => {
const p = page('mandatory-update')
const locale = resolveDesktopLocale(language)
const base: MandatoryUpdateView = { locale, deferred: false, policy: { blocking: true, checking: false,
page: 'https://example.invalid/download' }, update: { phase: 'available', version: '0.1.7-alpha.2' } }
let publish!: (view: MandatoryUpdateView) => void
const api: MandatoryUpdateApi = { status: async () => base, action: async () => {},
subscribe: (listener) => { publish = listener; return () => {} } }
Object.defineProperty(p.dom.window, 'dshMandatoryUpdate', { value: api })
p.run()
await expect.poll(() => p.element('title').textContent).toBe(locale.messages.mandatoryTitle)
const states: MandatoryUpdateView[] = [base,
{ ...base, update: { ...base.update, phase: 'ready' }, confirmation: { active: false, version: '0.1.7-alpha.2', revision: 1 } },
{ ...base, update: { ...base.update, phase: 'ready' }, confirmation: { active: true, version: '0.1.7-alpha.2', revision: 1 } },
{ ...base, update: { ...base.update, phase: 'ready' }, deferred: true },
{ ...base, update: { ...base.update, phase: 'installing' }, restart: 'stopping-tasks' },
{ ...base, update: { ...base.update, phase: 'error', failedOperation: 'install' } },
{ ...base, update: { phase: 'idle' }, navigation: { page: 'failed', copy: 'failed' } },
]
const snapshots = states.map((state) => {
publish(state)
return Object.fromEntries(['title', 'detail', 'version', 'error', 'update', 'later', 'page', 'browser-message', 'copy-message']
.filter(id => !p.element(id).hidden && !p.element(id).closest('[hidden]'))
.map(id => [id, p.element(id).textContent]))
})
await expect(JSON.stringify(snapshots, null, 2) + '\n').toMatchFileSnapshot(`./expected/update-guidance-${language}.json`)
})
+4 -2
View File
@@ -223,8 +223,10 @@ export function createAssembledRemote(options: AssembledRemoteOptions = {}): Ass
return ok(undefined)
})
mock.unary('session/list', () => ok({ items: structuredClone(sessions) }))
// The shipped Web composition mounts the Schedule client, whose catalog and
// per-Session list read empty here: these scenarios drive no reminder.
// The shipped bundle layers this graph composes carry the `ui-schedule` row
// disabled, so the Schedule client does not mount and no case here reads the
// catalog or the per-Session list. Both stay declared so an enabled
// composition reads empty lists instead of a missing-rule failure.
mock.unary('schedule/catalog', () => ok([]))
mock.unary('schedule/list', () => ok([]))
mock.unary('session/projections', (request: unknown) => {
@@ -3,7 +3,7 @@
- button "刷新"
- button "添加插件"
- heading "官方" [level=3]
- text: "7"
- text: "8"
- list:
- listitem:
- button "查看 智能体团队": 智能体团队
@@ -13,6 +13,10 @@
- button "查看 自动授权审查": 自动授权审查
- text: 实验性 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。
- switch "启用 自动授权审查"
- listitem:
- button "查看 定时与时间上下文": 定时与时间上下文
- text: 实验性 启用持久提醒、自动化任务页面与逐步时钟上下文。
- switch "启用 定时与时间上下文"
- listitem:
- button "查看 语音输入": 语音输入
- text: 实验性 在本机使用 SenseVoice 转写录音;首次使用需安装依赖
@@ -3,7 +3,7 @@
- button "刷新"
- button "添加插件"
- heading "官方" [level=3]
- text: "7"
- text: "8"
- list:
- listitem:
- button "查看 智能体团队": 智能体团队
@@ -13,6 +13,10 @@
- button "查看 自动授权审查": 自动授权审查
- text: 实验性 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。
- switch "启用 自动授权审查"
- listitem:
- button "查看 定时与时间上下文": 定时与时间上下文
- text: 实验性 启用持久提醒、自动化任务页面与逐步时钟上下文。
- switch "启用 定时与时间上下文"
- listitem:
- button "查看 语音输入": 语音输入
- text: 实验性 在本机使用 SenseVoice 转写录音;首次使用需安装依赖
@@ -3,7 +3,7 @@
- button "刷新"
- button "添加插件"
- heading "官方" [level=3]
- text: "7"
- text: "8"
- list:
- listitem:
- button "查看 智能体团队": 智能体团队
@@ -13,6 +13,10 @@
- button "查看 自动授权审查": 自动授权审查
- text: 实验性 提供自动审查权限模式,由模型在每次工具调用前判断是否授权。
- switch "启用 自动授权审查"
- listitem:
- button "查看 定时与时间上下文": 定时与时间上下文
- text: 实验性 启用持久提醒、自动化任务页面与逐步时钟上下文。
- switch "启用 定时与时间上下文"
- listitem:
- button "查看 语音输入": 语音输入
- text: 实验性 在本机使用 SenseVoice 转写录音;首次使用需安装依赖
@@ -1,18 +1,18 @@
- tree "Sessions":
- treeitem "{{workspace}}" [expanded]
- treeitem "New Session" [selected]
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} Session actions for {{workspace}} Archive session Pin session":
- text: {{workspace}}
- button "Session actions for {{workspace}}"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled Session actions for Untitled Archive session Pin session":
- text: Untitled
- button "Session actions for Untitled"
- button "Archive session"
- button "Pin session"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- button "Show 6 more sessions"
@@ -4,9 +4,9 @@
- button "Workspace actions for {{workspace}}"
- button "New session in {{workspace}}"
- treeitem "New Session" [selected]
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "{{workspace}} 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- treeitem "Untitled 1min"
- button "Show 11 more sessions"
+13 -3
View File
@@ -27,6 +27,7 @@ const MODE = webSnapshotMode()
interface OtlpCapture {
resourceLogs: { scopeLogs: { logRecords: {
eventName: string
attributes: { key: string; value: { stringValue?: string; intValue?: number | string } }[]
}[] }[] }[]
}
@@ -53,8 +54,11 @@ describe.each(MODE === 'record' ? ['deepseek-official'] : ['deepseek-official',
return capture.resourceLogs.flatMap(resource => resource.scopeLogs.flatMap(scope =>
scope.logRecords.map((record) => {
const attribute = (key: string) => record.attributes.find(value => value.key === key)?.value
return [attribute('session.id')?.stringValue, Number(attribute('event.seq')?.intValue),
attribute('event.type')?.stringValue] as [string | undefined, number, string | undefined]
expect(record.eventName).toBe('session-log')
const content = attribute('content')?.stringValue
expect(typeof content).toBe('string')
const event = JSON.parse(content!) as SessionEvent
return [attribute('sessionId')?.stringValue, event.seq, event.type] as [string | undefined, number, string | undefined]
})))
})
}
@@ -71,6 +75,10 @@ describe.each(MODE === 'record' ? ['deepseek-official'] : ['deepseek-official',
const expected = authorized.map(event => [sessionId, event.seq, event.type])
// No teardown, flush hook, or subsequent interaction may cause this delivery.
await expect.poll(captured, { timeout: 10_000 }).toEqual(expected)
const uploadedEvents = uploads.flatMap(upload => (JSON.parse(upload) as OtlpCapture).resourceLogs
.flatMap(resource => resource.scopeLogs.flatMap(scope => scope.logRecords))
.map(record => JSON.parse(record.attributes.find(attribute => attribute.key === 'content')!.value.stringValue!) as SessionEvent))
expect(uploadedEvents).toEqual(authorized)
suffixes.push(authorized.slice(releasedCount).map(event => event.type))
releasedCount = authorized.length
expect(scaffold.ctx.agents.get(sessionId)).toBe(agent)
@@ -268,7 +276,9 @@ describe.each(MODE === 'record' ? ['deepseek-official'] : ['deepseek-official',
}
})
await compareOrRefreshGolden(RELEASE_EXPECTED, JSON.stringify({
mode: 'FEEDBACK_ONLY', feedback,
mode: 'FEEDBACK_ONLY',
eventName: (JSON.parse(uploads[0]!) as OtlpCapture).resourceLogs[0]!.scopeLogs[0]!.logRecords[0]!.eventName,
feedback,
// The prefix is compared with each provider's actual canonical log above.
laterSubmissionSuffixes: suffixes.slice(1),
}, null, 2), MODE)
+2 -5
View File
@@ -1,9 +1,6 @@
# Browser-zone assertions need a fresh reading after the non-browser preparation turn.
# The Schedule rows stay disabled in the shipped composition; the scenario enables them
# with the shipped `@deepseek-ai/dsh-experimental-schedule-bundle` patch.
- id: time-context
disabled: false
config:
refreshIntervalMs: 0
- id: schedule
disabled: false
- id: ui-schedule
disabled: false
+2
View File
@@ -345,6 +345,8 @@ describe('web e2e: Models settings page configures a dormant provider', () => {
await expect(scaffold.ctx.llm.resolveModelInfo('acme-gateway', 'acme-large')).resolves.toMatchObject({
inputModalities: ['image'],
})
// The saved notice confirms the browser directory refresh after the editor closes.
await dialog.getByText('已保存 Acme 网关 (acme-gateway)。', { exact: true }).waitFor({ timeout: 10_000 })
await dialog.getByRole('button', { name: '编辑 Acme 网关 (acme-gateway)' }).click()
await dialog.getByText('自定义设置').click()
await dialog.getByRole('button', { name: '模型选项 1' }).click()
+5 -2
View File
@@ -44,6 +44,9 @@ declare module '@deepseek-ai/dsh-llm' {
}
const MODE = webSnapshotMode()
/** The optional Schedule bundle is the switch these scenarios turn on. */
const SCHEDULE_BUNDLE = fileURLToPath(new URL('../../../packages/experimental/schedule-bundle/cordis.patch.yml', import.meta.url))
const TIME_CONTEXT_EVERY_STEP = fileURLToPath(new URL('./fixtures/time-context-every-step.patch.yml', import.meta.url))
const SNAPSHOT_DIR = fileURLToPath(new URL('./expected/schedule-after', import.meta.url))
const AFTER_EXPECTED = join(SNAPSHOT_DIR, 'conversation.expected.md')
const AT_EXPECTED = join(SNAPSHOT_DIR, 'at-conversation.expected.md')
@@ -272,7 +275,7 @@ describe.skipIf(MODE === 'record')('web e2e: conversational reminders', () => {
beforeAll(async () => {
scaffold = await launchWebScaffold({
extraOverlayPath: fileURLToPath(new URL('./fixtures/time-context-every-step.patch.yml', import.meta.url)),
extraOverlayPath: [SCHEDULE_BUNDLE, TIME_CONTEXT_EVERY_STEP],
})
scaffold.ctx.effect(
() => scaffold.ctx.llm.registerAdapter([AFTER_PROVIDER], afterAdapter),
@@ -710,7 +713,7 @@ describe.skipIf(MODE === 'record')('web e2e: active Schedule catalog', () => {
beforeAll(async () => {
const fixture = await readFile(CATALOG_FIXTURE, 'utf8')
scaffold = await launchWebScaffold({
extraOverlayPath: fileURLToPath(new URL('./fixtures/time-context-every-step.patch.yml', import.meta.url)),
extraOverlayPath: [SCHEDULE_BUNDLE, TIME_CONTEXT_EVERY_STEP],
})
await seedSession(scaffold, fixture, CATALOG_SESSION_ID, 'standard')
const records = foldScheduleEvents(fixture.trim().split('\n').slice(1).map(line => JSON.parse(line) as SessionEvent)).active
@@ -71,7 +71,7 @@ describe('web e2e: blank New Session folding quota', () => {
const sidebar = page.getByRole('tree', { name: 'Sessions' })
await expect.poll(() => sidebar.getByRole('treeitem').count(), { timeout: 15_000 }).toBe(7)
expect(await sidebar.getByText('New Session', { exact: true }).count()).toBe(1)
expect(await sidebar.getByText(basename(scaffold.workspaceCwd), { exact: true }).count()).toBe(6)
expect(await sidebar.getByText('Untitled', { exact: true }).count()).toBe(5)
const showMore = sidebar.getByRole('button', { name: 'Show 11 more sessions' })
await showMore.waitFor({ timeout: 15_000 })
await compareOrRefreshGolden(
@@ -82,7 +82,7 @@ describe('web e2e: blank New Session folding quota', () => {
await showMore.click()
await expect.poll(() => sidebar.getByRole('treeitem').count(), { timeout: 10_000 }).toBe(12)
expect(await sidebar.getByText(basename(scaffold.workspaceCwd), { exact: true }).count()).toBe(11)
expect(await sidebar.getByText('Untitled', { exact: true }).count()).toBe(10)
await compareOrRefreshGolden(
join(EXPECTED_DIR, 'first-batch.expected.md'),
await captureStableAria(page, '[role="tree"][aria-label="Sessions"]', scaffold.workspaceCwd),
+2 -2
View File
@@ -6,5 +6,5 @@
en: 41d772192075e133
zh: 5ef3da69b4de1d8a
/capability-seams-and-core-services:
en: 0d14d210d41c6770
zh: 964e92962b7cfa88
en: 8d7ae3eb1265031d
zh: 970ce559f5f95a65
+7 -1
View File
@@ -112,11 +112,13 @@ flowchart LR
pkg_api_account_controller["api-account-controller"]
pkg_authorization["authorization"]
svc_authorization["ctx.authorization<br/>Authorization flow registry"]
pkg_otel["otel"]
svc_otel["ctx.otel<br/>Shared OTel reporting channels"]
pkg_host_product_telemetry_otel["host-product-telemetry-otel"]
pkg_session_telemetry_otel["session-telemetry-otel"]
svc_productTelemetry["ctx.productTelemetry<br/>Product usage event sender"]
pkg_session_telemetry["session-telemetry"]
svc_sessionTelemetry["ctx.sessionTelemetry<br/>Session telemetry seam"]
pkg_session_telemetry_otel["session-telemetry-otel"]
pkg_storage["storage"]
svc_storage["ctx.storage<br/>Non-session storage hub"]
pkg_storage_json["storage-json"]
@@ -348,6 +350,7 @@ flowchart LR
pkg_mcp_resources --> svc_mcpResources
pkg_message_feedback --> svc_messageFeedback
pkg_office_to_pdf --> svc_officeToPdf
pkg_otel --> svc_otel
pkg_permission_presets --> svc_permissionPresets
pkg_plan_mode --> svc_planMode
pkg_plugin_manager --> svc_pluginManager
@@ -472,6 +475,8 @@ flowchart LR
svc_lsp --> pkg_tool_lsp
svc_mcpResources --> pkg_mcp_resources
svc_officeToPdf --> pkg_client_ui_sidebar_documentpreview
svc_otel --> pkg_host_product_telemetry_otel
svc_otel --> pkg_session_telemetry_otel
svc_pluginManager --> pkg_plugin_manager
svc_pluginManager --> pkg_ui_settings_plugin_inventory
svc_pluginRegistryProbe --> pkg_client_ui_plugin_manager
@@ -605,6 +610,7 @@ flowchart LR
| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`api-settings-controller`](../packages/api/settings-controller), [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | Configuration carries references to secrets; providers own the values. Consumers resolve per operation, so a rotated credential reaches the very next request; the settings controller exposes value-free views and write-only storage. |
| `ctx.deepseekAccount` | `seam` | [`deepseek-account`](../packages/credentials/deepseek-account) | [`deepseek-account-platform`](../packages/credentials/deepseek-account-platform) | [`api-account-controller`](../packages/api/account-controller), [`llm-deepseek`](../packages/llm/llm-deepseek) | - | The Host owns browser authorization and local credentials; UI consumers receive state without tokens. |
| `ctx.authorization` | `seam` | [`authorization`](../packages/credentials/authorization) | - | [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | Flows are registered by the plugin that knows how to obtain one credential and keyed by the record they write; the seam owns the conversation and the one-attempt-per-key lifecycle, never the protocol. |
| `ctx.otel` | `service` | [`otel`](../packages/telemetry/otel) | - | [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel), [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | Product analytics and Session feedback adapters create independent reporting channels through one injected service. |
| `ctx.productTelemetry` | `service` | [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | - | - | - | Exports explicitly submitted analytics events through OTLP/HTTP; mounting alone collects nothing. |
| `ctx.sessionTelemetry` | `seam` | [`session-telemetry`](../packages/session/session-telemetry) | [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | - | The seam captures, redacts, and hands session records to one backend; nothing else consumes the service — its output leaves the process. |
| `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | Backends register side by side under names; data forms (domain first) mount on the hub and translate typed operations into opaque KV-unit primitives. |
+7 -1
View File
@@ -114,11 +114,13 @@ flowchart LR
pkg_api_account_controller["api-account-controller"]
pkg_authorization["authorization"]
svc_authorization["ctx.authorization<br/>Authorization flow registry"]
pkg_otel["otel"]
svc_otel["ctx.otel<br/>Shared OTel reporting channels"]
pkg_host_product_telemetry_otel["host-product-telemetry-otel"]
pkg_session_telemetry_otel["session-telemetry-otel"]
svc_productTelemetry["ctx.productTelemetry<br/>Product usage event sender"]
pkg_session_telemetry["session-telemetry"]
svc_sessionTelemetry["ctx.sessionTelemetry<br/>Session telemetry seam"]
pkg_session_telemetry_otel["session-telemetry-otel"]
pkg_storage["storage"]
svc_storage["ctx.storage<br/>Non-session storage hub"]
pkg_storage_json["storage-json"]
@@ -350,6 +352,7 @@ flowchart LR
pkg_mcp_resources --> svc_mcpResources
pkg_message_feedback --> svc_messageFeedback
pkg_office_to_pdf --> svc_officeToPdf
pkg_otel --> svc_otel
pkg_permission_presets --> svc_permissionPresets
pkg_plan_mode --> svc_planMode
pkg_plugin_manager --> svc_pluginManager
@@ -474,6 +477,8 @@ flowchart LR
svc_lsp --> pkg_tool_lsp
svc_mcpResources --> pkg_mcp_resources
svc_officeToPdf --> pkg_client_ui_sidebar_documentpreview
svc_otel --> pkg_host_product_telemetry_otel
svc_otel --> pkg_session_telemetry_otel
svc_pluginManager --> pkg_plugin_manager
svc_pluginManager --> pkg_ui_settings_plugin_inventory
svc_pluginRegistryProbe --> pkg_client_ui_plugin_manager
@@ -607,6 +612,7 @@ flowchart LR
| `ctx.credentials` | `seam` | [`credentials`](../packages/credentials/credentials) | [`credentials-local`](../packages/credentials/credentials-local) | [`api-settings-controller`](../packages/api/settings-controller), [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | 配置携带对机密信息的引用;提供方拥有实际值。消费方按操作解析,因此轮换后的凭据会在紧接着的下一次请求中生效;settings controller 提供不含实际值的视图和只写存储。 |
| `ctx.deepseekAccount` | `seam` | [`deepseek-account`](../packages/credentials/deepseek-account) | [`deepseek-account-platform`](../packages/credentials/deepseek-account-platform) | [`api-account-controller`](../packages/api/account-controller), [`llm-deepseek`](../packages/llm/llm-deepseek) | - | Host 负责浏览器授权和本地凭证;UI 使用方只接收不含 token 的状态。 |
| `ctx.authorization` | `seam` | [`authorization`](../packages/credentials/authorization) | - | [`llm-pi-ai`](../packages/llm/llm-pi-ai) | - | flow 由知道如何取得某份凭据的插件注册,并以其写入的记录为键;seam 拥有这段对话与"每个键同时只跑一次尝试"的生命周期,而非协议本身。 |
| `ctx.otel` | `service` | [`otel`](../packages/telemetry/otel) | - | [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel), [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | 产品埋点和 Session 反馈适配器通过同一个注入服务创建独立上报通道。 |
| `ctx.productTelemetry` | `service` | [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | - | - | - | 通过 OTLP/HTTP 发送显式提交的分析事件;仅挂载插件不会采集信息。 |
| `ctx.sessionTelemetry` | `seam` | [`session-telemetry`](../packages/session/session-telemetry) | [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | - | - | 该 seam 捕获会话记录、进行脱敏并交给一个后端;没有其他组件消费该服务,其输出会离开当前进程。 |
| `ctx.storage` | `seam` | [`storage`](../packages/storage/storage) | [`storage-json`](../packages/storage/storage-json), [`storage-sqlite`](../packages/storage/storage-sqlite) | [`storage-domain`](../packages/storage/storage-domain) | - | 各后端以不同名称并列注册;数据形态(领域优先)挂载到枢纽上,并将类型化操作转换为不透明的 KV 单元原语。 |
+14 -11
View File
@@ -1435,7 +1435,8 @@ export interface Config {
## `@deepseek-ai/dsh-host-product-telemetry-otel`
- `source`: [`packages/host/product-telemetry-otel/src/index.ts:37`](../packages/host/product-telemetry-otel/src/index.ts)
- `inject`: `otel`
- `source`: [`packages/host/product-telemetry-otel/src/index.ts:18`](../packages/host/product-telemetry-otel/src/index.ts)
```ts config-catalog
/** Collector routing, application identity, and bounded in-memory batch settings. */
@@ -2701,36 +2702,36 @@ export interface Config {
## `@deepseek-ai/dsh-session-telemetry-otel`
- `inject`: `sessions`
- `inject`: `sessions` · `otel`
- `refs`: `BatchLogRecordProcessorOptions` (`@opentelemetry/sdk-logs`) · `OTLPExporterNodeConfigBase` (`@opentelemetry/otlp-exporter-base`)
- `source`: [`packages/session/session-telemetry-otel/src/index.ts:100`](../packages/session/session-telemetry-otel/src/index.ts)
- `source`: [`packages/session/session-telemetry-otel/src/index.ts:87`](../packages/session/session-telemetry-otel/src/index.ts)
```ts config-catalog
/**
* Plugin configuration: one sharing policy, two verbatim SDK option objects,
* and one DSH-owned shutdown bound. Uploading modes validate their endpoint
* Plugin configuration: sharing policy, SDK transport options, byte/count queue
* settings, and an overall shutdown bound. Uploading modes validate their endpoint
* and shutdown deadline at plugin load; `DISABLED` reads neither.
*/
export interface Config {
/** Defaults to `FEEDBACK_ONLY`: capture session history only when feedback is explicitly submitted. */
mode?: SessionTelemetryMode
/**
* Passed verbatim to the SDK's OTLP/HTTP log exporter — the complete
* `OTLPExporterNodeConfigBase` shape (`headers`, `timeoutMillis`,
* `compression`, `keepAlive`, …), owned and documented by the SDK. `url`
* is the one field this package requires and validates itself.
* Explicit SDK HTTP transport settings, including optional routing headers.
* Ambient credentials are not inherited. URL is required while uploading.
*/
exporter?: OTLPExporterNodeConfigBase & {
/** Full logs endpoint (e.g. `https://collector.example.com/v1/logs`). Required outside `DISABLED`; validated at load. */
url?: string
}
/**
* Passed verbatim to `BatchLogRecordProcessor` (minus the exporter slot,
* which this plugin fills); the SDK owns and documents these knobs.
* Count, queue, cadence, and per-request watchdog settings for the byte-bounded
* processor. A watchdog warning never releases an unsettled transport slot.
*/
processor?: Omit<BatchLogRecordProcessorOptions, 'exporter'>
/** Maximum time spent awaiting the SDK provider's complete shutdown path. */
shutdownTimeoutMillis?: number
/** Uncompressed OTLP request byte limit, at most 4,000,000. */
maxRequestBytes?: number
}
/** Session-sharing policy selected by {@link Config.mode}. */
@@ -4354,6 +4355,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
| `@deepseek-ai/dsh-llm` | — | [`packages/llm/llm/src/index.ts`](../packages/llm/llm/src/index.ts) |
| `@deepseek-ai/dsh-lsp` | — | [`packages/lsp/lsp/src/index.ts`](../packages/lsp/lsp/src/index.ts) |
| `@deepseek-ai/dsh-mcp-resources` | `tools` | [`packages/mcp/mcp-resources/src/index.ts`](../packages/mcp/mcp-resources/src/index.ts) |
| `@deepseek-ai/dsh-otel` | — | [`packages/telemetry/otel/src/index.ts`](../packages/telemetry/otel/src/index.ts) |
| `@deepseek-ai/dsh-sandbox-ssh` | `ssh` | [`packages/ssh/sandbox-ssh/src/index.ts`](../packages/ssh/sandbox-ssh/src/index.ts) |
| `@deepseek-ai/dsh-session` | — | [`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts) |
| `@deepseek-ai/dsh-session-checkpoint-policy` | `llm` · `sessionPersistence` · `sessions` · `tools` | [`packages/session/session-checkpoint-policy/src/index.ts`](../packages/session/session-checkpoint-policy/src/index.ts) |
@@ -4424,6 +4426,7 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them.
| `@deepseek-ai/dsh-deque` | — | [`packages/util/deque/src/index.ts`](../packages/util/deque/src/index.ts) |
| `@deepseek-ai/dsh-experimental-agent-team-profile` | — | [`packages/experimental/agent-team-profile/src/index.ts`](../packages/experimental/agent-team-profile/src/index.ts) |
| `@deepseek-ai/dsh-experimental-browser-use-runtime` | — | [`packages/experimental/browser-use-runtime/src/index.ts`](../packages/experimental/browser-use-runtime/src/index.ts) |
| `@deepseek-ai/dsh-experimental-schedule-bundle` | — | [`packages/experimental/schedule-bundle/src/index.ts`](../packages/experimental/schedule-bundle/src/index.ts) |
| `@deepseek-ai/dsh-experimental-voice-input-bundle` | — | [`packages/experimental/voice-input-bundle/src/index.ts`](../packages/experimental/voice-input-bundle/src/index.ts) |
| `@deepseek-ai/dsh-experimental-webworker-packer` | — | [`packages/experimental/webworker-packer/src/index.ts`](../packages/experimental/webworker-packer/src/index.ts) |
| `@deepseek-ai/dsh-experimental-webworker-runtime` | — | [`packages/experimental/webworker-runtime/src/index.ts`](../packages/experimental/webworker-runtime/src/index.ts) |
+14 -11
View File
@@ -1437,7 +1437,8 @@ export interface Config {
## `@deepseek-ai/dsh-host-product-telemetry-otel`
- `source`: [`packages/host/product-telemetry-otel/src/index.ts:37`](../packages/host/product-telemetry-otel/src/index.ts)
- `inject`: `otel`
- `source`: [`packages/host/product-telemetry-otel/src/index.ts:18`](../packages/host/product-telemetry-otel/src/index.ts)
```ts config-catalog
/** Collector routing, application identity, and bounded in-memory batch settings. */
@@ -2703,36 +2704,36 @@ export interface Config {
## `@deepseek-ai/dsh-session-telemetry-otel`
- `inject`: `sessions`
- `inject`: `sessions` · `otel`
- `refs`: `BatchLogRecordProcessorOptions` (`@opentelemetry/sdk-logs`) · `OTLPExporterNodeConfigBase` (`@opentelemetry/otlp-exporter-base`)
- `source`: [`packages/session/session-telemetry-otel/src/index.ts:100`](../packages/session/session-telemetry-otel/src/index.ts)
- `source`: [`packages/session/session-telemetry-otel/src/index.ts:87`](../packages/session/session-telemetry-otel/src/index.ts)
```ts config-catalog
/**
* Plugin configuration: one sharing policy, two verbatim SDK option objects,
* and one DSH-owned shutdown bound. Uploading modes validate their endpoint
* Plugin configuration: sharing policy, SDK transport options, byte/count queue
* settings, and an overall shutdown bound. Uploading modes validate their endpoint
* and shutdown deadline at plugin load; `DISABLED` reads neither.
*/
export interface Config {
/** Defaults to `FEEDBACK_ONLY`: capture session history only when feedback is explicitly submitted. */
mode?: SessionTelemetryMode
/**
* Passed verbatim to the SDK's OTLP/HTTP log exporter — the complete
* `OTLPExporterNodeConfigBase` shape (`headers`, `timeoutMillis`,
* `compression`, `keepAlive`, …), owned and documented by the SDK. `url`
* is the one field this package requires and validates itself.
* Explicit SDK HTTP transport settings, including optional routing headers.
* Ambient credentials are not inherited. URL is required while uploading.
*/
exporter?: OTLPExporterNodeConfigBase & {
/** Full logs endpoint (e.g. `https://collector.example.com/v1/logs`). Required outside `DISABLED`; validated at load. */
url?: string
}
/**
* Passed verbatim to `BatchLogRecordProcessor` (minus the exporter slot,
* which this plugin fills); the SDK owns and documents these knobs.
* Count, queue, cadence, and per-request watchdog settings for the byte-bounded
* processor. A watchdog warning never releases an unsettled transport slot.
*/
processor?: Omit<BatchLogRecordProcessorOptions, 'exporter'>
/** Maximum time spent awaiting the SDK provider's complete shutdown path. */
shutdownTimeoutMillis?: number
/** Uncompressed OTLP request byte limit, at most 4,000,000. */
maxRequestBytes?: number
}
/** Session-sharing policy selected by {@link Config.mode}. */
@@ -4356,6 +4357,7 @@ export interface Config {
| `@deepseek-ai/dsh-llm` | — | [`packages/llm/llm/src/index.ts`](../packages/llm/llm/src/index.ts) |
| `@deepseek-ai/dsh-lsp` | — | [`packages/lsp/lsp/src/index.ts`](../packages/lsp/lsp/src/index.ts) |
| `@deepseek-ai/dsh-mcp-resources` | `tools` | [`packages/mcp/mcp-resources/src/index.ts`](../packages/mcp/mcp-resources/src/index.ts) |
| `@deepseek-ai/dsh-otel` | — | [`packages/telemetry/otel/src/index.ts`](../packages/telemetry/otel/src/index.ts) |
| `@deepseek-ai/dsh-sandbox-ssh` | `ssh` | [`packages/ssh/sandbox-ssh/src/index.ts`](../packages/ssh/sandbox-ssh/src/index.ts) |
| `@deepseek-ai/dsh-session` | — | [`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts) |
| `@deepseek-ai/dsh-session-checkpoint-policy` | `llm` · `sessionPersistence` · `sessions` · `tools` | [`packages/session/session-checkpoint-policy/src/index.ts`](../packages/session/session-checkpoint-policy/src/index.ts) |
@@ -4426,6 +4428,7 @@ export interface Config {
| `@deepseek-ai/dsh-deque` | — | [`packages/util/deque/src/index.ts`](../packages/util/deque/src/index.ts) |
| `@deepseek-ai/dsh-experimental-agent-team-profile` | — | [`packages/experimental/agent-team-profile/src/index.ts`](../packages/experimental/agent-team-profile/src/index.ts) |
| `@deepseek-ai/dsh-experimental-browser-use-runtime` | — | [`packages/experimental/browser-use-runtime/src/index.ts`](../packages/experimental/browser-use-runtime/src/index.ts) |
| `@deepseek-ai/dsh-experimental-schedule-bundle` | — | [`packages/experimental/schedule-bundle/src/index.ts`](../packages/experimental/schedule-bundle/src/index.ts) |
| `@deepseek-ai/dsh-experimental-voice-input-bundle` | — | [`packages/experimental/voice-input-bundle/src/index.ts`](../packages/experimental/voice-input-bundle/src/index.ts) |
| `@deepseek-ai/dsh-experimental-webworker-packer` | — | [`packages/experimental/webworker-packer/src/index.ts`](../packages/experimental/webworker-packer/src/index.ts) |
| `@deepseek-ai/dsh-experimental-webworker-runtime` | — | [`packages/experimental/webworker-runtime/src/index.ts`](../packages/experimental/webworker-runtime/src/index.ts) |
+1 -1
View File
@@ -60,7 +60,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `plugin-manager/install-log` | `emit` | [`packages/boot/plugin-manager/src/types.ts:249`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
| `plugin-manager/install-state` | `emit` | [`packages/boot/plugin-manager/src/types.ts:256`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
| `schedule/changed` | `emit` | [`packages/schedule/schedule/src/types.ts:471`](../packages/schedule/schedule/src/types.ts) | [`schedule`](../packages/schedule/schedule) (`emit`) | `remotes` |
| `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:43`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - |
| `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:44`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:55`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`compaction`](../packages/compaction/compaction), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`schedule`](../packages/schedule/schedule), `server`, [`session`](../packages/core/session), `session-controller`, [`session-log-deepseek`](../packages/session/session-log-deepseek), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tool-workflow`](../packages/workflow/tool-workflow), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:65`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `agent-team`, `file-upload`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`workspace-changes`](../packages/deliverables/workspace-changes) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:77`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`agent-preset-registry`](../packages/preset/agent-preset-registry), `agent-team`, [`compaction`](../packages/compaction/compaction), [`compaction-basic`](../packages/compaction/compaction-basic), [`file-reference-local`](../packages/context/file-reference-local), `file-upload`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), [`hook-protocol`](../packages/hooks/hook-protocol), [`loader-smoke`](../packages/test-support/loader-smoke), `server`, [`session`](../packages/core/session), `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-telemetry-otel`](../packages/session/session-telemetry-otel), [`session-title`](../packages/session/session-title), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`tool-workflow`](../packages/workflow/tool-workflow), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval), [`workspace-changes`](../packages/deliverables/workspace-changes) |
+1 -1
View File
@@ -62,7 +62,7 @@
| `plugin-manager/install-log` | `emit` | [`packages/boot/plugin-manager/src/types.ts:249`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
| `plugin-manager/install-state` | `emit` | [`packages/boot/plugin-manager/src/types.ts:256`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
| `schedule/changed` | `emit` | [`packages/schedule/schedule/src/types.ts:471`](../packages/schedule/schedule/src/types.ts) | [`schedule`](../packages/schedule/schedule) (`emit`) | `remotes` |
| `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:43`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - |
| `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:44`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - |
| `session/created` | `emit` | [`packages/core/session/src/index.ts:55`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`compaction`](../packages/compaction/compaction), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`schedule`](../packages/schedule/schedule), `server`, [`session`](../packages/core/session), `session-controller`, [`session-log-deepseek`](../packages/session/session-log-deepseek), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tool-workflow`](../packages/workflow/tool-workflow), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:65`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `agent-team`, `file-upload`, `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`workspace-changes`](../packages/deliverables/workspace-changes) |
| `session/event` | `emit` | [`packages/core/session/src/index.ts:77`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`agent-preset-registry`](../packages/preset/agent-preset-registry), `agent-team`, [`compaction`](../packages/compaction/compaction), [`compaction-basic`](../packages/compaction/compaction-basic), [`file-reference-local`](../packages/context/file-reference-local), `file-upload`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), [`hook-protocol`](../packages/hooks/hook-protocol), [`loader-smoke`](../packages/test-support/loader-smoke), `server`, [`session`](../packages/core/session), `session-controller`, [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-telemetry-otel`](../packages/session/session-telemetry-otel), [`session-title`](../packages/session/session-title), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`tool-workflow`](../packages/workflow/tool-workflow), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval), [`workspace-changes`](../packages/deliverables/workspace-changes) |
+11 -2
View File
@@ -258,6 +258,7 @@ flowchart TD
pkg_experimental_computer_use_cua_driver_native["experimental-computer-use-cua-driver-native"]
pkg_experimental_inspector["experimental-inspector"]
pkg_experimental_ptc_runtime_python["experimental-ptc-runtime-python"]
pkg_experimental_schedule_bundle["experimental-schedule-bundle"]
pkg_experimental_speech_to_text["experimental-speech-to-text"]
pkg_experimental_speech_to_text_sensevoice["experimental-speech-to-text-sensevoice"]
pkg_experimental_tool_agent_team["experimental-tool-agent-team"]
@@ -394,6 +395,9 @@ flowchart TD
pkg_subprocess_local["subprocess-local"]
pkg_win32_process["win32-process"]
end
subgraph group_telemetry["packages/telemetry"]
pkg_otel["otel"]
end
subgraph group_terminal["packages/terminal"]
pkg_terminal["terminal"]
pkg_terminal_bash["terminal-bash"]
@@ -509,6 +513,7 @@ flowchart TD
pkg_settings --> pkg_brand
pkg_settings --> pkg_invariants
pkg_settings --> pkg_session
pkg_otel --> pkg_session
pkg_session_snapshot --> pkg_http_proxy
pkg_session_snapshot --> pkg_session
pkg_fs --> pkg_brand
@@ -521,6 +526,7 @@ flowchart TD
pkg_hmr --> pkg_app_boot
pkg_hmr --> pkg_cmdline
pkg_experimental_speech_to_text --> pkg_settings
pkg_host_product_telemetry_otel --> pkg_otel
pkg_ptc_runtime --> pkg_sandbox
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
@@ -1036,6 +1042,7 @@ flowchart TD
pkg_session_telemetry_otel --> pkg_command_feedback
pkg_session_telemetry_otel --> pkg_llm
pkg_session_telemetry_otel --> pkg_message_feedback
pkg_session_telemetry_otel --> pkg_otel
pkg_session_telemetry_otel --> pkg_session
pkg_session_telemetry_otel --> pkg_session_telemetry
pkg_tool_bash --> pkg_agent
@@ -1452,6 +1459,7 @@ flowchart TD
| [`client-web`](../packages/client/web) | `client` | — |
| [`office-to-pdf`](../packages/document/office-to-pdf) | `document` | — |
| [`experimental-agent-team-profile`](../packages/experimental/agent-team-profile) | `experimental` | — |
| [`experimental-schedule-bundle`](../packages/experimental/schedule-bundle) | `experimental` | — |
| [`experimental-voice-input-bundle`](../packages/experimental/voice-input-bundle) | `experimental` | — |
| [`experimental-webworker-packer`](../packages/experimental/webworker-packer) | `experimental` | — |
| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | — |
@@ -1460,7 +1468,6 @@ flowchart TD
| [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | `host` | — |
| [`host-directory-picker-native`](../packages/host/directory-picker-native) | `host` | — |
| [`host-open-in-app`](../packages/host/open-in-app) | `host` | — |
| [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | `host` | — |
| [`host-webserver`](../packages/host/webserver) | `host` | — |
| [`invariants`](../packages/runtime-diagnostics/invariants) | `runtime-diagnostics` | — |
| [`session-format`](../packages/session/session-format) | `session` | — |
@@ -1520,12 +1527,14 @@ flowchart TD
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`session-projection`](../packages/session/session-projection) | `session` | [`session`](../packages/core/session) |
| [`settings`](../packages/settings/settings) | `settings` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`otel`](../packages/telemetry/otel) | `telemetry` | [`session`](../packages/core/session) |
| [`session-snapshot`](../packages/test-support/session-snapshot) | `test-support` | [`http-proxy`](../packages/util/http-proxy), [`session`](../packages/core/session) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`spill`](../packages/spill/spill) |
| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`hmr`](../packages/boot/hmr) | `boot` | [`app-boot`](../packages/boot/app-boot), [`cmdline`](../packages/boot/cmdline) |
| [`experimental-speech-to-text`](../packages/experimental/speech-to-text) | `experimental` | [`settings`](../packages/settings/settings) |
| [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | `host` | [`otel`](../packages/telemetry/otel) |
| [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime) | `ptc-runtime` | [`sandbox`](../packages/sandbox/sandbox) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
@@ -1635,7 +1644,7 @@ flowchart TD
| [`host-plugin-inventory`](../packages/host/plugin-inventory) | `host` | [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`brand`](../packages/util/brand), [`typert-protocol`](../packages/typert/protocol) |
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-resources`](../packages/mcp/mcp-resources), [`scope`](../packages/core/scope), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`agent-preset`](../packages/preset/agent-preset) | `preset` | [`agent-preset-registry`](../packages/preset/agent-preset-registry) |
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`command-feedback`](../packages/feedback/command-feedback), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`command-feedback`](../packages/feedback/command-feedback), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`otel`](../packages/telemetry/otel), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`tool-bash`](../packages/shell/tool-bash) | `shell` | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-pwsh`](../packages/shell/tool-pwsh) | `shell` | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`fs-ssh`](../packages/ssh/fs-ssh) | `ssh` | [`fs`](../packages/fs/fs), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`ssh`](../packages/ssh/ssh) |
+11 -2
View File
@@ -260,6 +260,7 @@ flowchart TD
pkg_experimental_computer_use_cua_driver_native["experimental-computer-use-cua-driver-native"]
pkg_experimental_inspector["experimental-inspector"]
pkg_experimental_ptc_runtime_python["experimental-ptc-runtime-python"]
pkg_experimental_schedule_bundle["experimental-schedule-bundle"]
pkg_experimental_speech_to_text["experimental-speech-to-text"]
pkg_experimental_speech_to_text_sensevoice["experimental-speech-to-text-sensevoice"]
pkg_experimental_tool_agent_team["experimental-tool-agent-team"]
@@ -396,6 +397,9 @@ flowchart TD
pkg_subprocess_local["subprocess-local"]
pkg_win32_process["win32-process"]
end
subgraph group_telemetry["packages/telemetry"]
pkg_otel["otel"]
end
subgraph group_terminal["packages/terminal"]
pkg_terminal["terminal"]
pkg_terminal_bash["terminal-bash"]
@@ -511,6 +515,7 @@ flowchart TD
pkg_settings --> pkg_brand
pkg_settings --> pkg_invariants
pkg_settings --> pkg_session
pkg_otel --> pkg_session
pkg_session_snapshot --> pkg_http_proxy
pkg_session_snapshot --> pkg_session
pkg_fs --> pkg_brand
@@ -523,6 +528,7 @@ flowchart TD
pkg_hmr --> pkg_app_boot
pkg_hmr --> pkg_cmdline
pkg_experimental_speech_to_text --> pkg_settings
pkg_host_product_telemetry_otel --> pkg_otel
pkg_ptc_runtime --> pkg_sandbox
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
@@ -1038,6 +1044,7 @@ flowchart TD
pkg_session_telemetry_otel --> pkg_command_feedback
pkg_session_telemetry_otel --> pkg_llm
pkg_session_telemetry_otel --> pkg_message_feedback
pkg_session_telemetry_otel --> pkg_otel
pkg_session_telemetry_otel --> pkg_session
pkg_session_telemetry_otel --> pkg_session_telemetry
pkg_tool_bash --> pkg_agent
@@ -1454,6 +1461,7 @@ flowchart TD
| [`client-web`](../packages/client/web) | `client` | — |
| [`office-to-pdf`](../packages/document/office-to-pdf) | `document` | — |
| [`experimental-agent-team-profile`](../packages/experimental/agent-team-profile) | `experimental` | — |
| [`experimental-schedule-bundle`](../packages/experimental/schedule-bundle) | `experimental` | — |
| [`experimental-voice-input-bundle`](../packages/experimental/voice-input-bundle) | `experimental` | — |
| [`experimental-webworker-packer`](../packages/experimental/webworker-packer) | `experimental` | — |
| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | — |
@@ -1462,7 +1470,6 @@ flowchart TD
| [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | `host` | — |
| [`host-directory-picker-native`](../packages/host/directory-picker-native) | `host` | — |
| [`host-open-in-app`](../packages/host/open-in-app) | `host` | — |
| [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | `host` | — |
| [`host-webserver`](../packages/host/webserver) | `host` | — |
| [`invariants`](../packages/runtime-diagnostics/invariants) | `runtime-diagnostics` | — |
| [`session-format`](../packages/session/session-format) | `session` | — |
@@ -1522,12 +1529,14 @@ flowchart TD
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`session-projection`](../packages/session/session-projection) | `session` | [`session`](../packages/core/session) |
| [`settings`](../packages/settings/settings) | `settings` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`otel`](../packages/telemetry/otel) | `telemetry` | [`session`](../packages/core/session) |
| [`session-snapshot`](../packages/test-support/session-snapshot) | `test-support` | [`http-proxy`](../packages/util/http-proxy), [`session`](../packages/core/session) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`spill`](../packages/spill/spill) |
| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`hmr`](../packages/boot/hmr) | `boot` | [`app-boot`](../packages/boot/app-boot), [`cmdline`](../packages/boot/cmdline) |
| [`experimental-speech-to-text`](../packages/experimental/speech-to-text) | `experimental` | [`settings`](../packages/settings/settings) |
| [`host-product-telemetry-otel`](../packages/host/product-telemetry-otel) | `host` | [`otel`](../packages/telemetry/otel) |
| [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime) | `ptc-runtime` | [`sandbox`](../packages/sandbox/sandbox) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
@@ -1637,7 +1646,7 @@ flowchart TD
| [`host-plugin-inventory`](../packages/host/plugin-inventory) | `host` | [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`brand`](../packages/util/brand), [`typert-protocol`](../packages/typert/protocol) |
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-resources`](../packages/mcp/mcp-resources), [`scope`](../packages/core/scope), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`agent-preset`](../packages/preset/agent-preset) | `preset` | [`agent-preset-registry`](../packages/preset/agent-preset-registry) |
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`command-feedback`](../packages/feedback/command-feedback), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`session-telemetry-otel`](../packages/session/session-telemetry-otel) | `session` | [`anonymous-user-id`](../packages/identity/anonymous-user-id), [`command-feedback`](../packages/feedback/command-feedback), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`otel`](../packages/telemetry/otel), [`session`](../packages/core/session), [`session-telemetry`](../packages/session/session-telemetry) |
| [`tool-bash`](../packages/shell/tool-bash) | `shell` | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`tool-pwsh`](../packages/shell/tool-pwsh) | `shell` | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
| [`fs-ssh`](../packages/ssh/fs-ssh) | `ssh` | [`fs`](../packages/fs/fs), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`ssh`](../packages/ssh/ssh) |
+2 -2
View File
@@ -3,5 +3,5 @@
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/README.md
/subsystems:
en: 58466f6fd60755eb
zh: ab46a374f5a3c3ed
en: ab6cd11804f6b644
zh: 7a82dc5b22c40703
+1
View File
@@ -67,6 +67,7 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures
| [conversation.md](conversation.md) | target-neutral Session-event assembly: Context identity, Location data, replay paths, view builders, and target-owned render nodes |
| [session-projection.md](session-projection.md) | the projection seam: `SessionProjectionMap`, the pure `ProjectionDefinition` unit, `ProjectionSnapshot`'s consistent cut, the change feed |
| [session-telemetry.md](session-telemetry.md) | the outbound session-reporting capability seam: `SessionTelemetryRecord`/`SessionTelemetrySeverity`, the `SessionTelemetrySink` contract, and the `session-telemetry/record` redact waterfall |
| [otel.md](otel.md) | Shared ordinary-event and Session-log OTLP channels |
| [product-telemetry.md](product-telemetry.md) | Explicit product analytics submission and OTLP/HTTP transport |
> Type declarations and their JSDoc on these pages are source-equivalent and drift-checked by `pnpm run verify-type-equiv` (see [development.md](../development.md#documenting-types-verbatim-ts-type-equiv)). Ordinary blocks preserve complete declarations; `public-api` blocks preserve body-stripped public class declarations. Cordis services and events use each page's generated **Cordis API** section.
+1
View File
@@ -67,6 +67,7 @@
| [conversation.md](conversation.zh.md) | 目标无关的会话事件组装:上下文标识、位置数据、回放路径、视图构建器与目标自有的渲染节点 |
| [session-projection.md](session-projection.zh.md) | 投影 seam:`SessionProjectionMap`、纯函数 `ProjectionDefinition` 单元、`ProjectionSnapshot` 的一致切面、变更馈送 |
| [session-telemetry.md](session-telemetry.zh.md) | 对外会话上报能力 seam:`SessionTelemetryRecord`/`SessionTelemetrySeverity`、`SessionTelemetrySink` 约定和 `session-telemetry/record` 脱敏 waterfall(瀑布式事件) |
| [otel.md](otel.zh.md) | 共享普通事件与 Session 日志 OTLP 通道 |
| [product-telemetry.md](product-telemetry.zh.md) | 显式产品分析事件提交与 OTLP/HTTP 传输 |
> 这些页面上的类型声明及其 JSDoc 与源码等价,并由 `pnpm run verify-type-equiv` 检查漂移(见 [development.md](../development.zh.md#documenting-types-verbatim-ts-type-equiv))。普通块保留完整声明;`public-api` 块保留去除实现体的公开 class 声明。Cordis 服务与事件使用每页生成的 **Cordis API** 小节。
+7
View File
@@ -0,0 +1,7 @@
# Bilingual-pair consistency record for otel.md (docs/i18n/README.md): per heading
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/otel.md
/otel-reporting:
en: adf67758d37c46f2
zh: 9e41b63531fa875a
+44
View File
@@ -0,0 +1,44 @@
# OTel reporting
English | [中文](otel.zh.md)
The [OTel plugin](../../packages/telemetry/otel/README.md) registers `ctx.otel`, a shared factory for independent ordinary-event and Session-log channels. It owns SDK transport and batching implementations. Business consumers inject the service, supply explicit transport/resource/scope options, and own each returned channel's shutdown.
UI feedback reaches the Session feedback services through their existing Remote APIs. Feedback recording authorizes the Session adapter's canonical-prefix capture and redaction before it reports through an OTel channel. Ordinary product consumers submit selected fields through the product adapter. The shared service has no automatic identity or capture policy, and channels never share queues or requests.
The [product adapter](../../packages/host/product-telemetry-otel/README.md) and [Session adapter](../../packages/session/session-telemetry-otel/README.md) retain their deployment configuration and disposal deadlines. Consumers must not reuse a channel after shutdown. Mounting only the OTel service allocates no provider or transport.
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
<a id="cordis-surface"></a>
## Cordis API
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
<a id="ctxotel--otel"></a>
### `ctx.otel` — `OTel`
Shared transport provider. Mounting creates no queue, identity, or network connection.
```ts cordis-catalog
/**
* Create an independent ordinary-event channel with count-based batching.
* The injected consumer must drain it during its fiber disposal.
* @param options - transport, scope, resource, queue, and diagnostic settings selected by the consumer.
* @returns the caller-owned channel; no state is shared with other channels.
*/
createEventReporter(options: EventLogOptions): EventLogReporter
/**
* Create an independent byte-bounded Session-log channel.
* Authorization and redaction precede reporting; the consumer owns shutdown and its outer deadline.
* @param options - transport, scope, resource, queue, and diagnostic settings selected by the consumer.
* @returns the caller-owned channel, preserving complete accepted events within the request byte ceiling.
*/
createSessionLogReporter(options: SessionLogOptions): SessionLogReporter
```
Source: [`packages/telemetry/otel/src/index.ts`](../../packages/telemetry/otel/src/index.ts)
<!-- END GENERATED cordis-surface -->
+44
View File
@@ -0,0 +1,44 @@
# OTel 上报
[English](otel.md) | 中文
[OTel 插件](../../packages/telemetry/otel/README.zh.md) 注册 `ctx.otel`,作为独立普通事件通道和 Session 日志通道的共享工厂。它持有 SDK 传输与聚合实现。业务调用方注入服务,显式提供传输、resource 和 scope 选项,并负责返回通道的关闭。
UI 反馈通过现有 Remote API 到达 Session 反馈服务。记录反馈后,Session 适配器才获得规范日志前缀的捕获授权,完成脱敏后通过 OTel 通道上报。普通产品调用方通过产品适配器提交选定字段。共享服务没有自动身份或捕获策略,通道之间不共享队列或请求。
[产品适配器](../../packages/host/product-telemetry-otel/README.zh.md) 和 [Session 适配器](../../packages/session/session-telemetry-otel/README.zh.md) 保留各自部署配置及销毁期限。通道关闭后调用方不得复用。只挂载 OTel 服务不会分配 provider 或 transport。
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
<a id="cordis-surface"></a>
## Cordis API
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.zh.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
<a id="ctxotel--otel"></a>
### `ctx.otel` — `OTel`
Shared transport provider. Mounting creates no queue, identity, or network connection.
```ts cordis-catalog
/**
* Create an independent ordinary-event channel with count-based batching.
* The injected consumer must drain it during its fiber disposal.
* @param options - transport, scope, resource, queue, and diagnostic settings selected by the consumer.
* @returns the caller-owned channel; no state is shared with other channels.
*/
createEventReporter(options: EventLogOptions): EventLogReporter
/**
* Create an independent byte-bounded Session-log channel.
* Authorization and redaction precede reporting; the consumer owns shutdown and its outer deadline.
* @param options - transport, scope, resource, queue, and diagnostic settings selected by the consumer.
* @returns the caller-owned channel, preserving complete accepted events within the request byte ceiling.
*/
createSessionLogReporter(options: SessionLogOptions): SessionLogReporter
```
Source: [`packages/telemetry/otel/src/index.ts`](../../packages/telemetry/otel/src/index.ts)
<!-- END GENERATED cordis-surface -->
+4 -15
View File
@@ -11,24 +11,13 @@ Enqueue is synchronous and does not acknowledge delivery. The SDK owns batching
## Record types
```ts type-equiv
/** Scalar values accepted by the collector's Arrow attributes map. */
type ProductTelemetryScalar = string | number | boolean
/** Scalar values accepted in product attributes. */
type ProductTelemetryScalar = OTelEventScalar
```
```ts type-equiv
/** Explicitly selected analytics fields; object values may contain scalars only. */
interface ProductTelemetryRecord {
/** Product/DA-owned event name. */
eventName: string
/** Human-readable summary; never a prompt, response, credential, or file contents. */
body: string
/** Event occurrence time in Unix milliseconds. Observation time is assigned on enqueue. */
timestamp: number
/** OTel severity; omitted values use INFO. */
severityNumber?: SeverityNumber
/** Business fields selected by the caller; no automatic device or account identity. */
attributes?: Record<string, ProductTelemetryScalar | Record<string, ProductTelemetryScalar>>
}
/** Caller-selected ordinary analytics record. */
type ProductTelemetryRecord = OTelEventRecord
```
Source: [`packages/host/product-telemetry-otel/src/index.ts`](../../packages/host/product-telemetry-otel/src/index.ts)
+4 -15
View File
@@ -11,24 +11,13 @@
## 记录类型
```ts type-equiv
/** Scalar values accepted by the collector's Arrow attributes map. */
type ProductTelemetryScalar = string | number | boolean
/** Scalar values accepted in product attributes. */
type ProductTelemetryScalar = OTelEventScalar
```
```ts type-equiv
/** Explicitly selected analytics fields; object values may contain scalars only. */
interface ProductTelemetryRecord {
/** Product/DA-owned event name. */
eventName: string
/** Human-readable summary; never a prompt, response, credential, or file contents. */
body: string
/** Event occurrence time in Unix milliseconds. Observation time is assigned on enqueue. */
timestamp: number
/** OTel severity; omitted values use INFO. */
severityNumber?: SeverityNumber
/** Business fields selected by the caller; no automatic device or account identity. */
attributes?: Record<string, ProductTelemetryScalar | Record<string, ProductTelemetryScalar>>
}
/** Caller-selected ordinary analytics record. */
type ProductTelemetryRecord = OTelEventRecord
```
Source: [`packages/host/product-telemetry-otel/src/index.ts`](../../packages/host/product-telemetry-otel/src/index.ts)
+4 -4
View File
@@ -9,8 +9,8 @@
en: 41cd197b5c74bfa3
zh: 18ed11259036d726
/host-wide-schedule/absolute-time-input:
en: e730fda52ba2854f
zh: 01a6c682e327a133
en: 0876a14a60ba2160
zh: e7d4bd9099c14fb2
/host-wide-schedule/daily-wall-clock-input:
en: 2d249edc3dfccb8a
zh: 33fecda132b03b84
@@ -27,8 +27,8 @@
en: 0656173de50ee132
zh: 54176540371d0f28
/host-wide-schedule/web-catalog:
en: 39f24636dd788034
zh: ba3ff541fee5854a
en: 60c88b08abde9bb0
zh: 0a3644cfa86d014d
/host-wide-schedule/timing-edits:
en: ce5d2095a3ad2e82
zh: 167b3715f8eaffc1
+2 -2
View File
@@ -116,7 +116,7 @@ interface LocalAtInput {
type AtInput = string | LocalAtInput
```
The shipped Web bundle mounts time-context, which samples the browser's IANA zone for every prompt. Time-context tells the model to interpret otherwise-unqualified natural-language dates and times in that request-local zone when the open turn has one unambiguous browser zone; mixed or missing browser-zone records tell the model to ask. That guidance is not a durable Session default: the model must still pass an offset in the string form or `time_zone` in the local form, and Schedule never reads browser, Session, process, or model context.
The shipped Web composition carries the `time-context` row disabled; the optional experimental bundle `@deepseek-ai/dsh-experimental-schedule-bundle`, enabled from the Plugins page, mounts time-context, which samples the browser's IANA zone for every prompt. Time-context tells the model to interpret otherwise-unqualified natural-language dates and times in that request-local zone when the open turn has one unambiguous browser zone; mixed or missing browser-zone records tell the model to ask. That guidance is not a durable Session default: the model must still pass an offset in the string form or `time_zone` in the local form, and Schedule never reads browser, Session, process, or model context.
Schedule rejects invalid offsets and zones, offset-free strings, non-future targets, and local times inside daylight-saving gaps. A daylight-saving overlap chooses its first, earlier instant. Successful creation stores only canonical UTC `scheduledAt`, so replay never depends on ambient time-zone state.
@@ -339,7 +339,7 @@ type ScheduleCatalogEntry = ScheduleRecord & {
The Remote `schedule.list({ sessionId })`, model `schedule_list`, and Session-header catalog return only active tasks. A model view's derived timing `state` remains distinct from stored lifecycle `status`. Deletion uses `schedule.delete({ sessionId, id })` with the entry's original binding; a mismatched binding returns not found. Model tools supply the current Agent's Session, whereas the global user interface supplies the selected task's binding. The binding check alone does not establish caller authorization. The payload-free `schedule/changed` event invalidates client lists; reconnecting clients fetch current state again.
The Web bundle mounts `ui-schedule` with the Host capability. The [client package](../../packages/client/ui-schedule/README.md) owns the catalog, empty state, and deletion controls. The page separately filters all, active, and inactive tasks, retains inactive details and an original-Session control in the detail tab strip, and requires explicit confirmed deletion. Rules and Delivery records separate task settings from lazily paged saved receipts. Neither a receipt nor inactive status confirms model execution.
The shipped Web composition carries the `ui-schedule` row disabled; the optional experimental bundle `@deepseek-ai/dsh-experimental-schedule-bundle`, enabled from the Plugins page, mounts `ui-schedule` with the Host capability. The [client package](../../packages/client/ui-schedule/README.md) owns the catalog, empty state, and deletion controls. The page separately filters all, active, and inactive tasks, retains inactive details and an original-Session control in the detail tab strip, and requires explicit confirmed deletion. Rules and Delivery records separate task settings from lazily paged saved receipts. Neither a receipt nor inactive status confirms model execution.
## Timing edits
+2 -2
View File
@@ -116,7 +116,7 @@ interface LocalAtInput {
type AtInput = string | LocalAtInput
```
发布的 Web bundle 会挂载 time-context,它为每条提示词采样浏览器的 IANA 时区。当 open turn 只有一个无歧义的浏览器时区时,Time-context 会告诉模型按该请求本地时区解释未明确限定时区的自然语言日期和时间;浏览器时区记录混合或缺失时,则告诉模型询问用户。该指引不是持久 Session 默认值:模型仍必须在字符串形式中传入偏移量,或在本地形式中传入 `time_zone`;Schedule 绝不会读取浏览器、Session、进程或模型上下文。
随发行版交付的 Web 组合把 `time-context` 行保持禁用;在插件管理页启用可选实验性 bundle `@deepseek-ai/dsh-experimental-schedule-bundle` 会挂载 time-context,它为每条提示词采样浏览器的 IANA 时区。当 open turn 只有一个无歧义的浏览器时区时,Time-context 会告诉模型按该请求本地时区解释未明确限定时区的自然语言日期和时间;浏览器时区记录混合或缺失时,则告诉模型询问用户。该指引不是持久 Session 默认值:模型仍必须在字符串形式中传入偏移量,或在本地形式中传入 `time_zone`;Schedule 绝不会读取浏览器、Session、进程或模型上下文。
Schedule 会拒绝无效偏移量与时区、不带偏移量的字符串、非未来目标,以及落在夏令时缺口内的本地时间。遇到夏令时重叠时,会选择第一次出现的较早时点。创建成功后只存储规范化后的 UTC `scheduledAt`,因此回放绝不依赖环境时区状态。
@@ -339,7 +339,7 @@ type ScheduleCatalogEntry = ScheduleRecord & {
Remote 方法 `schedule.list({ sessionId })`、模型 `schedule_list` 和 Session 页头目录仅返回活动任务。模型视图派生的时间 `state` 与存储的生命周期 `status` 仍是不同概念。删除通过 `schedule.delete({ sessionId, id })` 使用条目的原始绑定;绑定不匹配时返回未找到。模型工具传入当前 Agent 的 Session,全局用户界面则传入所选任务的绑定。仅校验绑定并不构成调用者鉴权。无 payload 的 `schedule/changed` 事件通知客户端刷新列表;重新连接后,客户端再次获取当前状态。
Web bundle 将 `ui-schedule` 与宿主能力一起挂载。[客户端包](../../packages/client/ui-schedule/README.zh.md) 负责目录、空状态与删除控件。页面单独筛选全部、活动和未运行任务,保留未运行任务详情及详情页签条内的原 Session 入口,并要求显式确认删除。“规则”和“发送记录”将任务设置与按需分页加载的已保存回执分开。回执与未运行状态均不确认模型执行。
随发行版交付的 Web 组合把 `ui-schedule` 行保持禁用;在插件管理页启用可选实验性 bundle `@deepseek-ai/dsh-experimental-schedule-bundle` 会将它与宿主能力一起挂载。[客户端包](../../packages/client/ui-schedule/README.zh.md) 负责目录、空状态与删除控件。页面单独筛选全部、活动和未运行任务,保留未运行任务详情及详情页签条内的原 Session 入口,并要求显式确认删除。“规则”和“发送记录”将任务设置与按需分页加载的已保存回执分开。回执与未运行状态均不确认模型执行。
## 修改时间
+2 -2
View File
@@ -3,8 +3,8 @@
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/subsystems/session-telemetry.md
/sessiontelemetrybackend:
en: d400c8668cd76829
zh: 67689676c19daa7f
en: b82e4db22a4d58aa
zh: 439181fa2c27542d
/sessiontelemetrybackend/the-logical-record:
en: 9695489076496a76
zh: 457c54698bc38c2f
+3 -1
View File
@@ -2,7 +2,7 @@
English | [中文](session-telemetry.zh.md)
Outbound session reporting is split as a [capability seam](../capability-seams.md): the Service Definition and capture coordinator ([dsh-session-telemetry](../../packages/session/session-telemetry), `ctx.sessionTelemetry`) own complete canonical-event capture, the `session-telemetry/record` redaction waterfall, the handoff cursor, and the minimal backend contract; the Service Provider a deployment loads ([dsh-session-telemetry-otel](../../packages/session/session-telemetry-otel)) is the OpenTelemetry JS SDK's log pipeline configured verbatim. It is one optional capability, not part of the agent-loop spine, and nothing here reaches a model request. The boundary axiom — the harness's aspect ends at `emit()`; batching, retry, queueing, and loss policy belong to the reporting SDK — and the rejected alternatives are pinned in the [revival Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md); the capture and cursor contracts live in the [Service Definition README](../../packages/session/session-telemetry/README.md).
The [capture service](../../packages/session/session-telemetry/README.md) owns canonical-event capture, redaction, and handoff cursors. The [feedback backend](../../packages/session/session-telemetry-otel/README.md) authorizes Session prefixes and owns their independent queue, per-request scheduling, and 4,000,000-byte ceiling. No reporting data reaches model requests.
Source: [`packages/session/session-telemetry/src/index.ts`](../../packages/session/session-telemetry/src/index.ts)
@@ -29,6 +29,8 @@ type SessionTelemetrySeverity = 'info' | 'warn' | 'error'
* identity so they can never be mistaken for ledger rows.
*/
interface SessionTelemetryRecord {
/** Canonical envelope without data; body carries the separately redacted payload. Absent for operational records. */
sourceEvent?: { sessionId: SessionId; envelope: Omit<SessionEvent, 'data'> }
/** Ledger (session-log mirror) or ops (operational signal) channel; backends keep the two under separate instrumentation scopes. */
channel: 'ledger' | 'ops'
/** Unix epoch milliseconds — the source event's append time for ledger records, the emission time for ops records. */
+3 -1
View File
@@ -2,7 +2,7 @@
[English](session-telemetry.md) | 中文
对外的会话上报拆分为一项[能力 seam](../capability-seams.zh.md):Service Definition 与捕获协调器([dsh-session-telemetry](../../packages/session/session-telemetry),`ctx.sessionTelemetry`)拥有完整的权威事件捕获、`session-telemetry/record` 脱敏 waterfall(瀑布式事件)、handoff 游标与最小后端约定;部署方加载的 Service Provider([dsh-session-telemetry-otel](../../packages/session/session-telemetry-otel))则是原样配置的 OpenTelemetry JS SDK 日志流水线。它是一项可选能力,不属于 agent loop(智能体循环)主干,这里也没有任何内容会进入模型请求。边界公理(harness 的职责止于 `emit()`;批处理、重试、排队与丢失策略都属于上报 SDK)连同被否决的替代方案,均已在[复活 Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.zh.md)中定案;捕获与游标约定见 [Service Definition README](../../packages/session/session-telemetry/README.zh.md)。
[捕获服务](../../packages/session/session-telemetry/README.zh.md) 负责规范事件捕获、脱敏和交接游标。[反馈后端](../../packages/session/session-telemetry-otel/README.zh.md) 授权 Session 前缀,并负责独立队列、逐请求调度及 4,000,000 字节上限。上报数据不会进入模型请求。
源码:[`packages/session/session-telemetry/src/index.ts`](../../packages/session/session-telemetry/src/index.ts)
@@ -29,6 +29,8 @@ type SessionTelemetrySeverity = 'info' | 'warn' | 'error'
* identity so they can never be mistaken for ledger rows.
*/
interface SessionTelemetryRecord {
/** Canonical envelope without data; body carries the separately redacted payload. Absent for operational records. */
sourceEvent?: { sessionId: SessionId; envelope: Omit<SessionEvent, 'data'> }
/** Ledger (session-log mirror) or ops (operational signal) channel; backends keep the two under separate instrumentation scopes. */
channel: 'ledger' | 'ops'
/** Unix epoch milliseconds — the source event's append time for ledger records, the emission time for ops records. */
+2 -2
View File
@@ -12,8 +12,8 @@
en: b3b2a6e0ce02bdee
zh: 2ddb168de79fd19c
/schedule-reminders/create-reminders:
en: 70ae56895847c453
zh: 19b38057c7462123
en: c76ee99f59529942
zh: be1d75c226231dd7
/schedule-reminders/inspect-and-delete-tasks:
en: a44fc1aabc2ab957
zh: 31f444d4c7fd5c7a
+1 -1
View File
@@ -17,7 +17,7 @@ Create reminders in a conversation, then inspect active and inactive tasks and e
<a id="create-reminders"></a>
## Create reminders
The shipped Web profile mounts Schedule with the clock context that gives the model the current time and the browser's zone. Configure a model provider before asking it to create reminders.
The shipped Web profile carries the `time-context`, `schedule`, and `ui-schedule` rows disabled. Open Plugins and enable `Scheduling and time context` in the Official group; the optional experimental bundle `@deepseek-ai/dsh-experimental-schedule-bundle` then mounts Schedule with the clock context that gives the model the current time and the browser's zone. Configure a model provider before asking it to create reminders.
Ask the model to create, list, edit, or delete reminders. It uses `schedule_create`, `schedule_list`, `schedule_update`, and `schedule_delete` (update changes one reminder in place and keeps its id and saved delivery records); the Automation tasks page's New action opens a New Session for a creation instead, with the request already written in its composer. Supported choices are a one-time delay in positive whole seconds, an absolute date and time, a fixed interval of at least one minute, a daily local time with an IANA time zone, a weekly local time with an IANA time zone and ISO weekdays from Monday 1 through Sunday 7, or a five-field cron expression with an explicit IANA time zone, stored in canonical form.
+1 -1
View File
@@ -17,7 +17,7 @@
<a id="create-reminders"></a>
## 创建提醒
发布的 Web profile 会挂载 Schedule 及其时钟上下文,后者把当前时间和浏览器时区交给模型。在请求模型创建提醒前,先配置模型 provider。
发布的 Web profile 把 `time-context`、`schedule` 与 `ui-schedule` 三行保持禁用。打开插件管理页,在 Official 分组中启用“定时与时间上下文”;可选实验性 bundle `@deepseek-ai/dsh-experimental-schedule-bundle` 随即挂载 Schedule 及其时钟上下文,后者把当前时间和浏览器时区交给模型。在请求模型创建提醒前,先配置模型 provider。
向模型提出创建、列出、修改或删除提醒的请求。模型使用 `schedule_create`、`schedule_list`、`schedule_update` 和 `schedule_delete`(更新原地修改一条提醒,保留其 id 与已保存的投递记录);「自动化任务」页面的「新建」操作则为创建打开一个新会话,其 composer 已写好该请求。支持正整数秒的一次性延迟、绝对日期时间、至少一分钟的固定间隔、带 IANA 时区的每日本地时间、带 IANA 时区和 ISO 星期(周一 1 至周日 7)的每周本地时间,以及带显式 IANA 时区的五字段 cron 表达式(表达式以规范化形式保存)。
+2 -2
View File
@@ -15,8 +15,8 @@
en: ffc55792d6e922e1
zh: 83bd759638857e2d
/packages/package-groups:
en: 2818a70fa6bbb1b6
zh: 800b34102e82c192
en: d865abbfa65d1d4f
zh: ea079b795b51dd55
/packages/release-expectations:
en: 1ab880da3642bde1
zh: 6e168322f026a9cf
+1
View File
@@ -34,6 +34,7 @@ Every package lives in exactly one group; new packages join existing groups, and
| [`goal/`](goal/README.md) | Same-session goal persistence and lifecycle |
| [`schedule/`](schedule/README.md) | Host-owned scheduled follow-ups |
| [`feedback/`](feedback/README.md) | Human feedback capture and command |
| [`telemetry/`](telemetry/README.md) | Shared Cordis OTel reporting channels |
| [`identity/`](identity/README.md) | Shared anonymous identity |
| [`llm/`](llm/README.md) | LLM capability family: abstract service + provider adapters |
| [`subprocess/`](subprocess/README.md) | Subprocess capability family: Service Definition + local process-tree provider |
+1
View File
@@ -34,6 +34,7 @@ harness 由 `packages/` 下的 npm 包组装而成,按能力系列分组:会
| [`goal/`](goal/README.zh.md) | 同会话 goal 的持久化与生命周期 |
| [`schedule/`](schedule/README.zh.md) | Host 拥有的定时后续操作 |
| [`feedback/`](feedback/README.zh.md) | 人类反馈的采集与命令 |
| [`telemetry/`](telemetry/README.zh.md) | 共享 Cordis OTel 上报通道 |
| [`identity/`](identity/README.zh.md) | 共享匿名身份 |
| [`llm/`](llm/README.zh.md) | LLM(大语言模型)能力系列:抽象服务 + 提供方适配器 |
| [`subprocess/`](subprocess/README.zh.md) | 子进程能力系列:Service Definition + 本地进程树提供方 |
Loaded 100 of 216 files, more files were not shown because too many files have changed in this diff. Show more