Compare commits

...
811 Commits
Author SHA1 Message Date
imccyu fcc0ad4621 Merge pull request #7873 from deepseek-harness/merge/release-dsh-0.2.2-rc.1-to-master
Merge release/dsh-0.2.2 (0.2.2-rc.1) into master
2026-11-04 16:40:00 +08:00
Xu Hanxiang 0b4328d514 Merge pull request #7856 from deepseek-harness/docs/liveshare-surface-accuracy
docs(liveshare): state the viewer tool surface the code enforces
2026-11-04 08:39:36 +00:00
winewill 78be9be83b Merge pull request #7844 from deepseek-harness/fix/desktop-channel-version-policy
fix(desktop): 允许人工选择任意版本进入 stable 渠道
2026-11-04 16:07:16 +08:00
imccyu abc0e7092b Merge remote-tracking branch 'origin/release/dsh-0.2.2' into merge/release-dsh-0.2.2-rc.1-to-master
# Conflicts:
#	packages/client/ui-agent-team/package.json
#	packages/client/ui-brand-official/package.json
#	packages/client/ui-open-in-app/package.json
#	packages/client/ui-settings-web-search/package.json
#	packages/experimental/agent-team-profile/package.json
#	packages/team/agent-team/package.json
#	packages/web/web-search-exa/package.json
#	packages/web/web-search-perplexity/package.json
2026-11-04 15:59:50 +08:00
Kexin Liu f1b3487a51 Merge pull request #7853 from deepseek-harness/fix/web-prompt-retry
fix(web): deduplicate prompt retries after input acceptance
2026-11-04 15:46:24 +08:00
winewill c649fdae69 docs(desktop): preserve frozen stable channel migration guide 2026-11-04 15:44:33 +08:00
liukx0205 78167faa4f Merge remote-tracking branch 'origin/master' into fix/web-prompt-retry 2026-11-04 15:34:17 +08:00
winewill 0d03fb3ca3 Merge master into desktop channel version policy 2026-11-04 15:30:38 +08:00
liukx0205 a186602dc3 Merge remote-tracking branch 'origin/master' into fix/web-prompt-retry
# Conflicts:
#	packages/api/session-controller/README.i18n.yaml
#	packages/api/session-controller/src/commands.ts
2026-11-04 15:29:20 +08:00
std4453 0ddca24030 Merge pull request #7569 from deepseek-harness/ux/session-menu-open-in-app
feat(client): 将 Open in 收进 Session 菜单并适配窄屏
2026-11-04 15:26:58 +08:00
winewill f823eb9c60 Merge pull request #7864 from deepseek-harness/feat/desktop-staging-percentage
feat(desktop): 支持发布表单配置更新灰度比例
2026-11-04 15:23:47 +08:00
std4453 a9b2873303 Merge pull request #7787 from deepseek-harness/turtle/add-paste-plain-shortcut-d16f8fd43c4b
feat(desktop): bind Paste and Match Style to Shift+Command+V on macOS
2026-11-04 15:18:06 +08:00
winewill 08afd883d7 docs(desktop): scope build numbering to its version mode 2026-11-04 15:14:58 +08:00
winewill 90ff03beb7 Merge pull request #7777 from deepseek-harness/fix/desktop-artifact-zip-mtime
fix(desktop): 保留 Actions 安装包 ZIP 中的文件修改时间
2026-11-04 15:10:19 +08:00
winewill c05d36d6f3 test(desktop): update production rollout approval snapshots 2026-11-04 15:08:59 +08:00
winewill 69db579d7a docs(desktop): distinguish version mode and manual version 2026-11-04 15:04:24 +08:00
Turtle 08c061f8a7 Merge pull request #7425 from deepseek-harness/turtle/inspector-crash-bdc81a501c00
fix(inspector): reserve record overhead in the Cordis snapshot budget
2026-11-04 15:01:37 +08:00
winewill 6fe4c14f1e Merge branch 'codex/desktop-artifact-parent-refresh' into fix/desktop-channel-version-policy
# Conflicts:
#	apps/desktop/packaging/README.i18n.yaml
2026-11-04 14:56:27 +08:00
winewill 3a51d53966 docs(desktop): centralize and clarify archival diagnostics 2026-11-04 14:55:06 +08:00
Turtle 294a359142 docs(config): regenerate catalog for the source-frame minimum 2026-11-04 14:43:50 +08:00
Turtle e9273a7624 fix(inspector): reject source-frame limits without a Cordis snapshot budget
Share the topic length limit with the state record overhead and fail option resolution when maxSourceFrameBytes leaves no state payload budget.
2026-11-04 14:43:50 +08:00
Turtle 57aae95097 fix(inspector): reserve record overhead in the Cordis snapshot budget
The collector pruned snapshots to maxFrameBytes - 4096, but setState also counts the retained record's JSON around the payload, so a pruned snapshot near the budget threw and crashed the process.

Refs #7424
2026-11-04 14:43:50 +08:00
winewill 1ffe1a71db Merge branch 'codex/desktop-artifact-parent-refresh' into fix/desktop-channel-version-policy
# Conflicts:
#	apps/desktop/packaging/README.i18n.yaml
2026-11-04 14:42:06 +08:00
winewill 16743ca088 docs(desktop): document archive tool failure diagnostics 2026-11-04 14:38:26 +08:00
winewill 6958fd4e3a fix(desktop): default publication rollout to 100 percent 2026-11-04 14:38:16 +08:00
Turtle 7053f882a8 Merge pull request #7472 from deepseek-harness/turtle/fork-name-suffix-5f3435925211
fix(session-controller): give repeated forks distinct titles
2026-11-04 14:37:57 +08:00
winewill e1cd73e4de fix(desktop): type omitted rollout percentage explicitly 2026-11-04 14:35:43 +08:00
winewill 001bc22978 feat(desktop): configure staged rollout during publication 2026-11-04 14:34:45 +08:00
zhc e85a3f90be Merge master into ux/session-menu-open-in-app
重新生成冲突的 ui-conversation 双语配对记录与 slot 目录;其余改动自动合并。
2026-11-04 14:31:49 +08:00
Dudu-0223 a2f3a18580 Merge pull request #7697 from deepseek-harness/feat/subagent-catalog-names
feat(subagent): name subagents in their creator catalog
2026-11-04 14:30:06 +08:00
winewill 6bdaebb3ea Merge branch 'codex/desktop-artifact-parent-refresh' into fix/desktop-channel-version-policy 2026-11-04 14:29:02 +08:00
winewill 6447accb5f docs(desktop): clarify retained archive naming and timestamps 2026-11-04 14:28:46 +08:00
winewill 812f846489 docs(desktop): clarify version mode compatibility lifetime 2026-11-04 14:19:21 +08:00
winewill 6d89e7497e Merge branch 'codex/desktop-artifact-parent-refresh' into fix/desktop-channel-version-policy 2026-11-04 14:19:08 +08:00
winewill 679f3fbb2c test(desktop): expect archive settings failure before runner validation 2026-11-04 14:18:53 +08:00
Turtleandimccyu 4731be7515 Merge origin/master into turtle/fork-name-suffix-5f3435925211
Co-authored-by: imccyu <[email protected]>
2026-11-04 14:16:25 +08:00
Dudu-0223 db109a62fb Merge master into feat/subagent-catalog-names 2026-11-04 14:16:00 +08:00
Yichen Jiang 2cee373847 Merge pull request #7358 from deepseek-harness/worktree/continue-generation-after-failure
feat(web): continue generation after transient request failures
2026-11-04 14:09:33 +08:00
winewill e0e2c63a9c fix(desktop): bridge workflow modes for older sources 2026-11-04 13:57:19 +08:00
zhc 39471d572d Merge master and fix CI on shared menu branch
- 合并最新 master,无冲突。
- 清理 `no-unknown-casts` 基线中已失效的 `session-log-export` 测试条目。
- 会话行 ⋯ 改为共享 `contextMenu.button` 后会显示 tooltip;它在气泡尺寸
  测量完成后才可见,原用例在假时钟下立即记录,结果取决于渲染时机。用例
  改为等待该 tooltip 可见,快照 `more-hover` 阶段记录该 tooltip。
- 删除子菜单几何用例中对父卡片施加 `transform` 的步骤。它验证的是已删除
  的逐帧移动跟踪;产品中的菜单卡片不带 transform,CSS 锚点对 transform
  的跟随由浏览器决定,WebKit 在负载下不稳定。行外边距、字号、父列表滚动
  与视口缩窄的真实布局验证保留。
2026-11-04 13:56:23 +08:00
winewill d65fd521b5 fix(desktop): clarify workflow version mode names 2026-11-04 13:51:39 +08:00
Dudu-0223 bb3ac8e304 Merge master into feat/subagent-catalog-names 2026-11-04 13:51:31 +08:00
Yichen Jiang 14d3a091b8 test(conversation): remove redundant retry test scaffolding 2026-11-04 13:49:59 +08:00
winewill 0fdd91f3e6 docs(desktop): align channel selection guidance 2026-11-04 13:40:11 +08:00
winewill 4656d7bf5a Merge branch 'codex/desktop-artifact-parent-refresh' into fix/desktop-channel-version-policy 2026-11-04 13:38:31 +08:00
winewill c5219dcfb2 fix(desktop): validate archive settings before retained files 2026-11-04 13:38:16 +08:00
liukx0205 8706daa679 fix(web): deduplicate prompt retries after input acceptance 2026-11-04 13:37:53 +08:00
Yichen Jiang 4e56134fec test(conversation): keep injected context fixture client-safe 2026-11-04 13:34:03 +08:00
Yichen Jiang ee28dc8d1c fix(conversation): narrow pending inbox message fields 2026-11-04 13:30:44 +08:00
Yichen Jiang 399fe74c1e refactor(conversation): read retry state through existing projections 2026-11-04 13:28:19 +08:00
Jiawei Song e74ab10d10 Merge pull request #7714 from deepseek-harness/feat/apply-patch-row-summary
feat(ui): show and link the files an apply_patch row edits
2026-11-04 13:24:53 +08:00
Dudu-0223 5a28d81355 test(subagent): refresh PTC identity snapshots 2026-11-04 13:24:12 +08:00
winewill fa302c8343 Merge pull request #7636 from deepseek-harness/build/desktop-publish-recovery
feat(desktop): 官网安装包发布恢复与 GitHub 产物归档拆分
2026-11-04 13:17:10 +08:00
winewill dedf7e65c5 test(desktop): cover native prerelease downgrade and Windows config 2026-11-04 13:15:17 +08:00
Dudu-0223 4618fddfb5 fix(subagent): preserve compatible catalog checkpoints 2026-11-04 13:13:38 +08:00
winewill 950b63b37b test(desktop): verify prerelease downgrade rejection 2026-11-04 13:06:08 +08:00
Yichen Jiang 8d8684dc0f refactor(conversation): remove redundant retry source cache 2026-11-04 13:02:38 +08:00
winewill 83e1d6bcc4 Merge archive review fixes into channel version policy 2026-11-04 12:58:55 +08:00
winewill e2e7264585 fix(desktop): isolate archive tests and clarify retained timestamps 2026-11-04 12:58:15 +08:00
winewill 866b3bd06a Merge publication prerequisite documentation fixes into channel policy 2026-11-04 12:54:30 +08:00
winewill 9445f22471 Merge publication prerequisite documentation fixes 2026-11-04 12:54:04 +08:00
Yichen Jiang b0279fa8ae test: share continuation fixture across compiler faces 2026-11-04 12:53:54 +08:00
winewill 037a521662 docs(desktop): clarify per-job publication prerequisites 2026-11-04 12:53:24 +08:00
Yichen Jiang 36ed2133ff Merge remote-tracking branch 'origin/master' into worktree/continue-generation-after-failure
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-11-04 12:50:23 +08:00
Yichen Jiang dde72c5af1 test: classify shared continuation fixture as client 2026-11-04 12:49:31 +08:00
winewill 3952e9a08b Merge publication documentation review fixes into channel policy 2026-11-04 12:43:17 +08:00
winewill fb69466d62 Merge publication documentation review fixes 2026-11-04 12:42:48 +08:00
winewill 287a77525b docs(desktop): specify publication output failure boundaries 2026-11-04 12:42:10 +08:00
zhc 68312d2a4a Merge master into ux/session-menu-open-in-app
重新生成冲突的 slot 目录与 ui-conversation 双语配对记录;其余改动自动合并。
2026-11-04 12:31:38 +08:00
zhc 45bb3da47e Merge master and position submenus with CSS anchors
master 通过 #7700 将 portal 菜单改为 CSS Anchor Positioning,并删除了
`useAnchoredPosition`。本次合并让子菜单跟进同一机制:

- 子卡片以父卡片内一个不随列表滚动、与卡片同尺寸的锚点框为默认锚点,
  侧开时默认在右侧、放不下时由 CSS 翻到左侧;单面板模式占据父卡片位置。
- JavaScript 只决定侧开或单面板,并提供当前行在父卡片内的偏移和子卡片
  自然高度;两者都是相对尺寸,双指缩放与平移由 CSS 锚点处理。
- 锚点框绕开了 WebKit 的两个问题:锚定到自身由锚点定位的父卡片时不会
  在其定位稳定后重新计算;锚定到滚动列表内的行时不计入滚动偏移。
- 删除 `useAnchoredPosition` 及其测试;`menu-submenu-position` 只保留
  侧开/单面板判断,相应单测改为验证模式与锚点名。
- 解决 README、测试与 package.json 冲突,沿用 master 的 CSS 锚点说明并
  补充子菜单定位描述。
2026-11-04 12:26:20 +08:00
CreatixChu a0567adccb Merge pull request #7797 from deepseek-harness/feat/cua-publish-source-reuse
feat(release): 自动复用源码一致的 CUA helper 并简化发布输入
2026-11-04 12:21:32 +08:00
sjw1231 af86de8add test(snapshots): update apply_patch description in new scenarios
Five recorded scenarios that arrived from master pin the previous
apply_patch tool description; refresh their schema and prompt sidecars
to the current text.

Refs #7696
2026-11-04 12:07:45 +08:00
Ziya d70967e36b Merge pull request #6586 from deepseek-harness/experiment/home-code-ripple
feat(web): play a pixel and code whale on blank home clicks
2026-11-04 04:07:26 +00:00
Dudu-0223 a6f6b98053 Merge master into feat/subagent-catalog-names 2026-11-04 12:04:15 +08:00
sjw1231 1d5f9f7607 Merge remote-tracking branch 'origin/master' into feat/apply-patch-row-summary 2026-11-04 12:03:48 +08:00
Turtle b56220bd3a Merge pull request #7837 from deepseek-harness/turtle/fix-desktop-pdf-preview-ce6dbb8c4533
fix(ui-sidebar-documentpreview): lay out PDF text layers detached
2026-11-04 12:02:54 +08:00
Dudu-0223 bfc89ed4a9 fix(team): reuse compatible owner-name checkpoints 2026-11-04 11:56:50 +08:00
winewill 1c9cbf5a7e Merge publication review closeout into channel version policy 2026-11-04 11:54:43 +08:00
winewill eebc65428b Merge publication review closeout into artifact timestamp fix 2026-11-04 11:54:15 +08:00
winewill b8fd57ad43 fix(desktop): clarify publication retry limits and route evidence 2026-11-04 11:52:59 +08:00
Yichen Jiang 9ee92455c4 Merge pull request #7788 from deepseek-harness/worktree/forge-diff-base-branch-4ffd61
feat(forge): add a base branch picker to local changes and restyle merge
2026-11-04 11:38:17 +08:00
Yichen Jiang ee20b6e9ba test(web): align continuation snapshot with context groups 2026-11-04 11:30:11 +08:00
Yichen Jiang 46983a077f Merge remote-tracking branch 'origin/master' into worktree/continue-generation-after-failure 2026-11-04 11:27:00 +08:00
Turtle 799be95642 Merge pull request #7842 from deepseek-harness/turtle/doc-experimental-package-rule-8329b18d60de
docs(experimental): forbid the experimental prefix in new package names
2026-11-04 11:20:24 +08:00
sjw1231 ff42bb3f0d fix(ui): highlight only the hovered file in a multi-file patch row
Hovering one file link of a multi-file apply_patch row brightened the
whole row, so every file looked active. The row now keeps its idle color
and diff totals while a file link is hovered, and only that link
brightens.

Refs #7696
2026-11-04 11:20:04 +08:00
creatixchu 985afb0941 test(release): narrow native HTTP request assertions 2026-11-04 11:10:47 +08:00
winewill 152d0294c2 fix(desktop): decouple update channels from version suffixes 2026-11-04 11:06:11 +08:00
winewill 6ad048912e Merge current publication recovery into artifact timestamp fix
# Conflicts:
#	apps/desktop/packaging/README.i18n.yaml
#	apps/desktop/packaging/README.md
#	apps/desktop/packaging/README.zh.md
#	scripts/macos-package-workflow.spec.ts
#	scripts/windows-package-workflow.spec.ts
2026-11-04 11:05:13 +08:00
creatixchu 72b3dfdc6f fix(release): query helper artifacts with native Node fetch 2026-11-04 11:03:12 +08:00
Turtle cfa6f7359e fix(experimental): reject the experimental prefix in new package names
Freeze the existing prefixed directories in
LEGACY_PREFIXED_EXPERIMENTAL_PACKAGE_DIRECTORIES and reject the prefix
elsewhere. Record the naming decision in a new Agent Note and keep the
plugin identity note's facts current.

Refs #7841
2026-11-04 04:59:30 +08:00
Turtle 22f0226e49 docs(experimental): forbid the experimental prefix in new package names
New experimental packages use @deepseek-ai/dsh-<name> so that promotion
keeps their npm names. The workspace gate message now points at
EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS instead of the prefix.

Refs #7841
2026-11-04 04:38:55 +08:00
TurtleandYudong Han f1923f2e8a docs(ui-sidebar-documentpreview): record detached PDF text layer layout
Co-authored-by: Yudong Han <[email protected]>
2026-11-04 00:11:24 +08:00
TurtleandYudong Han c72582e587 fix(ui-sidebar-documentpreview): attach the text layer node that carries its styles
Append the builder's own container in onAppend instead of the callback
argument, document the attach timing, and name the test after what it
checks.

Co-authored-by: Yudong Han <[email protected]>
2026-11-04 00:09:38 +08:00
TurtleandYudong Han e7065bb5ac fix(ui-sidebar-documentpreview): lay out PDF text layers detached
PDF.js measures every text item with a canvas in the document. Appending
the text layer to the live page before rendering made each measurement
force a style update over all pages rendered so far, so paging got slower
with every page. Attach the layer through TextLayerBuilder's onAppend
after it finishes rendering, as the PDF.js viewer does.

Co-authored-by: Yudong Han <[email protected]>
2026-11-04 00:00:29 +08:00
Yichen Jiang e369822bc0 Merge pull request #7832 from deepseek-harness/worktree/fix-floating-panel-anchors
fix(web): preserve floating panel anchor alignment
2026-11-03 23:20:25 +08:00
Turtle 917aab8204 Merge pull request #7830 from deepseek-harness/turtle/render-context-injection-f1a5826b9910
feat(ui-chat): show context injections as tool-call rows
2026-11-03 23:13:35 +08:00
Yichen Jiang c4ecee9173 test(web): align menu expectations and await workspace readiness 2026-11-03 22:44:39 +08:00
TurtleandKaige-Gao f6e942a097 docs(ui-chat): document the context process category contract
Clarify the processActivity and summary JSDoc, correct the running label
in the title table, export RunningProcessActivity, and add the upgrade
guide for the ProcessActivity union change.

Co-authored-by: Kaige-Gao <[email protected]>
2026-11-03 22:05:51 +08:00
Yichen Jiang 0e8bfc1b9d fix(web): preserve floating panel anchor alignment 2026-11-03 21:58:56 +08:00
TurtleandKaige-Gao 44ad857798 feat(ui-chat): show context injections as tool-call rows
Ordinary Context injections were hidden from Chat. Show them as the
existing collapsed disclosure rows inside the Turn process group, and
title a group made only of Context "Injected context" instead of
"Analysis completed".

Co-authored-by: Kaige-Gao <[email protected]>
2026-11-03 21:26:17 +08:00
涂轶翔 28fcf5116f Merge pull request #7715 from deepseek-harness/feat/cua-app-approval-ui
feat(cua): 完善应用授权风险提示
2026-11-03 21:24:39 +08:00
Yixiang Tu dca8cfb77e fix(client): position approval risks with CSS anchors 2026-11-03 21:05:16 +08:00
Yixiang Tu 0f05518e69 fix(cua): keep risk hover tint inside the tag 2026-11-03 21:05:16 +08:00
Yixiang Tu 44df6f18cd fix(cua): remove outer risk tag hover fill 2026-11-03 21:05:16 +08:00
Yixiang Tu 9cfa6153ca fix(client): dismiss approval risks on focus and foreground loss 2026-11-03 21:05:16 +08:00
Yixiang Tu 43e9de8170 fix(cua): restore existing approval button emphasis 2026-11-03 21:05:16 +08:00
Yixiang Tu 4ff169c8ad refactor(cua): refine application control risk notice 2026-11-03 21:05:16 +08:00
Yixiang Tu ba3c339cc7 fix(cua): dismiss pointer-focused risks and refine localized notice 2026-11-03 21:05:16 +08:00
Yixiang Tu 882b8b406f test(client): cover risk card focus and dismissal paths 2026-11-03 21:05:16 +08:00
Yixiang Tu 85da1c0f66 fix(client): remove close button from app risk hover card 2026-11-03 21:05:16 +08:00
Yixiang Tu c03f27db21 fix(client): show formatted app risks on hover and correct approval docs 2026-11-03 21:05:16 +08:00
Yixiang Tu fdf8f140ba feat(cua): clarify application approval risks and session choice 2026-11-03 21:05:16 +08:00
_Kerman 23b678df13 Merge pull request #7579 from deepseek-harness/xtr/ptc-timeout-step-1
feat(ptc): 协作停止保留状态并提供宿主重置
2026-11-03 21:00:19 +08:00
ihsiang 15e53db1bc Merge pull request #7474 from deepseek-harness/Ihsiang/ui-design-standards
docs: 统一 UI 设计 skill 与审查流程
2026-11-03 20:54:33 +08:00
_Kerman 9314e2de16 Merge remote-tracking branch 'origin/master' into xtr/ptc-timeout-step-1 2026-11-03 20:45:34 +08:00
_Kerman e923f8a353 test(ptc): refresh the script-imports tool schema after the stop-preservation change 2026-11-03 20:42:11 +08:00
_Kerman b0dd2f044e Merge pull request #7770 from deepseek-harness/fix/ptc-run-code-call-limit
fix(ptc): 按超时预算限制 run_code 工具调用总数
2026-11-03 20:40:14 +08:00
yixiangihsiang a8eaa9e29e docs: route icon interactions through UI design entry 2026-11-03 20:39:04 +08:00
yixiangihsiang 6299923878 docs: clarify responsive UI skill routing 2026-11-03 20:39:04 +08:00
yixiangihsiang 47e4bbe3db docs: expose UI design references through one skill 2026-11-03 20:39:03 +08:00
yixiangihsiang f9a806c0f1 docs: integrate UI review cases and address review feedback 2026-11-03 20:39:03 +08:00
yixiangihsiang 4d7a673e32 docs: clarify minimum UI inspection requirements 2026-11-03 20:38:25 +08:00
yixiangihsiang 491251f618 docs: scale UI review effort to change impact 2026-11-03 20:38:25 +08:00
yixiangihsiang 013a486fc5 docs: preserve actionable UI conditions and examples 2026-11-03 20:38:25 +08:00
yixiangihsiang 7647b11542 docs: retain practical alignment and spacing examples 2026-11-03 20:38:25 +08:00
yixiangihsiang 02e7591591 docs: condense UI skills while preserving design rules 2026-11-03 20:38:25 +08:00
yixiangihsiang 34740e6db0 docs: consolidate UI skill rules and geometry guidance 2026-11-03 20:38:25 +08:00
yixiangihsiang cb6834f011 docs: define UI design decisions and review workflow 2026-11-03 20:38:25 +08:00
imccyu 54cb44c006 Merge pull request #7792 from deepseek-harness/fix-goal-disarmed-zh-label
fix(ui-goal): tell an inactive goal from a paused one in Chinese
2026-11-03 20:30:41 +08:00
_Kerman 31f783bb90 fix(ptc): word the call-limit diagnostic in plain language
Pass the per-minute budget as a third value and describe it as
"per minute of its timeout" instead of naming the configuration field.
2026-11-03 20:22:30 +08:00
_Kerman cea324f719 fix(ptc): report the per-minute call budget with the limit
The model-visible limit diagnostic only stated the timeout-derived total,
so the number could not be traced back to maxSubCallsPerMinute.
2026-11-03 20:17:01 +08:00
_Kerman ab2bee0051 Merge remote-tracking branch 'origin/master' into xtr/ptc-timeout-step-1 2026-11-03 20:12:59 +08:00
creatixchu 9e761119bb test(release): share standalone helper fixture setup 2026-11-03 17:42:25 +08:00
creatixchu 681cf973e2 fix(release): verify helper CLI execution on clean checkouts 2026-11-03 17:29:37 +08:00
zhc 13a8cc11c7 refactor(client): move menu target kinds to their owners
Address review on context-menu target ownership and MenuRow typing.

- `ui-workspace` declares the `workspace` target and `ui-conversation`
  declares `sessionHeader`; `ui-context-menu` no longer depends on
  `@deepseek-ai/dsh-workspace`.
- Remove `buttonTargetKey`. A shared button now toggles only the popup it
  opened, tracked by its instance id, so the menu service needs no
  per-kind identity rule.
- Name the optional-or-undefined props helper in `MenuRow`, document its
  package-internal props, and replace the `MenuDecoration` component with a
  typed render helper.
2026-11-03 17:27:32 +08:00
Dudu-0223 7c03d2a267 Merge pull request #7772 from deepseek-harness/fix/wait-agent-code-guidance
fix(subagent): prevent repeated wait_agent wakeups within code calls
2026-11-03 17:14:56 +08:00
creatixchu 41beadfa2a fix(release): verify helper producers and preserve rerun artifacts 2026-11-03 17:09:19 +08:00
Yichen Jiang 6f84e118b1 Merge remote-tracking branch 'origin/master' into worktree/continue-generation-after-failure
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-11-03 16:48:34 +08:00
winewill df37050b48 Fail visibly when website preparation outputs are missing 2026-11-03 16:09:48 +08:00
Dudu-0223 fd8dfa270d test(subagent): refresh naming snapshots against current base 2026-11-03 16:02:27 +08:00
80cec4bde3 refactor(desktop): spell out the macOS Edit menu copy
Co-authored-by: std4453 <[email protected]>
Co-authored-by: 07akioni <[email protected]>
2026-11-03 15:57:19 +08:00
Dudu-0223 7b2d7841dd Merge master into subagent naming fixes 2026-11-03 15:52:55 +08:00
Yichen Jiang a703e9e4b0 fix(forge): address review of the base branch picker
Identify a local review comparison by its compared ref instead of the
displayed base name. Branch records stored before this change appear in
the automatic comparison again, and comparisons that share a name, such
as a worktree start and a local branch, stay apart.

List base branch refs within maxDiffBytes, so a repository with more refs
than maxOutputBytes allows keeps a working menu, and document the
remaining limit.

Move the merge button fill toward the label color so its label reaches
4.5:1 contrast in both themes, name the automatic menu group, and drop an
unused DOM attribute.

Refs #7781
2026-11-03 15:52:36 +08:00
Dudu-0223 6c0b6c7aa3 fix(subagent): validate persisted names and clarify presentation 2026-11-03 15:51:40 +08:00
creatixchu 3f579d45eb fix(release): bind helper builds to the Actions run commit 2026-11-03 15:49:44 +08:00
winewill 2cda0156a8 Avoid unsafe JSON access in website recovery tests 2026-11-03 15:46:01 +08:00
imccyu 36626c7dfa Merge pull request #7700 from deepseek-harness/refactor/css-anchored-panels
refactor(client): position floating panels with CSS anchors
2026-11-03 15:43:31 +08:00
winewill b941eded7c Retain completion evidence and refresh repaired website metadata 2026-11-03 15:39:11 +08:00
Magolor d27dc27538 fix(ui-conversation): rebuild the conversation window after a failed incremental flush (#7537)
* fix(ui-conversation): rebuild the conversation window after a failed incremental flush

* fix(ui-conversation): keep the original failure when the rebuild also fails

* test(ui-conversation): type the recovery probe view for type-aware lint

* fix(ui-conversation): finish the rebuild inside flush and report it with ConversationUpdateError

* test(ci): retain process identity while awaiting runner recovery
2026-11-03 15:37:22 +08:00
creatixchu 0ea2846c3e fix(release): keep explicit helper reuse in the publish form 2026-11-03 15:36:49 +08:00
winewill 3d1e011ea5 Close publication qualification review gaps 2026-11-03 15:34:14 +08:00
Yichen Jiang 00fbab4c14 Merge remote-tracking branch 'origin/master' into worktree/continue-generation-after-failure
# Conflicts:
#	docs/persistence-schema.json
2026-11-03 15:28:52 +08:00
imccyu 8bd073392e Merge pull request #7798 from deepseek-harness/worktree-sync-dsh-0.2.2-to-master-1103b
release(dsh): sync 0.2.2-alpha.3 back to master
2026-11-03 15:27:29 +08:00
creatixchu 447e1b88aa docs(release): preserve the existing tag-check description 2026-11-03 15:27:24 +08:00
winewill 797b8b4455 Preserve installer filenames in fixed website downloads 2026-11-03 15:27:24 +08:00
creatixchu 8454ed9771 docs(release): shorten CUA reuse input labels 2026-11-03 15:26:36 +08:00
winewill 8ecdc76b41 Merge master and align desktop publication recovery 2026-11-03 15:20:56 +08:00
creatixchu 47d7788fe5 perf(release): skip run details for nonmatching helper artifacts 2026-11-03 15:15:28 +08:00
imccyu 24db102f15 release(dsh): align master packages with 0.2.2-alpha.3 2026-11-03 15:13:20 +08:00
imccyu f590c13c92 Merge master into worktree-sync-dsh-0.2.2-to-master-1103b 2026-11-03 15:10:23 +08:00
creatixchu 8577f42832 fix(release): declare dependency-free packaging settings reader 2026-11-03 15:08:12 +08:00
creatixchu aa035e6845 feat(release): reuse CUA helper builds with matching source bytes 2026-11-03 15:05:26 +08:00
Dudu-0223 0d18d256c3 Merge master and simplify wait_agent input guidance 2026-11-03 14:51:55 +08:00
34d1ff94f7 fix(desktop): localize macOS Edit submenu titles
Co-authored-by: std4453 <[email protected]>
Co-authored-by: 07akioni <[email protected]>
2026-11-03 14:43:04 +08:00
Dudu-0223 19e5a95802 Merge pull request #7773 from deepseek-harness/feat/sdk-team-mode
feat(team): support enabling Team mode through SDK Cordis patches
2026-11-03 14:32:04 +08:00
Stevring 82b953acc5 Merge master into refactor/css-anchored-panels 2026-11-03 14:26:17 +08:00
Dudu-0223 f2c8c243e7 Merge master and refresh the MCP wait_agent snapshot 2026-11-03 14:23:51 +08:00
imccyu 85c775322f fix(ui-goal): tell an inactive goal from a paused one in Chinese
The Chinese GoalBar used 已暂停的目标 both for a durably paused goal and for an
active goal whose automatic continuation is disarmed, the state every active
goal has after a restart. Users could not tell whether a goal had been paused.
The disarmed label now reads 待继续的目标; paused goals keep 已暂停的目标.

Refs #7757
2026-11-03 14:20:39 +08:00
Dudu-0223 8223beef55 docs(subagent): explain wait input delivery after code execution 2026-11-03 14:14:56 +08:00
Yichen Jiang bc98ceb69f test(api): include retry eligibility in default session summaries 2026-11-03 14:12:21 +08:00
Yichen Jiang 2fb4ea0bec Merge origin/master into worktree/continue-generation-after-failure 2026-11-03 14:08:46 +08:00
imccyu c18851aa30 Merge pull request #7780 from deepseek-harness/worktree-sync-dsh-0.2.2-to-master-1103
release(dsh): sync release/dsh-0.2.2 back to master (#7704)
2026-11-03 14:07:24 +08:00
Yichen Jiang e2f5686a13 Merge pull request #3372 from deepseek-harness/feat/mcp-manager-core
feat(mcp): 支持插件注册与 Remote 管理的 MCP 服务器
2026-11-03 06:07:04 +00:00
Dudu-0223 8c523d9be1 refactor(team): remove startup step-history guard 2026-11-03 14:06:44 +08:00
Yichen Jiang c9a4e0033e feat(forge): give the merge button the product accent and a solid caret
The merge button of an open change request used the near-black shared
primary fill and an outline chevron. It now fills with the product's
brand blue, with ink that stays readable in light and dark themes, and
shows a solid caret that points up while its menu is open.

Refs #7781
2026-11-03 14:05:58 +08:00
winewill ece8f7cb77 Merge pull request #7495 from deepseek-harness/feat/desktop-update-channels
feat(desktop): 在设置中切换更新渠道
2026-11-03 14:03:04 +08:00
09f5e52fdd feat(desktop): bind Paste and Match Style to Shift+Command+V on macOS
Refs #7782

Co-authored-by: std4453 <[email protected]>
Co-authored-by: 07akioni <[email protected]>
2026-11-03 14:02:29 +08:00
gengruilin 385879882c test(mcp): refresh the template snapshot for master's read and web_search guidance 2026-11-03 13:52:46 +08:00
Yichen Jiang 61addcbcb9 Merge remote-tracking branch 'origin/master' into worktree/forge-diff-base-branch-4ffd61 2026-11-03 13:49:51 +08:00
Dudu-0223 9f57925000 Merge remote-tracking branch 'origin/master' into feat/subagent-catalog-names
# Conflicts:
#	docs/persistence-schema.json
#	docs/tool-catalog.i18n.yaml
2026-11-03 13:49:18 +08:00
Yichen Jiang fcc1becf11 feat(forge): choose the base branch of local changes
The Branch scope of the local changes view compared only with the
automatic fork point. Its header now names the checked-out branch and a
searchable base branch menu that lists the automatic fork point, then
remote-tracking and local branches; a chosen branch compares with its
merge base with HEAD. The choice lives in the view tab, a missing or
unrelated branch offers the default instead of a retry, and local Branch
review quotes name the base they were compared with.

Refs #7781
2026-11-03 13:49:13 +08:00
Dudu-0223 dd9719f67a fix(team): initialize configured mode before child activation ownership 2026-11-03 13:39:02 +08:00
gengruilin d9d3b80fe3 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-11-03 13:37:58 +08:00
imccyu c08621b991 Merge master into worktree-sync-dsh-0.2.2-to-master-1103 2026-11-03 13:32:45 +08:00
Stevring 0667efe813 Merge master into refactor/css-anchored-panels 2026-11-03 13:32:06 +08:00
imccyu 70d9b57f5c Merge pull request #7565 from deepseek-harness/feat/web-search-fetch-settings
feat(web): 网页搜索与获取设置页分开选择 Search 与 Fetch,Search 默认改为 DeepSeek Search API
2026-11-03 13:31:41 +08:00
gengruilin df078ddc82 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-11-03 13:31:23 +08:00
Dudu-0223 a3baa4d6e4 Merge remote-tracking branch 'origin/master' into feat/sdk-team-mode
# Conflicts:
#	docs/persistence-schema.json
2026-11-03 13:29:41 +08:00
Dudu-0223 0cb47b9d03 fix(subagent): integrate root-call scoped wait protection
Integrate PR #7774 with one-shot code guidance and matching snapshots. Cover distinct nested call IDs sharing a root and concurrent model calls with pending input.
2026-11-03 13:29:33 +08:00
winewill a4135f1f2f fix(desktop): use the system ZIP writer and verify archive failures 2026-11-03 13:23:18 +08:00
winewill 3c46127816 Merge remote-tracking branch 'origin/master' into feat/desktop-update-channels 2026-11-03 13:20:42 +08:00
winewill 0753977b60 test(desktop): assert structured settings failures 2026-11-03 13:20:35 +08:00
winewill 5f1811c91c Merge pull request #7709 from deepseek-harness/fix/windows-cli-short-path
Fix Windows CLI startup through short paths
2026-11-03 13:07:23 +08:00
yudshj 3859913c68 feat(web): 网页搜索与获取设置页分开选择 Search 与 Fetch,Search 默认改为 DeepSeek Search API 2026-11-03 13:04:26 +08:00
Dudu-0223 7f9d01007f Merge pull request #7769 from deepseek-harness/fix/team-task-workflow-prompt
fix(team): restore pre-refactor shared-task guidance
2026-11-03 13:01:56 +08:00
Stevring 48b2f9adc0 fix(client): keep model menu anchored during page zoom 2026-11-03 12:57:46 +08:00
winewill 6a3c52c92b docs(desktop): record the updated archive download instructions 2026-11-03 12:55:07 +08:00
winewill 912e0383fb fix(desktop): preserve modification times in installer artifacts 2026-11-03 12:54:24 +08:00
winewill 5b6366f5bf Type the canonical CLI module import 2026-11-03 12:51:08 +08:00
winewill cbe275fe99 Keep embedded CLI imports on the carrier module tree 2026-11-03 12:51:08 +08:00
winewill 8632896e84 Resolve Windows CLI entry aliases before loading runtime modules 2026-11-03 12:51:08 +08:00
winewill d7ca2e0ac2 fix(desktop): keep configuration recovery dialog interactive 2026-11-03 12:50:36 +08:00
_Kerman 03e654e8a5 Merge remote-tracking branch 'github/master' into fix/ptc-run-code-call-limit
# Conflicts:
#	docs/tool-catalog.i18n.yaml
2026-11-03 12:46:41 +08:00
_Kerman 4f790c874d fix(ptc): address call allowance review feedback 2026-11-03 12:45:28 +08:00
Dudu-0223 2a7d2f38fb Merge branch 'master' into fix/team-task-workflow-prompt 2026-11-03 12:43:57 +08:00
imccyu b3f301e573 Revert "fix(settings): show saved API keys and preserve environment detection…" (#7775)
This reverts commit 9bb49604b63bb5d50e879bcf63c9f5cb2afbd737.
2026-11-03 12:35:13 +08:00
imccyu 06bf7ce024 Revert "fix(settings): show saved API keys and preserve environment detection…"
This reverts commit 9bb49604b63bb5d50e879bcf63c9f5cb2afbd737.
2026-11-03 12:33:44 +08:00
imccyu e4ac64c9c7 Merge pull request #7734 from deepseek-harness/worktree-sync-dsh-0.2.2-to-master
release(dsh): sync release/dsh-0.2.2 back to master
2026-11-03 12:28:59 +08:00
Dudu-0223 bed550f81e test(subagent): sync wait guidance across SDK and Web snapshots 2026-11-03 12:26:34 +08:00
_Kerman 21aa215092 fix(ptc): terminate programs that exhaust the tool call allowance 2026-11-03 12:19:55 +08:00
Dudu-0223 c3fda8ed51 docs(subagent): retain wait_agent input examples 2026-11-03 12:16:55 +08:00
imccyu a3f6f94c1e test(snapshot): refresh fixtures for the 10-query web_search default 2026-11-03 12:13:05 +08:00
Dudu-0223 cc673ec174 fix(team): identify the coordinator as the root agent 2026-11-03 12:12:37 +08:00
Dudu-0223 e180fce05a feat(team): enable new root Team mode through Cordis configuration 2026-11-03 12:09:30 +08:00
_Kerman 9ac3ef2ca7 fix(ptc): derive tool call allowance from runtime timeout 2026-11-03 12:06:58 +08:00
Dudu-0223 1f522605ed fix(subagent): clarify one-shot waits from code 2026-11-03 12:06:10 +08:00
imccyu aa4774e685 Merge master into worktree-sync-dsh-0.2.2-to-master 2026-11-03 12:05:47 +08:00
涂轶翔 012a7a2e4a Merge pull request #6958 from deepseek-harness/fix/6953-deepseek-deferred-tools
fix(llm): rebuild tool baselines after removals
2026-11-03 11:59:07 +08:00
Dudu-0223 f7c184ffd1 fix(team): restore the original shared-task policy wording 2026-11-03 11:58:27 +08:00
winewill 0535b85a1d fix(desktop): recover update settings under mandatory policy 2026-11-03 11:54:27 +08:00
Dudu-0223 48e21ac6d7 fix(team): guide agents to create and track shared tasks 2026-11-03 11:49:44 +08:00
_Kerman d3e3a2bce9 fix(ptc): cap nested tool calls per run_code invocation 2026-11-03 11:49:38 +08:00
imccyu ef5f28d0fb Merge master into worktree-sync-dsh-0.2.2-to-master 2026-11-03 11:47:40 +08:00
winewill eb5b32c9c9 fix(desktop): report and recover saved channel apply failures 2026-11-03 11:47:04 +08:00
Yichen Jiang c1ab3a24b0 refactor(web): name failed generation recovery as retry 2026-11-03 11:42:35 +08:00
Turtle 582c334472 Merge pull request #7022 from deepseek-harness/turtle/websearch-query-limit-96c308027005
feat(web): raise web_search defaults to 10 queries and 10 sources
2026-11-03 11:41:39 +08:00
winewill 224fe9a658 fix(desktop): distinguish committed and uncertain settings saves 2026-11-03 11:26:57 +08:00
Ziya 27fb833732 Merge pull request #7712 from deepseek-harness/fix/page-card-remove-keep
fix(creation-recommendations): 个性化页面卡片去掉“保留”,装好后只提供撤销和恢复
2026-11-02 19:18:45 -08:00
ZiyaZhang 87f2531360 Merge remote-tracking branch 'origin/master' into fix/page-card-remove-keep 2026-11-02 19:00:32 -08:00
Yichen Jiang 2ba385e9c9 test(web): align continuation receipt accessibility snapshot 2026-11-03 10:54:04 +08:00
Yichen Jiang 3d47094702 test(web): edit continuation drafts through keyboard input 2026-11-03 10:53:03 +08:00
Yichen Jiang 9eee00fa7d fix(web): show continuation request receipts 2026-11-03 10:50:46 +08:00
Stevring dd8fb95670 Merge master into refactor/css-anchored-panels 2026-11-03 10:50:14 +08:00
imccyu 6e77ea0131 feat(web): boot past failed optional client plugins (#7733)
* feat(web): boot past failed optional client plugins

The Web client boot audit now follows Host startup strictness. An
inactive entry on the global required list (module system, Session
transport, renderer, application frame, Session view, and sidebar)
still rejects startup and the report marks the required entries.
Other inactive entries produce one console warning and the
application mounts without them. The boot page no longer draws a
failed entry before the audit decides.

* fix(web): audit boot entries whose creation rejects and name activation errors

ClientEntries.start() now logs a row whose Loader creation rejects, such
as a module that exports no plugin, and leaves it inactive for the boot
audit, as the Host Loader group does. The audit awaits only failed
fibers to report their recorded activation error. Notes, READMEs, and
the Web client subsystem page describe the browser boot audit; tests
cover invalid exports, carrier presentation of optional failures, and
required names absent from the boot graph.

* test(web): fail test carriers and e2e on inactive client entries

The product boot only warns about inactive entries outside its required
list, but tests must still surface every plugin that fails. TestClient
and createClientTest now require every roster entry to activate unless
a case passes allowInactiveEntries. The watchConsole tripwire, the
real-host smoke, and the assembled jsdom boot treat a web boot warning
as a failure with no opt-out.

Docs: the Web README names Session control, notes that an entry without
a fiber is reported as an import failure like the Host audit's "failed
to import", and the startup strictness note lists the browser tests.
2026-11-02 16:20:50 +00:00
gengruilin 9cd3fbec45 revert(webworker): drop the cross-spawn refusal from the preview worker
The refusal kept the MCP manager loading in the browser preview while
the worker had no `global` binding, which cross-spawn's isexe dependency
reads at evaluation. The worker now publishes `global`, so the real
package loads. A stdio MCP server still cannot start in the preview: it
now fails through the worker child_process instead of an explicit
refusal, and HTTP MCP servers are unaffected.
2026-11-02 23:31:27 +08:00
Dudu-0223 606729b5b5 test(web): keep the queue-actions throughput golden
Refreshing the Web lane rewrote this golden without the throughput suffix,
which no change in this branch affects.
2026-11-02 23:02:22 +08:00
Dudu-0223 a4c933ad1f fix(subagent): fail unmatched name paths and tighten name admission
Review round on the naming change.

`resolveName` now distinguishes an address from a Session id: a
slash-separated address never is one, so an unmatched path fails with
AGENT_NAME_NOT_FOUND instead of reaching member lookup as an unknown id,
while a single token keeps the Session-id fallback. Path resolution also
treats external and unknown children as leaves, so a name below an external
run reports the same clear failure instead of surfacing a session-query error
for an identity that owns no local Session.

Native V4 admission rejects a repeated sibling name in own catalog facts,
symmetrically with its duplicate-child check, so a log that would make name
lookup ambiguous fails before any member is resolved.

`SiblingNameReservations.release()` checks that it still owns the map entry,
so a stale or repeated release cannot drop a newer reservation for the same
creator.

`list_agents` explains corrupt, unsupported, and unavailable: they report that
the row's own descendants could not be read, and the row remains a member the
caller can message. The earlier wording restricted messaging to running or
inactive rows, which excluded addressable members.

`isAgentName` stays module-local; the package entry exports only
ROOT_AGENT_NAME, matching its current callers.

Package READMEs, the subsystem page, the Agent Note, and the generated API
catalogue follow; recorded-session, CLI, and Web expectations are refreshed.
2026-11-02 22:43:01 +08:00
63026c7a5c feat(client): 左侧栏纵向空间优化,工作区滚动吸顶优化 (#7337)
* feat(client): share sidebar scrolling and dock workspace header

* fix(client): keep shared sidebar scrolling vertical

* fix(client): match sticky workspace header to sidebar fill

* fix(client): stabilize transparent sticky sidebar and compact row layout

* refactor(ui-primitives): keep sticky section observer internal

* fix(ui-primitives): limit row animations to visible scrollports

* feat(ui-workspace): drive sticky clipping with scroll timelines

* refactor(ui-primitives): bound row animations by the window

* feat(client): hand off sidebar wheel scrolling to the workspace list

The workspace list scrolls inside its own one-viewport section below the toolbar instead of sliding under a clipped sticky toolbar. The sidebar viewport scrolls the navigation away first; ui-sidebar's scroll hand-off splits each wheel event between the viewport and the section scroller, which the occupant binds through the slot-level useSectionScroller hook supplied by hookContext.

* test(client): cover the sidebar scroll hand-off and align the list scrollbar

Test-owned sidebar roles declare the slot-level scroller inject and pass its hookContext. Unit tests cover the hand-off split, wheel filtering, and listener rebinding; the e2e scenario checks one wheel event crossing from the navigation into the list and back. The list area reaches 2px from the sidebar edge for its scrollbar while rows keep the 8px inset. The unused clip module, its tests, and the sticky clearance variable are removed; READMEs and the slot catalog describe the nested scrolling.

* refactor(ui-primitives): restore MenuGroup sticky observation

The section observer was extracted for a sticky workspace toolbar that nested scrolling no longer has, so MenuGroup is its only consumer again. MenuGroup and its tests return to the master implementation.

* fix(client): keep native sidebar scrolling away from the hand-off boundary

The hand-off splits a wheel gesture in JavaScript only when its first event must move the sidebar viewport or starts outside the section scroller, and then keeps that gesture, including momentum, until wheel events pause for 100ms. Gestures the section scroller can take alone stay native, an uncancelable native gesture that reaches the section top passes the rest to the viewport, and events another listener canceled are left alone. The section is at most one viewport tall, and exactly that tall while search is expanded, so a short list leaves the navigation in place. Stale absence assertions and unused viewport declarations are removed; the READMEs document portals and the data-sidebar markers.

---------

Co-authored-by: Yif <[email protected]>
Co-authored-by: imccyu <[email protected]>
2026-11-02 22:27:26 +08:00
Yif 52ba88132e Merge master and address shared menu ownership review 2026-11-02 22:05:54 +08:00
Yixiang Tu 624c3e91a9 fix(llm-deepseek): expose all-deferred request failures 2026-11-02 22:04:53 +08:00
Xu Hanxiang f54967a90d Merge pull request #7543 from deepseek-harness/feat/sandbox-policy-caps
feat(liveshare): run PTC programs on the shared surface
2026-11-02 13:20:18 +00:00
Dudu-0223 12cae2a88f Merge remote-tracking branch 'origin/master' into feat/subagent-catalog-names
# Conflicts:
#	snapshots/session/ptc-script-imports/session.v4.jsonl
#	snapshots/session/ptc-script-imports/system-prompt.expected.md
2026-11-02 21:09:12 +08:00
gengruilin 30bf5733d6 refactor(mcp-manager): publish the manager as ctx.mcpManager
The `ctx.mcpServers` key reads as a collection of servers, but the
service manages that collection, and ACP already uses `mcpServers` for
a plain server list. Publish the service as `ctx.mcpManager`, matching
the package name, the `mcp-manager/` event prefix, and
`ctx.pluginManager`. Remote endpoints become `mcpManager/*`; the class
keeps its name, McpServerManager.
2026-11-02 21:01:42 +08:00
Xu Hanxiang 4658cba7e0 Merge pull request #7564 from deepseek-harness/fix/http-proxy-http1
fix(liveshare): 客户端优化
2026-11-02 12:59:03 +00:00
imccyuandimccyu bedcb559ac test(snapshot): refresh the ptc-script-imports system prompt (#7738)
* test(snapshot): refresh the ptc-script-imports system prompt

The pinned prompt kept the fs-observation wording from before the apply_patch guidance
landed, so the scenario failed on the prompt after the session records matched.

* test(snapshot): carry the current tool declarations into the ptc-script-imports prompt

The pinned prompt still listed the tools from before `apply_patch` and `wait_agent`
landed and before the subagent tool docs changed; the tool block now matches the
current declarations, the same 51 added and 12 removed lines CI reported.

---------

Co-authored-by: imccyu <[email protected]>
2026-11-02 20:55:02 +08:00
Yixiang Tu a5c44b9384 test(llm-deepseek): refresh snapshots for root agent identity 2026-11-02 20:34:08 +08:00
Yixiang Tu 1f3bc8b22b fix(llm): rebuild tool history for additions to an empty set 2026-11-02 20:30:57 +08:00
Yixiang Tu 523e741a31 test(llm-deepseek): cover all-deferred requests and separate tool updates 2026-11-02 20:30:57 +08:00
Yixiang Tu 9d53909bcf docs(llm): clarify deferred tool activation and placeholder effects 2026-11-02 20:30:57 +08:00
Yixiang Tu be93b94e6e docs(notes): state that every tool removal rebuilds the baseline 2026-11-02 20:30:57 +08:00
Yixiang Tu be6ed7f0aa fix(llm): rebuild addition-only history on every removal 2026-11-02 20:30:57 +08:00
Yixiang Tu f8217a4fe1 fix(llm): rebuild addition-only baselines after tool removals 2026-11-02 20:30:56 +08:00
Yixiang Tu 156dad153d fix(llm-deepseek): anchor all-deferred tools with a placeholder
Preserve deferred activation when every initial tool has been removed. Add serializer, provider, live-API, and recorded-session regression coverage.

Fixes #6953
2026-11-02 20:30:56 +08:00
imccyuandimccyu 07f2b259e7 ci: run the merge checks on release branches, and fix the ptc-script-imports fixture (#7732)
* ci: run the merge checks on release branches too

All three `Policy` workflows filtered on `master`, so a pull request into `release/dsh-*`
got no `Master Change` and no `Pre-Merge Check`, and its review check only refreshed when
somebody submitted a review. The checks already take their base branch from the event, so
the filters now name the release branches as well.

* ci: limit the release branch filter to x.y.z

`release/dsh-*` also matched prerelease branches such as `release/dsh-0.1.2-alpha.4`;
the filter now spells the version digits, so only `release/dsh-x.y.z` counts.

* test(snapshot): carry the subagent identity section into the ptc-script-imports scenario

The runtime context gained a `subagent:identity` section and every session fixture but
this one was regenerated, so replay failed with a mismatch on the added section.

(cherry picked from commit c7a4119965cb26c69fd8fccbfd703c7d73351742)

---------

Co-authored-by: imccyu <[email protected]>
2026-11-02 20:12:14 +08:00
Stevring 0cb15241c0 fix(client): preserve CSS menu lifecycle and mobile bounds 2026-11-02 19:52:32 +08:00
ZiyaZhang ec7a4e9df5 fix(creation-recommendations): show Cancel beside the location while creating
Creating cards now put Cancel on the location row, where Undo and Restore
sit, instead of a separate row below.
2026-11-02 02:35:59 -08:00
Dudu-0223 26c9fcfe2e test(snapshot): refresh PTC imports after subagent naming merge 2026-11-02 17:53:24 +08:00
Dudu-0223 6644a234a5 Merge master into feat/subagent-catalog-names 2026-11-02 17:46:04 +08:00
Dudu-0223 d828bf20bc docs(subagent): correct delegation receipt metadata 2026-11-02 17:34:53 +08:00
_Kerman 5c9029b57e Merge pull request #6978 from deepseek-harness/xtr/ptc-runner-imports
feat(ptc-runtime-node): support runner-local import handlers
2026-11-02 17:24:05 +08:00
sjw1231 7e6ff762e9 build(client): let client bundles inline dsh-fs-patch
The apply_patch row now parses patches with dsh-fs-patch, which the
client bundle purity gate rejected as a cross-plugin value import. The
package is a pure parser with no runtime imports or shared state, so it
joins the inline-safe layers.

Refs #7696
2026-11-02 17:14:11 +08:00
winewill 88b01c24ea fix(desktop): discard stale channel check prompts and align error copy 2026-11-02 17:12:00 +08:00
gengruilin 62c6a2658d Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-11-02 17:11:12 +08:00
sjw1231 d85a922e12 fix(ui): read apply_patch files with the executor's parser
Review found the client header scan disagreed with dsh-fs-patch on
indented headers, Move to placement, and path whitespace, so a patch the
executor applied could list no files. The row now uses parsePatch, links
files only after a successful result so running rows keep their shared
highlight, keys links by header line, and shares the list separators.
The tool.title.applyPatch locale key returns with each locale's Edit
text, and the ui-tool README describes the new row.

Refs #7696
2026-11-02 17:06:56 +08:00
Chinesezjc fdff707b82 Merge pull request #7303 from deepseek-harness/fix/read-guidance-line-ranges
fix(prompt): name line ranges and shell readers in the read guidance
2026-11-02 16:57:54 +08:00
ZiyaZhang e0129468c3 fix(creation-recommendations): offer only Undo on installed page cards
Keep only recorded a `kept` phase: the page was already installed when the
card offered it. Installed cards now show Undo beside the location, and
removed cards show Restore there; the "You can remove it at any time" line
is gone. Decisions that earlier builds recorded as `kept` still read as
installed and keep Undo.
2026-11-02 00:56:49 -08:00
Chinesezjc ec587a015d test(snapshot): carry the read guidance into the apply-patch scenarios
The apply-patch scenarios were recorded before this restore; their shared
system prompt sidecar still shows the old read sentence.
2026-11-02 16:42:47 +08:00
Dudu-0223 36a0cab333 Merge pull request #7533 from deepseek-harness/fix/subagent-model-selection-guidance
fix(subagent): 推理强度要求完整路由,显式选路使用模型默认强度
2026-11-02 16:30:49 +08:00
Yichen Jiang 309f438354 docs(web): record empty retry message validation 2026-11-02 16:26:58 +08:00
sjw1231 ca3d19aafb feat(ui): open each file a multi-file apply_patch row names
A patch naming several files, or a move, showed its file list as plain
text, so none of the files could be opened from the collapsed row. Each
added, updated, or moved-to file is now its own link with its own file
context menu; deleted files and move sources stay plain text.

Refs #7696
2026-11-02 16:23:29 +08:00
Chinesezjc 401bfa8b3c Merge branch 'master' into fix/read-guidance-line-ranges 2026-11-02 16:22:24 +08:00
CreatixChu 4eb2118dba Merge pull request #7515 from deepseek-harness/feat/cua-optional-bundle
feat(cua): 随 DSH 分发 Computer Use,并在实验性插件列表中显示
2026-11-02 16:21:57 +08:00
CreatixChu d203f5c418 Merge pull request #7516 from deepseek-harness/feat/cua-desktop-helper
fix(desktop): 随附并独立启动 macOS Computer Use helper
2026-11-02 16:19:16 +08:00
Turtle 58a3febaf2 Merge pull request #7663 from deepseek-harness/agent/stale-auth-cookie-431-6faa8d4c5eb6
fix(desktop): stop accumulating Host auth cookies across launches
2026-11-02 16:15:56 +08:00
Chinesezjc 1d2e1c6be5 Merge remote-tracking branch 'origin/master' into fix/read-guidance-line-ranges
# Conflicts:
#	packages/fs/tool-fs/tests/tools.spec.ts
2026-11-02 16:14:57 +08:00
Yichen Jiang 9ac67a545d test(web): use the current subagent session fields 2026-11-02 16:14:13 +08:00
Dudu-0223 05225052f9 docs(subagent): clarify discovery covers explicit model selection 2026-11-02 16:13:04 +08:00
Yichen Jiang 998ebe4e74 fix(web): adapt retry rendering and projection tests to current types 2026-11-02 16:09:19 +08:00
Stevring 953500de9f fix(client): preserve layout margins in anchored menu candidates 2026-11-02 16:03:47 +08:00
Yichen Jiang fa9f77cc42 Merge remote-tracking branch 'origin/master' into worktree/continue-generation-after-failure
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.md
#	docs/persistence-catalog.zh.md
#	docs/persistence-changes/historical-formats/README.i18n.yaml
#	docs/persistence-changes/historical-formats/README.md
#	docs/persistence-changes/historical-formats/README.zh.md
#	docs/persistence-schema.json
#	packages/api/session-controller/src/types.ts
#	packages/api/session-controller/tests/session-projections.host.spec.ts
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/README.md
#	packages/client/ui-conversation/README.zh.md
#	packages/client/ui-conversation/package.json
2026-11-02 16:02:18 +08:00
Dudu-0223 a7f21a8dd6 fix(subagent): persist task owner names and simplify receipts 2026-11-02 16:01:05 +08:00
Yichen Jiang 818e8eced2 refactor(web): use empty retry user messages without loop changes 2026-11-02 15:58:09 +08:00
Stevring 33566bb43e test(client): update CSS menu and overlay margin coverage 2026-11-02 15:57:55 +08:00
Dudu-0223 65a87a5860 fix(subagent): clarify selection guidance and stabilize Web smoke capture 2026-11-02 15:52:24 +08:00
winewill c05a34e153 feat(desktop): reload update settings from configuration errors 2026-11-02 15:48:13 +08:00
sjw1231 822229c588 fix(tool-fs): update apply_patch tool description
Describe what the patch tool does and the patch text it expects, and
refresh the tool catalog and recorded schema snapshots to match.

Refs #7696
2026-11-02 15:26:37 +08:00
winewill f387930ebe docs(desktop): align channel guidance with saved settings 2026-11-02 15:17:46 +08:00
Dudu-0223 fbfc91bcba test(cli): follow agent names in profile e2e expectations 2026-11-02 15:12:56 +08:00
Dudu-0223 5a75c8b4a5 fix(subagent): require an explicit route for reasoning overrides 2026-11-02 15:06:10 +08:00
Dudu-0223 7301fa98b0 fix(subagent): state when to query child routes and when to omit them
The discovery tool presented itself as the place to inspect options before
selecting a child route, and rejected every provider or model outside the
Session's authorized routes, including providers that do not exist. A model
that skipped the no-argument call therefore had no way to learn the correct
ids or that omission was legal, and repeated identical denials.

Say that queries exist to override the child route, that omitting both route
fields inherits the parent route, that a field is omitted rather than sent
empty, and, in each optional route field, that omitting it inherits the
parent value.
2026-11-02 15:05:53 +08:00
sjw1231 ec76a667b1 feat(ui): name the files an apply_patch row edits
Collapsed apply_patch rows showed only the "Apply patch" title, so users
had to expand them to learn what changed. The row now uses the shared
Edit title and summarizes the Add/Update/Delete/Move headers once the
patch text is complete; a lone added or updated file is openable.

Refs #7696
2026-11-02 15:05:15 +08:00
Chinesezjc 73090b5e56 Merge pull request #7602 from deepseek-harness/turtle/plugin-detail-git-auth-525da1436301
fix(plugin-market): retry detail clone fetches over SSH after https is refused
2026-11-02 15:02:20 +08:00
Magolor 8d0c3b4633 fix(settings): show saved API keys and preserve environment detection (#7695)
* fix(settings): show saved API keys in provider editors

* test(settings): retain DeepSeek environment detection states

* fix(settings): classify saved-key reads and align browser expectations

* test(settings): cover saved keys under derived references

* test(settings): verify saved endpoints and keys on provider requests

* fix(settings): preserve credential drafts without changing authentication
2026-11-02 15:00:19 +08:00
Dudu-0223 ff563d07e0 feat(subagent): name subagents in their creator catalog
Models coordinate subagents through send_message, interrupt_agent, list_agents,
and Team task owners, but every one of those addressed a member by a
36-character Session id. Each new child now has a name, unique among its
creator's children, recorded as a field of the subagent/catalog entry the
creator already appends (payload version 3).

The Session id stays the only execution identity for routing, authorization,
persistence, task ownership, and navigation. A name is a model-facing
reference: 1-32 lower-case letters, digits, or hyphens starting with a letter,
never `lead`, so a UUID never parses as one. Omission selects the first free
`agent-N`; an explicit duplicate or invalid name fails before startup. The
creator reserves the name synchronously at admission and releases it when the
catalog entry commits or startup fails, so no second write, rollback protocol,
or optional naming service exists.

The root is `lead`, and a descendant's full name is its path from the root
without a `lead/` prefix, such as `reviewer/checker`. Sibling-unique segments
make full names tree-unique without a root registry, and cold resume rebuilds
a member's path from the catalogs along its creation chain. A member below an
unnamed historical creator keeps its Session id as its name. resolveName()
reads only the catalogs along the path and grants no authority.

Tools, runtime-authored text, and the Web client consume names: delegation
takes an optional name and returns { kind, subagentId, name }; send_message and
interrupt_agent take `agent`; list_agents rows carry name, status, and
description; Team task results report the owner's name while storage keeps
ownerId; identity runtime context, message framing, and settlement notices use
full names. Recorded sessions, CLI and Web expectations, the Python runtime
snapshots, bilingual docs, an upgrade guide, and an Agent Note follow.
2026-11-02 14:58:32 +08:00
creatixchu 7d9bc01a50 Merge updated master and helper identity into bundled Computer Use 2026-11-02 14:58:31 +08:00
creatixchu 4ac71ed747 Merge master with separate development helper identity 2026-11-02 14:55:17 +08:00
CreatixChu 0089109f00 Merge pull request #7538 from deepseek-harness/feat/cua-dev-signing
feat(cua): 区分开发 helper 身份,支持显式签名配置与开发者 skill
2026-11-02 14:50:57 +08:00
Stevring 2c990f0b14 refactor(client): position floating panels with CSS anchors 2026-11-02 14:43:58 +08:00
winewill 26c077aa5f test(desktop): verify settings recovery in the browser 2026-11-02 14:37:28 +08:00
winewill c0795c003a fix(desktop): explain current download filesystem failures 2026-11-02 14:32:06 +08:00
creatixchu a313ff916b docs(cua): proactively recommend developer signing identities 2026-11-02 14:31:28 +08:00
winewill 598006d608 fix(desktop): log obsolete update cleanup failures without blocking 2026-11-02 14:30:14 +08:00
winewill f578528f1f fix(desktop): guide recovery from invalid settings files 2026-11-02 14:28:37 +08:00
winewill d2bd3f3893 feat(desktop): rename stable update channel consistently 2026-11-02 14:27:21 +08:00
winewill 1c9a7e6a71 fix(desktop): preserve updates on repeated channel selection 2026-11-02 14:26:09 +08:00
winewill 8e30aa7bdc fix(desktop): simplify update channel description 2026-11-02 14:25:33 +08:00
Turtle 77ef574af7 Merge pull request #7691 from deepseek-harness/turtle/fix-math-copy-mojibake-815c3ff5d09c
fix(client): copy selected KaTeX math as TeX source
2026-11-02 14:24:55 +08:00
_Kerman a6e406c685 test(ptc): refresh snapshots for current shipped composition 2026-11-02 13:29:04 +08:00
Stevring dd124c5790 fix(client): position model picker with CSS anchors 2026-11-02 13:28:44 +08:00
_Kerman 56ff20d45f Merge remote-tracking branch 'origin/master' into xtr/ptc-timeout-step-1 2026-11-02 13:24:04 +08:00
gengruilin 683daea572 test(mcp): await the status reload and refresh the template snapshot after master sync 2026-11-02 11:56:58 +08:00
gengruilin be167ec989 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-11-02 11:43:57 +08:00
creatixchu 2bd4226dc9 fix(cua): preserve system keychain lock lifecycle 2026-11-02 11:41:57 +08:00
creatixchu d052805f96 fix(cua): preserve unowned files during developer signing setup 2026-11-02 11:37:23 +08:00
creatixchu 8de3a97f8d Merge origin/master into feat/cua-dev-signing 2026-11-02 11:19:00 +08:00
Turtle e17596cf1f Merge remote-tracking branch 'origin/master' into turtle/fix-math-copy-mojibake-815c3ff5d09c 2026-11-02 10:10:15 +08:00
fz a2119d6184 Merge pull request #7689 from deepseek-harness/fix/7688-locked-runtime-deploy
fix(python): 按锁文件部署 runtime 依赖
2026-11-02 10:03:39 +08:00
Turtle 0039a8879a fix(client): scope math copy to one Markdown document
Refs #7690
2026-11-01 22:00:57 +08:00
Turtle 898b2875b3 fix(client): copy selected KaTeX math as TeX source
Refs #7690
2026-11-01 21:47:19 +08:00
fz eab19efcf8 fix(python): preserve deployed dependency validation 2026-11-01 21:24:41 +08:00
fz a7d0d02146 fix(python): deploy the runtime from the workspace lockfile 2026-11-01 20:31:59 +08:00
imccyu e428c74d51 Merge pull request #7207 from deepseek-harness/feat/sidebar-plugin-pin-preview
feat(ui): 优化侧栏插件固定与 More 交互
2026-11-01 01:05:56 +08:00
imccyu 24ac78d283 fix(ui): show unpinned sidebar panels while More is absent 2026-10-31 22:47:22 +08:00
Dudu-0223andimccyu 0487699ae4 feat(subagent): share ID-based identity and collaboration guidance (#7519)
* feat(subagent): share identity and collaboration guidance

* fix(subagent): complete shared guidance snapshot expectations

* fix(subagent): match task descriptions to the user language

* refactor(subagent): retain shared guidance with Session ids

* test(subagent): recognize parent ids in shared identity

* Checkpoint ID collaboration guidance generated expectations

* Preserve historical Web schema sidecars

* fix(test-support): preserve job references during snapshot refresh

* test(subagent): refresh ID-only collaboration guidance outputs

* fix(subagent): rely on list_agents tool description

* fix(subagent): keep collaboration guidance consistent with live identity

* test(team): parse parent identity from shared guidance

* test(subagent): address parent from shared identity context

* test(subagent): refresh compiled Web guidance snapshots

* test(subagent): refresh browser collaboration context fixtures

* docs(subagent): attribute required identity to standard controls

* fix(subagent): clarify collaboration identity prompts

* test(subagent): remove duplicate PowerShell collaboration context

* refactor(subagent): match unavailable-root errors by name and code

---------

Co-authored-by: imccyu <[email protected]>
2026-10-31 22:38:07 +08:00
Yif 36e66f583e fix(ui): align sidebar focus coverage and chrome snapshots 2026-10-31 22:19:45 +08:00
Yif 5b559def7c feat(ui): customize sidebar plugin pins and More menu 2026-10-31 22:19:44 +08:00
Turtleandlsdsjy 8394c706fc test(desktop): cover legacy Host cookie cleanup failure
Co-authored-by: lsdsjy <[email protected]>
2026-10-31 20:22:50 +08:00
Turtleandlsdsjy 93bef27ec7 fix(desktop): remove legacy Host auth cookies from the default session
Earlier releases left one dsh-auth-* cookie per launch in the default
Electron session. Each Host start now removes them so other requests to
127.0.0.1 stop carrying them.

Co-authored-by: lsdsjy <[email protected]>
2026-10-31 19:55:51 +08:00
Turtleandlsdsjy 5868dbc357 fix(desktop): stop storing Host auth cookies in the Electron session
Each Desktop launch picks a random Host port and the native welcome client
authenticated through net.fetch with credentials: 'include', so the default
Electron session kept one port-named 30-day dsh-auth cookie per launch.
Cookies ignore ports, so every request carried all of them; after about 70
launches the Cookie header exceeded Node's 16 KiB limit, the Host answered 431,
and startup failed with 'Web authentication failed'.

The welcome RPC client and the account WebSocket now send the Cookie header
returned by authenticateWebHost explicitly and use no cookie jar.

Co-authored-by: lsdsjy <[email protected]>
2026-10-31 19:46:38 +08:00
Yifffan a1c3458e12 Merge pull request #7504 from deepseek-harness/feat-agent-teamwork-ui-polish
feat(web): improve agent collaboration panels and tool receipts
2026-10-31 11:10:03 +08:00
Dudu-0223 56e79b50ea Merge branch 'codex/stack-review-6288' into codex/stack-review-7504 2026-10-31 10:41:09 +08:00
Dudu-0223 82095d7f04 Merge branch 'codex/stack-review-6285' into codex/stack-review-6288 2026-10-31 10:40:47 +08:00
Dudu-0223 fe9cd80b28 revert: remove follow-up CI repairs 2026-10-31 10:40:31 +08:00
Yif cce6dca987 refactor(client): share menu positioning and remove duplicate tracking 2026-10-31 02:36:23 +08:00
Dudu-0223 b4c6a40270 Merge branch 'codex/stack-review-6288' into codex/stack-review-7504 2026-10-31 01:33:50 +08:00
Dudu-0223 0071ee8785 Merge branch 'codex/stack-review-6285' into codex/stack-review-6288 2026-10-31 01:33:20 +08:00
Dudu-0223 5a17181af2 perf(llm): avoid copying unchanged request histories 2026-10-31 01:32:46 +08:00
Dudu-0223 e02701365c Merge branch 'codex/stack-review-6288' into codex/stack-review-7504 2026-10-31 01:10:00 +08:00
Dudu-0223 f3bf53bfc7 Merge branch 'codex/stack-review-6285' into codex/stack-review-6288 2026-10-31 01:09:32 +08:00
Dudu-0223 c948b16d50 fix(ci): scope navigator styles and calibrate streaming budgets 2026-10-31 01:09:03 +08:00
Dudu-0223 45be4c1cb0 Merge branch 'codex/stack-review-6288' into codex/stack-review-7504 2026-10-31 00:38:23 +08:00
Dudu-0223 be1500b4b6 Merge branch 'codex/stack-review-6285' into codex/stack-review-6288 2026-10-31 00:35:45 +08:00
Dudu-0223 934a228d47 test(ptc): observe synchronous readiness before canceling a busy process 2026-10-31 00:34:52 +08:00
Dudu-0223 141a54efed fix(ci): keep benchmarks on their calibrated hosted runner 2026-10-31 00:31:48 +08:00
Dudu-0223 908005191d Merge branch 'codex/stack-review-6288' into codex/stack-review-7504 2026-10-31 00:15:31 +08:00
Dudu-0223 8e5fa7bd24 Merge branch 'codex/stack-review-6285' into codex/stack-review-6288 2026-10-31 00:14:38 +08:00
Dudu-0223 ff6960093a fix(ci): isolate benchmark measurement workers 2026-10-31 00:14:05 +08:00
Dudu-0223 b6a75e1f54 fix(ci): stabilize Goal clocks and dependency restoration 2026-10-31 00:09:40 +08:00
Dudu-0223 45ae1133bc Merge branch 'codex/stack-review-6288' into codex/stack-review-7504 2026-10-30 23:46:05 +08:00
Dudu-0223 127923cfec fix(client): expose complete subagent labels and total counts 2026-10-30 23:46:02 +08:00
Dudu-0223 67644e94ae Merge branch 'codex/stack-review-6285' into codex/stack-review-6288
# Conflicts:
#	.agents/notes/implemented/feature/2026-08-05-agent-teams.i18n.yaml
2026-10-30 23:42:48 +08:00
Dudu-0223 8178dd08c2 fix(team): align schema test budget and mode migration guidance 2026-10-30 23:42:08 +08:00
Dudu-0223 f5acd9ac51 docs(team): align task updates with inbox-only waits 2026-10-30 23:41:45 +08:00
_Kerman 5cf9348314 refactor(ptc-runtime-node): reuse sequential RPC evaluation lifecycle 2026-10-30 20:55:23 +08:00
_Kerman adc54201f3 fix(ptc-runtime-node): preserve existing native output behavior 2026-10-30 20:54:53 +08:00
_Kerman e3c9703eca refactor(tool-executor-ptc): limit reset coordination to owned resources 2026-10-30 20:49:13 +08:00
_Kerman 3ff5f9a034 refactor(ptc-runtime-node): separate interruption from termination 2026-10-30 20:27:25 +08:00
_Kerman 16d1566313 refactor(tool-executor-ptc): inline host environment reset 2026-10-30 20:27:10 +08:00
winewill 0744be3d9f test(desktop): pin archival workflow inputs to the original build 2026-10-30 18:59:01 +08:00
winewill 70bfd4d516 fix(desktop): share website destination and clarify manual recovery 2026-10-30 18:39:22 +08:00
winewill 8caf3d7838 fix(desktop): clarify publication approval and recovery diagnostics 2026-10-30 18:20:41 +08:00
winewill 7502558cc5 fix(desktop): address publication recovery review 2026-10-30 17:52:00 +08:00
winewill 32036b372e fix(desktop): separate channel save feedback from update suspension 2026-10-30 17:48:17 +08:00
winewill 2653c587ec feat(desktop): recover website publication and artifact archival 2026-10-30 17:09:00 +08:00
Dudu-0223 9bc99d7e02 test(web): make subagent ID clipping fixture deterministic 2026-10-30 17:04:21 +08:00
creatixchu 68a9a068cd Merge type-safe notarization assertions 2026-10-30 17:02:21 +08:00
creatixchu 1a294f7f79 test(desktop): keep notarization assertions type-safe 2026-10-30 17:01:29 +08:00
_Kerman 964231856d refactor(ptc): reuse run completion for cooperative stop 2026-10-30 17:00:42 +08:00
Dudu-0223 d37b3d98f5 test(web): align collaboration fixtures with panel presentation 2026-10-30 16:57:28 +08:00
creatixchu b5799c7444 Merge CUA notarization diagnostics 2026-10-30 16:52:24 +08:00
creatixchu ee9ae56599 test(cua): reuse the prepared extraction directory 2026-10-30 16:50:55 +08:00
creatixchu d9e89b3b37 fix(cua): retain redacted notarization failure diagnostics 2026-10-30 16:47:49 +08:00
Dudu-0223 61e219d546 Merge branch 'refactor/restack-id-ui' into refactor/restack-id-polish 2026-10-30 16:38:14 +08:00
Dudu-0223 2499a22e1d Merge commit 'd37b32d8538dc67a405ab4fdea5711532286d9e3' into refactor/restack-id-ui 2026-10-30 16:38:11 +08:00
Dudu-0223 b71bb26890 test(web): scope sidebar fold actions to the child row 2026-10-30 16:37:29 +08:00
Dudu-0223 0392d7c2bd test: refresh collapse timing tool catalog count 2026-10-30 16:33:03 +08:00
Dudu-0223 9656ecb162 Merge commit '0f434d505aba1220d92015d61cc64e39442f6304' into refactor/restack-id-polish 2026-10-30 16:04:30 +08:00
Dudu-0223 5c0bfd1193 merge: refresh ID-only UI from validated core snapshot headers 2026-10-30 16:03:54 +08:00
Dudu-0223 63ac6b46a6 test: refresh merged subagent tool schemas 2026-10-30 16:02:36 +08:00
creatixchu 5744837ef5 Merge master updates into bundled Computer Use 2026-10-30 15:58:02 +08:00
creatixchu e5a6231573 Merge origin/master into feat/cua-dev-signing 2026-10-30 15:58:00 +08:00
Dudu-0223 59689da92d test(team): assert ID-only headless collaboration records 2026-10-30 15:57:35 +08:00
creatixchu b177a08e53 Merge master into desktop helper packaging 2026-10-30 15:56:24 +08:00
Dudu-0223 454374be98 Merge current master through ID-only Team UI into polish 2026-10-30 15:16:28 +08:00
Dudu-0223 43c4919f86 merge: refresh ID-only Team UI from core master updates 2026-10-30 15:13:05 +08:00
Dudu-0223 71676cbc21 Merge master into the ID-only subagent and Team core 2026-10-30 14:57:57 +08:00
Dudu-0223 dcc3bde068 test(ui): refresh ID-only teamwork and subagent expectations 2026-10-30 14:52:15 +08:00
creatixchu 10f10216a3 Merge updated Computer Use application icon 2026-10-30 14:51:47 +08:00
creatixchu af2c4fedf9 Merge updated Computer Use application icon 2026-10-30 14:51:29 +08:00
Dudu-0223 b22e90f042 Merge commit '715535c7f19ccdcb10bd65c162b743ca6f1a36d0' into refactor/restack-id-polish 2026-10-30 14:44:27 +08:00
Dudu-0223 d80a818471 docs(team): describe ID-only teammate addressing 2026-10-30 14:41:38 +08:00
creatixchu b90f5fea79 Merge standalone helper restoration and provider-owned guidance 2026-10-30 14:41:17 +08:00
Dudu-0223 6766a694fc Merge refreshed ID-only Team base into UI polish 2026-10-30 14:40:43 +08:00
creatixchu e8f79f135d fix(cua): restore standalone helper launch 2026-10-30 14:40:36 +08:00
Dudu-0223 210e5094d3 test(subagent): refresh ID receipts and settlement attribution 2026-10-30 14:38:53 +08:00
creatixchu 08b2b984e2 Merge provider-owned permission guidance 2026-10-30 14:38:09 +08:00
Dudu-0223 b5fed8ca06 test(team): refresh ID-only UI and session snapshots 2026-10-30 14:37:04 +08:00
Dudu-0223 5a052d6767 Merge ID-only Team base into agent teamwork UI polish 2026-10-30 14:32:55 +08:00
Dudu-0223 7123412ae8 Merge previous PR 6288 history into ID-only Team UI layer
The reviewed Team UI delta has been rebuilt on the ID-only core. Preserve its review fixes as ancestry while the optional naming implementation moves to the top stack layer.
2026-10-30 14:25:55 +08:00
Dudu-0223 0eb1bb52e2 refactor(team): keep session UI and task tools keyed by IDs 2026-10-30 14:25:27 +08:00
Dudu-0223 da10d4de31 refactor(ui): keep agent identities as Session IDs below naming 2026-10-30 14:23:42 +08:00
creatixchu 2361eaebec test(plugin-market): isolate Git probe call assertions 2026-10-30 13:58:35 +08:00
creatixchu dec3a642df test(plugin-market): make Git probe timeout classification deterministic 2026-10-30 13:57:21 +08:00
creatixchu f2ad80451d build(cua): exclude developer signing tools from package files 2026-10-30 13:45:29 +08:00
creatixchu 850c035fd1 test(cua): verify developer signing CLI with canonical paths 2026-10-30 13:42:46 +08:00
creatixchu 6d33eb56ca refactor(cua): move fixed signing setup into a developer skill 2026-10-30 13:41:02 +08:00
creatixchu 791d04e003 Merge permission guide recovery and window handling updates 2026-10-30 13:38:21 +08:00
creatixchu 42869bf664 Merge permission guide recovery and window handling updates 2026-10-30 13:37:55 +08:00
Yif c766038238 Merge master and align submenu visual viewport placement 2026-10-30 12:33:26 +08:00
Turtle 51856c3f4f fix(plugin-market): keep network failures of the SSH retry retryable
Refs #7598
2026-10-30 12:17:35 +08:00
Turtle 8cce23db44 fix(plugin-market): retry detail clone fetches over SSH after https is refused
Refs #7598
2026-10-30 12:04:58 +08:00
Dudu-0223 999f30aae9 fix(sdk): identify subagent ownership errors by name and code 2026-10-30 11:55:46 +08:00
Dudu-0223 1e44387042 fix(subagent): render creation receipts from persisted metadata 2026-10-30 11:47:39 +08:00
_Kerman 718f28ee9a refactor(ptc): simplify model-visible state and recovery messages 2026-10-30 11:30:37 +08:00
creatixchu 1b9b05a367 Merge remote-tracking branch 'origin/master' into feat/cua-dev-signing 2026-10-30 11:15:13 +08:00
creatixchu 64326a0189 Merge updated permission refresh behavior 2026-10-30 10:57:35 +08:00
creatixchu be0e6fd57a Merge updated permission refresh behavior 2026-10-30 10:57:19 +08:00
creatixchu 4301596be1 test(cua): align signing fixtures with home-aware recovery and optional keychains 2026-10-30 10:56:27 +08:00
creatixchu aa5fde3700 feat(cua): default to a DSH-home identity and allow user certificates 2026-10-30 10:49:44 +08:00
creatixchu 99b59d73de Merge updated desktop helper packaging and permission guide 2026-10-30 10:48:42 +08:00
creatixchu 02e307aef5 Merge permission guide into the desktop helper packaging branch 2026-10-30 10:47:59 +08:00
creatixchu 4d4a7e5859 fix(cua): remove the standalone helper copy 2026-10-30 10:47:29 +08:00
_Kerman afde188c6f fix(tool-executor-ptc): report blocked resets and queue notices before reuse 2026-10-29 21:58:51 +08:00
_Kerman 7d20d1c361 fix(ptc-runtime-node): contain teardown and validate bootstrap readiness 2026-10-29 21:58:51 +08:00
Yif 16f54f5ecf fix(client): address Session menu review and coverage failures 2026-10-29 21:57:21 +08:00
_Kerman aaa7f45d4f Merge remote-tracking branch 'origin/master' into xtr/ptc-timeout-step-1 2026-10-29 21:22:30 +08:00
_Kerman 33492246a5 feat(tool-executor-ptc): add host reset for retained environments 2026-10-29 21:22:26 +08:00
_Kerman 9800c764ef feat(ptc-runtime-node): preserve state after cooperative cancellation 2026-10-29 21:22:25 +08:00
Yif 8504351cc3 test(web): isolate context gestures from native app discovery 2026-10-29 20:34:30 +08:00
Yif e5029f419a Merge master and finalize Session menu regression coverage 2026-10-29 20:17:02 +08:00
Yif b8ede96fd8 feat(client): move native app opening into Session menus 2026-10-29 20:03:57 +08:00
creatixchu 2e4fddec5d fix(cua): preserve recorded signing identities when the keychain is missing 2026-10-29 19:41:19 +08:00
creatixchu 887356ba1a test(cua): inspect projected configuration fields without unsafe matchers 2026-10-29 19:26:28 +08:00
creatixchu 159c918680 test(config): document fields in object schema spread fixtures 2026-10-29 19:25:27 +08:00
creatixchu c9d2c5f908 fix(cua): preserve object schema fields when extending macOS configuration 2026-10-29 19:23:03 +08:00
Dudu-0223 fc36b58fc3 Merge remote-tracking branch 'origin/fix/team-descendant-ui' into codex/sync-stack-7396-7504 2026-10-29 19:01:07 +08:00
Dudu-0223 0c08afe52f Use explicit void cleanup in preset integration test 2026-10-29 18:59:34 +08:00
Dudu-0223 8f7cef03da Keep UI polish expectations aligned with its stack layer 2026-10-29 18:55:35 +08:00
Dudu-0223 664e64debe Align integration checks with shared conditional Team tools 2026-10-29 18:52:03 +08:00
Dudu-0223 63f8e812e1 Propagate Team mode CI fixes into UI polish 2026-10-29 18:38:57 +08:00
Dudu-0223 9e0557e2cf Align Team mode snapshot assertions with filtered requests 2026-10-29 18:35:53 +08:00
Dudu-0223 422cb047a9 Sync Team UI polish with current stack parent 2026-10-29 18:32:48 +08:00
creatixchu 84b26858d3 refactor(cua): keep source signing configuration in the macOS provider 2026-10-29 18:32:47 +08:00
Dudu-0223 997fc4b05e Sync current schedule integration into Team UI layer 2026-10-29 18:22:55 +08:00
Dudu-0223 1287db6c8d Sync latest master schedule changes into naming layer 2026-10-29 18:20:02 +08:00
Dudu-0223 a0a5a24556 Merge current master schedule APIs into stack base 2026-10-29 18:18:04 +08:00
Dudu-0223 1623af7f06 Sync refreshed parent snapshots into Team UI layer 2026-10-29 18:15:14 +08:00
Dudu-0223 5daf251d7c Sync and refresh new master snapshots for named subagents 2026-10-29 18:13:41 +08:00
Dudu-0223 42247c31e2 Refresh new master snapshots for tree-wide subagent controls 2026-10-29 18:12:16 +08:00
Dudu-0223 3306de499e Sync Team session UI layer with updated naming parent 2026-10-29 18:09:18 +08:00
winewill 47c6e1da03 refactor(desktop): unify settings storage and field validation 2026-10-29 18:08:58 +08:00
winewill acf3f66325 refactor(desktop): share shell settings path resolution 2026-10-29 17:57:09 +08:00
Dudu-0223 af37232ab5 Sync naming layer with updated stack base 2026-10-29 17:56:36 +08:00
Dudu-0223 11c02855e1 Merge master into subagent runtime stack base 2026-10-29 17:55:40 +08:00
gengruilin b95d5c992b chore(mcp-manager): match the root version and export the locale metadata 2026-10-29 17:50:08 +08:00
Dudu-0223 be7ce3b64b Merge current parent into feat/subagent-names 2026-10-29 17:48:53 +08:00
Dudu-0223 80b7985a24 refactor(team): resolve tools and prompts from saved mode 2026-10-29 17:48:52 +08:00
winewill a7c8c3ac25 test(desktop): record macOS updater qualification 2026-10-29 17:46:39 +08:00
Dudu-0223 cfe6747460 test(web): refresh merged plugin recommendation catalog 2026-10-29 17:44:57 +08:00
Dudu-0223 4514b99d9c Merge master into feat/subagent-names 2026-10-29 17:43:46 +08:00
creatixchu b464b633ff Merge remote-tracking branch 'origin/feat/cua-desktop-helper' into feat/cua-optional-bundle 2026-10-29 17:42:06 +08:00
creatixchu e87a535d57 fix(cua): keep standalone application URLs stable across reuse 2026-10-29 17:37:31 +08:00
creatixchu 1c9d95a778 fix(cua): keep this change scoped to standalone helper identity 2026-10-29 17:34:35 +08:00
creatixchu a93b1ecdf1 fix(cua): launch nested helpers with independent macOS permissions 2026-10-29 17:26:27 +08:00
Dudu-0223 f47c61882d fix(team): keep task and mode updates independent of waits 2026-10-29 17:21:53 +08:00
Dudu-0223 76a36fee3b fix(subagent): wait only for the caller inbox 2026-10-29 17:19:15 +08:00
gengruilin 1644e752ca Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-10-29 17:11:59 +08:00
Chinesezjc 58c375d8a5 docs(notes): avoid fixed sidecar counts in the read note 2026-10-29 16:51:48 +08:00
Chinesezjc 09bc71f831 test(snapshot): carry the read guidance into scenarios merged from master
The merge from master adds four scenarios recorded before the restore
(ptc-script-fresh, ptc-script-retained, command-attachments, plan-review);
their system-prompt sidecars still show the old sentence.
2026-10-29 16:50:16 +08:00
Chinesezjc 745c6e7f45 Merge remote-tracking branch 'origin/master' into fix/read-guidance-line-ranges 2026-10-29 16:50:01 +08:00
gengruilin e7ce2d2373 fix(mcp): follow master's config hook, tool fixture, LiveShare endpoint table, and prompt changes 2026-10-29 16:24:21 +08:00
creatixchu 91474bb32e fix(cua): distinguish the development helper application name 2026-10-29 16:17:26 +08:00
Chinesezjc ce988594ee docs(notes): correct the read guidance evidence and cross-links
Split the sidecar coverage into 83 test:snapshot sidecars and 6 web
snapshots run by test:web:ci, state the Pass@3 denominator, record that
the benchmark is neutral for this sentence, and link the read and subagent
notes to each other in both languages.
2026-10-29 16:14:35 +08:00
gengruilin 3ee7b6829c chore(mcp-manager): add plugin metadata in every shipped language 2026-10-29 16:11:27 +08:00
gengruilin 79a33e3211 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-10-29 16:09:47 +08:00
winewill a635d6d908 test(desktop): record Windows updater qualification 2026-10-29 15:52:20 +08:00
creatixchu b5197dd07d test(cua): type invalid signing configuration explicitly 2026-10-29 15:39:02 +08:00
creatixchu e57fe79696 feat(cua): make local development signing opt in 2026-10-29 15:35:59 +08:00
winewill c2cda0bdd7 Merge origin/master into feat/desktop-update-channels 2026-10-29 15:27:08 +08:00
winewill 6d0a5f0f12 refactor(desktop): isolate updater qualification and gate dependency versions 2026-10-29 15:23:41 +08:00
creatixchu 6439400885 feat(cua): sign source-built helpers with a shared development identity
Source checkouts build DSH Computer Use Dev with its own bundle identifier.
An explicit per-user setup creates a self-signed identity in a dedicated
keychain; every development build signs with it, so one macOS grant covers
rebuilds and other worktrees. Unchanged sources reuse their signed generation.

Fixes #7536
2026-10-29 15:12:23 +08:00
creatixchu 4247226074 refactor(plugins): drop catalog listedByDefault after Computer Use leaves the on-demand catalog
No on-demand catalog entry declares listedByDefault once Computer Use
ships with the installation, so the generator field, catalog type, and
plugin-manager propagation lose their only consumer. Also correct the
packed-worker comment and the Computer Use README wording.

Refs #7514
2026-10-29 13:40:54 +08:00
creatixchu a40d1f4c36 test(cua): capture the card status while Computer Use enables 2026-10-29 13:40:54 +08:00
creatixchu 3b544b0416 feat(cua): ship Computer Use as an optional bundle in the Experimental plugins list
Refs #7514
2026-10-29 13:40:54 +08:00
creatixchu 2b1368f317 fix(cua): match the app.asar segment with either path separator
The archive mapping missed Windows-joined paths in the cross-platform
test run; anchor on the whole app.asar segment instead of a literal
forward-slash string.

Refs #7514
2026-10-29 13:40:39 +08:00
Yif 51177ea96d fix(web): clarify teamwork benefits and token costs in all locales 2026-10-29 13:32:21 +08:00
creatixchu fc1ed8c1cd fix(desktop): verify only the Computer Use helper bundle and stage it only when shipped
Limit the no-resign exemption to the staged helper so LibreOffice's
unsigned bundle keeps per-file signing, verify the helper's ticket and
Gatekeeper acceptance, stage it after the configuration check only for
sources that ship the bundle, and report a missing helper by path.

Refs #7514
2026-10-29 13:27:42 +08:00
Dudu-0223 1fc187bf8a merge: keep shared subagent prompts in the following layer 2026-10-29 13:20:05 +08:00
Dudu-0223 ef97a03734 Merge remote-tracking branch 'origin/feat/subagent-names' into codex/pr-6288-session-team 2026-10-29 13:15:05 +08:00
Dudu-0223 0036824b70 refactor(team): extract shared subagent prompts into a separate layer 2026-10-29 13:15:03 +08:00
Dudu-0223 366bd584b4 docs(subagent): align message attribution with naming service 2026-10-29 13:09:46 +08:00
Dudu-0223 43b7ffa1b3 merge: sync naming services and Team cleanup from #6288 2026-10-29 13:06:53 +08:00
creatixchu 6137225576 fix(desktop): ship the notarized Computer Use helper in macOS packages
Build and stage the helper before packaging, keep its bundle unpacked
outside app.asar, verify it instead of re-signing its files, and let the
provider open the unpacked copy.

Refs #7514
2026-10-29 12:56:32 +08:00
Dudu-0223 9a3ad20a15 merge: inherit naming and sender attribution from #6346 2026-10-29 12:50:50 +08:00
Dudu-0223 d5ed4eaf97 refactor(subagent): own sender naming in the names layer 2026-10-29 12:47:46 +08:00
Dudu-0223 1559737591 refactor(docs): drop unused bail event graph support 2026-10-29 12:37:43 +08:00
Dudu-0223 82ff391f4b refactor(subagent): resolve sender names through root sessions 2026-10-29 12:08:15 +08:00
Dudu-0223 12dc2f1f58 fix(web): restore team menu focus and accessible activity feedback 2026-10-29 11:53:02 +08:00
Dudu-0223 03af1488b4 fix(subagent): remove unused settlement task description 2026-10-29 11:24:25 +08:00
Dudu-0223 6c05579452 refactor(team): drop retired roster and mailbox projections 2026-10-29 10:54:32 +08:00
Yif 970a728e78 test: repair teamwork CI coverage and inherited expectations 2026-10-29 03:14:54 +08:00
Yif f5856bd23d fix(web): keep compact agent and job controls recognizable 2026-10-29 03:12:51 +08:00
Yif 5b3eadd14e merge: sync latest PR 6288 into teamwork UI polish 2026-10-29 02:22:01 +08:00
Dudu-0223 f09d260578 fix(client): shorten agent message target labels 2026-10-29 00:02:50 +08:00
Yif f248511981 merge: integrate PR 6288 Team Remote and wait presentation 2026-10-28 22:55:43 +08:00
Yif b71943900d feat(web): polish agent teamwork menus and background job controls 2026-10-28 22:29:23 +08:00
Dudu-0223 66e9db2b6c fix(client): show one status label for agent waits 2026-10-28 21:16:02 +08:00
_Kerman c400f504e6 Merge remote-tracking branch 'origin/master' into xtr/ptc-runner-imports 2026-10-28 21:12:04 +08:00
winewill 9743690280 test(desktop): qualify update channel switching on Windows CI 2026-10-28 19:24:21 +08:00
winewill dc1eab34cc fix(desktop): align update configuration locale key order 2026-10-28 19:11:44 +08:00
winewill 536a4a6c58 test(desktop): retire real downloaded updates when switching channels 2026-10-28 19:10:03 +08:00
winewill 0cf76b9fad feat(desktop): switch update channels from settings 2026-10-28 18:56:20 +08:00
Dudu-0223 ea39687d51 Merge remote team wait notification optimization 2026-10-28 18:01:26 +08:00
Dudu-0223 c77ecc73f1 refactor(subagent): share identity and collaboration guidance across team modes 2026-10-28 17:58:09 +08:00
Dudu-0223 ba54e7f6ce perf(team): notify current waits once per subagent tree 2026-10-28 17:36:17 +08:00
Dudu-0223 b98549729c test(team): align headless fixture with unified identity 2026-10-28 17:07:20 +08:00
Dudu-0223 76cc8b21d1 refactor(team): select mode directly without slash commands 2026-10-28 17:05:39 +08:00
Dudu-0223 4a5298cc94 refactor(team): unify agent identity instructions 2026-10-28 17:04:38 +08:00
Dudu-0223 dea7313318 docs(subagent): clarify default names and returned paths 2026-10-28 16:54:59 +08:00
Dudu-0223 28f71287d9 docs(subagent): shorten role name guidance 2026-10-28 16:53:09 +08:00
Dudu-0223 bee33716fe docs(subagent): guide names toward short roles 2026-10-28 16:06:06 +08:00
Turtleandimccyu 8818fdd2e0 docs(session-controller): document fork title limits
Co-authored-by: imccyu <[email protected]>
2026-10-28 15:35:43 +08:00
Turtleandimccyu 3be7d56c6d fix(session-controller): give repeated forks distinct titles
Fixes #7471

Co-authored-by: imccyu <[email protected]>
2026-10-28 15:22:54 +08:00
Yif 066f615cdf feat(subagent): refine delegation and team collaboration UI
Add dedicated fork tool presentation and localized settlement outcomes. Simplify the subagent catalog, clarify team collaboration controls, and update documentation and replay expectations.
2026-10-28 11:21:57 +08:00
Yichen Jiang 22db5af493 docs(agent): record retry control compatibility and validation 2026-10-27 12:15:57 +08:00
Yichen Jiang 40599db7c8 test(agent): verify retry control identity across SDK recovery 2026-10-27 12:11:22 +08:00
Yichen Jiang 156cd83eb8 Merge remote-tracking branch 'origin/master' into worktree/continue-generation-after-failure
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
2026-10-27 12:07:46 +08:00
Yichen Jiang 2e0c73a895 refactor(agent): record continuation as retry control input 2026-10-27 12:07:24 +08:00
Yichen Jiang 04f1a699ee fix(api): return the synchronous continuation admission promise 2026-10-26 22:49:03 +08:00
Yichen Jiang 53d4ed8f21 refactor(api): use canceled inbox splice semantics 2026-10-26 22:40:07 +08:00
_Kerman e86b278053 test(benchmarks): retain paired scaling samples 2026-10-26 22:38:33 +08:00
_Kerman f17a2c24c0 test(worktree): match normalized restoration paths 2026-10-26 22:38:03 +08:00
Yichen Jiang 207b55a5e6 Merge master and preserve Session stream observation during initialization 2026-10-26 22:33:38 +08:00
_Kerman bfe1c5e47c Merge remote-tracking branch 'origin/master' into xtr/ptc-runner-imports 2026-10-26 22:29:02 +08:00
Yichen Jiang 1b907b26aa test(web): pin continuation without a user message bubble 2026-10-26 22:25:05 +08:00
Yichen Jiang 4daabfd629 fix(test): include shared continuation events in client checks 2026-10-26 22:20:41 +08:00
Yichen Jiang 4a4aabaa57 fix(core): align empty continuation checks and generated references 2026-10-26 22:16:24 +08:00
Yichen Jiang d8254627fe feat(core): continue failed turns through empty prompt input 2026-10-26 22:12:13 +08:00
_Kerman 7c0af3101b refactor(ptc): reuse native module format resolution 2026-10-26 22:05:56 +08:00
_Kerman 289d6f9f9c refactor(webworker): keep process alias packing local 2026-10-26 21:51:16 +08:00
_Kerman 5acd104bfb test(ptc): reuse process-bound project for VM imports 2026-10-26 21:38:46 +08:00
_Kerman 10c9852f21 docs: remove upgrade guide for unreleased import API 2026-10-26 21:08:04 +08:00
_Kerman 43a213fdd3 test(ptc): reuse shared snapshot composition 2026-10-26 21:08:03 +08:00
_Kerman 9675268c9f test: apply shared isolation setup to VM module project 2026-10-26 21:08:03 +08:00
_Kerman 73f3c6c6f9 fix(ptc): pin private package metadata reader dependency 2026-10-26 21:08:02 +08:00
_Kerman cf6e05df6d fix(ptc): preserve import semantics across evaluation lifetimes 2026-10-26 21:07:10 +08:00
Yichen Jiang 38e5684d5c fix(web): project continuation eligibility from complete session history 2026-10-26 19:59:54 +08:00
Yichen Jiang a8b1a9b4a8 fix(web): balance continue icon with composer controls 2026-10-26 18:17:43 +08:00
_Kerman 81dc04d87a docs: disclose import-meta-resolve runtime dependency 2026-10-26 17:58:07 +08:00
_Kerman 064b1c998c test(ci): include VM project in isolation and budget checks 2026-10-26 17:58:07 +08:00
Yichen Jiang 953548cd59 feat(web): continue generation after transient request failures 2026-10-26 17:50:55 +08:00
_Kerman eab1228daa fix(webworker): register builtins required by bundled PTC resolver 2026-10-26 17:44:41 +08:00
_Kerman 23d968b0ba test(ptc): refresh imports snapshot after master merge 2026-10-26 17:19:19 +08:00
_Kerman 7fad73160d Merge remote-tracking branch 'origin/master' into xtr/ptc-runner-imports 2026-10-26 17:15:55 +08:00
Dudu-0223 fdb514cc29 refactor(ui-subagent): restore renderFooter callback 2026-10-26 13:31:06 +08:00
Turtle 7196f51a63 test(snapshots): update new ptc-script schema pins for the 10-query cap 2026-10-26 12:28:23 +08:00
Turtle b1b0627c23 docs(upgrade-guide): move web search defaults guide to v0.2.1-rc.3 2026-10-26 12:25:20 +08:00
Turtle b690b20ff0 Merge origin/master into turtle/websearch-query-limit-96c308027005 2026-10-26 12:25:06 +08:00
Chinesezjc 5b25f04e6f docs(notes): record the read guidance evidence
The note keeps the #6057 observation and the 2026-10-23 evaluation of the
single sentence so a later prompt trim does not remove it again.
2026-10-26 11:26:43 +08:00
Chinesezjc c74015c81e test(snapshot): re-render expected prompts and the read guidance fence
89 recorded system prompts carry the restored sentence; the two pinned
historical sidecars keep their recorded text. The package README fence is
updated in both languages.
2026-10-26 11:26:43 +08:00
Chinesezjc 15c9e6006b fix(prompt): name line ranges and shell readers in the read guidance
Restore the 2026-10-13 wording removed together with a later prompt
revert: sed, head, and tail join cat as shell commands the read tool
replaces, and offset/limit now describes reading a line range. The unit
expectation for the rendered guidance follows.
2026-10-26 11:26:05 +08:00
Dudu-0223 106b6f1319 test(web): sample sidebar spacing in one animation frame 2026-10-25 17:12:02 +08:00
Dudu-0223 7d6342932d fix(team): isolate mode updates and repair command navigation 2026-10-25 17:00:10 +08:00
Dudu-0223 d62220a4c8 test(team): refresh localized description accessibility labels 2026-10-25 16:32:46 +08:00
Dudu-0223 acece81128 fix(team): register every bundled client locale 2026-10-25 16:20:48 +08:00
Dudu-0223 14445c191e test(team): brand the restored checkpoint cursor 2026-10-25 16:14:03 +08:00
Dudu-0223 78083c4ff4 Merge remote-tracking branch 'origin/fix/team-descendant-ui' into codex/team-task-split 2026-10-25 16:10:33 +08:00
Dudu-0223 122dc3af42 refactor(team): isolate package group and address review feedback 2026-10-25 16:10:07 +08:00
Dudu-0223 8128f8dd2b test(team): refresh creation skill catalog after bundle retirement 2026-10-25 16:08:56 +08:00
Dudu-0223 2c59c80608 Merge remote-tracking branch 'origin/feat/subagent-names' into codex/resolve-6288-20261025 2026-10-25 16:08:19 +08:00
Dudu-0223 463b19bb90 test(subagent): include optional names in shipped preset schemas 2026-10-25 16:07:46 +08:00
Dudu-0223 55a8215566 Merge remote-tracking branch 'origin/feat/subagent-names' into codex/resolve-6288-20261025 2026-10-25 15:58:52 +08:00
Dudu-0223 a4e6d190d7 test(subagent): refresh PowerShell and Web fork naming snapshots 2026-10-25 15:58:32 +08:00
Dudu-0223 e752785a65 test(team): call synchronous interruption without await 2026-10-25 15:57:09 +08:00
Dudu-0223 bfcf58b52b Merge commit 'b95d9763bbb' into codex/resolve-6288-20261025 2026-10-25 15:52:28 +08:00
Dudu-0223 efe90ca419 test(subagent): refresh CLI naming and communication snapshots 2026-10-25 15:51:55 +08:00
Dudu-0223 b2ca187d40 Merge naming plugin CI repairs and align Team integration fixtures 2026-10-25 15:51:53 +08:00
Dudu-0223 9c4dafdfb8 fix(ci): reconcile naming plugin metadata and measured npm budget 2026-10-25 15:46:03 +08:00
Dudu-0223 80f70969d9 Merge current subagent naming stack into Team session UI 2026-10-25 15:45:40 +08:00
Dudu-0223 6ad043e920 fix(subagent): publish naming plugin locale resources 2026-10-25 15:32:09 +08:00
Dudu-0223 aa1540903d Merge refreshed subagent tree parent into named agents 2026-10-25 15:26:49 +08:00
Dudu-0223 c49c4f11de Merge master into subagent tree stack and reconcile fixtures 2026-10-25 15:24:45 +08:00
Dudu-0223 448f146307 refactor(subagent): require names in delegation tools 2026-10-25 14:45:56 +08:00
Dudu-0223 1a1ba4f0a9 test(subagent): retain canceled inbox snapshot event 2026-10-25 13:57:01 +08:00
Dudu-0223 f84c7371ca refactor(subagent): keep names in a durable alias map 2026-10-25 13:46:55 +08:00
Dudu-0223 769022766f test(subagent): assert named discovery values 2026-10-25 12:16:12 +08:00
Dudu-0223 f49f2bcf91 test(subagent): make named snapshot request explicit 2026-10-25 12:08:18 +08:00
Dudu-0223 15386b4440 feat(subagent): move optional names into a separate plugin 2026-10-25 12:06:43 +08:00
Dudu-0223 f09648ddca Merge published subagent activation ownership changes 2026-10-23 18:28:46 +08:00
Dudu-0223 00a2e76b87 Merge master and reconcile translation records 2026-10-23 18:19:07 +08:00
Dudu-0223 b117a9a24e Merge pull request #7059 from deepseek-harness/refactor/subagent-runtime-ownership
Remove generic Agent ownership and reuse subagent activations
2026-10-23 18:10:51 +08:00
Dudu-0223 300e0ded19 Merge latest Team base and regenerate ownership references 2026-10-23 17:21:14 +08:00
Dudu-0223 9c406a6325 refactor(subagent): derive membership from existing activations 2026-10-23 17:18:03 +08:00
Dudu-0223 807657cc05 Merge master and reconcile development translation records 2026-10-23 16:47:59 +08:00
Dudu-0223 dbcc458c12 docs(subagent): refresh configuration source locations 2026-10-23 16:46:14 +08:00
Dudu-0223 ffad919ed8 refactor(subagent): remove interrupt guidance from list_agents 2026-10-23 16:45:22 +08:00
Dudu-0223 5a7a6690a0 Simplify subagent archive labels through identity projection 2026-10-23 16:45:00 +08:00
Dudu-0223 cbc2738a94 Merge updated Team base and reconcile recommendation docs 2026-10-23 16:39:55 +08:00
Dudu-0223 a3a235321c test(subagent): migrate loader stubs and cover root exclusions 2026-10-23 16:29:54 +08:00
Dudu-0223 b5cbd3c08d Merge master compiler budget and recommendation composition 2026-10-23 16:20:59 +08:00
Dudu-0223 37a570622a docs(subagent): name both removed ownership parameters 2026-10-23 16:14:51 +08:00
Dudu-0223 b3d4933f39 refactor(subagent): own live lineage outside core agent registry 2026-10-23 16:07:11 +08:00
Dudu-0223 c32ac852d3 Allow cold Host TypeScript checks to exceed four GiB 2026-10-23 15:52:05 +08:00
Dudu-0223 3dde36b5bb Merge master while preserving subagent lifecycle changes 2026-10-23 15:41:00 +08:00
Dudu-0223 d7858505da Sync recommendation recordings with shared subagent tools 2026-10-23 15:34:26 +08:00
Dudu-0223 849380ee1e Align series-start recording with subagent control tools 2026-10-23 15:21:25 +08:00
Dudu-0223 871a2bab84 Merge remote-tracking branch 'origin/master' into refactor/agent-team-named-subagents 2026-10-23 15:20:02 +08:00
Dudu-0223 ad7c75c798 Bind subagent providers and preserve archived ancestor checks 2026-10-23 15:10:11 +08:00
Dudu-0223 e914c285d2 Merge master into subagent refactor 2026-10-23 14:45:13 +08:00
Turtle c03b1d247e feat(web): raise default web_search results to 10 and pace DeepSeek Search API requests
Default searchMaxResults rises to 10 so a full 10-query batch keeps one source per query. The Search API provider sends at most searchRequestsPerSecond (default 5) requests per second within one plugin instance, matching the account's 5 QPS limit. Adds an upgrade guide and Known Limitations entries.
2026-10-23 14:06:49 +08:00
Turtle 976e8b4a2f Merge origin/master into turtle/websearch-query-limit-96c308027005 2026-10-23 13:19:29 +08:00
Turtle 4bec077664 feat(tool-web): raise default web_search query cap to 10 2026-10-23 12:57:58 +08:00
Dudu-0223 e16f966470 Merge origin/master and align subagent UI locale keys 2026-10-23 12:49:41 +08:00
Dudu-0223 86d4c75516 Remove unused public subagent service methods 2026-10-23 12:46:04 +08:00
Dudu-0223 0b1899534b Align Web and Python child guidance expectations 2026-10-22 19:01:43 +08:00
Dudu-0223 29eb238bed Use typed text assertions for Team context 2026-10-22 18:44:31 +08:00
Dudu-0223 44e2be561b Scope Team identity assertions to the Team context 2026-10-22 18:35:29 +08:00
Dudu-0223 c9cf2d3116 Align Team prompt pin with upstream delegation guidance 2026-10-22 18:24:53 +08:00
Dudu-0223 b43a6c9e9c Merge remote-tracking branch 'origin/master' into HEAD 2026-10-22 18:22:17 +08:00
Dudu-0223 c8755f7460 Regenerate subagent control dependency graph 2026-10-22 18:21:23 +08:00
Dudu-0223 27b937944d Update delegated-child prompt snapshots 2026-10-22 18:15:31 +08:00
Dudu-0223 0cab444648 Merge activation option simplification into tool guidance cleanup 2026-10-22 18:11:03 +08:00
Dudu-0223 6038abe625 Limit communication guidance to delegated children 2026-10-22 18:10:36 +08:00
Dudu-0223 575606a356 refactor(subagent): flatten activation task options 2026-10-22 18:05:13 +08:00
Dudu-0223 f67ca5b866 Move subagent communication guidance into control tools 2026-10-22 17:59:48 +08:00
Dudu-0223 5b1a588284 chore: prune retired subagent control cast baseline 2026-10-22 17:43:41 +08:00
Dudu-0223 90a6646d6c test(subagent): align merged completion fixtures and Remote mock 2026-10-22 17:42:03 +08:00
Dudu-0223 21bd0789ec Merge master and preserve parent-message settlement deduplication 2026-10-22 17:33:12 +08:00
Dudu-0223 9581e13e78 refactor(subagent): name root wait for tree work 2026-10-22 17:19:33 +08:00
Dudu-0223 ea009821d6 Simplify subagent user controls and authorize target sessions 2026-10-22 17:06:13 +08:00
Dudu-0223 c7a71f753a refactor(subagent): inline activation lifecycle publication 2026-10-22 15:14:14 +08:00
Dudu-0223 0aee757bf0 test: refresh background wait subagent schemas 2026-10-22 13:51:37 +08:00
Dudu-0223 03ac3ea45e test: align subagent expectations with merged lifecycle 2026-10-22 13:44:05 +08:00
Dudu-0223 16ece34568 Merge master into subagent tree branch 2026-10-22 13:33:16 +08:00
Dudu-0223 89e2a525aa refactor(subagent): distinguish runtime ownership from Team identity 2026-10-22 11:59:09 +08:00
_Kerman 9d56130730 fix(ptc): refresh imported process environment after scrubbing 2026-10-22 11:10:47 +08:00
_Kerman 8faf20f660 Merge latest xtr/ptc-vm-script into xtr/ptc-runner-imports 2026-10-22 11:07:30 +08:00
_Kerman 83b04dd138 Merge latest xtr/ptc-vm-script into xtr/ptc-runner-imports 2026-10-21 18:55:50 +08:00
_Kerman fb59141935 feat(ptc-runtime-node): intercept recursive ESM imports 2026-10-21 15:11:40 +08:00
_Kerman 55cef1caf7 fix(ptc-runtime-node): preserve native then-export results 2026-10-21 13:24:39 +08:00
_Kerman c61f117ba0 fix(test): partition VM module suites by project 2026-10-21 13:24:38 +08:00
_Kerman c9cbdcf739 test(ptc-runtime-node): include import loaders in source fixture 2026-10-21 13:24:37 +08:00
_Kerman 091f00dfc7 feat(ptc-runtime-node): support runner-local import handlers 2026-10-21 13:11:08 +08:00
Dudu-0223 619c089b5c test(web): align plugin list with retired team bundle 2026-10-20 17:40:21 +08:00
Dudu-0223 f70979317f fix(web-app): retain parent dependency and regenerate module graph 2026-10-20 17:26:47 +08:00
Dudu-0223 305e581577 Merge branch 'codex/resolve-stack-6346' into codex/resolve-stack-6288 2026-10-20 17:21:14 +08:00
Dudu-0223 13badc4d48 test(sdk): align desktop snapshot with named subagent controls 2026-10-20 17:21:03 +08:00
Dudu-0223 a57e5e0d3e fix(test): preserve Team SDK context and remove retired package residue 2026-10-20 17:20:51 +08:00
Dudu-0223 f3436fe3c2 Merge named subagent parent and reconcile Team package migration 2026-10-20 17:15:46 +08:00
Dudu-0223 d046335532 Merge updated subagent tree parent into named agents 2026-10-20 17:11:08 +08:00
Dudu-0223 c6d6c87b3b Merge master into subagent tree stack 2026-10-20 17:10:31 +08:00
Dudu-0223 6da2afabf7 revert(subagent): keep label in agent listing output 2026-10-20 17:09:50 +08:00
Dudu-0223 febeb4e28b test(ui): cover named subagent navigation 2026-10-20 17:06:12 +08:00
Dudu-0223 59edab8c0f fix(subagent): expose task description in agent listings 2026-10-20 16:52:10 +08:00
gengruilin 9ce7b0e6f6 Revert "refactor(mcp): keep only plugin-registered servers in the MCP manager"
This reverts commit 1c45a63dd65. Settings-configured servers, credential
templates, probes, stdio and enabled entries, and the status event implement
the backend of #3371, which remains open.
2026-10-20 16:31:24 +08:00
Dudu-0223 cca63c475c fix(team): restore named messaging guidance 2026-10-20 16:26:05 +08:00
Dudu-0223 fb3fb18f48 Merge updated agent-team parent into subagent names 2026-10-20 16:25:26 +08:00
Dudu-0223 1dfbeb7c3b refactor(subagent): retain only names during initialization 2026-10-20 16:12:46 +08:00
Dudu-0223 cfd16170f0 fix(subagent): restore optional interrupt tool registration 2026-10-20 15:55:11 +08:00
Dudu-0223 cd8be93e77 refactor(client): show shared task boards only in root sessions 2026-10-20 15:46:37 +08:00
gengruilin 4e796f736e merge: update the MCP manager with master 2026-10-20 15:45:33 +08:00
Dudu-0223 350c40818a test(team): reuse request snapshots for mode commands 2026-10-20 15:28:03 +08:00
Dudu-0223 ac7165e635 test(sdk): align desktop context snapshots with subagent controls 2026-10-20 15:24:23 +08:00
Dudu-0223 a96d8a8bb5 docs: refresh Team persistence dependency graph 2026-10-20 15:16:23 +08:00
Dudu-0223 3331b91a52 Merge origin/master into refactor/agent-team-named-subagents 2026-10-20 15:10:31 +08:00
Dudu-0223 eee91744f9 fix(agent-team): require confirmed persistence before task notifications 2026-10-20 15:03:30 +08:00
Dudu-0223 e29671eedb fix(agent-team): resolve task owner names before commits 2026-10-20 15:00:41 +08:00
Dudu-0223 6a8667f459 refactor(team): project session mode with the task board 2026-10-20 14:44:07 +08:00
Dudu-0223 53aa5f5fd8 fix(team): apply mode changes before queued task operations 2026-10-20 14:26:13 +08:00
Dudu-0223 3cb3d7e5a7 test(web): set model popup hover before snapshots 2026-10-20 13:28:19 +08:00
Dudu-0223 0ba1fa7853 test(web): refresh built-in team menu snapshots 2026-10-20 13:19:18 +08:00
Dudu-0223 598e7421e8 fix(ui): use subagent names in breadcrumbs 2026-10-20 12:47:26 +08:00
Dudu-0223 455a40944e fix(ui): show compact subagent names and descriptions 2026-10-20 12:18:26 +08:00
Dudu-0223 8de1fa0cf8 fix(ui): hide agent coordination details from cards 2026-10-20 11:58:30 +08:00
Dudu-0223 d05e9fbd3c refactor(team): remove the relocated experimental package sources 2026-10-20 11:50:24 +08:00
Dudu-0223 35018c3726 feat(team): use one Session switch and shared Subagent panel 2026-10-20 11:48:04 +08:00
gengruilin 5a30774840 refactor(mcp): keep only plugin-registered servers in the MCP manager
The Settings management page this manager was built for is not shipping, and
every connector plugin registers its server. Remove what only that page used:

- mcp-manager: the volatile `servers` configuration and its write-time
  validation, stdio entries, the `enabled` flag, the `probe` operation, and the
  `mcp-manager/status` event. A registration takes a Streamable HTTP `url`, and
  a `startupTimeoutMs` setting bounds each registered server's startup.
- mcp-client: `${credential:REF}` templates (`expandTemplates`), the
  `waiting-credentials` state, `probeServer`, and the exports that existed only
  for the manager's write-time validation.

The Web profile mounts the manager without injecting credentials, and the
`mcp-template-error` snapshot goes with the templates.
2026-10-20 11:19:40 +08:00
gengruilin a5aa90b6b6 merge: update the MCP manager with master 2026-10-20 01:16:50 +08:00
Dudu-0223 a9be73af14 test(team): acknowledge connection closure during page reload 2026-10-19 21:11:40 +08:00
Dudu-0223 eefc765e10 Merge parent workflow and Web PTC snapshot corrections 2026-10-19 21:04:34 +08:00
Dudu-0223 e8bf822210 test(team): opt in to Session mode in assembled profiles 2026-10-19 21:03:24 +08:00
Dudu-0223 eddb6a8862 test(subagent): align workflow catalog and Web PTC name expectations 2026-10-19 21:03:23 +08:00
Dudu-0223 624f862294 Merge parent CLI descriptor snapshot corrections 2026-10-19 20:49:33 +08:00
Dudu-0223 072c4109e4 test(team): cover mode command outcomes and update module graph 2026-10-19 20:48:56 +08:00
Dudu-0223 110e61f70f test(subagent): remove duplicate names from CLI descriptor expectations 2026-10-19 20:48:53 +08:00
Dudu-0223 8ad7456367 Merge task-board fixes and share mode snapshot schemas 2026-10-19 20:41:48 +08:00
Dudu-0223 ded397b5d1 docs(subagent): align return guidance and naming failures 2026-10-19 20:39:24 +08:00
Dudu-0223 588cae0f04 Merge current subagent naming changes into Session Team controls 2026-10-19 20:38:38 +08:00
Dudu-0223 06d921cc29 Merge updated subagent parent and reconcile named task cancellation 2026-10-19 20:35:23 +08:00
Dudu-0223 9aecd40ecb feat(team): select Agent Teams per Session from the model menu 2026-10-19 20:29:09 +08:00
Dudu-0223 bc70995b38 fix(subagent): generate optional sibling names and unify catalog paths 2026-10-19 20:26:57 +08:00
Dudu-0223 8c8b3027a6 fix: address subagent and task-board review findings 2026-10-19 19:58:37 +08:00
Dudu-0223 960705d2b1 feat(subagent): address teammates by hierarchical paths 2026-10-19 17:58:49 +08:00
Dudu-0223 7f86f79480 Merge master clock cleanup into refactor/agent-team-named-subagents 2026-10-19 17:51:07 +08:00
Dudu-0223 cafa0b7f54 Merge origin/master into refactor/agent-team-named-subagents 2026-10-19 17:50:30 +08:00
Dudu-0223 078040e055 Clarify subagent result delivery and shorten wait guidance 2026-10-19 17:49:18 +08:00
Dudu-0223 299eecdc28 Remove global identity from Python authoring snapshots 2026-10-19 16:33:27 +08:00
Dudu-0223 c89070dc4b Align scoped prompt replay fixtures 2026-10-19 16:14:00 +08:00
Dudu-0223 02a1e013f2 Merge master projection registration API 2026-10-19 16:10:13 +08:00
Dudu-0223 82cd8fe6e0 Keep collaboration prompts scoped to Team mode 2026-10-19 15:57:26 +08:00
Dudu-0223 47582f4ec9 Keep task-board wakeups in memory without inbox schema changes 2026-10-19 13:55:18 +08:00
Dudu-0223 ddc3678cdb test(web): align inbox wait and subagent presentation snapshots 2026-10-19 12:06:02 +08:00
Dudu-0223 a6fbd906ba test(team): align profile and packaged expectations with inbox controls 2026-10-19 11:53:51 +08:00
gengruilin 55e76567ae merge: update the MCP manager with master
# Conflicts:
#	docs/capability-seams.i18n.yaml
2026-10-19 11:52:07 +08:00
gengruilin cc71597e73 docs(mcp-manager): link the entry validator by its current name 2026-10-19 11:51:31 +08:00
Dudu-0223 f664bd98e2 Merge master and reconcile team inbox snapshots and generated catalogs 2026-10-19 11:38:03 +08:00
Dudu-0223 aa6a4249b8 refactor(team): derive teams from subagent trees and wait on inbox input 2026-10-19 11:34:11 +08:00
gengruilin a6fa50e204 test(mcp): refresh the template snapshot prompt after master sync 2026-10-18 04:08:10 +08:00
gengruilin 191ea92bfa chore(mcp-manager): align the package version with the 0.2.1-rc.2 release 2026-10-18 04:01:28 +08:00
gengruilin 990b8831bc merge: update the MCP manager with master
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/experimental/webworker-runtime/README.i18n.yaml
#	packages/experimental/webworker-runtime/tests/node/node-stubs.spec.ts
2026-10-18 04:01:22 +08:00
Dudu-0223 742133aa38 test(subagent): retain the Team recording under its headless owner 2026-10-17 21:18:33 +08:00
Dudu-0223 894a825c73 fix(subagent): preserve creator identity and admit prompts through Team roots 2026-10-17 21:10:41 +08:00
Dudu-0223 7a8e9c8ccf Merge master and reconcile shared subagent tool registration 2026-10-17 20:26:15 +08:00
Dudu-0223 fc0f34a618 refactor(agent-team): derive roster from subagent catalog 2026-10-17 20:21:03 +08:00
Dudu-0223 914bfdc381 test(subagent): assert cancellation and Team shutdown outcomes 2026-10-17 16:15:24 +08:00
Dudu-0223 72a492fbb7 test(subagent): align depth replay with independent member completion 2026-10-17 16:02:15 +08:00
Dudu-0223 213eec55f5 refactor(subagent): remove obsolete sibling catalog helper 2026-10-17 15:55:45 +08:00
Dudu-0223 87f4233cae Merge master into Agent Team refactor and align the rc.2 upgrade guide 2026-10-17 15:52:39 +08:00
Dudu-0223 9c58a4b89d refactor(subagent): own instances by Team and restore only message targets 2026-10-17 15:49:47 +08:00
Dudu-0223 3b49a0c91d Merge branch 'codex/team-wide-names' into codex/team-wide-descendants 2026-10-17 12:42:14 +08:00
Dudu-0223 05c510575e test(subagent): update multiline root discovery output pins 2026-10-17 12:41:33 +08:00
Dudu-0223 34ed76272c Merge Team-wide routing and current catalog names into descendant UI 2026-10-17 12:37:29 +08:00
Dudu-0223 fdbe86ef60 Merge verified Team replay assertions into naming layer 2026-10-17 12:34:18 +08:00
Dudu-0223 c65c8a338d test(subagent): require successful nested Team delivery 2026-10-17 12:33:12 +08:00
Dudu-0223 eefd0e1ef4 Merge Team-wide communication into named subagents 2026-10-17 12:31:10 +08:00
Dudu-0223 46144145f4 Merge master into Agent Team refactor and refresh its fixtures 2026-10-17 12:22:57 +08:00
Dudu-0223 3a575a3385 docs(subagent): describe Team-wide messaging consistently 2026-10-17 12:18:49 +08:00
Dudu-0223 27716078ef feat(subagent): discover and message the entire Team 2026-10-17 12:17:12 +08:00
Dudu-0223 4e0b05e243 fix(subagent): retain legacy catalog identity reservations 2026-10-16 16:57:18 +08:00
Dudu-0223 1837928d04 refactor(subagent): register tree names in the root catalog 2026-10-16 16:51:35 +08:00
Dudu-0223 8603c173e1 test(subagent): compare persisted names without untyped matchers 2026-10-16 16:00:52 +08:00
Dudu-0223 049e1d3d76 fix(subagent): align identity schemas and integration fixtures 2026-10-16 15:49:12 +08:00
Dudu-0223 6a8425ac38 docs(subagent): clarify lazy name-index reconstruction 2026-10-16 15:37:30 +08:00
Dudu-0223 72aeefb633 fix(subagent): keep invalid name errors at admission 2026-10-16 15:35:29 +08:00
Dudu-0223 3c0bbdfeb1 refactor(subagent): derive agent names from creation records 2026-10-16 15:33:23 +08:00
Dudu-0223 c3a4c95800 test(subagent): simplify name fixtures and replay overrides 2026-10-16 14:24:34 +08:00
gengruilin d00bcae24d chore(mcp): align the MCP manager version with the 0.2.1-alpha.4 release 2026-10-16 14:19:09 +08:00
gengruilin ba0da1c4d6 merge: update MCP manager core with master
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/api/remotes/package.json
#	packages/api/remotes/src/client/index.ts
#	packages/api/remotes/tsconfig.client.json
#	packages/api/remotes/tsconfig.host.json
#	packages/experimental/webworker-runtime/README.i18n.yaml
#	packages/experimental/webworker-runtime/tests/node/node-stubs.spec.ts
#	tsconfig.base.json
2026-10-16 14:13:28 +08:00
Dudu-0223 4ff75d1586 Merge branch 'feat/subagent-names' into integrate/team-descendants-names 2026-10-16 13:27:25 +08:00
Dudu-0223 fcb837620a test(subagent): include names in native preset schemas 2026-10-16 13:26:53 +08:00
Dudu-0223 d26e9bef67 Merge branch 'feat/subagent-names' into integrate/team-descendants-names 2026-10-16 13:15:23 +08:00
Dudu-0223 010f584c13 Update live Web subagent fixtures and name displays 2026-10-16 13:14:27 +08:00
Dudu-0223 709b144017 Merge branch 'feat/subagent-names' into integrate/team-descendants-names 2026-10-16 13:05:11 +08:00
Dudu-0223 ce50737b41 Record workflow child name in session replay 2026-10-16 13:04:45 +08:00
Dudu-0223 dcfe7479ac Merge branch 'feat/subagent-names' into integrate/team-descendants-names 2026-10-16 12:59:16 +08:00
Dudu-0223 f2bd43f4e8 Migrate headless delegation replay fixtures to names 2026-10-16 12:58:26 +08:00
Dudu-0223 34f94a974e Merge branch 'refactor/agent-team-named-subagents' into feat/subagent-names 2026-10-16 12:56:31 +08:00
Dudu-0223 2f9dec8fbd Merge master into Team subagent unification 2026-10-16 12:55:30 +08:00
Dudu-0223 3f67f2ce2b Merge branch 'feat/subagent-names' into integrate/team-descendants-names 2026-10-16 12:48:58 +08:00
Dudu-0223 d5ef28def1 Update remaining named-subagent test consumers 2026-10-16 12:48:20 +08:00
Dudu-0223 59588a8cc8 Merge named subagents into descendant Team support 2026-10-16 12:46:25 +08:00
Dudu-0223 7fd0dba80d Add tree-wide names for subagent collaboration 2026-10-16 12:33:15 +08:00
Dudu-0223 0518302384 test(web): preserve native timers when pinning fixture dates 2026-10-16 10:56:57 +08:00
Dudu-0223 296eb6af45 test(subagent): expect tree positions in cold discovery 2026-10-16 00:07:24 +08:00
Dudu-0223 dccc3ecf6f Merge origin/master and align the Team upgrade guide with alpha.4 2026-10-15 23:55:38 +08:00
Dudu-0223 3cd0624b8d refactor(subagent): list descendants without a scope switch 2026-10-15 23:40:14 +08:00
Dudu-0223 a5cf681cc9 Merge remote-tracking branch 'origin/refactor/agent-team-named-subagents' into fix/team-descendant-ui 2026-10-15 23:19:45 +08:00
Dudu-0223 a9ddb4271e test(team): align CLI and plugin manager assertions 2026-10-15 17:58:28 +08:00
Dudu-0223 7ec9cc2c6d docs(team): refresh the session query dependency graph 2026-10-15 17:57:49 +08:00
Dudu-0223 9ed7f57c75 fix(team): include nested descendants in the shared team 2026-10-15 17:49:03 +08:00
Dudu-0223 11479fca13 refactor(team): reuse subagents with id-only addressing 2026-10-15 17:43:51 +08:00
Dudu-0223 1e477804c6 fix(checks): preserve Cordis preset and event identifiers 2026-10-15 17:42:41 +08:00
gengruilin 4b6fd16be3 chore(mcp): align the MCP manager version with the release
The master sync moved every release member to 0.2.1-alpha.2; the MCP
manager, added in this branch, kept the previous version.
2026-10-13 18:01:56 +08:00
gengruilin 71d5e61aa1 test(mcp): restore the credential store for the template error snapshot
The headless profile now disables the managed credential store, which
left the MCP entry waiting for credentials and its tool unregistered.
The scenario re-enables the credentials row, and its system prompt
picks up the current master text.
2026-10-13 17:11:10 +08:00
gengruilin 032cd273a3 merge: update MCP manager core with master
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/experimental/webworker-runtime/README.i18n.yaml
#	snapshots/session/headless.snapshot.ts
2026-10-13 17:10:56 +08:00
gengruilin 6050bab6cc fix(webworker): refuse cross-spawn in the preview worker host
The Web composition now mounts the MCP manager, whose stdio transport
imports cross-spawn. Evaluating it in the browser worker failed on Node
globals and kept the manager from activating. Serve cross-spawn from a
structural refusal like execa, so HTTP servers keep working and a stdio
launch reports that it needs a Node Host.
2026-10-09 17:08:21 +08:00
gengruilin fe03b5f070 test(mcp): refresh template snapshot after master sync 2026-10-09 16:29:16 +08:00
gengruilin 802da1f916 merge: update MCP manager with master 2026-10-09 16:22:14 +08:00
gengruilin 6719b28294 feat(mcp): let plugins register servers and manage them over Remote
Plugins that integrate an MCP service now call ctx.mcpServers.register()
for their own lifetime instead of writing the manager's Settings entry, so
two applications never overwrite each other's servers, a settings write
needs no namespace lookup or restart to take effect, and disabling or
uninstalling the plugin withdraws the server's tools.

- The manager is a Typert Remote service: watch streams the complete
  server list, and restart and probe run the Host operations. The Web
  application mounts one shared manager after the credentials provider.
- Credential templates replace every resolved value with [redacted] in
  diagnostics, tool-call failures, and isError results instead of hiding
  all upstream text, so the model can act on a server's error.
- Remove health checks, the ${env:NAME} template scheme that credential
  references already cover, and the per-request credential re-check that
  duplicated the rotation-driven reconnect.
- Client-safe status and probe types move to the ./types exports, and the
  manager documentation moves from the tools reference to the MCP page.
2026-10-04 19:34:19 +08:00
gengruilin 072fab7181 chore(mcp): align manager version with the release line 2026-10-04 17:31:36 +08:00
gengruilin 3f1770b98e test(mcp): refresh template snapshot schemas after master sync 2026-10-04 17:31:36 +08:00
gengruilin f7ef25e89b merge: update MCP manager with master
Drop the manager's runtime invariant companion now that master removed
dsh-invariants, and regenerate the merged catalogs, graphs, and pairing
records.
2026-10-04 17:31:36 +08:00
gengruilin 94e09fab6d fix(mcp): 脱敏工具错误结果并精简连接状态 2026-10-01 17:58:43 +08:00
gengruilin c12b80daaf merge: propagate master updates to MCP lifecycle 2026-09-28 16:09:43 +08:00
gengruilin d56c84264c merge: update MCP manager with master 2026-09-28 16:09:16 +08:00
gengruilin 5ea147c87e merge: propagate MCP status listener isolation 2026-09-27 21:51:30 +08:00
gengruilin 29d6afdeb9 fix(mcp): isolate status listener failures 2026-09-27 21:46:50 +08:00
gengruilin 7d3083f1e5 merge: 同步 MCP 基础分支的主干适配 2026-09-27 20:51:51 +08:00
gengruilin 5db30c26f2 merge: 同步 master 并更新 MCP 文档索引 2026-09-27 20:46:44 +08:00
gengruilin 9b64765045 test(mcp): 录制模板凭据错误脱敏快照 2026-09-27 17:40:41 +08:00
gengruilin 27ae5a9022 feat(mcp): 补齐连接关闭、取消与运行期凭据处理 2026-09-27 17:39:44 +08:00
gengruilin eb0a43c796 Merge master and adapt MCP manager to volatile configuration
Use Settings and ConfigEditor through the manager's Loader entry, and select
the health-check method supported by the negotiated MCP protocol.

Update affected fixtures, package dependencies, and documentation while
preserving the original MCP manager implementation and commit history.
2026-09-26 15:48:46 +08:00
Yichen Jiang c98330ab04 chore(mcp): align mcp-manager with the 0.1.6-alpha.1 release version 2026-09-16 10:29:48 +08:00
Yichen Jiang 471b1767d2 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-09-16 10:27:24 +08:00
Yichen Jiang 94e2fd84fc fix(mcp): settle the merged supervisor against master's gates
The README Summary fits the new 100-word gate, and the failed-generation
settlement receives its diagnostic explicitly instead of re-deriving it
through an unreachable branch.
2026-09-14 19:45:34 +08:00
Yichen Jiang fac0bd08e8 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/capability-seams.md
#	docs/capability-seams.zh.md
#	docs/config-catalog.i18n.yaml
#	docs/config-catalog.md
#	docs/config-catalog.zh.md
#	docs/event-producer-consumer.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/subsystems/tools.i18n.yaml
#	packages/extensions/tool-cordis/src/api-catalog.ts
#	packages/mcp/README.i18n.yaml
#	packages/mcp/README.md
#	packages/mcp/README.zh.md
#	packages/mcp/mcp-client/README.i18n.yaml
#	packages/mcp/mcp-client/README.md
#	packages/mcp/mcp-client/README.zh.md
#	packages/mcp/mcp-client/src/connection.ts
#	packages/mcp/mcp-client/src/index.ts
#	packages/mcp/mcp-client/tests/reconnect.spec.ts
#	scripts/gen-cordis-catalog.ts
#	tsconfig.base.json
#	tsconfig.host.json
2026-09-14 19:41:18 +08:00
Yichen Jiang 4a9ec1ac81 chore(mcp-manager): align the package version with the 0.1.3-alpha.2 release 2026-09-07 23:03:16 +08:00
Yichen Jiang 3cbe0fbebf Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/config-catalog.i18n.yaml
#	docs/event-producer-consumer.i18n.yaml
#	docs/module-graph.i18n.yaml
#	packages/mcp/README.i18n.yaml
#	packages/mcp/README.md
#	packages/mcp/README.zh.md
#	packages/mcp/mcp-client/README.i18n.yaml
2026-09-07 23:02:09 +08:00
Yichen Jiang 015bf9c04d chore(mcp-manager): align the package version with the 0.1.3-alpha.1 release 2026-09-05 14:34:15 +08:00
Yichen Jiang eaefd91ac7 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core
# Conflicts:
#	docs/event-producer-consumer.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-09-05 14:32:55 +08:00
Yichen Jiang 080ce25425 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-09-04 15:07:12 +08:00
Yichen Jiang 5428df154f Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core
# Conflicts:
#	docs/event-producer-consumer.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
2026-09-04 14:36:39 +08:00
Yichen Jiang 8a3d86c683 chore(mcp-manager): align the package version with the 0.1.2-rc.1 release 2026-09-03 15:43:56 +08:00
Yichen Jiang efbb6d5e1c Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-09-03 15:42:56 +08:00
Yichen Jiang ff94a57778 chore(mcp-manager): align the package version with the 0.1.2-alpha.4 release 2026-09-02 09:53:46 +08:00
Yichen Jiang f9c5dd237b Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-09-02 09:52:32 +08:00
Yichen Jiang 08e8815e85 refactor(mcp-manager): keep the settings validator and schema alias package-private
ConfigSchema only aliased the already-public McpServerManager.Config, and
validateServers was exported for its tests alone; construction runs the
same validation synchronously, so the tests judge entries through the
constructor and the schema through the static Config.
2026-09-02 09:50:00 +08:00
Yichen Jiang bc486922d8 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-09-01 15:50:41 +08:00
Yichen Jiang 64e9caa667 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core 2026-09-01 15:45:16 +08:00
Yichen Jiang 5c928aa40e test(mcp-client): wait for a ping floor, not an exact count
Pings resume on the recovery generation, so the counter keeps growing;
the exact-2 wait raced the reconnect and timed out on slow Windows
runners observing 31 calls. The re-arm claim is the second ping existing
at all — the failure handling is proven by the diagnostic assertions.
2026-09-01 14:15:55 +08:00
Yichen Jiang 6a1c50825f fix(mcp-manager): keep assertTemplateSyntax JSDoc attached to its export 2026-09-01 14:02:21 +08:00
Yichen Jiang 35b18c04bf fix(mcp-manager): align the package version with the root workspace 2026-09-01 13:55:28 +08:00
Yichen Jiang 48857665d1 Merge remote-tracking branch 'origin/master' into feat/mcp-manager-core
# Conflicts:
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/mcp/mcp-client/README.i18n.yaml
#	packages/mcp/mcp-client/README.md
#	packages/mcp/mcp-client/README.zh.md
#	tsconfig.base.json
2026-09-01 13:47:43 +08:00
Yichen Jiang 5285c6dcc4 fix(mcp-client): a sync commits its tools only for a still-entitled generation
The startup budget's timeout only stopped waiting; a listing that answered
after it could still swap tools in for the abandoned generation — permanent
zombies under reconnect: enabled: false, and a false 'no tools were
registered' log. syncTools now re-judges eligibility at the commit point
(after the listing await): the attempt catch flags abandonment synchronously
before any close await, and the commit predicate also re-includes currency,
which closes the same window for notification re-syncs against a just-closed
generation. State publishes at its commit point or not at all.
2026-09-01 12:49:30 +08:00
Yichen Jiang 09570cf383 fix(mcp): review round one — write-time bounds, template anchoring, probe abort, listener containment
- ServerEntrySchema mirrors the client's startupTimeoutMs/healthCheckIntervalMs
  bounds and validateServers re-judges the reconnect policy via the client's
  own resolveReconnectPolicy, so a bad entry is refused where it is written
- assertTemplateSyntax anchors each ${...} occurrence to a whole token; a
  nested prefix the resolver would leave behind is refused at the write
- probeServer answers a pre-aborted signal immediately and breaks a hung
  connect by closing the client on abort; the option's JSDoc says exactly that
- mcp-manager/status emission and async status observers are contained per
  docs/defensive-patterns.md; onChange admits void | Promise<void>
- toClientConfig resolves through the client schema (no type assertions),
  the gave-up disposal loop contains a disposer throw, and the mount re-check
  comment states the serialized-tail truth
2026-09-01 12:14:51 +08:00
Yichen Jiang bf1e5d470e feat(mcp-manager): env and header values echo to management surfaces
The role('secret') marks made the settings wire redact stored env/header
values, so an editor could never show what the document holds. The values
stay literal now — template strings included — and a value that must stay
hidden belongs in a ${env:} or ${credential:} template, which never
resolves into the document.
2026-09-01 11:39:32 +08:00
Yichen Jiang 032fd5f6d5 fix(mcp-client): keep the cause chain in connection diagnostics
String(error) flattened a fetch failure to 'TypeError: fetch failed',
dropping the actionable cause (connect ECONNREFUSED host:port, a DNS or
TLS code). describeError walks the cause chain into
'outer, cause: inner, …' — raw identifiers verbatim, since they are what
gets searched or handed to a model — and feeds the attempt, ping, probe,
and unclosed-generation diagnostics.
2026-09-01 10:58:59 +08:00
Yichen Jiang 2425fa4f84 feat(mcp-client): liveness health pings on established connections
Status downgrades were driven by the transport close signal alone. A stdio
child's death closes the transport, but Streamable HTTP holds no connection
between requests, so a dead server kept showing connected until the next
actual call. An established generation now sends a protocol ping every
healthCheckIntervalMs (default 30s, the same value bounding each answer);
a failed or timed-out ping closes the generation into the existing
reconnect path and its diagnostic rides the reconnecting status.
The manager passes healthCheckIntervalMs through per server entry.
2026-08-31 20:03:35 +08:00
Yichen Jiang 78d7690b79 refactor(mcp-client): share the settlement deadline between the startup and probe budgets
The startup-budget and probe-deadline wrappers carried the same timer body,
which the duplication gate rejects; both now delegate to one withDeadline in
src/deadline.ts with their own diagnostics.
2026-08-31 17:45:08 +08:00
Yichen Jiang 6acca42d71 feat(mcp): settings-driven MCP server manager (dsh-mcp-manager)
One service plugin (ctx.mcpServers) owns the mcp settings namespace and
reconciles a fleet of dsh-mcp-client child instances: entries mount,
rebuild, disable, and unmount live as the settings document changes, with
the composition entry as the non-deletable base layer.

- CRUD and enable/disable ride the settings seam (revision fencing, secret
  redaction); no new write API
- flat entry schema so role('secret') env/header values stay redactable;
  validateServers refuses unserviceable entries at the write
- ${env:NAME} / ${credential:REF} templates resolve at mount; credential
  rotation rebuilds referencing servers, healing earlier resolve failures
- list() + mcp-manager/status event aggregate per-server status; restart()
  is the manual recovery from terminal failed states; probe() tests an
  entry without touching the live set
- real-composition Loader test, invariant companion, bilingual README,
  config/cordis catalogs, module graph, Agent Note
2026-08-31 16:29:45 +08:00
Yichen Jiang 0648ca528f feat(mcp-client): connection status surface, one-shot probe, and a startup budget
- statusOf/connectionStatusOf: a per-scope read-and-observe handle over the
  supervisor's states (connecting/connected/reconnecting/failed) registered
  beside the serverName reservation
- probeServer(): connect + identify + list tools + close without reserving a
  name or touching ctx.tools, stage-classified failures, bounded pages/names
- startupTimeoutMs (default 30s) bounds every connection attempt plus its
  initial tool sync; reconnect semantics unchanged
- exports ReconnectSchema and SERVER_NAME_PATTERN for composing managers
2026-08-31 16:29:33 +08:00
2559 changed files with 78201 additions and 32124 deletions

No files matched your search

+3
View File
@@ -2662,6 +2662,9 @@
"simplification/2026-09-19-deepseek-messages-only.i18n.yaml": "sha256:7ffd708c5823c39cc9536178292e7e7fa57db5267bbc9acdda0a87e4953d5ba8",
"simplification/2026-09-19-deepseek-messages-only.md": "sha256:241861200ce73e02f77d182633d6ea10b1f29c696c81bff002680762db01d9b5",
"simplification/2026-09-19-deepseek-messages-only.zh.md": "sha256:311ef6f8f4cf60dae2a43ca94d772028b645631501fb3648aaeb406ba174991e",
"simplification/2026-09-26-team-direct-inbox.i18n.yaml": "sha256:9a6f1137f0c68d976fda4f44c5c301b851a8ee8e46965f3ea808ec5551045fb0",
"simplification/2026-09-26-team-direct-inbox.md": "sha256:88f7d9be3ae0b740ddb5a61a16e3c64044a207584b973acab8c3a40c36a7ff30",
"simplification/2026-09-26-team-direct-inbox.zh.md": "sha256:d6d1fafa4aa7035a11930cca3bf998d676a62b5f9b75176dff69c3c5ff6b92f0",
"testing/2026-06-11-property-based-testing.i18n.yaml": "sha256:9d378912101ad30203f1fcbcad23e0f5d6ec6f39e04a83f17f90887b5c5d761e",
"testing/2026-06-11-property-based-testing.md": "sha256:cca3b6299784cff94965f21b6d59966109a0bb41fdc2f8cf56653fa6c417a20a",
"testing/2026-06-11-property-based-testing.zh.md": "sha256:09f3d1e2defd2792f87fb0e9892fb7e1db006f3c75ea4581f97e9cde4ea98175",
@@ -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 .agents/notes/archived/simplification/2026-09-26-team-direct-inbox.md
2026-09-26-team-direct-inbox.md: 9b90d7ae30e0b711c602de9280cc11845d0525f0
2026-09-26-team-direct-inbox.zh.md: 8838fb4a2824838b76142f30a6ff4d0aedf0276b
@@ -1,6 +1,7 @@
# Agent Note: Team messages use the target inbox
Status: implemented
Archived: 2026-10-15
English | [中文](2026-09-26-team-direct-inbox.zh.md)
@@ -1,6 +1,7 @@
# Agent Note: Team 消息使用目标 inbox
Status: implemented
Archived: 2026-10-15
[English](2026-09-26-team-direct-inbox.md) | 中文
@@ -6,23 +6,23 @@
en: ca120635fc40431c
zh: 6be7c5ae3c86cdcc
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/problem:
en: c8b932b02a4e1c80
zh: 96c466be1a521f3f
en: df1e82a74c42fa1d
zh: 8d5452360a0dec8a
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/decision:
en: 2e08d801099cbe18
zh: 8c5ab162ada9cc41
en: 7a4921d82cd11749
zh: 6ab506109afc1351
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/package-topology:
en: 5ea3ddfae82df181
zh: 2d05d95e51081696
en: 3394139c17d28ba8
zh: 66857733ba51d98c
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/ctx-web-contract:
en: b432f80a424c3e49
zh: faa26925fdd98387
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/provider-availability-and-selection:
en: 4e4ec068c2a22383
zh: b7030d0f00bfdb74
en: afc540d986e00d68
zh: 84795770d00228c3
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/search-request-and-result-schema:
en: ec3703b7e7c6b23d
zh: 325755e6e90092da
en: ccf6404ae7f3172e
zh: 3253035ea49e2548
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/fetch-request-and-result-schema:
en: 6dba3b9a51c28b6d
zh: 58a93f6ba2d269ea
@@ -33,8 +33,8 @@
en: 3440f2b34b93f4a1
zh: 57a8f784c7e26eff
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/testing:
en: 7f73699ac4459432
zh: c16be49ac50c34c8
en: 03da2f3422183384
zh: 10fede51a415ce11
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered:
en: 6119e2aff3b6be5a
zh: 76136ef5249c23eb
@@ -42,8 +42,8 @@
en: e0d84a9b1b9c2123
zh: 9e116a7b96751563
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/put-provider-dispatch-directly-in-dsh-tool-web:
en: dc01d014d03b49d2
zh: b956b02f41c62682
en: 68ef45e8e5a36de9
zh: 0013786ed77e03a8
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/alternatives-considered/split-search-and-fetch-into-two-seams-dsh-search-dsh-fetch:
en: 3fb72ba3e554f20a
zh: aff5f0add5773388
@@ -63,11 +63,11 @@
en: 3ea94ffb142d144a
zh: 89645f68412f6769
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/consequences:
en: 9774448fd6b21c41
zh: 8411c590ad70d5c4
en: 8e7523b2858efaf3
zh: aff8fc74eb439ed6
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/deferred-work:
en: 48b7b8e7310cd56b
zh: d406cd6a8a649e58
en: ddb599fd2ac1647c
zh: c0b8f446cf483b3c
/agent-note-web-capability-seam-stable-tools-over-multiple-providers/open-questions:
en: 63848c6ef9a6ab44
zh: 041b340802091e60
@@ -6,7 +6,7 @@ English | [中文](2026-06-24-web-capability-seam.zh.md)
## Problem
The harness needs model-facing web tools without binding the model contract to one vendor's API shape. Search is the immediate pressure point: supporting both Exa search and Perplexity search from the start — two deliberately different provider shapes (Exa returns a flat `results[]` of `{title, url, highlights, publishedDate}`; Perplexity returns a generated answer plus citations) — is what proves the normalized web contract does not just mirror one vendor. Fetch is a separate operation: an anonymous public HTTP(S) fetch backend has transport, security, redirect, decoding, and size-limit concerns that are not the same as provider-backed search.
The harness needs model-facing web tools without binding the model contract to one vendor's API shape. Search is the immediate pressure point: search APIs return deliberately different formats (Exa returns a flat `results[]` of `{title, url, highlights, publishedDate}`, Perplexity returns a generated answer plus citations, and the DeepSeek Search API returns a flat `results[]` with snippets and summaries), and the normalized web contract must accept each of them without mirroring one vendor. The seam was designed against the Exa and Perplexity formats; neither provider ships, and a plugin can register one. Fetch is a separate operation: an anonymous public HTTP(S) fetch backend has transport, security, redirect, decoding, and size-limit concerns that are not the same as provider-backed search.
The model-facing API must stay stable while backends change. A search provider swap should not change how the model asks for a query, and a fetch implementation swap should not change how the model asks for a URL. Conversely, a provider package should not expose its own model-facing tool schema just because it has extra provider-specific knobs.
@@ -19,7 +19,7 @@ There is also a provider-selection question. Existing `tool-bash` and `tool-fs`
Web access is a first-class capability seam following [the capability-seam Agent Note](2026-06-13-capability-seams.md):
1. `@deepseek-ai/dsh-web` (`packages/web/web`) owns `ctx.web`, provider registration, provider selection, shared request/result vocabulary, and web-specific errors.
2. Provider packages implement concrete backends and register capabilities with `ctx.web`, for example `@deepseek-ai/dsh-web-search-exa`, `@deepseek-ai/dsh-web-search-perplexity`, `@deepseek-ai/dsh-web-search-deepseek`, and `@deepseek-ai/dsh-web-fetch-http`.
2. Provider packages implement concrete backends and register capabilities with `ctx.web`, for example `@deepseek-ai/dsh-web-search-deepseek`, `@deepseek-ai/dsh-web-deepseek-api`, and `@deepseek-ai/dsh-web-fetch-http`.
3. `@deepseek-ai/dsh-tool-web` (`packages/web/tool-web`) owns the model-facing `web_search` and `web_fetch` tool schemas, prompt sections, argument validation, result formatting, and tool-owned presentation over `ctx.web`.
Providers do not register tools. Providers register capabilities. `dsh-tool-web` is the only owner of model-facing names, descriptions, prompt guidance, JSON schemas, and presentation.
@@ -32,9 +32,9 @@ Search and fetch are separate tools but one web-access seam. `ctx.web` owns prov
- A tool is never unregistered merely because its selected provider is missing, misconfigured, missing credentials, ambiguous, or temporarily unavailable.
- The provider is resolved at execution time, and a structured `WebError` is returned when the selected capability cannot run.
This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. If web search is enabled but no usable search provider exists, `web_search` remains visible and execution fails with a structured `WebError` such as `WEB_PROVIDER_UNAVAILABLE` or `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`. `dsh-tool-web` narrows this per model request: it [withholds `web_search` while the selected provider reports that the request's search cannot authenticate](2026-09-27-web-search-offered-by-provider-readiness.md). If a provider appears after `dsh-tool-web`, the next execution can use it without changing the schema. If a provider disappears mid-call, execution fails with a structured `WebError` instead of silently choosing another provider or falling through to `UNKNOWN_TOOL`.
This keeps the model schema stable without making plugin load order, credential state, or HMR timing part of the model-facing contract. If web search is enabled but no usable search provider exists, `web_search` remains visible and execution fails with a structured `WebError` such as `WEB_PROVIDER_UNAVAILABLE` or `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`. `dsh-tool-web` narrows this per model request: it [withholds `web_search` or `web_fetch` while the selected provider reports that the request's operation cannot authenticate](2026-09-27-web-search-offered-by-provider-readiness.md). If a provider appears after `dsh-tool-web`, the next execution can use it without changing the schema. If a provider disappears mid-call, execution fails with a structured `WebError` instead of silently choosing another provider or falling through to `UNKNOWN_TOOL`.
The seam deliberately exposes no observation surface — no registry-change event and no aggregated capability-status query. Unavailability is a fact a caller observes by executing: `search()`/`fetch()` resolve the provider at call time and throw the structured `WebError` that names what failed. [The observation-surface Agent Note](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) records that judgment: derived-on-call selection and enablement-based registration leave no consumer that needs a change signal or an availability probe distinct from executing and routing the error, and a future provider-status panel reintroduces the smallest signal or query it actually consumes.
The seam deliberately exposes no availability observation — no aggregated capability-status query and no per-provider availability report. Unavailability is a fact a caller observes by executing: `search()`/`fetch()` resolve the provider at call time and throw the structured `WebError` that names what failed. [The observation-surface Agent Note](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) records that judgment: derived-on-call selection and enablement-based registration leave no consumer that needs a change signal or an availability probe distinct from executing and routing the error, and a future provider-status panel reintroduces the smallest signal or query it actually consumes. Provider pickers consume registration, not availability: `listProviders()` and the Remote method `web/listProviders` list every registered provider with its optional `title` and `description` (including unavailable providers), and the payload-free `web/providers-changed` event fires after each registry mutation. The [web provider selection decision](2026-10-29-web-provider-selection.md) records why registration is observable while availability is not.
## Package topology
@@ -43,11 +43,9 @@ The three-package Service Definition / Service Provider / Consumer split follows
The dependency direction mirrors bash and filesystem:
```text
@deepseek-ai/dsh-tool-web --depends on--> @deepseek-ai/dsh-web <--depends on-- @deepseek-ai/dsh-web-search-exa
@deepseek-ai/dsh-tool-web --depends on--> @deepseek-ai/dsh-web <--depends on-- @deepseek-ai/dsh-web-search-deepseek
consumer interface implementation
<--depends on-- @deepseek-ai/dsh-web-search-perplexity
implementation
<--depends on-- @deepseek-ai/dsh-web-search-deepseek
<--depends on-- @deepseek-ai/dsh-web-deepseek-api
implementation
<--depends on-- @deepseek-ai/dsh-web-fetch-http
implementation
@@ -57,9 +55,7 @@ At runtime, provider packages register capabilities with `ctx.web`; `tool-web` r
```mermaid
flowchart LR
exa["@deepseek-ai/dsh-web-search-exa"] -->|registerSearchProvider| web["@deepseek-ai/dsh-web / ctx.web"]
perplexity["@deepseek-ai/dsh-web-search-perplexity"] -->|registerSearchProvider| web
deepseek["@deepseek-ai/dsh-web-search-deepseek"] -->|registerSearchProvider| web
deepseek["@deepseek-ai/dsh-web-search-deepseek"] -->|registerSearchProvider| web["@deepseek-ai/dsh-web / ctx.web"]
deepseekApi["@deepseek-ai/dsh-web-deepseek-api"] -->|registerSearchProvider / registerFetchProvider| web
fetchLocal["@deepseek-ai/dsh-web-fetch-http"] -->|registerFetchProvider| web
toolWeb["@deepseek-ai/dsh-tool-web"] -->|search/fetch| web
@@ -67,9 +63,9 @@ flowchart LR
toolWeb -->|ctx.tools.register| webFetch["tool: web_fetch"]
```
`@deepseek-ai/dsh-web` depends only on Cordis and low-level harness support. It declares `ctx.web`, provider interfaces, request/result types, the provider availability contract, and error codes. It does not import tool, agent, session, LLM, or provider packages.
`@deepseek-ai/dsh-web` depends only on Cordis and low-level harness support, including the Typert protocol for its Remote method. It declares `ctx.web`, provider interfaces, request/result types, the provider availability contract, and error codes. It does not import tool, agent, session, LLM, or provider packages.
Provider packages depend only on `dsh-web` and Cordis. They own credentials, endpoints, wire mapping, parsing, and `WebError` translation, using platform `fetch`. Each provider injects the shared service and registers a backend; only `dsh-web` owns the `ctx.web` key. Provider-private protocol shapes do not create dependencies on `ctx.llm` or a Cordis HTTP service.
Provider packages depend on `dsh-web`, Cordis, and their credential/configuration helpers. They own credentials, endpoints, wire mapping, parsing, and `WebError` translation, using platform `fetch`. Each provider injects the shared service and registers a backend; only `dsh-web` owns the `ctx.web` key. Provider-private protocol shapes do not create dependencies on `ctx.llm` or a Cordis HTTP service.
`@deepseek-ai/dsh-tool-web` depends on `@deepseek-ai/dsh-web`, `@deepseek-ai/dsh-tools`, `@deepseek-ai/dsh-system-prompt`, and Cordis. It never imports concrete provider packages.
@@ -78,16 +74,22 @@ Provider packages depend only on `dsh-web` and Cordis. They own credentials, end
`ctx.web` is a provider registry plus a provider-selecting execution API. The registry half stays close to `LlmRuntime`: a `Map<id, provider>` per capability kind, `registerSearchProvider` / `registerFetchProvider` methods that return disposers, duplicate ids that throw `WebError`, and execution-time resolution that throws when the selected provider is absent or unusable. The authoritative signatures live in `packages/web/web/src/types.ts`; the seam's shape:
```ts
import type { WebFetchRequest, WebFetchResult, WebSearchRequest, WebSearchResult } from '@deepseek-ai/dsh-web'
import type {
WebFetchRequest, WebFetchResult, WebProviderInfo, WebProviderList, WebSearchRequest, WebSearchResult,
} from '@deepseek-ai/dsh-web'
interface WebSearchProvider {
readonly id: string
readonly title?: string
readonly description?: WebProviderInfo['description']
available(): boolean
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
}
interface WebFetchProvider {
readonly id: string
readonly title?: string
readonly description?: WebProviderInfo['description']
available(): boolean
fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
}
@@ -95,6 +97,7 @@ interface WebFetchProvider {
interface WebRuntime {
registerSearchProvider(provider: WebSearchProvider): () => void
registerFetchProvider(provider: WebFetchProvider): () => void
listProviders(): WebProviderList
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
@@ -131,18 +134,15 @@ The "single provider auto-selects" rule is for tests, demos, and simple deployme
- id: web
name: '@deepseek-ai/dsh-web'
config:
searchProvider: exa
searchProvider: deepseek-search-api
fetchProvider: http
- id: web-search-exa
name: '@deepseek-ai/dsh-web-search-exa'
- id: web-search-perplexity
name: '@deepseek-ai/dsh-web-search-perplexity'
- id: web-search-deepseek
name: '@deepseek-ai/dsh-web-search-deepseek'
- id: web-deepseek-api
name: '@deepseek-ai/dsh-web-deepseek-api'
- id: web-fetch-http
name: '@deepseek-ai/dsh-web-fetch-http'
@@ -150,9 +150,9 @@ The "single provider auto-selects" rule is for tests, demos, and simple deployme
name: '@deepseek-ai/dsh-tool-web'
```
Operational overrides feed the same explicit selection path: `DSH_WEB_SEARCH_PROVIDER=perplexity` is equivalent to config `searchProvider: perplexity`, not a hidden priority chain inside `dsh-tool-web`.
Operational overrides feed the same explicit selection path: `DSH_WEB_SEARCH_PROVIDER=deepseek-search-api` is equivalent to config `searchProvider: deepseek-search-api`, not a hidden priority chain inside `dsh-tool-web`.
Each search or fetch reads the configured id when it runs, so a saved settings change selects another provider for the next operation without remounting `dsh-web`. The Web search settings switch relies on this to move both capabilities to the [DeepSeek Search and Fetch APIs](../feature/2026-10-15-deepseek-search-fetch-api.md) at once.
Each search or fetch reads the configured id when it runs, so a saved settings change selects another provider for the next operation without remounting `dsh-web`. The Search and Fetch menus on the Web search and fetch settings page rely on this to switch each capability's provider independently, including the [DeepSeek Search and Fetch APIs](../feature/2026-10-15-deepseek-search-fetch-api.md); the [web provider selection decision](2026-10-29-web-provider-selection.md) owns the shipped defaults.
`ctx.web.search()` and `ctx.web.fetch()` resolve the provider at execution time using the selection rules above. If the selected capability is unavailable, they throw `WebError` with a structured code such as `WEB_PROVIDER_UNAVAILABLE`, `WEB_PROVIDER_CONFIGURED_MISSING`, `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`, or `WEB_PROVIDER_AMBIGUOUS`. If no provider is explicitly configured and no usable provider exists, the execution error is the generic `WEB_PROVIDER_UNAVAILABLE` case; there is deliberately no diagnostic summary of every unavailable provider.
@@ -168,8 +168,8 @@ The `web_search` model-facing tool is small. The only model-facing argument is:
- `dsh-tool-web` owns the value and puts it on `WebSearchRequest.maxResults`.
- `ctx.web` passes the request through to the selected provider unchanged.
- A provider applies `maxResults` at the request layer when its API supports it (Exa's `numResults`), as a cost/latency optimization.
- `ctx.web` enforces the bound on the result: if a provider returns more than `maxResults` sources — because its API has no result-count control (Perplexity) or ignored the hint — the seam truncates `sources[]` to `maxResults` and sets `WebSearchResult.truncated` to `true` before returning. This makes the bound a single cross-provider guarantee the model-facing layer can rely on, rather than something each provider must remember to honor.
- A provider applies `maxResults` at the request layer when its API supports it (the DeepSeek Search API's `count`), as a cost/latency optimization.
- `ctx.web` enforces the bound on the result: if a provider returns more than `maxResults` sources — because its API has no result-count control or ignored the hint — the seam truncates `sources[]` to `maxResults` and sets `WebSearchResult.truncated` to `true` before returning. This makes the bound a single cross-provider guarantee the model-facing layer can rely on, rather than something each provider must remember to honor.
The seam request carries no provider-specific controls — no Perplexity model selection, search recency, domain filters, Exa `livecrawl`, Exa `type`, regional hints, generated-answer budgets, or search depth. Such a field is added only when it has provider-neutral semantics that both the tool schema and selected providers can honor honestly.
@@ -190,13 +190,14 @@ interface WebSearchSource {
readonly url: string
readonly title?: string
readonly snippet?: string
readonly summary?: string
readonly publishedAt?: string
}
```
`content` is optional provider-generated answer text, search context, or summary. `sources[]` is the portable citation shape. A source always has a URL; title, snippet, and `publishedAt` are optional because not every provider returns them. `title` is not required: Perplexity-style citations may provide only URLs, and forcing adapters to invent titles would make the seam lie. `dsh-tool-web` renders a `title ?? hostname(url)`-style fallback label for display. `publishedAt` is an optional publication/crawl timestamp as an ISO-8601 string — Exa returns it as `publishedDate` on each result and Perplexity returns a `date` on search results, so it is real provider data, not derived; the seam carries it as a string and leaves date parsing to the consumer.
`content` is optional provider-generated answer text, search context, or summary. `sources[]` is the portable citation shape. A source always has a URL; title, snippet, summary, and `publishedAt` are optional because not every provider returns them. `snippet` is a short excerpt for display, and `summary` is a longer excerpt for model reading whose length the consumer bounds. `title` is not required: Perplexity-style citations may provide only URLs, and forcing adapters to invent titles would make the seam lie. `dsh-tool-web` renders a `title ?? hostname(url)`-style fallback label for display. `publishedAt` is an optional publication/crawl timestamp as an ISO-8601 string — the DeepSeek Search API returns it as a nullable `date` on each result, Exa as `publishedDate`, and Perplexity as `date`, so it is real provider data, not derived; the seam carries it as a string and leaves date parsing to the consumer.
Exa search maps each entry of the provider's flat `results[]` into a `WebSearchSource`: `url` ← `url`, `title` ← `title`, `snippet` ← the first `highlights[]` entry (an entry with no highlight has no portable snippet and is dropped), `publishedAt` ← `publishedDate`. Exa returns no provider-generated answer, so `content` is omitted. Perplexity search maps `choices[0].message.content` to `content` and prefers the structured top-level `search_results[]` for `sources[]` — `url` ← `url`, `title` ← `title`, `snippet` ← `snippet` (often empty), `publishedAt` ← `date` — falling back to the URL-only `citations[]` array only when `search_results` is absent (those sources carry just a `url`). If a provider returns fewer structured fields than the seam supports, the adapter omits those optional fields.
The DeepSeek Search API provider maps each entry of the response's `results[]` into a `WebSearchSource`: `url` ← `url`, `title` ← `title`, `snippet` ← `snippet`, `summary` ← `summary`, `publishedAt` ← `date`, omitting empty values. It returns no provider-generated answer, so `content` is omitted. If a provider returns fewer structured fields than the seam supports, the adapter omits those optional fields.
Full page retrieval remains the job of `web_fetch(url)`. Search snippets are discovery context, not fetched page bodies.
@@ -283,7 +284,7 @@ Tool execution lets these errors flow through `ToolRuntime.execute()`, which alr
## Testing
Each layer is pinned at its own boundary: the registry/selection/truncation/abort contract and the `WebError` codes in `dsh-web`; per-provider request/response mapping over recorded fixtures (Perplexity fixtures include URL-only citations so the optional source fields stay honest) plus a self-skipping with-key smoke per real provider; real local-HTTP behavior in `web-fetch-http`; and enablement-driven registration, structured execution errors, and result formatting through the real tool registry in `dsh-tool-web`. A real-Loader smoke guards the two export shapes ([postmortem 0001](../../../../docs/postmortem/0001-acp-default-export-drops-inject.md)): `dsh-web` is a default-exported service, while the providers and `tool-web` are namespace plugins where a stray `export default` would drop `inject`.
Each layer is pinned at its own boundary: the registry/selection/truncation/abort contract and the `WebError` codes in `dsh-web`; per-provider request/response mapping over recorded fixtures plus a self-skipping with-key smoke per real provider; real local-HTTP behavior in `web-fetch-http`; and enablement-driven registration, structured execution errors, and result formatting through the real tool registry in `dsh-tool-web`. A real-Loader smoke guards the two export shapes ([postmortem 0001](../../../../docs/postmortem/0001-acp-default-export-drops-inject.md)): `dsh-web` is a default-exported service, while the providers and `tool-web` are namespace plugins where a stray `export default` would drop `inject`.
## Alternatives considered
@@ -293,7 +294,7 @@ This matches the most flexible provider-plugin systems: every provider can expos
### Put provider dispatch directly in `dsh-tool-web`
This resembles OpenCode's local web search: one stable `websearch` tool dispatches to Exa or Parallel internally. It is acceptable for a small product path but wrong as a harness foundation. The tool package would own provider selection, credentials, request mapping, transport, response parsing, and presentation, making it hard to add Exa and Perplexity without baking their differences into the tool schema.
This resembles OpenCode's local web search: one stable `websearch` tool dispatches to Exa or Parallel internally. It is acceptable for a small product path but wrong as a harness foundation. The tool package would own provider selection, credentials, request mapping, transport, response parsing, and presentation, making it hard to add providers with different formats without baking their differences into the tool schema.
### Split search and fetch into two seams (`dsh-search`, `dsh-fetch`)
@@ -321,9 +322,9 @@ Rejected for the shipped presets. Per-call confirmation would interrupt ordinary
## Consequences
**The search schema is deliberately thin.** Exa and Perplexity both expose useful provider-specific controls; a control is added only once it can be defined provider-neutrally and enforced honestly by both tool registration and provider execution.
**The search schema is deliberately thin.** Search APIs expose useful provider-specific controls, such as Exa's `livecrawl`, Perplexity's search recency, and the DeepSeek Search API's `localization`; a control is added only once it can be defined provider-neutrally and enforced honestly by both tool registration and provider execution.
**Perplexity citations can be sparse.** A citation may be only a URL. Making `title` and `snippet` optional keeps the seam truthful but means `tool-web` renders fallback labels.
**Citations can be sparse.** A Perplexity-style citation may be only a URL. Making `title` and `snippet` optional keeps the seam truthful but means `tool-web` renders fallback labels.
**Stable tool registration defers misconfiguration to execution.** Keeping the tool visible is correct when the product enabled web access, but product apps that expect web search should surface the structured `WEB_PROVIDER_CONFIGURED_MISSING` / `WEB_PROVIDER_CONFIGURED_UNAVAILABLE` / `WEB_PROVIDER_AMBIGUOUS` failures loudly so users do not discover setup problems only after the model calls the tool.
@@ -337,7 +338,7 @@ Rejected for the shipped presets. Per-call confirmation would interrupt ordinary
- A `pdf` `WebFetchBody` kind: the `http` provider decodes text-extractable PDFs (best-effort, capped, `truncated`) into a `{ kind: 'pdf'; content; pageCount? }` arm, and `tool-web` renders it. This is fetch, not `web_extract` — PDF retrieval is a concrete HTTP 200 plus deterministic local decoding, not provider-side extraction of a non-HTTP resource. Adding it is a coordinated change across `dsh-web` (declare the arm), the provider (decode + narrow "binary rejection" to "reject binary except text-extractable PDF"; scanned/image PDFs needing OCR stay out of scope), and `tool-web` (render). The closed `WebFetchBody` union makes the consumer side fail to compile until the new arm is handled.
- Provider-backed extraction as a separate `web_extract` capability, rather than widening `web_fetch` silently.
- Provider-neutral search controls beyond `query` and `maxResults`, once Exa and Perplexity can both honor them honestly.
- Provider-neutral search controls beyond `query` and `maxResults`, once the registered providers can honor them honestly.
## Open questions
@@ -6,7 +6,7 @@ Status: implemented
## 问题
harness 需要面向模型的 web 工具,但不能将模型约定绑定到某一家厂商的 API 形状上。搜索是当前的压力点:从一开始就同时支持 Exa 搜索和 Perplexity 搜索——两种刻意不同的提供方形状(Exa 返回扁平的 `results[]`,每项包含 `{title, url, highlights, publishedDate}`;Perplexity 返回一段生成式回答加引用列表)——正是用来证明归一化的 web 约定并非只是镜像某一家厂商。Fetch 是另一项独立操作:匿名公开 HTTP(S) fetch 后端涉及传输、安全、重定向、解码和大小限制等关注点,与提供方支撑的搜索并不相同。
harness 需要面向模型的 web 工具,但不能将模型约定绑定到某一家厂商的 API 形状上。搜索是当前的压力点:各搜索 API 返回刻意不同的格式(Exa 返回扁平的 `results[]`,每项包含 `{title, url, highlights, publishedDate}`;Perplexity 返回一段生成式回答加引用列表;DeepSeek Search API 返回带 snippet 与摘要的扁平 `results[]`),归一化的 web 约定必须能接纳每一种格式,而不是镜像某一家厂商。seam 是以 Exa 和 Perplexity 的格式为参照设计的;这两个提供方都不随附,插件可以注册它们。Fetch 是另一项独立操作:匿名公开 HTTP(S) fetch 后端涉及传输、安全、重定向、解码和大小限制等关注点,与提供方支撑的搜索并不相同。
面向模型的 API 必须保持稳定,而后端可以更换。更换搜索提供方不应改变模型发起查询的方式;更换 fetch 实现不应改变模型请求 URL 的方式。反过来,提供方包也不应仅仅因为自己有额外的提供方特有旋钮就暴露自己的面向模型工具 schema。
@@ -19,7 +19,7 @@ harness 需要面向模型的 web 工具,但不能将模型约定绑定到某
Web 访问是一个一等能力 seam,遵循[能力 seam Agent Note](2026-06-13-capability-seams.zh.md):
1. `@deepseek-ai/dsh-web`(`packages/web/web`)拥有 `ctx.web`、提供方注册、提供方选择、共享的请求/结果词汇,以及 web 特有的错误。
2. 提供方包实现具体后端并向 `ctx.web` 注册能力,例如 `@deepseek-ai/dsh-web-search-exa`、`@deepseek-ai/dsh-web-search-perplexity`、`@deepseek-ai/dsh-web-search-deepseek` 和 `@deepseek-ai/dsh-web-fetch-http`。
2. 提供方包实现具体后端并向 `ctx.web` 注册能力,例如 `@deepseek-ai/dsh-web-search-deepseek`、`@deepseek-ai/dsh-web-deepseek-api` 和 `@deepseek-ai/dsh-web-fetch-http`。
3. `@deepseek-ai/dsh-tool-web`(`packages/web/tool-web`)拥有面向模型的 `web_search` 和 `web_fetch` 工具 schema、提示词段落、参数校验、结果格式化,以及通过 `ctx.web` 实现的工具展示。
提供方不注册工具。提供方注册能力。`dsh-tool-web` 是面向模型的名称、描述、提示词引导、JSON Schema、展示的唯一所有者。
@@ -32,9 +32,9 @@ Web 访问是一个一等能力 seam,遵循[能力 seam Agent Note](2026-06-13
- 工具绝不会仅仅因为其选定的提供方缺失、配置错误、缺少凭证、存在歧义或暂时不可用就被注销。
- 提供方在执行时解析,当选定的能力无法运行时返回结构化的 `WebError`。
这使模型 schema 保持稳定,而不将插件加载顺序、凭证状态或 HMR(热模块替换)时序纳入面向模型的约定。如果 web 搜索已启用但不存在可用的搜索提供方,`web_search` 仍然可见,执行时以结构化的 `WebError`(如 `WEB_PROVIDER_UNAVAILABLE` 或 `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`)失败。发行版的 `dsh-tool-web` 按模型请求收窄了这一点:选中的提供方报告该请求的搜索无法鉴权时,它会[撤下 `web_search`](2026-09-27-web-search-offered-by-provider-readiness.zh.md)。如果某个提供方在 `dsh-tool-web` 之后出现,下一次执行即可使用它而无需更改 schema。如果某个提供方在调用过程中消失,执行以结构化的 `WebError` 失败,而不是静默选择另一个提供方或回退到 `UNKNOWN_TOOL`。
这使模型 schema 保持稳定,而不将插件加载顺序、凭证状态或 HMR(热模块替换)时序纳入面向模型的约定。如果 web 搜索已启用但不存在可用的搜索提供方,`web_search` 仍然可见,执行时以结构化的 `WebError`(如 `WEB_PROVIDER_UNAVAILABLE` 或 `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`)失败。`dsh-tool-web` 按模型请求收窄了这一点:选中的提供方报告该请求的操作无法鉴权时,它会[撤下 `web_search` 或 `web_fetch`](2026-09-27-web-search-offered-by-provider-readiness.zh.md)。如果某个提供方在 `dsh-tool-web` 之后出现,下一次执行即可使用它而无需更改 schema。如果某个提供方在调用过程中消失,执行以结构化的 `WebError` 失败,而不是静默选择另一个提供方或回退到 `UNKNOWN_TOOL`。
该 seam 刻意不暴露任何观察面——没有注册表变更事件,也没有聚合的能力状态查询。不可用性是调用方通过执行观察到的事实:`search()`/`fetch()` 在调用时解析提供方,并抛出命名了失败原因的结构化 `WebError`。[观察面 Agent Note](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) 记录了这一判断:基于调用的派生选择与基于启用的注册使得没有消费方需要变更信号或独立于执行和错误路由的可用性探测;未来的提供方状态面板会重新引入它实际消费的最小信号或查询。
该 seam 刻意不暴露可用性观察——没有聚合的能力状态查询,也没有逐个提供方的可用性报告。不可用性是调用方通过执行观察到的事实:`search()`/`fetch()` 在调用时解析提供方,并抛出命名了失败原因的结构化 `WebError`。[观察面 Agent Note](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) 记录了这一判断:基于调用的派生选择与基于启用的注册使得没有消费方需要变更信号或独立于执行和错误路由的可用性探测;未来的提供方状态面板会重新引入它实际消费的最小信号或查询。提供方选择器消费的是注册情况而非可用性:`listProviders()` 与 Remote 方法 `web/listProviders` 列出每个已注册提供方及其可选的 `title` 与 `description`(包括不可用的提供方),且每次注册表变更后都会触发无载荷的 `web/providers-changed` 事件。[Web 提供方选择决策](2026-10-29-web-provider-selection.zh.md)记录了为何注册情况可观察而可用性不可观察。
## 包拓扑
@@ -43,11 +43,9 @@ Web 访问是一个一等能力 seam,遵循[能力 seam Agent Note](2026-06-13
依赖方向与 bash 和 filesystem 一致:
```text
@deepseek-ai/dsh-tool-web --depends on--> @deepseek-ai/dsh-web <--depends on-- @deepseek-ai/dsh-web-search-exa
@deepseek-ai/dsh-tool-web --depends on--> @deepseek-ai/dsh-web <--depends on-- @deepseek-ai/dsh-web-search-deepseek
consumer interface implementation
<--depends on-- @deepseek-ai/dsh-web-search-perplexity
implementation
<--depends on-- @deepseek-ai/dsh-web-search-deepseek
<--depends on-- @deepseek-ai/dsh-web-deepseek-api
implementation
<--depends on-- @deepseek-ai/dsh-web-fetch-http
implementation
@@ -57,9 +55,7 @@ Web 访问是一个一等能力 seam,遵循[能力 seam Agent Note](2026-06-13
```mermaid
flowchart LR
exa["@deepseek-ai/dsh-web-search-exa"] -->|registerSearchProvider| web["@deepseek-ai/dsh-web / ctx.web"]
perplexity["@deepseek-ai/dsh-web-search-perplexity"] -->|registerSearchProvider| web
deepseek["@deepseek-ai/dsh-web-search-deepseek"] -->|registerSearchProvider| web
deepseek["@deepseek-ai/dsh-web-search-deepseek"] -->|registerSearchProvider| web["@deepseek-ai/dsh-web / ctx.web"]
deepseekApi["@deepseek-ai/dsh-web-deepseek-api"] -->|registerSearchProvider / registerFetchProvider| web
fetchLocal["@deepseek-ai/dsh-web-fetch-http"] -->|registerFetchProvider| web
toolWeb["@deepseek-ai/dsh-tool-web"] -->|search/fetch| web
@@ -67,9 +63,9 @@ flowchart LR
toolWeb -->|ctx.tools.register| webFetch["tool: web_fetch"]
```
`@deepseek-ai/dsh-web` 仅依赖 Cordis 和底层 harness 支持。它声明 `ctx.web`、提供方接口、请求/结果类型、提供方可用性约定和错误码。它不导入工具、agent(智能体)、会话、LLM 或提供方包。
`@deepseek-ai/dsh-web` 仅依赖 Cordis 和底层 harness 支持,其中包括其 Remote 方法所用的 Typert 协议。它声明 `ctx.web`、提供方接口、请求/结果类型、提供方可用性约定和错误码。它不导入工具、agent(智能体)、会话、LLM 或提供方包。
提供方包仅依赖 `dsh-web` 和 Cordis。它们拥有凭证、端点、协议格式映射、解析和 `WebError` 转换,使用平台 `fetch`。每个提供方注入共享服务并注册后端;只有 `dsh-web` 拥有 `ctx.web` 键。提供方私有的协议形状不会产生对 `ctx.llm` 或 Cordis HTTP 服务的依赖。
提供方包依赖 `dsh-web`、Cordis 及其凭据和配置函数。它们拥有凭证、端点、协议格式映射、解析和 `WebError` 转换,使用平台 `fetch`。每个提供方注入共享服务并注册后端;只有 `dsh-web` 拥有 `ctx.web` 键。提供方私有的协议形状不会产生对 `ctx.llm` 或 Cordis HTTP 服务的依赖。
`@deepseek-ai/dsh-tool-web` 依赖 `@deepseek-ai/dsh-web`、`@deepseek-ai/dsh-tools`、`@deepseek-ai/dsh-system-prompt` 和 Cordis。它从不导入具体的提供方包。
@@ -78,16 +74,22 @@ flowchart LR
`ctx.web` 是一个提供方注册表加上一个带提供方选择的执行 API。注册表部分与 `LlmRuntime` 保持接近:每种能力类别一个 `Map<id, provider>`,`registerSearchProvider`/`registerFetchProvider` 方法返回 disposer,重复 id 抛出 `WebError`,执行时解析在选定提供方缺失或不可用时抛出异常。权威签名见 `packages/web/web/src/types.ts`;seam 的形状:
```ts
import type { WebFetchRequest, WebFetchResult, WebSearchRequest, WebSearchResult } from '@deepseek-ai/dsh-web'
import type {
WebFetchRequest, WebFetchResult, WebProviderInfo, WebProviderList, WebSearchRequest, WebSearchResult,
} from '@deepseek-ai/dsh-web'
interface WebSearchProvider {
readonly id: string
readonly title?: string
readonly description?: WebProviderInfo['description']
available(): boolean
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
}
interface WebFetchProvider {
readonly id: string
readonly title?: string
readonly description?: WebProviderInfo['description']
available(): boolean
fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
}
@@ -95,6 +97,7 @@ interface WebFetchProvider {
interface WebRuntime {
registerSearchProvider(provider: WebSearchProvider): () => void
registerFetchProvider(provider: WebFetchProvider): () => void
listProviders(): WebProviderList
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>
fetch(request: WebFetchRequest, signal?: AbortSignal): Promise<WebFetchResult>
@@ -131,18 +134,15 @@ interface WebRuntime {
- id: web
name: '@deepseek-ai/dsh-web'
config:
searchProvider: exa
searchProvider: deepseek-search-api
fetchProvider: http
- id: web-search-exa
name: '@deepseek-ai/dsh-web-search-exa'
- id: web-search-perplexity
name: '@deepseek-ai/dsh-web-search-perplexity'
- id: web-search-deepseek
name: '@deepseek-ai/dsh-web-search-deepseek'
- id: web-deepseek-api
name: '@deepseek-ai/dsh-web-deepseek-api'
- id: web-fetch-http
name: '@deepseek-ai/dsh-web-fetch-http'
@@ -150,9 +150,9 @@ interface WebRuntime {
name: '@deepseek-ai/dsh-tool-web'
```
运维覆盖走同一条显式选择路径:`DSH_WEB_SEARCH_PROVIDER=perplexity` 等同于配置 `searchProvider: perplexity`,而非 `dsh-tool-web` 内部的隐式优先级链。
运维覆盖走同一条显式选择路径:`DSH_WEB_SEARCH_PROVIDER=deepseek-search-api` 等同于配置 `searchProvider: deepseek-search-api`,而非 `dsh-tool-web` 内部的隐式优先级链。
每次搜索或抓取在执行时读取配置的 id,因此保存的设置变更无需重新挂载 `dsh-web` 即对下一次操作选择另一个提供方。网页搜索设置页的开关借此把两种能力同时切换到 [DeepSeek Search 与 Fetch API](../feature/2026-10-15-deepseek-search-fetch-api.zh.md)。
每次搜索或抓取在执行时读取配置的 id,因此保存的设置变更无需重新挂载 `dsh-web` 即可让下一次操作选用新提供方。「网页搜索与获取」设置页的 Search 和 Fetch 下拉框借此分别切换各能力的提供方(包括 [DeepSeek Search 与 Fetch API](../feature/2026-10-15-deepseek-search-fetch-api.zh.md));[Web 提供方选择决策](2026-10-29-web-provider-selection.zh.md)负责内置默认值。
`ctx.web.search()` 和 `ctx.web.fetch()` 在执行时按上述选择规则解析提供方。如果选定的能力不可用,它们抛出带有结构化代码的 `WebError`,如 `WEB_PROVIDER_UNAVAILABLE`、`WEB_PROVIDER_CONFIGURED_MISSING`、`WEB_PROVIDER_CONFIGURED_UNAVAILABLE` 或 `WEB_PROVIDER_AMBIGUOUS`。如果未显式配置提供方且不存在可用提供方,执行错误是通用的 `WEB_PROVIDER_UNAVAILABLE` 情况;刻意不提供对每个不可用提供方的诊断汇总。
@@ -168,8 +168,8 @@ interface WebRuntime {
- `dsh-tool-web` 拥有该值并将其放在 `WebSearchRequest.maxResults` 上。
- `ctx.web` 将请求原样传递给选定的提供方。
- 当提供方的 API 支持结果数量控制时(Exa 的 `numResults`),提供方在请求层应用 `maxResults`,作为成本/延迟优化。
- `ctx.web` 在结果上强制执行上限:如果提供方返回的 source 数量超过 `maxResults`——因为其 API 没有结果数量控制(Perplexity)或忽略了提示——seam 将 `sources[]` 截断到 `maxResults` 并在返回前将 `WebSearchResult.truncated` 设为 `true`。这使上限成为面向模型层可以依赖的单一跨提供方保证,而非每个提供方都必须记得遵守的东西。
- 当提供方的 API 支持结果数量控制时(DeepSeek Search API 的 `count`),提供方在请求层应用 `maxResults`,作为成本/延迟优化。
- `ctx.web` 在结果上强制执行上限:如果提供方返回的 source 数量超过 `maxResults`——因为其 API 没有结果数量控制或忽略了提示——seam 将 `sources[]` 截断到 `maxResults` 并在返回前将 `WebSearchResult.truncated` 设为 `true`。这使上限成为面向模型层可以依赖的单一跨提供方保证,而非每个提供方都必须记得遵守的东西。
seam 请求不携带提供方特有的控制——没有 Perplexity 模型选择、搜索时效性、域名过滤器、Exa `livecrawl`、Exa `type`、区域提示、生成式回答预算或搜索深度。只有当某个字段具有提供方无关的语义,且工具 schema 和选定的提供方都能诚实地遵守时,才会添加。
@@ -190,13 +190,14 @@ interface WebSearchSource {
readonly url: string
readonly title?: string
readonly snippet?: string
readonly summary?: string
readonly publishedAt?: string
}
```
`content` 是可选的提供方生成的回答文本、搜索上下文或摘要。`sources[]` 是可移植的引用结构。source 必有 URL;title、snippet 和 `publishedAt` 可选,因为并非每个提供方都返回它们。`title` 不是必填:Perplexity 风格的引用可能只提供 URL,强制适配器编造标题会让 seam 说谎。`dsh-tool-web` 渲染 `title ?? hostname(url)` 风格的回退标签用于展示。`publishedAt` 是可选的发布/抓取时间戳,为 ISO-8601 字符串——Exa 在每条结果上以 `publishedDate` 返回它,Perplexity 在搜索结果上返回 `date`,因此它是真实的提供方数据而非派生值;seam 以字符串形式传递,日期解析留给消费方。
`content` 是可选的提供方生成的回答文本、搜索上下文或摘要。`sources[]` 是可移植的引用结构。source 必有 URL;title、snippet、summary 和 `publishedAt` 可选,因为并非每个提供方都返回它们。`snippet` 是用于展示的短摘录,`summary` 是供模型阅读的较长摘录,其长度由消费方限制。`title` 不是必填:Perplexity 风格的引用可能只提供 URL,强制适配器编造标题会让 seam 说谎。`dsh-tool-web` 渲染 `title ?? hostname(url)` 风格的回退标签用于展示。`publishedAt` 是可选的发布/抓取时间戳,为 ISO-8601 字符串——DeepSeek Search API 在每条结果上以可为 null 的 `date` 返回它,Exa 以 `publishedDate` 返回,Perplexity 以 `date` 返回,因此它是真实的提供方数据而非派生值;seam 以字符串形式传递,日期解析留给消费方。
Exa 搜索将提供方扁平 `results[]` 的每一项映射为 `WebSearchSource`:`url` ← `url`、`title` ← `title`、`snippet` ← 第一个 `highlights[]` 条目(没有 highlight 的条目没有可移植的 snippet,被丢弃)、`publishedAt` ← `publishedDate`。Exa 不返回提供方生成的回答,因此 `content` 省略。Perplexity 搜索将 `choices[0].message.content` 映射为 `content`,并优先使用结构化的顶层 `search_results[]` 作为 `sources[]`——`url` ← `url`、`title` ← `title`、`snippet` ← `snippet`(常为空)、`publishedAt` ← `date`——仅在 `search_results` 缺失时回退到纯 URL 的 `citations[]` 数组(这些 source 只有 `url`)。如果提供方返回的结构化字段少于 seam 支持的,适配器省略那些可选字段。
DeepSeek Search API 提供方将响应 `results[]` 的每一项映射为 `WebSearchSource`:`url` ← `url`、`title` ← `title`、`snippet` ← `snippet`、`summary` ← `summary`、`publishedAt` ← `date`,空值省略。它不返回提供方生成的回答,因此 `content` 省略。如果提供方返回的结构化字段少于 seam 支持的,适配器省略那些可选字段。
完整页面获取仍是 `web_fetch(url)` 的职责。搜索 snippet 是发现上下文,不是获取到的页面正文。
@@ -283,7 +284,7 @@ fetch 提供方的资源控制:
## 测试
每一层在自己的边界处固定:`dsh-web` 中的注册/选择/截断/abort 约定与 `WebError` 码;每个提供方基于录制的 fixture(测试前置数据)的请求/响应映射(Perplexity fixture 包含纯 URL 引用,以保持可选 source 字段的诚实性),加上每个真实提供方的自跳过带密钥冒烟测试;`web-fetch-http` 中的真实本地 HTTP 行为;`dsh-tool-web` 中通过真实工具注册表的启用驱动注册、结构化执行错误和结果格式化。一个真实 Loader 冒烟测试守护两种导出形状([事故复盘(postmortem) 0001](../../../../docs/postmortem/0001-acp-default-export-drops-inject.zh.md)):`dsh-web` 是默认导出的服务,而提供方和 `tool-web` 是命名空间插件,误加 `export default` 会丢失 `inject`。
每一层在自己的边界处固定:`dsh-web` 中的注册/选择/截断/abort 约定与 `WebError` 码;每个提供方基于录制的 fixture(测试前置数据)的请求/响应映射,加上每个真实提供方的自跳过带密钥冒烟测试;`web-fetch-http` 中的真实本地 HTTP 行为;`dsh-tool-web` 中通过真实工具注册表的启用驱动注册、结构化执行错误和结果格式化。一个真实 Loader 冒烟测试守护两种导出形状([事故复盘(postmortem) 0001](../../../../docs/postmortem/0001-acp-default-export-drops-inject.zh.md)):`dsh-web` 是默认导出的服务,而提供方和 `tool-web` 是命名空间插件,误加 `export default` 会丢失 `inject`。
## 曾考虑的替代方案
@@ -293,7 +294,7 @@ fetch 提供方的资源控制:
### 将提供方调度直接放在 `dsh-tool-web` 中
这类似 OpenCode 的本地 web 搜索:一个稳定的 `websearch` 工具在内部调度到 Exa 或 Parallel。对于小型产品路径可以接受,但作为 harness 基础是错误的。工具包将拥有提供方选择、凭证、请求映射、传输、响应解析和展示,使得在不将 Exa 和 Perplexity 的差异烘焙进工具 schema 的情况下难以添加它们。
这类似 OpenCode 的本地 web 搜索:一个稳定的 `websearch` 工具在内部调度到 Exa 或 Parallel。对于小型产品路径可以接受,但作为 harness 基础是错误的。工具包将拥有提供方选择、凭证、请求映射、传输、响应解析和展示,使得在不将各提供方格式差异烘焙进工具 schema 的情况下难以添加格式不同的提供方。
### 将搜索和 fetch 拆为两个 seam(`dsh-search`、`dsh-fetch`)
@@ -322,9 +323,9 @@ fetch 提供方的资源控制:
## 后果
**搜索 schema 刻意精简。** Exa 和 Perplexity 都暴露了有用的提供方特有控制;只有当某个控制能以提供方无关的方式定义、且工具注册和提供方执行都能诚实遵守时,才会添加。
**搜索 schema 刻意精简。** 各搜索 API 暴露了有用的提供方特有控制,例如 Exa 的 `livecrawl`、Perplexity 的搜索时效性和 DeepSeek Search API 的 `localization`;只有当某个控制能以提供方无关的方式定义、且工具注册和提供方执行都能诚实遵守时,才会添加。
**Perplexity 引用可能稀疏。** 一条引用可能只有 URL。将 `title` 和 `snippet` 设为可选使 seam 保持诚实,但意味着 `tool-web` 需要渲染回退标签。
**引用可能稀疏。** 一条 Perplexity 风格的引用可能只有 URL。将 `title` 和 `snippet` 设为可选使 seam 保持诚实,但意味着 `tool-web` 需要渲染回退标签。
**稳定的工具注册将配置错误推迟到执行时。** 当产品启用了 web 访问时,保持工具可见是正确的;但期望 web 搜索可用的产品应用应当明确暴露结构化的 `WEB_PROVIDER_CONFIGURED_MISSING`/`WEB_PROVIDER_CONFIGURED_UNAVAILABLE`/`WEB_PROVIDER_AMBIGUOUS` 失败,避免用户直到模型调用工具后才发现配置问题。
@@ -340,7 +341,7 @@ fetch 提供方的资源控制:
- `pdf` `WebFetchBody` 类别:`http` 提供方将可文本提取的 PDF 解码(尽力而为、有上限、`truncated`)为 `{ kind: 'pdf'; content; pageCount? }` 分支,`tool-web` 渲染它。这是 fetch 而非 `web_extract`——PDF 获取是具体的 HTTP 200 加确定性的本地解码,不是提供方侧对非 HTTP 资源的提取。添加它是跨 `dsh-web`(声明分支)、提供方(解码 + 将「二进制拒绝」收窄为「拒绝二进制,但可文本提取的 PDF 除外」;需要 OCR 的扫描/图片 PDF 不在范围内)和 `tool-web`(渲染)的协调变更。封闭的 `WebFetchBody` 联合类型使消费方在新分支被处理之前编译失败。
- 提供方支撑的提取作为独立的 `web_extract` 能力,而非静默扩展 `web_fetch`。
- `query` 和 `maxResults` 之外的提供方无关搜索控制,待 Exa 和 Perplexity 都能诚实遵守时再添加。
- `query` 和 `maxResults` 之外的提供方无关搜索控制,待已注册的提供方都能诚实遵守时再添加。
## 开放问题
@@ -18,11 +18,11 @@
en: ce28268021a38b89
zh: 7802540e4ef04752
/agent-note-the-slot-system-standard-single-register-four-props-shares-and-the-framework-store-seat/decision/component-props-four-shares-each-from-its-own-source-of-truth:
en: f3fca569fa0c5168
zh: 647a82662f6a887d
en: c1892cb7309c0824
zh: 21983594d0c7f24f
/agent-note-the-slot-system-standard-single-register-four-props-shares-and-the-framework-store-seat/decision/the-chain-kind-entries-self-nominate-first-match-renders:
en: 56278be7f3688805
zh: 16602ffbf4baef85
en: dad15e2e1309c9a4
zh: ee2d13b27c92a73b
/agent-note-the-slot-system-standard-single-register-four-props-shares-and-the-framework-store-seat/decision/the-store-seat-framework-engine-registrant-schema:
en: 2645acc9d29c20ec
zh: a8a2fd293ef5da04
@@ -50,12 +50,13 @@ A contributor whose activation order is independent from the declaring entry use
| child render | `PropsRenderSlots<S>` | register's `children` keys | `renderSlot(key, owner)`, key statically narrowed to S; chain keys add `renderSlotChain` |
| store | `PropsStore<H>` | store factory return type | `useStore` selector hook + `actions.*` (draft-param stripped) |
| business | `I` | inject return type | plain data + callbacks; a reserved `hooks` compartment of bare observables arrives bound as `use<Name>` selector hooks (`InjectFace<I>`) |
| kind-specific | `MatchedShare` / `HocShare` | SlotMap entry's kind | chain `matched`; hoc `children`, the next inner layer or the owner content |
`sessionId` is framework-supplied wherever `scope: 'session'` is declared — owner params do not carry it. The register call site is the double-lock choke point: a component whose renderSlot keys exceed the `children` declaration, or that misses a declared face, or whose store/inject shapes drift, is a compile error on that line. Delegation is ordinary props passing (hand the `renderSlot` function down, optionally behind a narrower signature) — there is no whitelist face object and no minting API.
### The chain kind: entries self-nominate, first match renders
The fourth `SlotKind`, `'chain'`, inverts routing authority relative to `keyed`: a keyed dispatch site picks its occupant by `entryKey`, while a chain entry nominates itself — the owner dispatches one common currency of owner props and never learns who takes over, so a new takeover package registers with zero owner edits. A chain registration carries a `select` pure selector (`ChainSelect<O, M>`: `(owner) => matched | null`) and an optional `priority` (ascending; ties keep registration = assembly order — the deployment-controllable inject topology — under the same stable sort as list `order`); registering without `select` is one of the loud-at-load cases above. At render, the outlet runs the selectors in chain order: the first non-null return elects its entry and the returned value joins the component's props as `matched` (the component never re-derives its own match), `null` passes the turn to the next entry, and all-null renders the owner's fallback body (`ChainRenderOpts`).
A fifth kind, `'hoc'`, nests entries around owner-supplied `children` and adds a kind-specific `children` prop the same way `chain` adds `matched`; its decision lives in the [hoc slot kind note](2026-11-03-hoc-slot-kind.md). The fourth `SlotKind`, `'chain'`, inverts routing authority relative to `keyed`: a keyed dispatch site picks its occupant by `entryKey`, while a chain entry nominates itself — the owner dispatches one common currency of owner props and never learns who takes over, so a new takeover package registers with zero owner edits. A chain registration carries a `select` pure selector (`ChainSelect<O, M>`: `(owner) => matched | null`) and an optional `priority` (ascending; ties keep registration = assembly order — the deployment-controllable inject topology — under the same stable sort as list `order`); registering without `select` is one of the loud-at-load cases above. At render, the outlet runs the selectors in chain order: the first non-null return elects its entry and the returned value joins the component's props as `matched` (the component never re-derives its own match), `null` passes the turn to the next entry, and all-null renders the owner's fallback body (`ChainRenderOpts`).
The decline decision lives in `select`, never in a mounted component probing its own props: a component that mounts only to render null still runs its hooks and effects for nothing, and the resulting mount/unmount churn breaks memoization and React key semantics, whereas a selector is a pure function — unit-testable, zero mount side effects — the same discipline as "presentation methods are pure functions of `args`". Purity is the selector's contract: it reads no external mutable state and produces no side effects, so the routing decision is entirely a function of the owner props and safe to run on every dispatch. Selectors route; they never mint — per-dispatch object construction would churn identity every render, so wrapping a matched value in a richer face happens inside the elected component (`useMemo` keyed on `matched`).
@@ -50,12 +50,13 @@ ctx.slots.register({
| 子 slot 渲染 | `PropsRenderSlots<S>` | register 的 `children` 键集 | `renderSlot(key, owner)`,键参静态收窄到 S;chain 键另有 `renderSlotChain` |
| store | `PropsStore<H>` | store 工厂的返回类型 | `useStore` selector 钩子 + `actions.*`(剥去 draft 形参) |
| 业务 | `I` | inject 的返回类型 | 普通数据+回调;保留的 `hooks` 区域内,裸 observable 经绑定后以 `use<Name>` 选择器钩子的形式到达(`InjectFace<I>`) |
| 按 kind | `MatchedShare` / `HocShare` | SlotMap entry 的 kind | chain 的 `matched`;hoc 的 `children`,即内一层或宿主内容 |
凡声明 `scope: 'session'` 之处,`sessionId` 一律由框架供给——owner 传参不携带它。register 调用点是双重类型约束的收口:组件的 renderSlot 键集超出 `children` 声明、漏接某个已声明的面、store/inject 形状漂移,任何一条都在那一行上报编译错误。转授就是普通的 props 传递(把 `renderSlot` 函数递下去,可按需包一层更窄的签名)——不存在白名单面对象,也不存在铸面 API。
### chain kind:entry 自荐,首个匹配项负责渲染
第四种 `SlotKind`——`'chain'`——把路由权相对 `keyed` 反转:keyed 的分派现场以 `entryKey` 点选占用 slot 的 entry,chain 则由 entry 自荐——owner 只分派一套格式统一的 owner props,永远不知道谁来接管,新的接管包注册进来 owner 零改动。chain 注册携带一个 `select` 纯选择器(`ChainSelect<O, M>`:`(owner) => matched | null`)与可选的 `priority`(升序;同值保持注册序 = 装配序——部署可控的 inject 拓扑——复用 list `order` 的同一稳定排序);注册缺 `select` 即上文装载即炸情形之一。渲染时 outlet 按链序依次执行各 select:首个非 null 返回值当选,该值以 `matched` 并入组件 props(组件绝不自行重新推导匹配);返回 `null` 则轮到下一个 entry;全 null 则渲染 owner 的 fallback 体(`ChainRenderOpts`)。
第五种 kind `'hoc'` 把 entry 层层嵌套在 owner 提供的 `children` 外面,并像 `chain` 提供 `matched` 那样提供按 kind 的 `children` prop;其决策见 [hoc 插槽类型 Note](2026-11-03-hoc-slot-kind.zh.md)。第四种 `SlotKind`——`'chain'`——把路由权相对 `keyed` 反转:keyed 的分派现场以 `entryKey` 点选占用 slot 的 entry,chain 则由 entry 自荐——owner 只分派一套格式统一的 owner props,永远不知道谁来接管,新的接管包注册进来 owner 零改动。chain 注册携带一个 `select` 纯选择器(`ChainSelect<O, M>`:`(owner) => matched | null`)与可选的 `priority`(升序;同值保持注册序 = 装配序——部署可控的 inject 拓扑——复用 list `order` 的同一稳定排序);注册缺 `select` 即上文装载即炸情形之一。渲染时 outlet 按链序依次执行各 select:首个非 null 返回值当选,该值以 `matched` 并入组件 props(组件绝不自行重新推导匹配);返回 `null` 则轮到下一个 entry;全 null 则渲染 owner 的 fallback 体(`ChainRenderOpts`)。
「不接」的判定住在 `select` 里,绝不在挂载后的组件里自探 props:组件为了渲染 null 也得先挂载,其钩子与 effect 全部白跑,随之而来的挂载/卸载抖动还会破坏 memo 化与 React key 语义;而选择器是纯函数——可单测、零挂载副作用——与「presentation methods are pure functions of `args`」是同一条纪律。纯,就是选择器的约定:不读外部可变状态、不产副作用,路由判定因此完全是 owner props 的函数,每次分派都可安全执行。选择器只做路由,绝不创建新对象——按分派逐次构造对象会让引用每次渲染都换新;把匹配值包成更丰富的面这件事,发生在当选组件内部(以 `matched` 为依赖的 `useMemo`)。
@@ -21,8 +21,8 @@
en: 0a4240d88cca240a
zh: 8f5f1527caf571cd
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/decision/the-loading-flow-end-to-end:
en: 15fb56befd328339
zh: 0ffb10ada7e52805
en: dd05aa1ee608c414
zh: 2863ced0086e3122
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/decision/live-graph-reconciliation:
en: 37e2fa1fe5cb2b95
zh: 80e513db87709cb4
@@ -33,8 +33,8 @@
en: 94ca0d09e6fd1e3a
zh: d5949a2bdec795ba
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/consequences:
en: 16e1f2467c559315
zh: a714ec6ff2722cf1
en: 624efdd136029771
zh: 7d60207147e631fe
/agent-note-client-plugin-loading-lazy-factories-cordis-lifecycle-and-hot-reload/alternatives-considered:
en: b8f036f50cff7cd8
zh: 6d623c4a1f4314b1
@@ -67,8 +67,8 @@ Why is the roster yml rows and not a scan? Because which plugins compose into a
1. The kernel mounts the vendored Loader and injects the module system as `internal` before any entry exists. Ordering matters: `tree.import`'s bare-import fallback must never run in a browser.
2. It creates every graph row uniformly. Importing the modules row returns the memoized bootstrap exports, whose `apply()` reads that tree's `Loader.internal` and provides the same instance as `ctx.modules`; rows that require that service remain PENDING until then, so the modules row needs no special creation position. Render assembly is an ordinary host-graph row provided by `dsh-client-ui-renderer`; the kernel appends no assembly pseudo-entry.
3. Graph order governs synchronous factory availability; Cordis activation remains independent and proceeds through service waiting.
4. `settled` = every entry created + `loader.await()` quiescent + an all-ACTIVE sweep. The sweep lists each import-failed, FAILED, or PENDING fiber with its missing services. It exists because cordis inject waits have no timeout — the sweep is the fail-loud floor.
5. The framework-free loading page projects real fiber states via `internal/status`. After the sweep, the kernel calls `ctx.uiRenderer.mount(container)` and replaces the page with the real UI in one pass.
4. `settled` = every entry created + `loader.await()` quiescent + the boot audit. The audit lists each import-failed, FAILED, or PENDING fiber with its activation error or missing services. It exists because cordis inject waits have no timeout. An inactive entry on the browser required list rejects startup; other inactive entries produce one console warning ([startup policy](2026-09-09-consumer-owned-startup-strictness.md)).
5. The framework-free loading page projects real fiber states via `internal/status`. After the audit accepts the tree, the kernel calls `ctx.uiRenderer.mount(container)` and replaces the page with the real UI in one pass.
### Live graph reconciliation
@@ -104,7 +104,7 @@ The current package inventory and build forms live in the [client shell layering
One governance implementation runs on both sides of the wire; the browser-specific layer is one module system plus one reload plugin. Dynamic packages have one artifact form, so the purity check covers them all. Cordis dependencies, module requests, and the boot tier live with their owners — the manifests — while the composing app holds only the roster. Host graph validation and recursive request arrival keep synchronous factory dependencies explicit. Browser-native script loading preserves the standard mapping among plugin network resources, generated bundles, and TypeScript/TSX sources, while the module system keeps two replaceable carrier hooks: `loadBundle` for bundle bytes and `resolveAsset` for package-asset references.
Costs accepted: the vendored Loader carries idle machinery in the browser (EntryTree persistence is a no-op, groups/isolation unused); every plugin edit in dev pays a bundle rebuild plus fiber remount; graph `inject` rows guide factory arrival but service availability remains the activation authority, so a mismatch appears at the settled sweep; the static UI libraries keep direct value exports; every bundle gains a source-map artifact; and external-script failures provide only coarse URL diagnostics instead of the HTTP status available to an explicit fetch. The Host retains bundle snapshots and lazy response plans for the current graph and one prior startup generation. Script and map bodies are cached after their first `GET`, so memory grows with bundle snapshots and requested response bodies. Each materialized response stays fixed at its URL; a prior-generation map first requested after a rebuild reads the current map file.
Costs accepted: the vendored Loader carries idle machinery in the browser (EntryTree persistence is a no-op, groups/isolation unused); every plugin edit in dev pays a bundle rebuild plus fiber remount; graph `inject` rows guide factory arrival but service availability remains the activation authority, so a mismatch appears at the boot audit; the static UI libraries keep direct value exports; every bundle gains a source-map artifact; and external-script failures provide only coarse URL diagnostics instead of the HTTP status available to an explicit fetch. The Host retains bundle snapshots and lazy response plans for the current graph and one prior startup generation. Script and map bodies are cached after their first `GET`, so memory grows with bundle snapshots and requested response bodies. Each materialized response stays fixed at its URL; a prior-generation map first requested after a rebuild reads the current map file.
Roster: it lives in the web bundle's config tree (`packages/bundle/web-app/cordis.patch.yml`); `mountWebPlugins` and the `CLIENT_PACKAGES` constant are gone, and recomposing a deployment means swapping the yml/overlay. The graph composer lives in the `dsh-client-modules` node half, while the parser-preloaded client face bootstraps the browser module table. The webserver remains a plain route-registration plugin; `/api/*` binding, browser authentication, RPC envelopes, and exact Fetch routes belong to the Connection node half, while Remote dispatch belongs to API Gateway and the dev bundle watch plus SSE channel belongs to the hmr node half.
@@ -67,8 +67,8 @@ Host 会快照每个已构建插件入口 bundle,并把每个调度阶段的
1. 内核挂载 vendored Loader,在任何 entry 存在之前就把模块系统注入为 `internal`。顺序有讲究:`tree.import` 的裸 import 兜底分支在浏览器里绝不能跑到。
2. 它统一创建每个 graph row。Import modules row 会返回记忆化的 bootstrap exports,其 `apply()` 读取该树的 `Loader.internal`,把同一个实例提供为 `ctx.modules`;需要该 service 的 row 会保持 PENDING 直至此时,因此 modules row 无需特殊创建位置。渲染组装是由 `dsh-client-ui-renderer` 提供的普通 host graph row;内核不追加组装伪 entry。
3. Graph 顺序治理同步 factory 可用性;Cordis 激活与之独立,仍经服务等待推进。
4. `settled` = 每个 entry 已创建 + `loader.await()` 完全停稳 + 一次全 ACTIVE 扫描。扫描列出每个 import 失败、FAILED 或 PENDING 的 fiber 及其缺失的服务。它存在的理由:cordis 的 inject 等待没有超时——这次扫描就是大声失败的兜底线。
5. 不依赖框架的 loading 页经 `internal/status` 投影真实 fiber 状态。检查完成后,内核调用 `ctx.uiRenderer.mount(container)`,一次切换到真实 UI。
4. `settled` = 每个 entry 已创建 + `loader.await()` 完全停稳 + 启动审计。审计列出每个 import 失败、FAILED 或 PENDING 的 fiber 及其激活错误或缺失的服务。它存在的理由:cordis 的 inject 等待没有超时。浏览器必需名单中的 entry 未激活会拒绝启动;其他未激活 entry 输出一条控制台警告([启动策略](2026-09-09-consumer-owned-startup-strictness.zh.md))。
5. 不依赖框架的 loading 页经 `internal/status` 投影真实 fiber 状态。审计接受插件树后,内核调用 `ctx.uiRenderer.mount(container)`,一次切换到真实 UI。
### 动态图对账
@@ -104,7 +104,7 @@ Bootstrap 替换会在失效或卸载前被拒绝:模块系统保留其初始
Wire 两侧运行同一份治理实现;浏览器特有层只包含一套模块系统和一个重载插件。动态包只有一种产物形态,因此纯度检查覆盖全部动态包。Cordis 依赖、模块请求与启动档位都与其所有者——manifest——同住,负责组合的 app 只握名册。Host graph 校验与递归请求到达使同步 factory 依赖保持显式。浏览器原生 script 装载保留插件网络资源、生成 bundle 与 TypeScript/TSX 源码之间的标准映射,模块系统只保留两个可替换的载体钩子:负责 bundle 字节的 `loadBundle` 和负责包资源引用的 `resolveAsset`。
接受的代价:vendored Loader 在浏览器里背着闲置机件(EntryTree 持久化是 no-op,分组/隔离未用);开发期每次修改插件都要付一次 bundle 重建加 fiber 重挂;graph `inject` row 指导 factory 到达,但服务可用性仍是激活权威,因此不匹配会在 settled 扫描时浮出;静态 UI 库保留直接实体导出;每个 bundle 多出一份 sourcemap 产物,外部 script 失败也只能给出粗粒度 URL 诊断,不能像显式 fetch 那样报告 HTTP 状态。Host 为当前图及上一代启动图保留 bundle 快照与惰性响应计划。脚本和 map body 在首次 `GET` 后缓存,因此内存随 bundle 快照和已请求的响应 body 增长。每份已物化响应在其 URL 下保持固定;若上一代 map 在重建后才首次被请求,则会读取当前 map 文件。
接受的代价:vendored Loader 在浏览器里背着闲置机件(EntryTree 持久化是 no-op,分组/隔离未用);开发期每次修改插件都要付一次 bundle 重建加 fiber 重挂;graph `inject` row 指导 factory 到达,但服务可用性仍是激活权威,因此不匹配会在启动审计时浮出;静态 UI 库保留直接实体导出;每个 bundle 多出一份 sourcemap 产物,外部 script 失败也只能给出粗粒度 URL 诊断,不能像显式 fetch 那样报告 HTTP 状态。Host 为当前图及上一代启动图保留 bundle 快照与惰性响应计划。脚本和 map body 在首次 `GET` 后缓存,因此内存随 bundle 快照和已请求的响应 body 增长。每份已物化响应在其 URL 下保持固定;若上一代 map 在重建后才首次被请求,则会读取当前 map 文件。
名册位于 web 组合包的配置树(`packages/bundle/web-app/cordis.patch.yml`);`mountWebPlugins` 与 `CLIENT_PACKAGES` 常量已消失,重组一次部署等于替换 yml/overlay。Graph 组合器位于 `dsh-client-modules` node 半,由 parser 预载的 Client face 则自举浏览器模块表。Webserver 继续作为朴素路由注册插件;`/api/*` 绑定、浏览器认证、RPC envelope 与精确 Fetch 路由属于 Connection node 半,Remote 分发属于 API Gateway,开发期 bundle 监视与 SSE 通道属于 HMR node 半。
@@ -12,8 +12,8 @@
en: aea80ece13f2905b
zh: a880bc1a7839cf12
/agent-note-one-ordering-for-configuration-sources-and-what-a-discovered-file-may-not-decide/consequences:
en: 640b7ee4e8c57ed0
zh: 1c17b3fba16861a8
en: 41ebe22077560090
zh: da3475e4c196c3f9
/agent-note-one-ordering-for-configuration-sources-and-what-a-discovered-file-may-not-decide/alternatives-considered:
en: ac43499cfe7b8495
zh: e5ab599e7d3ed753
@@ -58,7 +58,6 @@ The line is that these take effect with no user action, before any turn, outside
- A `.env` holding `DSH_*`, `PATH`, `BROWSER`, or — in the invoking directory — a proxy variable fails the launch instead of being applied. Developers keeping switches in a repository `.env` move them to their shell — a deliberate, loud break.
- Composition is no longer overridable by a stale shell endpoint. It is still overridable by a user's stored `settings.yaml`, which is the settings seam's layering and not something this note changes; the product CLI offers no flag above it, so a deployment that must win against stored settings owns its own bin or loader tree.
- Not solved: the layers are still materialized into `process.env`, so ordinary project variables continue to reach child processes under the subprocess scrub. Bootstrap variables cannot come from a file at all; the environment package records the remaining subprocess reach as a limitation.
- Exa and Perplexity still capture their key at load time rather than through the credential seam. They no longer read raw `process.env` — they resolve through the trusted layers — but converting them to per-request credential resolution is separate work.
## Alternatives considered
@@ -59,7 +59,6 @@ inherited process environment (read-only, wins)
- 含 `DSH_*`、`PATH`、`BROWSER` 或(在调用目录中)proxy 变量的 `.env` 会导致启动失败而不是被应用。把开关放在仓库 `.env` 里的开发者需要改放到 shell——这是一次刻意且响亮的破坏。
- composition 不再会被陈旧的 shell endpoint 覆盖。但它仍然会被用户已存的 `settings.yaml` 覆盖,这是 settings seam 的分层方式,本 Note 不改变它;产品 CLI 没有高于它的标志,因此需要压过已存 settings 的部署方要自带 bin 或 loader 配置树。
- 未解决的:各层仍然会被物化进 `process.env`,因此普通项目变量继续按子进程清洗规则抵达子进程。bootstrap 变量完全不能来自文件;环境包将其余变量仍可抵达子进程这一点记录为一项限制。
- Exa 与 Perplexity 仍在加载时捕获密钥,而不是经凭据 seam。它们不再读裸 `process.env`——改为经受信层解析——但把它们改造成按请求解析凭据是另一件事。
## Alternatives considered
@@ -21,8 +21,8 @@
en: 29def175ab33b7d6
zh: 87e55ec28c20c7c1
/agent-note-package-and-update-the-electron-desktop-application/updates-and-recovery:
en: 4cb7014acfe71762
zh: 2178e0337cba0324
en: 61a02870beb953ec
zh: 205ecce330959e9a
/agent-note-package-and-update-the-electron-desktop-application/security-and-release-policy:
en: 5faaa53b58121311
zh: e1c3fc8d21a27d1a
@@ -73,7 +73,7 @@ Electron update uses one `electron-updater` release stream and signed `electron-
The [immediate-window reference](../../../../apps/desktop/README.md) owns the local loading page, direct Host startup, and recovery in the main window. Profile reconciliation follows the [bundled-runtime decision](2026-09-08-desktop-bundled-runtime-and-external-plugins.md).
`DSH_DESKTOP_AUTO_UPDATE_ENV` selects the test deployment by default or the production deployment for both the target-specific generic-provider URL and COS destination. Release automation supplies the test HTTPS origin through `DOWNLOAD_TEST_ORIGIN` and each deployment's bucket through `DOWNLOAD_TEST_COS_BUCKET` or `DOWNLOAD_PROD_COS_BUCKET`; keeping mutable test routing and COS storage identities out of source lets deployment infrastructure change without a code release, while the public production origin remains fixed. Packaging resolves only the public updater URL, disables electron-builder publishing, removes every COS credential field from its subprocess environment, and writes a completion record only after electron-builder and every signing or notarization hook succeeds. Target upload additionally requires the selected bucket, then requires the completion record, root dsh version, Desktop version, version-derived channel metadata, artifact names, sizes, and SHA-512 values to agree before it reads the selected credentials or sends data. It uploads immutable versioned updater payloads and any separate blockmaps before replacing the channel metadata emitted by electron-builder, and it never deletes historical objects. Stable versions use the `latest` metadata name; prereleases use the first semantic-version prerelease identifier. NSIS embeds its blockmap in the signed executable; the macOS ZIP carries a separate blockmap. Both let electron-updater download changed blocks when supported, while application replacement and the local pnpm package operation remain separate operations.
`DSH_DESKTOP_AUTO_UPDATE_ENV` selects the test deployment by default or the production deployment for both the target-specific generic-provider URL and COS destination. Release automation supplies the test HTTPS origin through `DOWNLOAD_TEST_ORIGIN` and each deployment's bucket through `DOWNLOAD_TEST_COS_BUCKET` or `DOWNLOAD_PROD_COS_BUCKET`; keeping mutable test routing and COS storage identities out of source lets deployment infrastructure change without a code release, while the public production origin remains fixed. Packaging resolves only the public updater URL, disables electron-builder publishing, removes every COS credential field from its subprocess environment, and writes a completion record only after electron-builder and every signing or notarization hook succeeds. Target upload additionally requires the selected bucket, then requires the completion record, root dsh version, Desktop version, channel-selected metadata, artifact names, sizes, and SHA-512 values to agree before it reads the selected credentials or sends data. It uploads immutable versioned updater payloads and any separate blockmaps before replacing the channel metadata emitted by electron-builder, and it never deletes historical objects. The selected channel determines the metadata name: `stable.yml` or `nightly.yml`, with `-mac` before the extension for macOS; version suffixes do not select the channel. NSIS embeds its blockmap in the signed executable; the macOS ZIP carries a separate blockmap. Both let electron-updater download changed blocks when supported, while application replacement and the local pnpm package operation remain separate operations.
Test releases require `DOWNLOAD_TEST_RELEASE_ID` from the platform dotenv file. Their feeds and binaries share `dsh-desk/<release-id>/`; production retains its fixed directories. The ID is part of the packaged update URL, so upload rejects a different ID. Internal distribution can supply a new installer after rotation; installed clients retain their existing feed. Random directories reduce guessing but provide no authorization for link holders. The [release configuration](../../../../apps/desktop/publishing/README.md#upload-updates) defines generation and reuse.
@@ -73,7 +73,7 @@ Electron 更新只使用一个 `electron-updater` 发布流和签名 `electron-b
[立即显示窗口参考](../../../../apps/desktop/README.zh.md)负责本地加载页、直接启动 Host 和主窗口恢复。profile 协调遵循[内置运行时决策](2026-09-08-desktop-bundled-runtime-and-external-plugins.zh.md)。
`DSH_DESKTOP_AUTO_UPDATE_ENV` 默认为测试部署,也可以选择生产部署,并同时决定目标专用的 generic-provider URL 与 COS 目标。发布自动化通过 `DOWNLOAD_TEST_ORIGIN` 提供测试 HTTPS origin,并通过 `DOWNLOAD_TEST_COS_BUCKET` 或 `DOWNLOAD_PROD_COS_BUCKET` 提供各部署的 bucket;可变的测试路由与 COS 存储身份不写入源码,部署基础设施变更时无需发布新代码,而公开的生产 origin 仍固定。打包只解析公开更新 URL、禁止 electron-builder 发布、从子进程环境中删除每个 COS 凭据字段,并且只有在 electron-builder 以及每个签名或公证 hook 成功后才写入完成记录。目标上传还必须提供所选 bucket,随后会先要求完成记录、根 dsh 版本、Desktop 版本、根据版本得出的频道元数据、产物名称、大小与 SHA-512 全部一致,再读取所选凭据或发送数据。它先上传不可变且带版本的更新载荷与所有独立 blockmap,最后替换 electron-builder 生成的频道元数据,并且不会删除历史对象。稳定版本使用 `latest` 元数据名称,预发布版本则使用语义化版本的第一个预发布标识符。NSIS 把 blockmap 嵌入已签名的可执行文件,macOS ZIP 则使用独立 blockmap;两者都让 electron-updater 在平台支持时只下载变化的数据块,而应用替换与本地 pnpm 包操作仍是两个独立操作。
`DSH_DESKTOP_AUTO_UPDATE_ENV` 默认为测试部署,也可以选择生产部署,并同时决定目标专用的 generic-provider URL 与 COS 目标。发布自动化通过 `DOWNLOAD_TEST_ORIGIN` 提供测试 HTTPS origin,并通过 `DOWNLOAD_TEST_COS_BUCKET` 或 `DOWNLOAD_PROD_COS_BUCKET` 提供各部署的 bucket;可变的测试路由与 COS 存储身份不写入源码,部署基础设施变更时无需发布新代码,而公开的生产 origin 仍固定。打包只解析公开更新 URL、禁止 electron-builder 发布、从子进程环境中删除每个 COS 凭据字段,并且只有在 electron-builder 以及每个签名或公证 hook 成功后才写入完成记录。目标上传还必须提供所选 bucket,随后会先要求完成记录、根 dsh 版本、Desktop 版本、根据所选渠道确定的元数据、产物名称、大小与 SHA-512 全部一致,再读取所选凭据或发送数据。它先上传不可变且带版本的更新载荷与所有独立 blockmap,最后替换 electron-builder 生成的频道元数据,并且不会删除历史对象。所选渠道决定元数据名称:`stable.yml` 或 `nightly.yml`,macOS 在扩展名前加 `-mac`;版本后缀不决定渠道。NSIS 把 blockmap 嵌入已签名的可执行文件,macOS ZIP 则使用独立 blockmap;两者都让 electron-updater 在平台支持时只下载变化的数据块,而应用替换与本地 pnpm 包操作仍是两个独立操作。
测试发布必须从平台 dotenv 文件提供 `DOWNLOAD_TEST_RELEASE_ID`。清单和安装包共用 `dsh-desk/<release-id>/`,生产环境保留固定目录。ID 是打包更新 URL 的一部分,因此上传会拒绝不同的 ID。轮换后可通过内部分发提供新安装包,已安装客户端保留原清单地址。随机目录降低被猜中的概率,但不对持有链接者进行鉴权。[发布配置](../../../../apps/desktop/publishing/README.zh.md#upload-updates)定义生成与复用方式。
@@ -3,23 +3,23 @@
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md
/agent-note-adjacent-agents-share-one-steer-send-message-operation:
en: 758d31ae04ce0d4f
zh: 112475efbd52b9f6
en: 9d0af2bae38b1eec
zh: 8a6e8502a5195548
/agent-note-adjacent-agents-share-one-steer-send-message-operation/problem:
en: c988b8a3be4e6f19
zh: b58011fd8d2e878e
en: 5dc4c218f648c27e
zh: b48531fac94b25db
/agent-note-adjacent-agents-share-one-steer-send-message-operation/decision:
en: 1188c699ce6e6ca2
zh: 4d5b974cd1216f06
en: f4789878ac0e79b2
zh: 68eb68d34cba810d
/agent-note-adjacent-agents-share-one-steer-send-message-operation/decision/one-model-tool-and-one-return-instruction:
en: 4b0a25389324fa91
zh: c2cdd9f7e40ef43b
en: 62284bd7dd804188
zh: b3a8dfebffc01bdb
/agent-note-adjacent-agents-share-one-steer-send-message-operation/decision/complete-removal-and-reintroduction-condition:
en: 5a9ec6478cf7430c
zh: f4c2f067cc72040e
en: 8cd78e21602f17f0
zh: 3843fea07e9b93a3
/agent-note-adjacent-agents-share-one-steer-send-message-operation/alternatives-considered:
en: 1f62d5d938144666
zh: dec0f30228c5b7a7
en: 7386e88e7051dce7
zh: dcea5b9e30374be3
/agent-note-adjacent-agents-share-one-steer-send-message-operation/consequences:
en: d55de74742c3af4b
zh: d894f9a33c8e4691
en: 22a3af4c51aee23e
zh: 9d59315313d0f761
@@ -4,26 +4,25 @@ Status: implemented
English | [中文](2026-08-27-adjacent-agent-steer-messaging.zh.md)
The adjacency, discovery, and parent-lifetime restrictions below are superseded by [tree-wide communication](2026-10-16-tree-wide-agent-communication.md); the remaining decisions stay active. The tool identity marker and initial-task communication guidance below are superseded by [tool-owned communication](2026-10-22-subagent-tool-owned-communication.md).
## Problem
Continuable Agents originally used direction-specific model controls. A parent called `send_message({ subagent_id, message })`, which delegated to a FIFO `followup` service operation. A child instead received a child-scoped `report({ output })` tool, a `tool:report` system-prompt section, and deployment-selected quiet or waking delivery. The tools described one adjacent-Agent operation through different schemas, service paths, source attribution, and scheduling.
A continuable child owns its own Session, so its parent does not automatically receive the child's transcript, tool output, or reasoning. The return path must therefore remain explicit and repeatable: a child may send progress before it finishes, remain available after sending, or fail before it can cooperate. Turning every final assistant message into an implicit result would conflate turn completion with model-selected communication and would not cover abnormal endings.
A continuable child owns its own Session, so its parent does not automatically receive the child's transcript, tool output, or reasoning. Explicit messages let a child send actionable findings before finishing. The runtime separately delivers terminal output in a settlement notice when the direct creator is still online.
The child-only tool and system-prompt section also preceded every inherited fork turn. They made a continuable fork child's request head differ from its parent's before the history that fork exists to reuse, forcing the provider to prefill the entire copied transcript again.
## Decision
`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` is the only public model-authored message operation. The continuation manager accepts only the exact live sender and a target on one adjacent edge:
`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` is the only public model-authored message operation. The continuation manager accepts only the exact live sender and a target in the same root tree; [tree-wide communication](2026-10-16-tree-wide-agent-communication.md) owns authorization and discovery.
- parent to direct continuable child, authorized by the child's durable `SessionHeader.parentSession`;
- resident continuable child to its exact live direct parent, authorized by the child's Activation.
Self-targets, stale Agent objects, unknown targets, and external executions are not alternate routes. The operation has no caller-supplied source, delivery mode, offline root mailbox, or provider dispatch.
Siblings, self-targets, ancestors beyond one edge, stale Agent objects, unknown targets, and one-shot children are not alternate routes. The operation has no caller-supplied source, delivery mode, offline parent mailbox, or provider dispatch.
Every accepted message uses `Agent.steer()`. A running target receives it at the nearest step boundary; an idle target starts a turn. An absent target is cold-resumed under the root before the same Steer delivery; its creation ancestors can remain offline. The manager retains waking-send accounting so a continuation-managed target cannot settle between synchronous inbox insertion and driver admission.
Every accepted message uses `Agent.steer()`. A running target receives it at the nearest step boundary; an idle target starts a turn. An absent direct child is cold-resumed through the existing continuation lifecycle before the same Steer delivery. The manager retains waking-send accounting so a continuation-managed target cannot settle between synchronous inbox insertion and driver admission.
Every direction uses one durable source. The service derives `senderSessionId` from the authorized Agent and frames the model-visible content as `Agent <sender-id> sent a message:`, so attribution cannot diverge from authority.
Every direction uses one durable source. The service derives `senderSessionId` from the exact live sender and frames content as `Agent <sender-id> sent a message:`.
```ts
import type { SessionId } from '@deepseek-ai/dsh-session'
@@ -41,20 +40,20 @@ The globally registered model tool is direction-neutral and has one fixed schema
```ts
interface SendMessageInput {
readonly agent_id: string
readonly agent: string
readonly message: string
}
```
Parents and children inherit the same definition in the same registry order. The standard definition carries a process-stable internal identity that a scoped same-name tool does not satisfy. An independently selected preset may omit the tool, a historical descriptor filter may remove it on resume, and a scoped replacement may provide different semantics; neither case receives the standard call instruction. When the standard tool remains visible, the continuation manager appends the JSON-encoded direct parent id and the instruction to send one self-contained result before finishing, plus earlier actionable findings, to the child's initial user task. For a fork child this task follows the inherited completed-turn prefix; no child-only system-prompt section or tool schema precedes that prefix.
Parents and children inherit the same definition in the same registry order. The standard definition carries a process-stable internal identity that a scoped same-name tool does not satisfy. An independently selected preset may omit the tool, a historical descriptor filter may remove it on resume, and a scoped replacement may provide different semantics; none receives the standard call instruction. When the standard tool remains visible, the continuation manager appends the JSON-encoded direct parent id, automatic final-answer delivery conditions, and guidance to send earlier actionable findings to the child's initial user task. For a fork child this task follows the inherited completed-turn prefix; no child-only system-prompt section or tool schema precedes that prefix.
The instruction is guidance, not settlement enforcement. Sending does not end the child's turn, zero or several calls remain mechanically valid, and the runtime never rejects a child for staying silent. The manager-owned `subagent-settled` notice remains unconditional and separately attributed because it records how an Activation ended and preserves terminal output when the child cannot cooperate.
The instruction is guidance, not settlement enforcement. Sending does not end the child's turn, zero or several calls remain mechanically valid, and the runtime never rejects a child for staying silent. The separately attributed `subagent-settled` notice records the Activation's outcome and terminal output only when its direct creator is still online and delivery is `parent`.
Human browser prompts are not model-authored Agent messages. The remote prompt path keeps a private Queue delivery so each human prompt remains a distinct turn. Interrupt behavior and settlement delivery remain independent.
### Complete removal and reintroduction condition
The standalone `@deepseek-ai/dsh-tool-subagent-report` package, `report` schema, `tool:report` prompt section, `reportDelivery` configuration, report-specific message source, catalog entries, composition rows, and supported-behavior snapshots are absent. The unified tool gives up the recipient-free child shortcut and the old ability for a structural return tool to survive an explicit child allow-list. Those capabilities return only if a concrete use case requires semantics that an adjacent `agent_id` and fixed Steer cannot express; reintroducing them requires a distinct model operation and prefix-cost evidence, not an alias over `sendMessage()`.
The standalone `@deepseek-ai/dsh-tool-subagent-report` package, `report` schema, `tool:report` prompt section, `reportDelivery` configuration, report-specific message source, catalog entries, composition rows, and supported-behavior snapshots are absent. The unified tool gives up the recipient-free child shortcut and the old ability for a structural return tool to survive an explicit child allow-list. Those capabilities return only if a concrete use case requires semantics that a tree member’s `agent` reference and fixed Steer cannot express; reintroducing them requires a distinct model operation and prefix-cost evidence, not an alias over `sendMessage()`.
## Alternatives considered
@@ -64,16 +63,16 @@ The standalone `@deepseek-ai/dsh-tool-subagent-report` package, `report` schema,
**Make `report` global.** Roots, one-shot children, remote children, and agentless callers cannot derive a report recipient. Advertising it globally would make schema visibility disagree with authority, while `send_message` already makes the recipient explicit.
**Turn every child final message into an implicit send.** A long-lived child may have nothing useful to send in one turn and several findings in another. Automatic delivery would merge model-authored communication with the runtime's settlement account and could not replace the unconditional notice on errors, cancellation, or token exhaustion.
**Treat the final answer as a model-authored send.** A settlement notice must preserve terminal output and abnormal endings even when the child cannot cooperate. It keeps runtime attribution distinct from findings the child explicitly chose to send.
**Rely only on the tool description.** A tool description helps after the model considers that tool; the failure mode is a child that believes it is finished without considering any return call. Initial-task guidance reaches that decision without changing the inherited system or tool prefix.
**Rely only on the tool description.** A tool description helps after the model considers that tool; initial-task guidance also explains which earlier findings should reach the parent and what the final answer must contain, without changing the inherited system or tool prefix.
**Keep quiet delivery as deployment policy.** A quiet model-authored message can be accepted while an idle target never reads it. Fixed Steer gives both directions one delivery meaning and preserves accepted order with later settlement notices.
## Consequences
- Model consumers expose one `send_message({ agent_id, message })` definition to parents and children, with no model-selected Queue versus Steer parameter.
- The continuation manager remains the sole owner of adjacency authorization, residency, cold resume, waking admission, and teardown races.
- Model consumers expose one `send_message({ agent, message })` definition to parents and children, with no model-selected Queue versus Steer parameter.
- The continuation manager remains the sole owner of tree authorization, residency, cold resume, waking admission, and teardown races.
- Accepted messages may extend a running target's current turn; messages waiting together share next-step FIFO ordering.
- Caller cancellation owns work only until inbox acceptance and does not retract an accepted message or dispose the target.
- The initial task carries JSON-encoded dynamic parent addressing after a fork prefix, while the request-head system prompt and tool ordering remain reusable.
@@ -4,26 +4,25 @@ Status: implemented
[English](2026-08-27-adjacent-agent-steer-messaging.md) | 中文
邻接消息、发现范围和父子生命周期限制已由[整树通信](2026-10-16-tree-wide-agent-communication.zh.md)替代;其余决策仍然有效。 下文的工具身份标记和初始任务通信指导由[工具拥有通信指导](2026-10-22-subagent-tool-owned-communication.zh.md)取代。
## 问题
可继续 Agent 最初使用方向专属的模型控制。parent 调用 `send_message({ subagent_id, message })`,委托给 FIFO `followup` 服务操作。child 则获得 child 作用域的 `report({ output })` 工具、`tool:report` 系统提示词 section,以及由部署选择的静默或唤醒投递。两个工具用不同 schema、服务路径、来源与调度描述同一个相邻 Agent 操作。
可继续 child 拥有自己的 Session,因此 parent 不会自动收到 child 的 transcript(文本记录)、工具输出或推理。返回路径必须保持显式且可重复:child 可以在结束前发送进度、发送后仍保持可用,也可能在来得及配合前失败。把每条最终 assistant 消息变成隐式结果会混淆轮次完成与模型选择的通信,而且无法覆盖异常结束。
可继续 child 拥有自己的 Session,因此 parent 不会自动收到 child 的 transcript(文本记录)、工具输出或推理。显式消息让 child 在结束前发送可操作的发现。直接创建者仍在线时,runtime 另行通过结算通知投递终态输出。
child 专属工具与系统提示词 section 还位于每个继承 fork 轮次之前。它们让可继续 fork child 的请求头在 fork 旨在复用的历史之前就与 parent 不同,迫使提供方重新预填充整份复制 transcript。
## 决策
`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` 是唯一公开的模型编写消息操作。继续执行管理器只接受确切在线 sender 与一条相邻边上的目标:
`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` 是唯一公开的模型编写消息操作。继续执行管理器只接受确切在线 sender 与同一根树中的目标;[整树通信](2026-10-16-tree-wide-agent-communication.zh.md)负责授权与发现规则。
- parent 到直接可继续 child,由 child 的持久化 `SessionHeader.parentSession` 授权;
- 驻留的可继续 child 到其确切在线直接 parent,由 child 的 Activation 授权。
自身目标、过期 Agent 对象、未知目标和外部执行不是替代路由。操作不接受调用方提供的 source、投递模式、离线根邮箱或提供方分发。
sibling、自身目标、超过一条边的 ancestor、陈旧 Agent 对象、未知目标与一次性 child 都不是替代路由。该操作没有调用方提供的 source、投递模式、离线 parent mailbox 或提供方分发。
每条被接受的消息都使用 `Agent.steer()`。运行中目标在最近 step 边界接收消息;空闲目标启动轮次。不在线目标先在根下冷恢复,再接受同一 Steer 投递;其创建祖先可以保持离线。管理器保留唤醒发送记账,因此受继续执行管理的目标不会在同步 inbox 插入与 driver 准入之间结算。
每条被接受的消息都使用 `Agent.steer()`。运行中目标在最近 step 边界接收消息;空闲目标启动轮次。缺失的直接 child 会先通过现有继续执行生命周期冷恢复,再接受同一 Steer 投递。管理器保留唤醒发送记账,因此受继续执行管理的目标不会在同步 inbox 插入与 driver 准入之间结算。
两个方向使用同一种持久来源。服务从已授权 Agent 推导 `senderSessionId`,并把模型可见内容组装为 `Agent <sender-id> sent a message:`,因此来源信息不会偏离权限。
所有方向使用同一种持久 source。服务从确切在线发送方推导 `senderSessionId`,并以 `Agent <sender-id> sent a message:` 包装内容。
```ts
import type { SessionId } from '@deepseek-ai/dsh-session'
@@ -41,20 +40,20 @@ interface AgentMessageSource {
```ts
interface SendMessageInput {
readonly agent_id: string
readonly agent: string
readonly message: string
}
```
parent 与 child 以相同注册表顺序继承相同定义。标准定义携带进程稳定的内部身份,同名的作用域工具不满足该身份。独立选择的 preset 可以省略该工具,历史 descriptor 过滤器可在恢复时移除该工具,作用域替代工具也可以提供不同语义;两种情况都不会收到标准调用指令。当标准工具仍可见时,继续执行管理器会把经过 JSON 编码的直接 parent id、结束前发送一份自包含结果的指令,以及更早发送可操作发现的指令追加到 child 初始用户任务。对 fork child 而言,该任务位于继承的已完成轮次前缀之后;没有 child 专属系统提示词 section 或工具 schema 位于此前缀之前。
parent 与 child 以相同注册表顺序继承相同定义。标准定义携带进程稳定的内部身份,同名的作用域工具不满足该身份。独立选择的 preset 可以省略该工具,历史 descriptor 过滤器可在恢复时移除该工具,作用域替代工具也可以提供不同语义;这些情况都不会收到标准调用指令。当标准工具仍可见时,继续执行管理器会把经过 JSON 编码的直接 parent id、最终回答的自动投递条件,以及提前发送可操作发现的指导追加到 child 初始用户任务。对 fork child 而言,该任务位于继承的已完成轮次前缀之后;没有 child 专属系统提示词 section 或工具 schema 位于此前缀之前。
该指令是指导,不是结算强制。发送不会结束 child 轮次,机制仍允许零次或多次调用,runtime 绝不会因 child 保持沉默而拒绝它。由管理器负责的 `subagent-settled` 通知仍无条件发送并采用独立来源,因为它记录 Activation 如何结束,并在 child 无法配合时保留终态输出。
该指令是指导,不是结算强制。发送不会结束 child 轮次,机制仍允许零次或多次调用,runtime 绝不会因 child 保持沉默而拒绝它。采用独立来源的 `subagent-settled` 通知仅在直接创建者仍在线且投递方式为 `parent` 时,记录 Activation 的结果与终态输出。
浏览器中的人类提示不是模型编写的 Agent 消息。远程提示路径保留私有 Queue 投递,使每条人类提示保持为独立轮次。中断行为与结算投递保持独立。
### 完整移除与重新引入条件
独立的 `@deepseek-ai/dsh-tool-subagent-report` 包、`report` schema、`tool:report` 提示词 section、`reportDelivery` 配置、report 专属消息来源、目录项、组合行和受支持行为快照均已不存在。统一工具放弃了无需接收方的 child 快捷方式,也放弃了让结构性返回工具绕过显式 child allow-list 的旧能力。只有具体用例需要相邻 `agent_id` 与固定 Steer 无法表达的语义时,这些能力才会重新出现;重新引入需要独立的模型操作与前缀成本证据,而非 `sendMessage()` 之上的别名。
独立的 `@deepseek-ai/dsh-tool-subagent-report` 包、`report` schema、`tool:report` 提示词 section、`reportDelivery` 配置、report 专属消息来源、目录项、组合行和受支持行为快照均已不存在。统一工具放弃了无需接收方的 child 快捷方式,也放弃了让结构性返回工具绕过显式 child allow-list 的旧能力。只有具体用例需要树成员的 `agent` 引用与固定 Steer 无法表达的语义时,这些能力才会重新出现;重新引入需要独立的模型操作与前缀成本证据,而非 `sendMessage()` 之上的别名。
## 考虑过的替代方案
@@ -64,16 +63,16 @@ parent 与 child 以相同注册表顺序继承相同定义。标准定义携带
**让 `report` 全局可见。** 根 Agent、一次性 child、远程 child 与无 Agent 调用方无法推导 report 接收方。全局宣传它会让 schema 可见性与权限不一致,而 `send_message` 已显式给出接收方。
**把每条 child 最终消息变成隐式发送。** 长期运行的 child 可能在某个轮次没有值得发送的内容,在另一个轮次却有多条发现。自动投递会混合模型编写通信与 runtime 结算说明,而且无法替代错误、取消或 token 耗尽时的无条件通知。
**将最终回答当作模型编写的发送。** 即使 child 无法配合,结算通知仍须保留终态输出与异常结束信息。它将 runtime 来源与 child 显式选择发送的发现区分开。
**只依赖工具描述。** 工具描述会在模型考虑该工具后提供帮助;失败模式是 child 认为自己已经完成而根本没有考虑返回调用。初始任务指导能触及该决策,又不会改变继承的系统或工具前缀。
**只依赖工具描述。** 工具描述会在模型考虑该工具后提供帮助;初始任务指导还说明哪些早期发现应发送给 parent,以及最终回答须包含什么,又不会改变继承的系统或工具前缀。
**保留静默投递作为部署策略。** 静默的模型编写消息可能被接受,但空闲目标永远不会读取它。固定 Steer 为两个方向提供一种投递含义,并保持与后续结算通知的接受顺序。
## 后果
- 模型 Consumer 向 parent 与 child 公开一个 `send_message({ agent_id, message })` 定义,不提供模型选择的 Queue 与 Steer 参数。
- 继续执行管理器仍是相邻关系授权、驻留、冷恢复、唤醒准入与拆卸竞态的唯一所有者。
- 模型 Consumer 向 parent 与 child 公开一个 `send_message({ agent, message })` 定义,不提供模型选择的 Queue 与 Steer 参数。
- 继续执行管理器仍是整树授权、驻留、冷恢复、唤醒准入与拆卸竞态的唯一所有者。
- 被接受的消息可以延长运行中目标的当前轮次;一起等待的消息共享 next-step FIFO 顺序。
- 调用方取消只在 inbox 接受前掌管工作,不会撤回已接受消息或 dispose(资源释放)目标。
- 初始任务在 fork 前缀之后携带经过 JSON 编码的动态 parent 地址,而请求头系统提示词与工具顺序保持可复用。
@@ -9,14 +9,14 @@
en: 98b49b900b8d2f84
zh: 557faac2976592be
/agent-note-explicit-agent-identity-at-runtime-boundaries/decision:
en: 1c628cf9c743c639
zh: d969b2475fe4a155
en: 4a3ab5108f37520f
zh: d13fcd448101a95d
/agent-note-explicit-agent-identity-at-runtime-boundaries/verification:
en: 85130faaa171036b
zh: 06e5e9fab32dbfb4
en: ce1733edf03f0387
zh: 8e1ca2b69e81968e
/agent-note-explicit-agent-identity-at-runtime-boundaries/alternatives-considered:
en: 2c41456509d54d21
zh: 5704ee9a2274e300
en: 34f5f8571b21897c
zh: cf16094bc0027ca1
/agent-note-explicit-agent-identity-at-runtime-boundaries/consequences:
en: 08ab86b89fc3ec6f
zh: 3866e6557858543a
en: 0b5914067f14282e
zh: c41d21aeb28e357f
@@ -14,34 +14,34 @@ Without an explicit owner, `SubagentManager` creates and resumes children throug
## Decision
Runtime interfaces carry Agent identity at the point that owns it. `AgentSetup` receives `(agentCtx, agent)`; Agent creation and resume options carry `parentAgent` for a runtime child; scoped events carry their Agent in the payload; Remote forwarding verifies that `request.agent` is the carrier key; and Host Typert Context resolution maps wire identity to a live Agent Context without a reverse scan. `agent.ctx` remains the registration and lifecycle owner and exposes no reverse Agent property.
Runtime interfaces carry Agent identity at the point that owns it. `AgentSetup` receives `(agentCtx, agent)`; each subagent Activation retains its exact runtime root; scoped events carry their Agent in the payload; Remote forwarding verifies that `request.agent` is the carrier key; and Host Typert Context resolution maps wire identity to a live Agent Context without a reverse scan. `agent.ctx` remains the registration and lifecycle owner and exposes no reverse Agent property.
Scope-aware registries continue to use the opaque scope key only for registration membership. Tool-subagent does not classify that key or resolve an Agent from Context. A direct `AgentSetup` passes the unpublished Session explicitly and installs through the supplied Context before publication. For a settings-backed standing preset, the event payload supplies the Agent, its Session supplies the policy target, and its Context owns the registrations.
Scope-aware registries continue to use the opaque scope key only for registration membership. Tool-subagent does not classify that key or resolve an Agent from Context. A direct `AgentSetup` receives the Agent explicitly and installs through the supplied Context before Agent publication. For a settings-backed standing preset, the event payload supplies the Agent, its Session supplies the policy target, and its Context owns the registrations.
`SubagentManager` puts the exact parent in both fresh-creation and cold-resume options. A live continuable child is therefore excluded from `AgentRegistry.roots()` and satisfies `isOwnedBy(child.id, parent)`. Durable `parentSession` metadata does not substitute for this relation: a fork or resumed Session may be a runtime root when no live Agent owns it.
`SubagentManager.rootOf(agent)` reads runtime membership from existing Activations; both the child and root must match their live registry objects. Ordinary Sessions resolve to themselves, and unmanaged subagent headers grant no membership. Durable `parentSession` describes creation edges; an Activation retains the exact root while intermediate creators are offline, and same-id replacements cannot inherit it. Root-only consumers classify the explicit subagent origin.
The [scope reference](../../../../packages/core/scope/README.md) and [Agent loop reference](../../../../packages/core/agent-loop/README.md) document registration and lifecycle behavior. The [initiator-scope decision](2026-07-15-agent-initiator-scope.md) owns private-chain rationale; explicit runtime ownership does not change causal initiation or registration lifetime.
## Verification
Agent creation tests pin explicit root and child ownership. Continuation integration tests keep a real child live long enough to assert both `roots()` exclusion and `isOwnedBy()` membership. Existing Schedule tests verify that root-only registrations stay absent from an explicitly owned child.
Managed-child tests verify intermediate-creator disposal and exact-instance checks after child or root replacement. SDK approval tests verify routing during a turn after the direct creator exits. Schedule and user-question tests reject delegated Sessions while ordinary historical forks remain independent.
Remote-event tests reject a missing or mismatched Agent before forwarding a scoped waterfall. Tool-subagent tests verify that direct setup installs before Session publication; standing-preset tests verify per-Session policy sampling and inheritance.
Remote-event tests reject a missing or mismatched Agent before forwarding a scoped waterfall. Tool-subagent tests verify that direct setup installs before Agent publication; standing-preset tests verify per-Session policy sampling and inheritance.
## Alternatives considered
**Keep `Context.agent`.** A reverse accessor makes registration ownership look like operation identity and requires every Context derivation, adapter, and test double to preserve an association unrelated to Cordis service selection or effect cleanup.
**Infer runtime ownership from the caller Context.** A private manager Context, an Agent Context, and a standing preset Context can all call the same factory. Context ancestry therefore does not state which live Agent owns the result; the creator must put the parent it already knows in the request options.
**Infer runtime ownership from the caller Context.** A private manager Context, an Agent Context, and a standing preset Context can all call the same factory. Context ancestry therefore does not state which live Agent owns the result; the subagent Activation retains the exact root supplied at admission.
**Classify Agent scope keys.** An opaque scope key states routing membership, not domain identity. Classifying it would make Agent the center of composition and would still couple a plugin's effect owner to the Session whose policy it needs.
**Use the initiating Agent as creation ownership.** Initiator scope records causal asynchronous execution, not lifetime ownership. A parent may initiate work that intentionally creates a root, and setup remains outside the child's driver boundary.
**Use durable Session lineage.** `parentSession` records conversation ancestry across process lifetimes. Runtime ownership controls live roots and teardown, so equating the two would prevent a legitimately resumed fork from becoming a top-level Agent.
**Use durable Session lineage.** `parentSession` records creation ancestry across process lifetimes. An intermediate creator can be offline while its descendant remains managed by a live root; durable ids also cannot distinguish replaced Agent instances. Historical ordinary forks may retain parent metadata without being delegated.
## Consequences
Lifecycle options, events, service requests, and transport requests carry explicit Agent identities, so each operation states the identity it uses and TypeScript checks both sides. Context remains reusable for dependency access and effect ownership without becoming an alternate domain-object locator.
Continuable children have the same runtime parent relation as one-shot in-process children. Root-only consumers exclude them, parent teardown can reason from one live ownership graph, and durable lineage remains free to describe history rather than process-local lifetime.
Continuable children have one runtime root independent of creator residency. The subagent manager owns tree teardown and local membership; the core factory and registry retain only instance creation, publication, and disposal. Third-party callers create managed children through the subagent service rather than assigning ownership to arbitrary factory-created Agents.
@@ -14,34 +14,34 @@ Agent 的 Cordis Context 拥有注册及其清理。Agent 身份则为某项操
## 决策
运行时接口在拥有身份的位置携带 Agent 身份。`AgentSetup` 接收 `(agentCtx, agent)`;创建与恢复 Agent 的 options 通过 `parentAgent` 标识运行时子级;作用域事件在 payload 中携带 Agent;Remote 转发校验 `request.agent` 就是 carrier key;Host Typert Context 解析则把协议身份映射到存活 Agent Context,不执行反向扫描。`agent.ctx` 继续拥有注册和生命周期,不暴露反向 Agent 属性。
运行时接口在拥有身份的位置携带 Agent 身份。`AgentSetup` 接收 `(agentCtx, agent)`;每个 subagent Activation 保留其确切运行时树根;作用域事件在 payload 中携带 Agent;Remote 转发校验 `request.agent` 就是 carrier key;Host Typert Context 解析则把协议身份映射到存活 Agent Context,不执行反向扫描。`agent.ctx` 继续拥有注册和生命周期,不暴露反向 Agent 属性。
感知作用域的注册表继续仅使用不透明作用域键判断注册成员关系。tool-subagent 不会分类该键,也不会从 Context 解析 Agent。直接 `AgentSetup` 显式传入尚未发布的 Session,并在发布前通过所给 Context 完成安装。对于由设置控制的常驻 preset,事件 payload 提供 Agent,其 Session 提供策略目标,其 Context 拥有注册项。
感知作用域的注册表继续仅使用不透明作用域键判断注册成员关系。tool-subagent 不会分类该键,也不会从 Context 解析 Agent。直接 `AgentSetup` 显式接收 Agent,并在 Agent 发布前通过所给 Context 完成安装。对于由设置控制的常驻 preset,事件 payload 提供 Agent,其 Session 提供策略目标,其 Context 拥有注册项。
`SubagentManager` 会把确切父级放进全新创建与冷恢复的 options。因此,存活的可续跑子级不会出现在 `AgentRegistry.roots()` 中,并且满足 `isOwnedBy(child.id, parent)`。持久化 `parentSession` 元数据不能代替这项关系:没有存活 Agent 拥有 fork 或已恢复会话时,它仍可成为 runtime root。
`SubagentManager.rootOf(agent)` 从已有 Activation 读取运行时成员关系;子级和树根都必须匹配注册表中的存活对象。普通 Session 返回自身,非托管的 subagent header 不授予成员身份。持久化 `parentSession` 描述创建关系;中间创建者离线时,Activation 保留确切树根,同 id 替代实例不能继承它。仅限根级的消费方依据显式 subagent 来源分类。
[作用域参考](../../../../packages/core/scope/README.zh.md)与 [Agent loop 参考](../../../../packages/core/agent-loop/README.zh.md)说明注册和生命周期行为。[发起方作用域决策](2026-07-15-agent-initiator-scope.zh.md)负责私有调用链理由;显式运行时归属不改变因果发起关系或注册生命周期。
## 验证
Agent 创建测试锁定显式的根级与子级归属。continuation 集成测试让一个真实子级保持存活,直到断言其既不属于 `roots()`、又满足 `isOwnedBy()`。现有 Schedule 测试验证仅限根级的注册项不会出现在显式归属的子级中。
托管子级测试验证中间创建者释放,以及子级或树根替换后的确切实例检查。SDK 审批测试验证直接创建者退出后的轮次内路由。Schedule 与用户问题测试拒绝委派 Session,历史普通 fork 则保持独立。
Remote 事件测试会在转发作用域 waterfall 前拒绝缺失或不匹配的 Agent。tool-subagent 测试验证 direct setup 会在 Session 发布前完成安装;常驻 preset 测试验证逐 Session 的策略读取与继承。
Remote 事件测试会在转发作用域 waterfall 前拒绝缺失或不匹配的 Agent。tool-subagent 测试验证 direct setup 会在 Agent 发布前完成安装;常驻 preset 测试验证逐 Session 的策略读取与继承。
## 考虑过的替代方案
**保留 `Context.agent`。** 反向 accessor 会让注册所有权看起来等同于操作身份,还要求每个 Context 派生、适配器和测试替身保留一项与 Cordis 服务选择或 effect 清理无关的关联。
**从调用方 Context 推断运行时归属。** 私有管理器 Context、Agent Context 和常驻 preset Context 都能调用同一个工厂。因此,Context 祖先关系无法说明由哪个存活 Agent 拥有结果;创建方必须把它已知的父级放进请求 options。
**从调用方 Context 推断运行时归属。** 私有管理器 Context、Agent Context 和常驻 preset Context 都能调用同一个工厂。因此,Context 祖先关系无法说明由哪个存活 Agent 拥有结果;subagent Activation 保留准入时传入的确切树根。
**分类 Agent 作用域键。** 不透明作用域键表达路由成员关系,而不是领域身份。分类该键会让 Agent 成为组合中心,也仍会把插件的 effect 所有者与策略所需的 Session 耦合起来。
**使用发起 Agent 作为创建归属。** 发起方作用域记录异步执行的因果关系,而非生命周期归属。父级可能发起有意创建根级 Agent 的工作,而 setup 仍位于子级驱动边界之外。
**使用持久化会话谱系。** `parentSession` 跨进程生命周期记录对话祖先关系。运行时归属控制存活根级和 teardown,因此把二者等同会阻止合法恢复的 fork 成为顶层 Agent。
**使用持久化会话谱系。** `parentSession` 跨进程生命周期记录创建祖先关系。中间创建者可以离线,而后代仍由存活树根管理;持久 id 也无法区分替换后的 Agent 实例。历史普通 fork 可能保留父级元数据,但并不属于委派。
## 后果
生命周期 options、事件、服务请求和传输请求会携带显式 Agent 身份,因此每项操作都会声明自身使用的身份,TypeScript 也会检查两侧。Context 可以继续复用于依赖访问与 effect 所有权,而不会成为另一种领域对象定位器。
可续跑子级与一次性进程内子级使用同一种运行时父级关系。仅限根级的消费方会排除这些子级,父级 teardown 可以依据唯一的存活归属图推理,而持久化谱系仍可描述历史,不必承担进程内生命周期语义。
可续跑子级拥有独立于创建者驻留状态的唯一运行时树根。subagent 管理器拥有整树 teardown 和本地成员关系;核心工厂与注册表仅保留实例创建、发布和释放。第三方调用方通过 subagent 服务创建托管子级,而非给任意工厂创建的 Agent 指定归属。
@@ -9,8 +9,8 @@
en: 5a991fec31578cea
zh: 13b75aa36eb22907
/agent-note-parent-owned-subagent-catalog-events/decision:
en: 4319c502038ab34d
zh: b11825ff9d744822
en: d2290d8e70d42054
zh: 64a2d1d0915f6c3e
/agent-note-parent-owned-subagent-catalog-events/alternatives-considered:
en: 7a3d4b9f69c243d8
zh: 82b03d16ccc94dd2
@@ -14,7 +14,7 @@ The child descriptor remains necessary for recovery and composition, but it cann
The shared activation entry point, external execution ownership, and caller-versus-parent result delivery are governed by [Unified subagent activations](../simplification/2026-09-17-unified-subagent-activations.md). This record retains the independent rationale described below.
The parent Session's required `subagent/catalog` events are the persistent authority for direct-child discovery. Every local child enters the catalog independently of its result recipient; external children enter only with parent delivery. Workflow membership records describe execution progress and do not replace local Session discovery. Each event contains `childId`, `childCreatedAt`, mode, and the mode-discriminated label. Local children use `continuable`; external executions use `external` and cannot be opened as local Sessions. Historical `one-shot` and `unknown` entries remain readable. Invalid own facts, including unsupported payload versions, reject projection restoration because silently dropping a required fact would return an incomplete catalog.
The parent Session's required `subagent/catalog` events are the persistent authority for direct-child discovery. Every local child enters the catalog independently of its result recipient; external children enter only with parent delivery. Workflow membership records describe execution progress and do not replace local Session discovery. Each event contains `childId`, `childCreatedAt`, mode, the mode-discriminated label, and, from payload v3, the child's [name](2026-11-02-subagent-catalog-names.md). Local children use `continuable`; external executions use `external` and cannot be opened as local Sessions. Historical `one-shot` and `unknown` entries remain readable. Invalid own facts, including unsupported payload versions, reject projection restoration because silently dropping a required fact would return an incomplete catalog.
Creation publishes only successful facts. A local activation admits the initial prompt before appending its catalog event. An external activation appends its event after provider startup and before returning the receipt. Failed admission or publication releases the unpublished execution; there is no compensating catalog event or rollback protocol.
@@ -22,7 +22,7 @@ The child header and `subagent/descriptor` remain authoritative for recovery and
The registered `subagentCatalog` projection materializes the parent facts. It delegates storage, append, iteration, and checkpoint validation to [`dsh-chunked-list`](../../../../packages/util/chunked-list/README.md), which stores facts in a persistent stack of 64-entry chunks, so an append copies at most the head chunk in bounded O(1) work. Materialization visits chunks from oldest to newest and preserves parent catalog event order in O(D) time for D facts. Concurrent creation is ordered by successful catalog append, independent of child timestamps and ids. A projection checkpoint clones the state once in O(D); projection-cache writes remain asynchronous and use the existing mandatory creation, turn-end, and disposal points.
The utility owns chunk layout and its shared capacity constant; the catalog owns event validation, fork filtering, and row conversion. Catalog projection state version 6 accepts external membership, so the projection registry rebuilds incompatible caches from Session events. Payload v0 and v1 remain unchanged; v2 records external executions.
The utility owns chunk layout and its shared capacity constant; the catalog owns event validation, fork filtering, and row conversion. Catalog projection state version 7 accepts external membership and child names, so the projection registry rebuilds incompatible caches from Session events. Payload v0 and v1 remain unchanged; v2 records external executions; v3 adds names to continuable and external entries.
Fork isolation uses the exact `Session.inheritedEventCount` supplied to projection initialization. The fold ignores `subagent/catalog` events below that offset. The state stores the inherited offset but not each event seq because acceptance is decided during folding.
@@ -14,7 +14,7 @@ child descriptor 对恢复与 composition 仍然必要,但它不能作为 disc
共享 activation 入口、外部执行所有权,以及调用方与父级之间的结果投递选择由[统一 subagent activation](../simplification/2026-09-17-unified-subagent-activations.zh.md) 决策拥有。本记录保留下述独立理由。
父级 Session 的必读 `subagent/catalog` 事件是发现直接子级的持久化权威。每个本地子级都会进入目录,与结果接收方无关;外部子级仅在 parent 投递时进入目录。工作流成员记录描述执行进度,不替代本地 Session 发现。每个事件包含 `childId`、`childCreatedAt`、mode 与按 mode 区分的 label。本地子级使用 `continuable`;外部执行使用 `external`,不能作为本地 Session 打开。历史 `one-shot` 与 `unknown` 条目仍可读取。无效的自身事实(包括不支持的载荷版本)会使投影恢复失败,因为静默丢弃必读事实将返回不完整的 catalog。
父级 Session 的必读 `subagent/catalog` 事件是发现直接子级的持久化权威。每个本地子级都会进入目录,与结果接收方无关;外部子级仅在 parent 投递时进入目录。工作流成员记录描述执行进度,不替代本地 Session 发现。每个事件包含 `childId`、`childCreatedAt`、mode、按 mode 区分的 label,以及从载荷 v3 起的子级[名称](2026-11-02-subagent-catalog-names.zh.md)。本地子级使用 `continuable`;外部执行使用 `external`,不能作为本地 Session 打开。历史 `one-shot` 与 `unknown` 条目仍可读取。无效的自身事实(包括不支持的载荷版本)会使投影恢复失败,因为静默丢弃必读事实将返回不完整的 catalog。
创建只发布成功事实。本地 activation 先接纳初始提示词,再追加 catalog 事件。外部 activation 在提供方启动后、返回回执前追加事件。准入或发布失败会释放未发布的执行;不存在补偿 catalog 事件或回滚协议。
@@ -22,7 +22,7 @@ child header 与 `subagent/descriptor` 继续拥有恢复与 composition 权威
注册的 `subagentCatalog` projection 物化 parent fact。它将存储、追加、迭代和检查点校验交给 [`dsh-chunked-list`](../../../../packages/util/chunked-list/README.zh.md),后者以每块 64 项的持久 stack 保存事实,因此 append 最多复制 head chunk,以有界 O(1) 工作完成。materialization 从旧到新访问 chunk,对 D 条事实以 O(D) 时间保留父目录事件顺序。并发创建按目录成功追加的顺序排列,与 child 时间戳和 id 无关。projection checkpoint 以 O(D) 克隆 state;projection-cache 继续异步写入,并使用既有创建、turn-end 与 disposal 强制点。
工具库拥有 chunk 布局与共享容量常量;catalog 拥有事件校验、fork 过滤和行转换。catalog 投影状态版本 6 接受外部成员,因此投影注册表会从 Session 事件重建不兼容缓存。载荷 v0 与 v1 保持不变;v2 记录外部执行。
工具库拥有 chunk 布局与共享容量常量;catalog 拥有事件校验、fork 过滤和行转换。catalog 投影状态版本 7 接受外部成员和子级名称,因此投影注册表会从 Session 事件重建不兼容缓存。载荷 v0 与 v1 保持不变;v2 记录外部执行;v3 为可继续和外部条目增加名称。
fork 隔离使用 projection 初始化时提供的精确 `Session.inheritedEventCount`。fold 忽略该 offset 之前的 `subagent/catalog` 事件。state 保存 inherited offset,但不保存每条 event seq,因为接受判定已在 fold 时完成。
@@ -9,8 +9,8 @@
en: 4548e7fbdbee5b5a
zh: 0078762ea92fa180
/agent-note-consumer-owned-startup-strictness/decision:
en: 1f11e04162e19ad1
zh: d102e300eba9a2ea
en: 62e93742af5bed66
zh: 3944d685b6503a2c
/agent-note-consumer-owned-startup-strictness/alternatives-considered:
en: 1b67e062213184cd
zh: 91cc8a015de2bfe4
@@ -18,5 +18,5 @@
en: 283dec1b336c540d
zh: 2ed358079072cfff
/agent-note-consumer-owned-startup-strictness/testing:
en: c38221069ce98fd3
zh: 9a2f91f539eba4d4
en: cee14c22297e5471
zh: a68748cc36f4a574
@@ -20,6 +20,8 @@ The audit runs only during initial application boot. Later config HMR remains be
This policy governs [Web host boot](../../../../packages/boot/app-boot/README.md), including its [client plugin roster](2026-07-23-client-plugin-loading-model.md). [Per-session presets](2026-09-18-declarative-agent-presets.md) own a separate strict subtree audit.
The browser boot audit applies the same policy to the client plugin tree with its own global list in [`boot-client.ts`](../../../../packages/client/web/src/boot-client.ts): the module system, Session control, the renderer, the application frame, the Session view, and its sidebar. Browser entries are named by package, so the list holds package names; a name absent from the boot graph has no effect, and neither list is derived from the other. The list is the minimum fatal set: an entry that a listed entry waits on blocks startup too. Feature plugins, including credential masking, stay optional. A failed entry reports its activation error. Optional failures appear in one console warning, and in Settings → Plugins → Plugin list only after a later graph synchronization reconciles them. Renaming a listed package requires updating the list and its tests.
## Alternatives considered
- **Add transactional and best-effort modes to vendored Loader.** Rejected because strictness belongs to the application or resource owner, while a Loader group contains unrelated plugins. A mode would also expand the vendor patch and leave callers to select a policy at every group.
@@ -36,6 +38,8 @@ The compact terminal report keeps the failing plugins visible; a separate file r
App-boot unit tests cover absent and disabled required ids, optional import failure, config evaluation failure, synchronous and asynchronous `apply()` failure, pending dependencies, and required failure teardown. Unit expectations pin diagnostic grouping, preservation of original error objects and import logs, exporter cleanup, complete diagnostic values, private file creation, concurrent report names, and failed-write fallback. The built Web-profile acceptance asserts a single port-conflict stack without Node wrapper output, verifies the saved diagnostic file and its stderr fallback, serves the full UI with optional failures and exits nonzero without readiness when the required HTTP port is occupied or `modules` or `connection` cannot activate.
Browser audit unit tests (`boot-client.client.spec.ts`, `boot.client.spec.ts`) cover required and optional import, creation, activation, and pending failures, carrier presentation, and required names absent from the boot graph. Test carriers are stricter than the product: `TestClient` requires every roster entry to activate unless a case passes `allowInactiveEntries`, and the browser e2e lanes treat a `web boot: warning` as a failure with no opt-out.
The [Web process matrix](../../../../apps/cli/tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts) independently exercises optional and required failures at startup and after native patch-file edits. Authenticated HTTP requests and plugin lifecycle files distinguish a usable application from a surviving process. These keyless process checks complement the [controlled-delivery unit tests](../../../../packages/boot/app-boot/tests/user-patches.spec.ts): unit tests isolate reconciliation failures, while the process tests also require the shipped launcher, native watcher, and bounded shutdown to work together.
The matrix enables Chokidar's `awaitWriteFinish` to acknowledge stable file contents before each reload; otherwise its short change-event suppression window can discard the next test edit. Native events remain required, and assertions wait for observed activation or failure rather than a fixed settling sleep. This is explicit test configuration, not evidence for the default watcher timing.
@@ -20,6 +20,8 @@ Required id 为 `agent-loop`、`webserver`、`modules`、`connection`、`headles
该策略适用于 [Web host 启动](../../../../packages/boot/app-boot/README.zh.md),包括其 [client 插件名册](2026-07-23-client-plugin-loading-model.zh.md)。[按会话的 preset](2026-09-18-declarative-agent-presets.zh.md)持有独立的严格子树审计。
浏览器启动审计以同一策略审计客户端插件树,使用 [`boot-client.ts`](../../../../packages/client/web/src/boot-client.ts) 中独立的全局名单:模块系统、Session 控制、渲染器、应用框架、Session 视图及其侧栏。浏览器 entry 以包名命名,因此名单写包名;启动图中不存在的名字不产生影响,两份名单互不推导。名单是最小致命集合:已列出 entry 所等待的 entry 同样会阻止启动。功能插件(包括凭据遮挡)保持可选。失败 entry 报告其激活错误。可选失败出现在一条控制台警告中;只有后续图同步调和之后,才会出现在「设置 → 插件 → 插件列表」。重命名名单中的包需要同步更新名单及其测试。
## 考虑过的替代方案
- **给 vendored Loader 增加 transactional 与 best-effort mode。** 拒绝,因为严格语义属于应用或资源 owner,而一个 Loader group 包含互不相关的 plugin。Mode 还会扩大 vendor patch,并要求 caller 为每个 group 选择 policy。
@@ -36,6 +38,8 @@ Required id 为 `agent-loop`、`webserver`、`modules`、`connection`、`headles
App-boot 单元测试覆盖缺失和禁用的 required id、optional import failure、config evaluation failure、同步和异步 `apply()` failure、pending dependency,以及 required failure teardown。单元预期输出固定诊断分组、原始错误对象与导入日志的保留、exporter 清理、完整诊断值、私有文件创建、并发报告命名以及写入失败回退行为。构建后的 Web-profile acceptance 断言端口冲突堆栈只输出一次且不包含 Node 包装输出,验证已保存的诊断文件及其 stderr 回退,并会在 optional failure 存在时继续提供完整 UI,并在 required HTTP port 被占用或 `modules`、`connection` 无法激活时以非零码退出,且不报告就绪。
浏览器审计单元测试(`boot-client.client.spec.ts`、`boot.client.spec.ts`)覆盖必需与可选 entry 的导入、创建、激活与 pending 失败、carrier 呈现,以及启动图中不存在的必需名称。测试载体比产品更严格:除非用例传入 `allowInactiveEntries`,`TestClient` 要求每个 roster entry 都激活;浏览器 e2e 把 `web boot: warning` 视为失败,且不提供放宽。
[Web 进程矩阵](../../../../apps/cli/tests/profiles/web/tests/web-failure-matrix.expected.e2e.ts)分别验证启动时和原生补丁文件修改后的 optional 与 required 失败。经过认证的 HTTP 请求和插件生命周期文件区分可用应用与仅存活的进程。这些无需密钥的进程检查与[受控事件投递单元测试](../../../../packages/boot/app-boot/tests/user-patches.spec.ts)互补:单元测试隔离配置协调失败,进程测试还要求随附启动器、原生监听器和有界关闭流程协同工作。
矩阵启用 Chokidar 的 `awaitWriteFinish`,在每次重载前确认文件内容已稳定;否则它的短暂 change 事件抑制窗口可能丢弃下一次测试编辑。测试仍然依赖原生事件,并等待观察到激活或失败,而不是固定时长的休眠。这是显式测试配置,不能证明默认监听器的时序行为。
@@ -15,8 +15,8 @@
en: 3aef37bb4f8a8e53
zh: a7b477324d486ab3
/agent-note-sandboxed-node-execution-for-ptc/decision/control-and-lifetime:
en: 2c7a694b04951772
zh: 133b95b0477b127f
en: a8897bae00f5f674
zh: 2c959353c5a885a4
/agent-note-sandboxed-node-execution-for-ptc/decision/resource-limits:
en: 3f1d8a30f6d48243
zh: faa7c01d7c959893
@@ -28,7 +28,7 @@ The [experimental Python provider](../../../../packages/experimental/ptc-runtime
The subprocess owner supplies a dedicated inherited binary control channel, separate from program stdout/stderr and launcher lifecycle IPC. The host bounds frames, queued writes, pending calls and outstanding argument bytes, then validates call identity and the binding allowlist before dispatch. Model code can write to that channel, so its bytes remain untrusted.
Program completion, timeout, cancellation and protocol failure all close execution through the managed process owner. Result selection stops the execution timer; cleanup then waits for the direct outcome and managed-range quiescence. The PTC bridge separately aborts and drains nested tool dispatches before its outer tool result settles. A denial or transport failure never automatically replays a program whose effects may already have occurred.
For temporary executions, program completion, timeout, cancellation and protocol failure all close execution through the managed process owner. Result selection stops the execution timer; cleanup then waits for the direct outcome and managed-range quiescence. The PTC bridge separately aborts and drains nested tool dispatches before its outer tool result settles. A denial or transport failure never automatically replays a program whose effects may already have occurred.
### Resource limits
@@ -28,7 +28,7 @@ Host 在子进程启动时保留 `ELECTRON_RUN_AS_NODE`;bootstrap 在求值前
子进程所有者提供专用的继承式二进制控制通道,与程序 stdout/stderr 及 launcher 生命周期 IPC 分开。Host 限制帧、排队写入、待处理调用和未完成参数字节,然后在分派前验证调用身份与绑定允许列表。模型代码可以写入该通道,因此其中字节仍不可信。
程序完成、超时、取消与协议失败都通过受管进程所有者关闭执行。选择结果后停止执行计时器;清理随后等待直接结果与受管范围停稳。PTC 桥接在外层工具结果结算前,另行取消并排空嵌套工具分派。拒绝或传输失败绝不自动重放可能已经产生副作用的程序。
对于临时执行,程序完成、超时、取消与协议失败都通过受管进程所有者关闭执行。选择结果后停止执行计时器;清理随后等待直接结果与受管范围停稳。PTC 桥接在外层工具结果结算前,另行取消并排空嵌套工具分派。拒绝或传输失败绝不自动重放可能已经产生副作用的程序。
### 资源限制
@@ -9,8 +9,8 @@
en: 2abacff687661d28
zh: 2d7ba3ae27dba888
/agent-note-keep-live-configuration-in-the-owning-profile/decision:
en: 66c3884425cfb90a
zh: 0fe6a1f1a576470b
en: f9b1dd3d158457a9
zh: 06ca76fcbbc76cda
/agent-note-keep-live-configuration-in-the-owning-profile/alternatives-considered:
en: 4089464ad1fb60bd
zh: 3168e9e4a205145d
@@ -14,7 +14,7 @@ A plugin declares every configurable value in its Cordis Config. Fields that can
Form writes target the active profile. Home patches and command-line overlays remain higher-priority deployment inputs; an edit they would shadow fails before persistence. Two plugin instances are addressed by their distinct profile entry ids. Nested Includes retain independent ownership and are not writable through the profile form.
Cordis config patches replace complete entry configs. An edit preserves ordinary fields and unedited secrets, including raw configuration expressions. A field reset restores the currently inherited value; resetting the complete entry removes its config overrides so later bundle changes are inherited. Other edits store the entry's complete config in the profile row: the ordinary fields as composed at write time plus every volatile field. Later bundle changes to those fields do not reach that profile until the entry's config override is removed. In the shipped bundles this pins `permission.presets` after a default-preset change, `agent-presets` after a preset choice, `agent-loop.agents` after a parallelism edit, and `web-search-deepseek.apiKeyEnv` after a Web Search edit, and the client marks every volatile field of such a row as overridden, not only the edited one. Narrowing a write to the edited fields needs merge semantics for patch `config`, which Include does not provide.
Cordis config patches replace complete entry configs. An edit preserves ordinary fields and unedited secrets, including raw configuration expressions. A field reset restores the currently inherited value; resetting the complete entry removes its config overrides so later bundle changes are inherited. Other edits store the entry's complete config in the profile row: the ordinary fields as composed at write time plus every volatile field. Later bundle changes to those fields do not reach that profile until the entry's config override is removed. In the shipped bundles this pins `permission.presets` after a default-preset change, `agent-presets` after a preset choice, `agent-loop.agents` after a parallelism edit, and `web-search-deepseek.apiKeyEnv` after a legacy Messages search edit on the Web search and fetch page, and the client marks every volatile field of such a row as overridden, not only the edited one. Narrowing a write to the edited fields needs merge semantics for patch `config`, which Include does not provide.
The removed `$DSH_HOME/settings.yaml` is imported once into the active profile when the Loader has settled every entry after Settings starts: section ids are entry ids, with `ui-developer-tools` → `ui-settings`, `ui-onboarding` → `ui-settings-general`, and `shell` → the platform's shell executor entry. The file is renamed to `settings.yaml.imported` before the first write, so the import never repeats; a rejected section is logged and stays in the renamed file.
@@ -14,7 +14,7 @@ Status: implemented
表单写入当前 profile。home patch 和命令行 overlay 仍是优先级更高的部署输入;如果它们会遮蔽某次修改,编辑器会在持久化前拒绝写入。两个插件实例通过各自不同的 profile entry id 寻址。嵌套 Include 保留独立所有权,不能通过该 profile 表单写入。
Cordis config patch 替换完整 entry config。编辑会保留普通字段、未编辑的秘密值和原始配置表达式。字段重置恢复当前继承值;整个 entry 重置后会移除其 config override,从而继承后续 bundle 变化。其他编辑会把该 entry 的完整 config 存入 profile 行:写入时组合出的普通字段加上全部 volatile 字段。此后 bundle 对这些字段的修改在该 entry 的 config override 被移除之前不会到达该 profile。在已发布的 bundle 中,修改默认权限预设会钉住 `permission.presets`,选择预设会钉住 `agent-presets`,编辑并行度会钉住 `agent-loop.agents`,编辑网页搜索会钉住 `web-search-deepseek.apiKeyEnv`;客户端会把这类行的每个 volatile 字段都标为已覆盖,而不只是被编辑的那个。要把写入收窄到被编辑字段需要 patch `config` 的合并语义,Include 不提供。
Cordis config patch 替换完整 entry config。编辑会保留普通字段、未编辑的秘密值和原始配置表达式。字段重置恢复当前继承值;整个 entry 重置后会移除其 config override,从而继承后续 bundle 变化。其他编辑会把该 entry 的完整 config 存入 profile 行:写入时组合出的普通字段加上全部 volatile 字段。此后 bundle 对这些字段的修改在该 entry 的 config override 被移除之前不会到达该 profile。在已发布的 bundle 中,修改默认权限预设会钉住 `permission.presets`,选择预设会钉住 `agent-presets`,编辑并行度会钉住 `agent-loop.agents`,在「网页搜索与获取」页编辑旧版 Messages 搜索会钉住 `web-search-deepseek.apiKeyEnv`;客户端会把这类行的每个 volatile 字段都标为已覆盖,而不只是被编辑的那个。要把写入收窄到被编辑字段需要 patch `config` 的合并语义,Include 不提供。
被移除的 `$DSH_HOME/settings.yaml` 会在 Settings 启动后、Loader 完成所有条目加载时一次性导入当前 profile:section id 即 entry id,其中 `ui-developer-tools` → `ui-settings`、`ui-onboarding` → `ui-settings-general`、`shell` → 当前平台的 shell 执行器条目。文件在第一次写入前改名为 `settings.yaml.imported`,因此导入不会重复;被拒绝的 section 会记录日志并保留在改名后的文件中。
@@ -9,11 +9,11 @@
en: 97fe58a13135b4a1
zh: 7490373a8d941da5
/agent-note-active-tool-headers-and-stateful-request-projection/decision:
en: 0c40a9283650c643
zh: 91a18d752d09a9ce
en: 24138152d511bf12
zh: 95516d7566b6db7c
/agent-note-active-tool-headers-and-stateful-request-projection/alternatives-considered:
en: b78785f2a603eba8
zh: b8c268228da7e2e7
en: 311db1f781e386e7
zh: 7e3312544154a158
/agent-note-active-tool-headers-and-stateful-request-projection/consequences:
en: 60d7bf28fa5a8fd1
zh: a17b222e7aca01e2
en: 8a64dba42c01f647
zh: 9e7ba7a5bd855031
@@ -14,9 +14,9 @@ Plugin and MCP tools change during conversations. Providers differ in whether th
`Session.toolHistory()` caches a capability-independent fold of committed headers and developer messages. First access restores inherited events; later reads consume only unseen events. Immutable snapshots carry initial declarations and ordered update identities with additions resolved through their historical headers. Explicit series starts and changed retained definitions rebuild declarations. Restoring a retained name with an unchanged definition continues the series: the projection keeps the declaration unchanged and lets the recorded addition re-offer the tool after its earlier removal, so a removal never disables a declared tool and a loading mode never rewrites an earlier declaration.
Loop and compaction requests carry this snapshot as `GenerateOptions.toolHistory`. At adapter dispatch, `projectToolUpdates` uses the prepared route's `toolUpdate`. Unsupported routes receive active definitions without `deferLoading` or developer messages. Supported routes defer historical additions; `in-history` retains removed definitions and removal blocks, while `addition-only` omits both. Only current-series update identities survive. Missing history or a request prefix omitting recorded updates uses current declarations without developer updates. Explicit deferred loading remains available on capable routes.
Loop and compaction requests carry this snapshot as `GenerateOptions.toolHistory`. At adapter dispatch, `projectToolUpdates` uses the prepared route's `toolUpdate`. Unsupported routes receive active definitions without `deferLoading` or developer messages. Supported routes defer historical additions; `in-history` retains removed definitions and removal blocks, while `addition-only` omits both. On addition-only routes, removing any tool rebuilds the route's baseline after that update: the request declarations change even when the removed tool was activated later, so retaining superseded activation blocks cannot preserve the full conversation prefix. Adding tools when no declarations exist also rebuilds the baseline. Deferred declarations count as existing tools. The first native tool changes the empty declaration prefix; deferring it provides no cache advantage over declaring it immediately. Previously activated tools become immediate; explicitly deferred declarations with no activation remain deferred. Subsequent additions remain deferred until their recorded activation. Superseded tool blocks are omitted without discarding other content in those messages. The Session fold and durable events remain capability-independent. Missing history or a request prefix omitting recorded updates uses current declarations without developer updates.
DeepSeek translates projected updates into system-role `tool_addition` and `tool_removal` blocks and `defer_loading` declarations, sending the tool-changes beta header when update blocks occur. The default `deepseek-flash` entry declares `addition-only`; pi-ai declares no mode. Chat and Trajectory display developer messages as context nodes, using localized tool-change notices: single changes show the tool name; multiple changes show counts and expandable name lists.
DeepSeek translates projected updates into system-role `tool_addition` and `tool_removal` blocks and `defer_loading` declarations, sending the tool-changes beta header when update blocks occur. The default `deepseek-flash` entry declares `addition-only`; pi-ai selects `in-history` for compatible Anthropic routes and `addition-only` for compatible Chat Completions and Responses routes. Chat and Trajectory display developer messages as context nodes, using localized tool-change notices: single changes show the tool name; multiple changes show counts and expandable name lists.
## Alternatives considered
@@ -26,6 +26,8 @@ DeepSeek translates projected updates into system-role `tool_addition` and `tool
**Scan the complete log on every request.** The cached fold consumes new events and supplies stable snapshots to ordinary and auxiliary calls without adding arbitrary synchronous history readers.
**Rebuild the baseline on every removal.** Deferred tools also have request declarations; deleting a tool changes those declarations regardless of its activation position. Addition-only routes cannot express that deletion in history. The rebuild therefore also covers removal-only updates and removals of later-activated tools. A past reset cannot force every future addition to become immediate: each reset establishes a new baseline from which deferred additions resume.
**Reject same-name schema changes during tool registration.** This restriction is not enforced. Immediate failure for a plugin registering an incompatible tool requires comparing the new registration with historical definitions of tools called on each affected current surface, while excluding calls removed by compaction. That registration-time historical and scope-aware check is deferred because of its implementation complexity. Same-name schema changes remain allowed; they rebuild declarations and invalidate prefix reuse even on `in-history` routes.
## Consequences
@@ -34,4 +36,6 @@ Tool lists and logical update events are capability-independent; only outgoing d
The authored [headless prompt/tool scenario](../../../../snapshots/session/dynamic-tool-prompt-updates/snapshot.yml) pins an appended prompt beside a deferred tool addition and verifies that the first request's messages stay unchanged. It shares the stable-prompt scenario's tool lifecycle and schema expectations.
The [DeepSeek replacement scenario](../../../../snapshots/session/deepseek-deferred-tools/snapshot.yml) checks the first additions to an empty set, a rebuilt baseline, a subsequent deferred addition, and removal of that later-activated tool through Messages serialization. Explicit deferred-only requests remain unchanged under the [DeepSeek failure policy](../simplification/2026-11-02-deepseek-deferred-tool-failures.md). These checks establish request construction, not measured cache savings from rebuilding.
The [real DeepSeek SDK test](../../../../apps/cli/tests/profiles/sdk/dynamic-tool-cache.e2e.ts) measures the first request after a native tool addition with both unchanged and simultaneously updated instructions. Unique user history prevents a shared system-prompt cache hit from satisfying the preceding-input assertion. Removal checks tool availability without requiring cache retention; each case disables retries.
@@ -14,9 +14,9 @@ Status: implemented
`Session.toolHistory()` 缓存对已提交请求头和 developer 消息的归约,不依赖模型能力。首次访问恢复继承事件;后续读取仅处理新增事件。不可变快照携带初始声明和有序更新标识,添加定义通过历史请求头解析。显式新序列和保留定义变化会重建声明。以未变化的定义恢复保留名称时序列继续:投影保持声明不变,由记录的添加在先前移除之后重新提供该工具,因此移除不会禁用已声明的工具,加载模式也不会改写先前声明。
循环和压缩请求通过 `GenerateOptions.toolHistory` 携带此快照。适配器分发时,`projectToolUpdates` 使用已准备路由的 `toolUpdate`。不支持的路由接收有效定义,不携带 `deferLoading` 或 developer 消息。支持的路由延迟历史添加;`in-history` 保留已移除定义和移除块,`addition-only` 则省略两者。仅保留当前序列的更新标识。缺少历史或请求前缀遗漏已记录更新时,使用当前声明且不发送 developer 更新。显式延迟加载在支持的路由上仍然可用。
循环和压缩请求通过 `GenerateOptions.toolHistory` 携带此快照。适配器分发时,`projectToolUpdates` 使用已准备路由的 `toolUpdate`。不支持的路由接收有效定义,不携带 `deferLoading` 或 developer 消息。支持的路由延迟历史添加;`in-history` 保留已移除定义和移除块,`addition-only` 则省略两者。在 addition-only 路由移除任何工具时,使用该次更新后的状态重建路由基线:即使被移除工具是后来激活的,请求声明也已发生变化,保留已被取代的激活块无法保留完整对话前缀。不存在任何声明时添加工具,也会重建基线。延迟声明也计入已有工具。首个原生工具会改变空声明前缀;将其延迟加载并不比直接声明具有缓存优势。此前已激活工具变为立即加载;没有激活记录的显式延迟声明仍保持延迟。后续添加在记录的激活位置前保持延迟加载。已被取代的工具块会被省略,但不会丢弃这些消息中的其他内容。Session 归约和持久事件仍不依赖模型能力。缺少历史或请求前缀遗漏已记录更新时,使用当前声明且不发送 developer 更新。
DeepSeek 把投影后的更新转换为 system 角色的 `tool_addition` 和 `tool_removal` 块以及 `defer_loading` 声明,在存在更新块时发送工具变更 beta 请求头。默认 `deepseek-flash` 条目声明 `addition-only`;pi-ai 不声明模式。Chat 和 Trajectory 将 developer 消息显示为上下文节点,工具变更使用本地化通知:单项变更显示工具名,多项变更显示数量及可展开的名称列表。
DeepSeek 把投影后的更新转换为 system 角色的 `tool_addition` 和 `tool_removal` 块以及 `defer_loading` 声明,在存在更新块时发送工具变更 beta 请求头。默认 `deepseek-flash` 条目声明 `addition-only`;pi-ai 为兼容的 Anthropic 路由选择 `in-history`,为兼容的 Chat Completions 和 Responses 路由选择 `addition-only`。Chat 和 Trajectory 将 developer 消息显示为上下文节点,工具变更使用本地化通知:单项变更显示工具名,多项变更显示数量及可展开的名称列表。
## Alternatives considered
@@ -26,6 +26,8 @@ DeepSeek 把投影后的更新转换为 system 角色的 `tool_addition` 和 `to
**每次请求扫描完整日志。** 缓存归约处理新事件,为普通和辅助调用提供稳定快照,不增加任意同步历史读取接口。
**每次移除都重建基线。** 延迟工具也具有请求声明;删除工具会改变这些声明,与其激活位置无关。addition-only 路由无法通过历史表达删除。因此,仅移除的更新和移除后来激活的工具也会重建基线。一次历史重置不能让所有后续添加都变为立即加载:每次重置确立新基线,后续添加继续采用延迟加载。
**在工具注册时拒绝同名 schema 变化。** 当前不执行此限制。要让注册不兼容工具的插件立即收到异常,需要将新注册与各受影响当前 surface 中已调用工具的历史定义比较,同时排除已被压缩移除的调用。由于实现复杂,注册时结合历史和作用域的检查留待后续实现。同名 schema 变化仍被允许;它们会重建声明,即使在 `in-history` 路由上也会使前缀复用失效。
## Consequences
@@ -34,4 +36,6 @@ DeepSeek 把投影后的更新转换为 system 角色的 `tool_addition` 和 `to
手写的[无界面提示词/工具场景](../../../../snapshots/session/dynamic-tool-prompt-updates/snapshot.yml)钉住追加提示词与延迟工具添加同时发生的行为,并验证首次请求的消息保持不变。它复用稳定提示词场景的工具生命周期和 schema 期望值。
[DeepSeek 替换场景](../../../../snapshots/session/deepseek-deferred-tools/snapshot.yml)通过 Messages 序列化验证向空工具集首次添加工具、重建后的基线、后续延迟添加,以及移除该后来激活的工具。根据 [DeepSeek 失败策略](../simplification/2026-11-02-deepseek-deferred-tool-failures.zh.md),显式全延迟请求保持原样。这些检查验证请求构造,不代表已经测量重建带来的缓存收益。
[真实 DeepSeek SDK 测试](../../../../apps/cli/tests/profiles/sdk/dynamic-tool-cache.e2e.ts) 分别在指令不变和指令同时更新时测量原生工具新增后的首次请求。独有的用户历史确保仅命中共享系统提示词缓存无法满足前序输入断言。移除场景检查工具可用性,不要求保留缓存;每个用例均禁用重试。
@@ -36,8 +36,8 @@
en: 21a74f9031f86a92
zh: c6abd0a5dfbf56a8
/agent-note-definition-owned-grouping-with-generic-node-group-assembly/verification:
en: b42409d029fc0fde
zh: 0f8ab7d206cab772
en: ff829b9a2772df95
zh: df0e6524d00034df
/agent-note-definition-owned-grouping-with-generic-node-group-assembly/consequences:
en: 96f1d81b28534661
zh: 8cb1c7a0f000c335
en: 236ddec90acc6ca1
zh: d4807f635e217766
@@ -159,7 +159,7 @@ Business behavior is verified through keyed-update regressions and recorded Web
| Evidence | Required observation |
|---|---|
| Keyed notifications and React updates | Content growth affects the owning Node and affected Group; unchanged historical rows and unrelated Turns receive no extra notifications. Mode changes preserve keys and member parents. |
| Recorded Web replay | Current accessible titles, group disclosure, hidden ordinary Context, trigger notices, and footer placement match the committed expected output. |
| Recorded Web replay | Current accessible titles, group disclosure, Context rows, trigger notices, and footer placement match the committed expected output. |
[Recorded Web scenarios](../../../../apps/web/tests/steering.e2e.ts) cover online steering, reconnect handoff, and grouped presentation through the shipped Web profile. These behavior checks do not establish a quantified latency or memory improvement.
@@ -171,4 +171,4 @@ Business behavior is verified through keyed-update regressions and recorded Web
- Stable mounting does not eliminate layout, paint, or retained-memory costs. No measured latency or optimal-performance claim is made.
- Real group splits, merges, first-member changes, and pagination repairs can change identity. The mode-switch guarantee does not prohibit those legitimate changes.
- Revealing hidden parts, selection/copy, interruption notices, group disclosure, and outer Turn interaction belong to Chat's business adaptation; generic reference tests alone do not establish those behaviors.
- Turn status/duration replaces the old tool/message-count title, so status, accessible names, and empty-process Turns require explicit presentation coverage. Hiding ordinary Context retains non-human waking notices and original Session/Trajectory inspection; footer placement depends on the real Turn end rather than group membership.
- Turn status/duration replaces the old tool/message-count title, so status, accessible names, and empty-process Turns require explicit presentation coverage. Non-human waking notices remain independent of Context rows, and Session/Trajectory inspection is unchanged; footer placement depends on the real Turn end rather than group membership.
@@ -159,7 +159,7 @@ React key 由引用 kind、NodeKey/groupPart 或 GroupKey 的无歧义元组派
| 证据 | 必须观察到的结果 |
|---|---|
| 按键通知与 React 更新 | 正文增长影响所属 Node 和相关 Group;未变化历史行及其他 Turn 没有额外通知。模式变化保留 key 与成员父级。 |
| 已录制的 Web 回放 | 当前可访问标题、组开合、普通 Context 隐藏、触发通知及页脚位置与提交的预期输出一致。 |
| 已录制的 Web 回放 | 当前可访问标题、组开合、Context 行、触发通知及页脚位置与提交的预期输出一致。 |
[已录制的 Web 场景](../../../../apps/web/tests/steering.e2e.ts)通过正式 Web profile 覆盖在线 steering、重连接续与分组展示。这些行为检查不构成量化延迟或内存改善的证明。
@@ -171,4 +171,4 @@ React key 由引用 kind、NodeKey/groupPart 或 GroupKey 的无歧义元组派
- 稳定挂载不消除布局、绘制或保留内存的成本,不宣称实测延迟或最优性能。
- 真实组拆并、首成员变化及分页修正可以改变身份,模式切换保证不禁止这些正常变化。
- 隐藏部分的揭示、选择复制、中断提示、组开合与外层 Turn 联动属于 Chat 业务适配,仅凭通用引用测试不能确认这些行为。
- 整轮状态与用时替代旧工具/消息计数标题,因此状态、可访问名称和空过程轮次需要明确的展示覆盖。隐藏普通 Context 时保留非人工唤醒通知及原始 Session/Trajectory 查看能力;页脚定位依赖真实 Turn 结束,不依赖分组成员。
- 整轮状态与用时替代旧工具/消息计数标题,因此状态、可访问名称和空过程轮次需要明确的展示覆盖。非人工唤醒通知独立于 Context 行,Session/Trajectory 查看能力不变;页脚定位依赖真实 Turn 结束,不依赖分组成员。
@@ -9,8 +9,8 @@
en: e42db40b879fdbf4
zh: 6017da9c4da17835
/agent-note-node-ptc-execution-modes-and-in-memory-state/decision:
en: 7b5808a31493ac1e
zh: 8d9c698c41dbf18e
en: 411054071757d5fe
zh: e683ef7d80f33fc6
/agent-note-node-ptc-execution-modes-and-in-memory-state/alternatives-considered:
en: 396157a9fc293198
zh: a061f05b4423e26f
@@ -18,7 +18,7 @@ One npm package and one plugin entry keep existing installation and loading path
The Session records submitted programs, nested tool calls and returned output. It does not serialize the REPL heap or rerun earlier programs after restart, resume or fork: replaying automation could repeat external side effects. A missing variable is a normal program error, and the model-visible instructions require recreating variables and reacquiring resources. No environment identifier or new durable Session event is required because the provider exposes no unlogged model input.
Cancellation, deadline expiry, protocol or output-limit failure, owner disposal and process exit discard the environment. Ordinary exceptions retain earlier mutations. A retained function uses the active evaluation's host bindings; asynchronous work from an earlier evaluation cannot acquire a later evaluation's tool authority or output sink.
Protocol or output-limit failure, owner disposal and process exit discard the environment. The [interruption decision](2026-10-29-retained-ptc-interruption.md) owns timeout and cancellation recovery with retained state. Ordinary exceptions retain earlier mutations. A retained function uses the active evaluation's host bindings; asynchronous work from an earlier evaluation cannot acquire a later evaluation's tool authority or output sink.
## Alternatives considered
@@ -18,7 +18,7 @@ Status: implemented
Session 记录提交的程序、嵌套工具调用和返回输出。它不序列化 REPL 堆,也不在重启、恢复或派生后重新执行旧程序,因为重放自动化可能重复外部操作。缺失变量产生普通程序错误,模型指引要求重新创建变量并获取资源。提供方没有引入日志之外的模型输入,因此不需要环境标识或新的持久 Session 事件。
取消、截止时间到期、协议失败、输出超限、所有者销毁和进程退出都会丢弃环境。普通异常保留此前发生的修改。保留的函数使用当前执行的宿主绑定;较早执行中的异步任务不能获取后续执行的工具权限或向其输出。
协议失败、输出超限、所有者销毁和进程退出都会丢弃环境。[中断决策](2026-10-29-retained-ptc-interruption.zh.md)负责保留状态的超时和取消恢复。普通异常保留此前发生的修改。保留的函数使用当前执行的宿主绑定;较早执行中的异步任务不能获取后续执行的工具权限或向其输出。
## 考虑过的替代方案
@@ -6,14 +6,14 @@
en: 9163af91ea0e030d
zh: 92e44043b999d32f
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/problem:
en: 6fac3c73a4415c83
zh: f65b61cc1705348b
en: 32ba2b2b42e8deb5
zh: 6a6532561f81b8e2
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/decision:
en: 458416ed7e47f644
zh: 8b4dd820228e9dfd
en: 08a54b37e157da29
zh: 65f633055d9df242
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/alternatives-considered:
en: b731254983be656d
zh: 5dee2b09f7faa537
en: 6da221f6b821ea44
zh: b15774011abed7ed
/agent-note-offer-web-search-and-web-fetch-only-while-the-selected-provider-is-ready/consequences:
en: 798faeaa0b406eac
zh: 97bbfdaebc84b1d4
en: c90a122c1f33bb83
zh: 0175ca0f599d086b
@@ -6,17 +6,17 @@ English | [中文](2026-09-27-web-search-offered-by-provider-readiness.zh.md)
## Problem
`dsh-tool-web` registers `web_search` whenever search is enabled, and the [web capability seam decision](2026-06-24-web-capability-seam.md) keeps it visible while the selected provider cannot run. The shipped search provider, `dsh-web-search-deepseek`, authenticates a search with the DeepSeek account token only for a model request on the `deepseek-account` route; every other route needs a configured API key. A Session on an API-key route without a search key was therefore offered a tool whose every call failed with `WEB_PROVIDER_CREDENTIAL_MISSING`. Sessions in one Host can use different routes, a Session can switch routes between requests, and the Web app registers `web_search` once per agent preset, so no registration decision matches every request. The Search and Fetch APIs of `dsh-web-deepseek-api`, which the Web search settings switch selects together, need that credential for both operations, so the same Session was also offered a `web_fetch` whose every call failed.
`dsh-tool-web` registers `web_search` whenever search is enabled, and the [web capability seam decision](2026-06-24-web-capability-seam.md) keeps it visible while the selected provider cannot run. The DeepSeek web providers authenticate an operation with the DeepSeek account token only for a model request on the `deepseek-account` route; every other route needs a configured API key. Without a readiness check, a Session on an API-key route without a key is offered a tool whose every call fails with `WEB_PROVIDER_CREDENTIAL_MISSING`. Sessions in one Host can use different routes, a Session can switch routes between requests, and the Web app registers `web_search` once per agent preset, so no registration decision matches every request. The Fetch API of `dsh-web-deepseek-api`, which the user selects separately from the search provider ([provider selection](2026-10-29-web-provider-selection.md)), needs the same credential, so such a Session with it selected would also be offered a `web_fetch` whose every call fails.
## Decision
Each role of the web capability seam owns one part of a per-step check:
- **Service Definition.** `WebSearchProvider` and `WebFetchProvider` have an optional `ready(llmProvider)` that answers whether an operation made for a model request on that chat route has a credential to send. It checks configuration only and makes no network calls. `ctx.web.searchReady(llmProvider?)` and `ctx.web.fetchReady(llmProvider?)` resolve the provider with the same rules as `search()` and `fetch()` and return its answer. A provider without `ready()` counts as ready, and a selection failure answers true because the operation reports that failure as a structured `WebError`.
- **Service Provider.** `dsh-web-search-deepseek` answers true when the route is `deepseek-account` and `ctx.deepseekAccount.resolveToken()` returns a token for the configured search endpoint, when the section carries a literal `apiKey`, or when `ctx.credentials.describe()` reports the `apiKeyEnv` reference configured (without that service, when the launching environment carries it). It checks credentials in the order `search()` resolves them, and a failed account-token or credential-store read answers false, because `search()` fails on that read instead of trying the next credential. The Search and Fetch API providers of `dsh-web-deepseek-api` share one credential and answer the same way for their API base; their transport repeats these checks, so neither package depends on the other. The providers name no tool or prompt section.
- **Service Provider.** Each DeepSeek provider checks its credentials in the order its operation resolves them. `dsh-web-search-deepseek` checks, on the `deepseek-account` route, whether `ctx.deepseekAccount.resolveToken()` returns a token for its Messages endpoint, and then its literal `apiKey` or its `apiKeyEnv` reference. The Search and Fetch API providers of `dsh-web-deepseek-api` resolve the same plan as the operation ([credential resolution](2026-10-29-web-provider-selection.md)): a capability with its own YAML fields checks only the key under its `*ApiKeyEnv`; otherwise, on the account route with the account model route mounted, a token for that route's `/v1/tools` base counts, and then the key of the `deepseek-official` route, or `DEEPSEEK_API_KEY` without that route. A key counts when `ctx.credentials.describe()` reports its reference configured (without that service, when the launching environment carries it); the [shared credential helpers](../../../../packages/llm/llm-deepseek/src/credential.ts) perform these presence checks. A failed account-token or credential-store read answers false, because the operation fails on that read instead of trying the next credential. The providers name no tool or prompt section.
- **Consumer.** `dsh-tool-web` registers `web_search` and `web_fetch` with [step activation conditions](../../../../packages/core/tools/README.md#conditional-tools) that ask `ctx.web.searchReady()` and `ctx.web.fetchReady()` about the route of the step's model selection. The agent loop resolves conditions before it assembles the step, so the declared tools, the `tool:web_search` and `tool:web_fetch` prompt sections, PTC program bindings, and dispatch all read the same admitted set.
The route decides only whether the account token is checked before the API key, which happens on the `deepseek-account` route; an API key counts on every route, so a conversation on another provider's model is offered the DeepSeek web tools whenever a DeepSeek key is configured. The providers do not compare the chat route's endpoint with their own: each operation sends the configured key, `DEEPSEEK_API_KEY` by default, to the configured endpoint, even when the official route chats through a gateway.
The route decides only whether the account token is checked before the API key, which happens on the `deepseek-account` route; an API key counts on every route, so a conversation on another provider's model is offered the DeepSeek web tools whenever a DeepSeek key is configured. A Search or Fetch API capability with its own YAML fields ignores the route and is ready exactly when its own key is configured.
Only presence decides: no API key value is read and no endpoint is contacted, so a wrong key or an endpoint that does not serve the native `web_search_20250305` server tool keeps the tool admitted. A step that does not admit a web tool also refuses calls to it as `UNKNOWN_TOOL`, so a model that repeats an earlier call reaches no provider; an operation made outside a model step resolves its credential when it runs and fails with `WEB_PROVIDER_CREDENTIAL_MISSING` when there is none.
@@ -24,7 +24,7 @@ The request header records the assembled tools, so the agent loop announces each
## Alternatives considered
**Let the DeepSeek provider remove `web_search` in its own assemble listener.** The seam stays unchanged, but the provider then names another package's tool, prompt section, and PTC transport, and it removes the tool even when the deployment selects another search provider, such as Exa, whose searches would succeed.
**Let the DeepSeek provider remove `web_search` in its own assemble listener.** The seam stays unchanged, but the provider then names another package's tool, prompt section, and PTC transport, and it removes the tool even when the deployment selects another search provider, such as one a plugin registers, whose searches would succeed.
**Extend `available()`.** It is synchronous and takes no request, while this check reads an asynchronous credential store and depends on the request's route. A route-dependent `available()` would also make provider selection depend on the conversation.
@@ -36,9 +36,9 @@ The request header records the assembled tools, so the agent loop announces each
**Offer search only on the DeepSeek account route.** Account tokens are issued only for the official endpoint, so a gateway key could never reach DeepSeek. But every shipped profile defaults to the `deepseek-official` route, so search would be off by default, including the official API-key case.
**Withhold the tools from other providers' conversations unless search has a key of its own.** A search from another model's conversation bills the DeepSeek key, but DeepSeek search is one of the services that key pays for, and a user who configured it expects search to work whatever model chats. The Web search settings page saves its key under `apiKeyEnv`, `DEEPSEEK_API_KEY` by default, which the chat route also reads, so a key entered there could never serve another model; a `routes` setting that opens them would be one more thing a user has to find.
**Withhold the tools from other providers' conversations unless search has a key of its own.** A search from another model's conversation bills the DeepSeek key, but DeepSeek search is one of the services that key pays for, and a user who configured it expects search to work whatever model chats. The Search and Fetch APIs send the chat route's own key, and the settings page saves the legacy Messages search key under `apiKeyEnv`, `DEEPSEEK_API_KEY` by default, which the chat route also reads, so withholding them would leave a configured key unable to serve another model; a `routes` setting that opens them would be one more thing a user has to find.
**Compare the official chat route's endpoint with the search endpoint.** Withholding the tools or the key when the two endpoints differ in origin keeps a gateway's key away from DeepSeek, but the providers would have to find the chat route's configuration themselves. Taking the first active `dsh-llm-deepseek-api-key` Loader row answers for an unrelated instance when a composition isolates several, which disables a correct route or lets a gateway key through; only the route's owner knows which endpoint serves a request, and an endpoint field in `dsh-llm` would reach the browser through the Remote `listProviders()`. The check would also withhold search from users who chat through a proxy or development endpoint with a key the official search endpoint accepts. Without it, a gateway user's search fails at the configured endpoint.
**Compare the official chat route's endpoint with the web endpoint and withhold the tools when they differ.** It would keep a gateway's key away from DeepSeek, but it would also withhold web tools from users who chat through a proxy or development endpoint with a key the web endpoint accepts. The Search and Fetch APIs instead derive their endpoint from the origin of the route whose key they send, so the key and endpoint come from one route; legacy Messages search keeps its own endpoint, where a gateway user's search fails.
**Keep the tool offered and fail at execution.** The model spends a call on a search that cannot authenticate, and the failure repeats on every attempt.
@@ -46,6 +46,6 @@ The request header records the assembled tools, so the agent loop announces each
## Consequences
`dsh-web` gains public members: the optional `ready()` of both provider interfaces, `WebRuntime.searchReady()`, and `WebRuntime.fetchReady()`. Each readiness method is the smallest query its one consumer needs, which is how the [observation-surface decision](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) lets a status query return; neither reports availability or selection failures. The Exa, Perplexity, and HTTP fetch providers implement no `ready()`, so their behavior is unchanged, and `web_fetch` stays admitted whenever the switch leaves HTTP fetch selected. A step that does not admit a web tool leaves it out of PTC program bindings too. An endpoint that cannot serve the native server tool keeps the tool admitted and fails each call. Removing a tool on an unchanged route changes the declared tools and misses the prompt cache once; a model switch already invalidates it. Searches and fetches from other providers' conversations bill the configured DeepSeek key, and a deployment whose official route chats through a gateway sends the gateway's key to the configured endpoint, the official one by default, where each operation fails unless that endpoint accepts the key. The DeepSeek web packages read no Loader rows and depend on no LLM package.
`dsh-web` gains public members: the optional `ready()` of both provider interfaces, `WebRuntime.searchReady()`, and `WebRuntime.fetchReady()`. Each readiness method is the smallest query its one consumer needs, which is how the [observation-surface decision](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) lets a status query return; neither reports availability or selection failures. The HTTP fetch provider implements no `ready()`, so `web_fetch` stays admitted whenever HTTP fetch is selected. A step that does not admit a web tool leaves it out of PTC program bindings too. An endpoint that cannot serve the native server tool keeps the tool admitted and fails each call. Removing a tool on an unchanged route changes the declared tools and misses the prompt cache once; a model switch already invalidates it. Searches and fetches from other providers' conversations bill the configured DeepSeek key. A deployment whose official route chats through a gateway sends the gateway's key to that gateway's origin with `/v1/tools` for the Search and Fetch APIs, and to the configured Messages endpoint, the official one by default, for legacy Messages search, where each search fails unless that endpoint accepts the key. The Search and Fetch APIs read the DeepSeek model routes' Loader entries through the model provider directory.
Unit tests pin each role: `packages/web/web/tests/web.spec.ts` covers which provider `searchReady()` and `fetchReady()` ask and their answer on selection failure, `packages/web/web-search-deepseek/tests/deepseek.spec.ts` and `packages/web/web-deepseek-api/tests/readiness.spec.ts` cover each credential case of `ready()` on the account route and on other routes, and `packages/web/tool-web/tests/tool-web.spec.ts` resolves step admission of both tools through the real prompt, tool, and executor services, including the `UNKNOWN_TOOL` refusal of a call in a step that withheld the tool. In real compositions, the shipped headless keyless smoke test and the `web-search-unconfigured` recorded-session snapshot, whose search key references are unset, pin the withheld search tool. Snapshot replays supply a placeholder DeepSeek key, so their tool lists do not depend on the runner's environment; with it, the `pi-ai-conversation-updates` conversation on a pi-ai route keeps `web_search`.
Unit tests pin each role: [web.spec.ts](../../../../packages/web/web/tests/web.spec.ts) covers which provider `searchReady()` and `fetchReady()` ask and their answer on selection failure, [deepseek.spec.ts](../../../../packages/web/web-search-deepseek/tests/deepseek.spec.ts) and [credential-resolution.spec.ts](../../../../packages/web/web-deepseek-api/tests/credential-resolution.spec.ts) cover each credential case of `ready()` on the account route and on other routes, including readiness that matches each operation, and [tool-web.spec.ts](../../../../packages/web/tool-web/tests/tool-web.spec.ts) resolves step admission of both tools through the real prompt, tool, and executor services, including the `UNKNOWN_TOOL` refusal of a call in a step that withheld the tool. In real compositions, the shipped headless keyless smoke test and the `web-search-unconfigured` recorded-session snapshot, whose search key references are unset, pin the withheld search tool. Snapshot replays supply a placeholder DeepSeek key, so their tool lists do not depend on the runner's environment; with it, the `pi-ai-conversation-updates` conversation on a pi-ai route keeps `web_search`.
@@ -6,17 +6,17 @@ Status: implemented
## Problem
`dsh-tool-web` 在启用搜索时总会注册 `web_search`,而 [web 能力 seam 决策](2026-06-24-web-capability-seam.zh.md)让它在选中的提供方无法运行时仍然可见。发行版的搜索提供方 `dsh-web-search-deepseek` 只对 `deepseek-account` 路由上的模型请求使用 DeepSeek 账号 token 鉴权;其他路由都需要已配置的 API 密钥。因此,API 密钥路由上没有搜索密钥的会话会获得一个每次调用都以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败的工具。同一 Host 中的会话可以使用不同路由,一个会话可以在请求之间切换路由,而 Web 应用按 agent preset 只注册一次 `web_search`,因此任何注册层面的决定都无法与每个请求相符。Web 搜索设置开关会同时选中 `dsh-web-deepseek-api` 的 Search 与 Fetch API,两个操作都需要这份凭据,因此同一会话还会获得一个每次调用都失败的 `web_fetch`。
`dsh-tool-web` 在启用搜索时总会注册 `web_search`,而 [web 能力 seam 决策](2026-06-24-web-capability-seam.zh.md)让它在选中的提供方无法运行时仍然可见。DeepSeek Web 提供方只对 `deepseek-account` 路由上的模型请求使用 DeepSeek 账号 token 为操作鉴权;其他路由都需要已配置的 API 密钥。没有就绪检查时,API 密钥路由上没有密钥的会话会获得一个每次调用都以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败的工具。同一 Host 中的会话可以使用不同路由,一个会话可以在请求之间切换路由,而 Web 应用按 agent preset 只注册一次 `web_search`,因此任何注册层面的决定都无法与每个请求相符。`dsh-web-deepseek-api` 的 Fetch API 由用户与搜索提供方分别选择([提供方选择](2026-10-29-web-provider-selection.zh.md)),它需要同一份凭据,因此这样的会话选中它时,还会获得一个每次调用都失败的 `web_fetch`。
## Decision
web 能力 seam 的每个角色各负责逐步骤检查的一部分:
- **Service Definition。** `WebSearchProvider` 与 `WebFetchProvider` 有一个可选的 `ready(llmProvider)`,回答为该对话路由上的模型请求发起的操作是否有可发送的凭据。它只检查配置,不发起网络调用。`ctx.web.searchReady(llmProvider?)` 与 `ctx.web.fetchReady(llmProvider?)` 按与 `search()`、`fetch()` 相同的规则解析提供方并返回其回答。没有 `ready()` 的提供方视为就绪;选择失败时回答 true,因为对应操作会把该失败报告为结构化 `WebError`。
- **Service Provider。** `dsh-web-search-deepseek` 在以下任一情况下回答 true:路由为 `deepseek-account` 且 `ctx.deepseekAccount.resolveToken()` 为配置的搜索端点返回 token;配置段带有字面 `apiKey`;或 `ctx.credentials.describe()` 报告 `apiKeyEnv` 引用已配置(没有该服务时,启动环境中存在该变量)。它按 `search()` 解析凭据的顺序检查凭据,读取账号 token 或凭据存储失败时回答 false,因为 `search()` 会在该次读取上失败,而不会尝试下一种凭据。`dsh-web-deepseek-api` 的 Search 与 Fetch API 提供方共用一份凭据,并对其 API 基础地址以同样方式回答;它们的传输层重复了这些检查,因此两个包互不依赖。提供方不点名任何工具或提示词段落。
- **Service Provider。** 每个 DeepSeek 提供方都按其操作解析凭据的顺序检查凭据。`dsh-web-search-deepseek` 在 `deepseek-account` 路由上检查 `ctx.deepseekAccount.resolveToken()` 是否为其 Messages 端点返回 token,然后检查其字面 `apiKey` 或 `apiKeyEnv` 引用。`dsh-web-deepseek-api` 的 Search 与 Fetch API 提供方解析与操作相同的方案([凭据解析](2026-10-29-web-provider-selection.zh.md)):在 YAML 中设置了单独字段的能力只检查其 `*ApiKeyEnv` 下的密钥;否则,在账号路由上且账号模型路由已挂载时,该路由 `/v1/tools` 基础地址的 token 算数,然后是 `deepseek-official` 路由的密钥,没有该路由时为 `DEEPSEEK_API_KEY`。`ctx.credentials.describe()` 报告其引用已配置时(没有该服务时,启动环境中存在该变量),密钥即算存在;这些存在性检查由[共享凭据函数](../../../../packages/llm/llm-deepseek/src/credential.ts)执行。读取账号 token 或凭据存储失败时回答 false,因为操作会在该次读取上失败,而不会尝试下一种凭据。提供方不点名任何工具或提示词段落。
- **Consumer。** `dsh-tool-web` 注册 `web_search` 与 `web_fetch` 时各带一个[步骤激活条件](../../../../packages/core/tools/README.zh.md#conditional-tools),以该步骤模型选择的路由询问 `ctx.web.searchReady()` 与 `ctx.web.fetchReady()`。agent loop 在组装该步骤之前解析各条件,因此声明的工具、`tool:web_search` 与 `tool:web_fetch` 提示词段落、PTC 程序绑定与派发都读取同一个准入集合。
路由只决定是否在 API 密钥之前检查账号 token,这只发生在 `deepseek-account` 路由上;API 密钥在任何路由上都算数,因此只要配置了 DeepSeek 密钥,使用其他提供方模型的对话也会获得 DeepSeek Web 工具。提供方不比较聊天路由的地址与自己的端点:即使官方路由经网关聊天,每次操作也把配置的密钥(默认 `DEEPSEEK_API_KEY`)发往配置的端点。
路由只决定是否先检查账号 token 再检查 API 密钥,这发生在 `deepseek-account` 路由上;API 密钥在每条路由上都算数,因此只要配置了 DeepSeek 密钥,使用其他提供方模型的对话也会获得 DeepSeek Web 工具。在 YAML 中设置了单独字段的 Search 或 Fetch API 能力不看路由,恰在其自身密钥已配置时就绪。
只看凭据是否存在:不读取 API 密钥的值,也不访问任何端点,因此错误的密钥,或不提供原生 `web_search_20250305` 服务器工具的端点,仍会让该工具保持准入。未准入某个 Web 工具的步骤也会以 `UNKNOWN_TOOL` 拒绝对它的调用,因此模型重复先前的调用也到达不了任何提供方;在模型步骤之外发起的操作在运行时解析凭据,没有凭据时以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败。
@@ -24,7 +24,7 @@ web 能力 seam 的每个角色各负责逐步骤检查的一部分:
## Alternatives considered
**让 DeepSeek 提供方在自己的 assemble 监听器中移除 `web_search`。** seam 保持不变,但提供方因此要点名另一个包的工具、提示词段落与 PTC 传输工具;而且即使部署选择了另一个搜索成功的提供方(例如 Exa),它也会移除该工具。
**让 DeepSeek 提供方在自己的 assemble 监听器中移除 `web_search`。** seam 保持不变,但提供方因此要点名另一个包的工具、提示词段落与 PTC 传输工具;而且即使部署选择了另一个搜索成功的提供方(例如插件注册的提供方),它也会移除该工具。
**扩展 `available()`。** 它是同步的且不接收请求,而这项检查要读取异步凭据存储,并取决于请求的路由。依赖路由的 `available()` 还会让提供方选择取决于对话。
@@ -36,9 +36,9 @@ web 能力 seam 的每个角色各负责逐步骤检查的一部分:
**只在 DeepSeek 账号路由上提供搜索。** 账号 token 只对官方端点签发,因此网关的密钥不可能到达 DeepSeek。但所有发行 profile 的默认路由都是 `deepseek-official`,搜索会因此默认关闭,连官方 API 密钥的情况也一并关闭。
**除非搜索有自己的密钥,否则不向其他提供方的对话提供这些工具。** 其他模型对话中的搜索会由 DeepSeek 密钥计费,但 DeepSeek 搜索本就是该密钥付费使用的服务之一,配置了它的用户也期望无论用哪个模型对话,搜索都能用。Web 搜索设置页把密钥存在 `apiKeyEnv` 指向的引用下,默认是聊天路由也在读取的 `DEEPSEEK_API_KEY`,因此在那里输入的密钥永远无法服务其他模型;再加一个开放这些路由的 `routes` 配置项,只会让用户多找一样东西。
**除非搜索有自己的密钥,否则不向其他提供方的对话提供这些工具。** 其他模型对话中的搜索会由 DeepSeek 密钥计费,但 DeepSeek 搜索本就是该密钥付费使用的服务之一,配置了它的用户也期望无论用哪个模型对话,搜索都能用。Search 与 Fetch API 发送聊天路由自己的密钥,设置页把旧版 Messages 搜索的密钥存在 `apiKeyEnv` 指向的引用下,默认是聊天路由也在读取的 `DEEPSEEK_API_KEY`,因此撤下工具会让已配置的密钥无法服务其他模型;再加一个开放这些路由的 `routes` 配置项,只会让用户多找一样东西。
**比较官方聊天路由的端点与搜索端点。** 两个端点不同源时撤下工具或不发送密钥,能让网关的密钥远离 DeepSeek,但提供方得自己找出聊天路由的配置。取第一条激活的 `dsh-llm-deepseek-api-key` Loader 行时,如果组合隔离了多个实例,回答的可能是无关的实例,从而关掉正确的路由,或放过网关的密钥;只有路由的所有者知道哪个端点服务某个请求,而在 `dsh-llm` 加地址字段会让它经 Remote 方法 `listProviders()` 进入浏览器。这项检查还会让经代理或开发端点对话、而密钥能被官方搜索端点接受的用户失去搜索。不做这项检查时,网关用户的搜索会在配置的端点上失败。
**比较官方聊天路由的端点与 Web 端点,二者不同时撤下工具。** 这能让网关的密钥远离 DeepSeek,但也会让经代理或开发端点对话、而密钥能被 Web 端点接受的用户失去网页工具。Search 与 Fetch API 改为从其所发送密钥的路由的 origin 推导端点,因此密钥与端点来自同一条路由;旧版 Messages 搜索保留自己的端点,网关用户的搜索会在那里失败。
**保持工具可见,在执行时失败。** 模型会把一次调用花在无法鉴权的搜索上,并且每次尝试都会重复失败。
@@ -46,6 +46,6 @@ web 能力 seam 的每个角色各负责逐步骤检查的一部分:
## Consequences
`dsh-web` 增加以下公共成员:两个提供方接口上可选的 `ready()`、`WebRuntime.searchReady()` 与 `WebRuntime.fetchReady()`。每个就绪方法都是其唯一消费方所需的最小查询,这正是[观测接口决策](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md)允许状态查询回归的方式;它们既不报告可用性,也不报告选择失败。Exa、Perplexity 与 HTTP 抓取提供方没有实现 `ready()`,其行为不变;只要开关让 HTTP 抓取保持选中,`web_fetch` 就始终准入。未准入某个 Web 工具的步骤,其 PTC 程序绑定中也没有它。无法提供原生服务器工具的端点仍会让工具保持准入,并使每次调用失败。在路由不变时移除工具会改变声明的工具,并使提示词缓存失效一次;模型切换本身已经使缓存失效。其他提供方对话中的搜索与抓取由配置的 DeepSeek 密钥计费;官方路由经网关对话的部署会把网关的密钥发往配置的端点(默认是官方端点),除非该端点接受这个密钥,否则每次操作都会失败。DeepSeek Web 包不读取 Loader 的行,也不依赖任何 LLM 包。
`dsh-web` 增加以下公共成员:两个提供方接口上可选的 `ready()`、`WebRuntime.searchReady()` 与 `WebRuntime.fetchReady()`。每个就绪方法都是其唯一消费方所需的最小查询,这正是[观测接口决策](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md)允许状态查询回归的方式;它们既不报告可用性,也不报告选择失败。HTTP 抓取提供方没有实现 `ready()`,因此只要选中 HTTP 抓取,`web_fetch` 就始终准入。未准入某个 Web 工具的步骤,其 PTC 程序绑定中也没有它。无法提供原生服务器工具的端点仍会让工具保持准入,并使每次调用失败。在路由不变时移除工具会改变声明的工具,并使提示词缓存失效一次;模型切换本身已经使缓存失效。其他提供方对话中的搜索与抓取由配置的 DeepSeek 密钥计费。官方路由经网关对话的部署,对 Search 与 Fetch API 会把网关的密钥发往该网关 origin 加 `/v1/tools`,对旧版 Messages 搜索则发往配置的 Messages 端点(默认是官方端点),除非该端点接受这个密钥,否则每次搜索都会失败。Search 与 Fetch API 通过模型提供方目录读取 DeepSeek 模型路由的 Loader 条目。
单元测试分别固定每个角色:`packages/web/web/tests/web.spec.ts` 覆盖 `searchReady()` 与 `fetchReady()` 询问哪个提供方以及选择失败时的回答,`packages/web/web-search-deepseek/tests/deepseek.spec.ts` 与 `packages/web/web-deepseek-api/tests/readiness.spec.ts` 覆盖 `ready()` 在账号路由与其他路由上的每种凭据情况,`packages/web/tool-web/tests/tool-web.spec.ts` 通过真实的提示词、工具与执行器服务解析两个工具的步骤准入,包括在撤下该工具的步骤中调用它时以 `UNKNOWN_TOOL` 拒绝。在真实组合中,发行版 headless 无密钥冒烟测试与 `web-search-unconfigured` 录制会话快照的搜索密钥引用都未设置,二者固定了搜索工具的撤下行为。快照回放提供占位 DeepSeek 密钥,因此其工具列表不依赖运行环境;借助该密钥,`pi-ai-conversation-updates` 中 pi-ai 路由上的对话保留 `web_search`。
单元测试分别固定每个角色:[web.spec.ts](../../../../packages/web/web/tests/web.spec.ts) 覆盖 `searchReady()` 与 `fetchReady()` 询问哪个提供方以及选择失败时的回答,[deepseek.spec.ts](../../../../packages/web/web-search-deepseek/tests/deepseek.spec.ts) 与 [credential-resolution.spec.ts](../../../../packages/web/web-deepseek-api/tests/credential-resolution.spec.ts) 覆盖 `ready()` 在账号路由与其他路由上的每种凭据情况,包括就绪判断与每次操作一致,[tool-web.spec.ts](../../../../packages/web/tool-web/tests/tool-web.spec.ts) 通过真实的提示词、工具与执行器服务解析两个工具的步骤准入,包括在撤下该工具的步骤中调用它时以 `UNKNOWN_TOOL` 拒绝。在真实组合中,发行版 headless 无密钥冒烟测试与 `web-search-unconfigured` 录制会话快照的搜索密钥引用都未设置,二者固定了搜索工具的撤下行为。快照回放提供占位 DeepSeek 密钥,因此其工具列表不依赖运行环境;借助该密钥,`pi-ai-conversation-updates` 中 pi-ai 路由上的对话保留 `web_search`。
@@ -9,14 +9,14 @@
en: 8112c1c059d1cf10
zh: 852d23c56a93a555
/agent-note-preserve-web-and-desktop-plugin-identities-across-experimental-consolidation/decision:
en: 90d74300f045bf6e
zh: 088e88944478ca2a
en: 3cf1a8f986cea871
zh: 3105cf2608108ecd
/agent-note-preserve-web-and-desktop-plugin-identities-across-experimental-consolidation/deferred-naming-cleanup:
en: 4c428f65ef611d07
zh: 1fdc5467b0203d08
en: 7f59c9d14a36e133
zh: 2e7829c59b4fcd24
/agent-note-preserve-web-and-desktop-plugin-identities-across-experimental-consolidation/alternatives-considered:
en: 65af55b5775c9bc5
zh: 2ea75b3fd33ac0c2
/agent-note-preserve-web-and-desktop-plugin-identities-across-experimental-consolidation/consequences:
en: e8e96b0354f1a3c6
zh: ad1bfe3600e14ff1
en: 08613c24e7c552ab
zh: 6f2509d3e61cb2c7
@@ -10,7 +10,7 @@ Moving a capability into the experimental directory changes its ownership and su
## Decision
The moved packages retain the published names declared by the [experimental naming policy](../../../../scripts/experimental-package-policy.ts). That typed directory-to-name map is the sole maintained inventory of naming exceptions. Package manifests, consumers, installation checks, and bundler checks agree with it; a missing, moved, or duplicated exception is rejected. [Experimental status](../../../../packages/experimental/README.md#status) remains independent of spelling, Official discovery, and explicit selection.
The moved packages retain the published names declared by the [experimental naming policy](../../../../scripts/experimental-package-policy.ts). That typed directory-to-name map is the sole maintained inventory of unprefixed experimental names; under the [unprefixed naming decision](2026-11-04-unprefixed-experimental-package-names.md), every new experimental package joins it. Package manifests, consumers, installation checks, and bundler checks agree with it; a missing, moved, or duplicated entry is rejected. [Experimental status](../../../../packages/experimental/README.md#status) remains independent of spelling, Official discovery, and explicit selection.
Existing badge, Ralph, and native delegation row IDs remain valid Host row selectors. New isolation groups and newly introduced capabilities retain their new IDs. Later user layers still replace configuration or disable rows. Selecting an optional bundle is still explicit: a patch targeting an absent row does not select its bundle.
@@ -20,10 +20,9 @@ Compatibility protection covers Web and Desktop profiles, including existing nat
| Retained identity | Unconstrained cleanup | Why it is deferred |
|---|---|---|
| Published names in the naming-exception map | Give every experimental package the `@deepseek-ai/dsh-experimental-` prefix | Existing dependencies, imports, module declarations, and profile files use the established names |
| Existing badge, Ralph, and native-tool row IDs | Give optional contributions consistently distinct `optional-*` selectors | Existing user overrides use those IDs; the ownership move does not require a new selector |
These naming changes are deferred, not prerequisites for experimental classification. A later cleanup requires an explicit release and migration decision, an inventory of affected Web/Desktop configurations, and installed-profile upgrade evidence. Directory moves or pre-stable API status alone do not retire these exceptions. The package list stays in the typed policy rather than being copied into this note or package READMEs.
These naming changes are deferred, not prerequisites for experimental classification. A later cleanup requires an explicit release and migration decision, an inventory of affected Web/Desktop configurations, and installed-profile upgrade evidence. Directory moves or pre-stable API status alone do not retire these row IDs. The package list stays in the typed policy rather than being copied into this note or package READMEs.
## Alternatives considered
@@ -35,6 +34,6 @@ These naming changes are deferred, not prerequisites for experimental classifica
## Consequences
The repository carries a bounded naming-exception map and tests that classify retained names through direct imports, aliases, transitive dependencies, and packed or bundled artifacts. An unprefixed name does not grant admission to the default product. Existing row identities have Web/Desktop composition coverage for user overrides and disabled state. No general alias or automatic profile migration mechanism is introduced.
The repository carries a typed map of unprefixed experimental names and tests that classify retained names through direct imports, aliases, transitive dependencies, and packed or bundled artifacts. An unprefixed name does not grant admission to the default product. Existing row identities have Web/Desktop composition coverage for user overrides and disabled state. No general alias or automatic profile migration mechanism is introduced.
The [classification decision](../process/2026-10-05-product-package-classification.md), [optional-composition decision](2026-09-21-experimental-capabilities-as-optional-bundles.md), and [on-demand discovery decision](2026-10-05-official-on-demand-bundles.md) retain their separate responsibilities. Released Session generations and persisted hook event names remain protected independently of this profile compatibility scope.
@@ -10,7 +10,7 @@ Status: implemented
## 决策
迁移的包保留[实验包命名策略](../../../../scripts/experimental-package-policy.ts)声明的已发布名称。这个带类型的目录到包名映射是命名例外唯一手工维护的清单。包清单、消费者、安装检查和打包检查与其保持一致;缺失、迁移到其他位置或重复归属的例外会被拒绝。[实验状态](../../../../packages/experimental/README.zh.md#status)仍独立于名称拼写、Official 发现和显式选择。
迁移的包保留[实验包命名策略](../../../../scripts/experimental-package-policy.ts)声明的已发布名称。这个带类型的目录到包名映射是不带前缀的实验包名唯一手工维护的清单;按照[不带前缀命名决策](2026-11-04-unprefixed-experimental-package-names.zh.md),每个新实验包都加入其中。包清单、消费者、安装检查和打包检查与其保持一致;缺失、迁移到其他位置或重复归属的条目会被拒绝。[实验状态](../../../../packages/experimental/README.zh.md#status)仍独立于名称拼写、Official 发现和显式选择。
既有徽章、Ralph 和原生委派行 ID 作为 Host 行选择器保持有效。新增隔离组和新引入的能力保留各自的新 ID。后续用户层仍可替换配置或禁用行。可选 bundle 仍须显式选择:指向缺失行的 patch 不会自动选择其 bundle。
@@ -20,10 +20,9 @@ Status: implemented
| 保留的身份 | 不受兼容性约束时的清理 | 暂缓原因 |
|---|---|---|
| 命名例外映射中的已发布名称 | 为每个实验包使用 `@deepseek-ai/dsh-experimental-` 前缀 | 既有依赖、导入、模块声明和 profile 文件使用原有名称 |
| 既有徽章、Ralph 和原生工具行 ID | 为可选贡献统一使用独立的 `optional-*` 选择器 | 既有用户覆盖使用这些 ID;归属迁移不要求新的选择器 |
这些命名变更被暂缓,不是实验分类的前提。后续清理需要明确的发布与迁移决策、受影响 Web/Desktop 配置清单,以及已安装 profile 的升级验证。仅目录迁移或公共 API 处于稳定前阶段,不足以删除这些例外。包清单保留在带类型的策略中,不复制到本记录或各包 README。
这些命名变更被暂缓,不是实验分类的前提。后续清理需要明确的发布与迁移决策、受影响 Web/Desktop 配置清单,以及已安装 profile 的升级验证。仅目录迁移或公共 API 处于稳定前阶段,不足以删除这些行 ID。包清单保留在带类型的策略中,不复制到本记录或各包 README。
## 考虑过的替代方案
@@ -35,6 +34,6 @@ Status: implemented
## 后果
仓库维护范围明确的命名例外映射,并测试通过直接导入、别名、传递依赖及打包产物识别这些保留名称。没有实验前缀的名称不因此获准进入默认产品。既有行身份具有覆盖用户配置和禁用状态的 Web/Desktop 组合测试。不引入通用别名或自动 profile 迁移机制。
仓库维护带类型的不带前缀实验包名映射,并测试通过直接导入、别名、传递依赖及打包产物识别这些保留名称。没有实验前缀的名称不因此获准进入默认产品。既有行身份具有覆盖用户配置和禁用状态的 Web/Desktop 组合测试。不引入通用别名或自动 profile 迁移机制。
[分类决策](../process/2026-10-05-product-package-classification.zh.md)、[可选组合决策](2026-09-21-experimental-capabilities-as-optional-bundles.zh.md)和[按需发现决策](2026-10-05-official-on-demand-bundles.zh.md)继续承担各自独立的职责。已发布 Session 代际和持久化 hook 事件名的保护独立于此 profile 兼容范围。
@@ -9,8 +9,8 @@
en: 386864a282e5ad58
zh: a4ea75ec70eb7778
/agent-note-shared-native-cua-process-with-agent-scoped-state/decision:
en: 11bab6870c3d802f
zh: a22114f2217a3a41
en: 60ec2276ed0bff33
zh: 5fb48cc787194254
/agent-note-shared-native-cua-process-with-agent-scoped-state/alternatives-considered:
en: 883f71387f599160
zh: 4b5f1450be799397
@@ -12,7 +12,7 @@ On macOS 26.6.2, replayd derives capture-client identity from the executable pat
One user shares a native service per executable content digest across Agents and Hosts. A version lock elects the service; an additional canonical-executable-path lock prevents different builds published at one path from running together. Locks remain held until process exit. The managed startup command hands service lifetime to its client connections, and the last connection's completed cleanup ends the service. The diagnostic stdin/stdout entry proxies to this same service.
The service runs as a signed application launched through LaunchServices. Directly spawning the executable inherits its launcher’s TCC responsibility, so it cannot give the helper independent system permissions. A launch lock serializes discovery and startup. Complete signed applications use immutable content-addressed directories: replacing an executable used by a live process would violate both path-lock ownership and code-signature integrity. Ad-hoc builds can require fresh grants when their code hash changes; a stable bundle identifier alone does not preserve those grants. Installed packages retain their distributed signatures.
The service runs as a signed application launched through LaunchServices. Directly spawning the executable inherits its launcher’s TCC responsibility, so it cannot give the helper independent system permissions. A launch lock serializes discovery and startup. Complete signed applications use immutable content-addressed directories: replacing an executable used by a live process would violate both path-lock ownership and code-signature integrity. Ad-hoc builds can require fresh grants when their code hash changes; a stable bundle identifier alone does not preserve those grants. Source checkouts can keep grants with the [development signing identity](2026-10-29-cua-dev-signing-identity.md). Installed packages retain their distributed signatures.
Each Agent connection owns its application handles, AX snapshots, limits and cancellation state. An app handle grants no exclusive use of the application. A FIFO orders complete desktop operations, including clipboard transactions, while response writes are independent. Agent disposal retires only its connection. The [object API decision](2026-09-17-experimental-cua-object-api.md) retains evaluator independence and coordinate semantics; the [consent decision](2026-09-26-cua-application-consent.md) retains application authorization.
@@ -12,7 +12,7 @@ Status: implemented
同一用户的多个 Agent 和 Host 按可执行文件内容摘要共享原生服务。版本锁选出服务实例,规范化可执行路径上的另一把锁阻止发布在同一路径的不同构建同时运行。进程退出才释放锁。受管启动命令将服务生命期交给客户端连接,最后一条连接清理完成后服务退出。诊断用 stdin/stdout 入口也代理到同一个服务。
服务通过 LaunchServices 以已签名应用运行。直接启动可执行文件会继承启动器的 TCC 责任,无法让 helper 独立取得系统权限。启动锁串行处理发现与启动。完整签名应用保存在不可变的内容寻址目录中,替换运行中进程的可执行文件会破坏路径锁的归属与代码签名完整性。临时签名构建的代码哈希变化后可能需要重新授权,固定 Bundle ID 不能保证保留授权。安装版保留分发时的签名。
服务通过 LaunchServices 以已签名应用运行。直接启动可执行文件会继承启动器的 TCC 责任,无法让 helper 独立取得系统权限。启动锁串行处理发现与启动。完整签名应用保存在不可变的内容寻址目录中,替换运行中进程的可执行文件会破坏路径锁的归属与代码签名完整性。临时签名构建的代码哈希变化后可能需要重新授权,固定 Bundle ID 不能保证保留授权。源码检出可以用[开发签名身份](2026-10-29-cua-dev-signing-identity.zh.md)保留授权。安装版保留分发时的签名。
每条 Agent 连接独立持有应用句柄、AX 快照、资源限制和取消状态。持有 app 句柄不代表独占应用。FIFO 队列串行执行包含剪贴板事务的完整桌面操作,响应写入独立进行。Agent 销毁只释放自己的连接。[对象接口决策](2026-09-17-experimental-cua-object-api.zh.md)继续负责执行器独立性及坐标语义,[授权决策](2026-09-26-cua-application-consent.zh.md)继续负责应用许可。
@@ -2,18 +2,18 @@
# 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-10-13-team-identity-runtime-context.md
/agent-note-team-identity-in-required-runtime-context:
en: 8c88759320ae8481
zh: 4068b18723907eb9
/agent-note-team-identity-in-required-runtime-context/problem:
en: c9a81cb731ddf7b2
zh: 428c0e6f8937d7c5
/agent-note-team-identity-in-required-runtime-context/decision:
en: 6c73479973c1e378
zh: 98677c77f77f8d81
/agent-note-team-identity-in-required-runtime-context/alternatives-considered:
/agent-note-subagent-identity-in-required-runtime-context:
en: 3da6e33c5dc2c917
zh: 9c8d4542cda63fc4
/agent-note-subagent-identity-in-required-runtime-context/problem:
en: f944254565541454
zh: 27fa9f67c671dec7
/agent-note-subagent-identity-in-required-runtime-context/decision:
en: 85100936f97a139c
zh: 614081943944e28b
/agent-note-subagent-identity-in-required-runtime-context/alternatives-considered:
en: 774566ffbf9e9db8
zh: ea761d876af462f2
/agent-note-team-identity-in-required-runtime-context/consequences:
en: 18d1aa67936f1227
zh: 7e06c1f51553ecdd
/agent-note-subagent-identity-in-required-runtime-context/consequences:
en: 2c01a24cd8cc4556
zh: 37b5d1ca1173318a
@@ -1,20 +1,22 @@
# Agent Note: Team identity in required runtime context
# Agent Note: Subagent identity in required runtime context
Status: implemented
English | [中文](2026-10-13-team-identity-runtime-context.zh.md)
[Catalog names](2026-11-02-subagent-catalog-names.md) supersede the Session-id identity text below: agents see their own, direct parent, and root names. The runtime-context retention decision remains active.
## Problem
A teammate identity embedded in its initial task depends on ordinary history retention. Compaction can remove it, and an ordinary fork from that teammate can retain the old role even though the fork is an independent Lead. Identity must reflect current membership while keeping shared system instructions uniform.
A teammate identity embedded in its initial task depends on ordinary history retention. Compaction can remove it, and an ordinary fork from that teammate can retain the old role even though the fork is an independent Lead. Identity must reflect the current subagent tree while keeping shared system instructions uniform.
## Decision
`tool-agent-team` contributes `team:identity` through the Agent-scoped `systemPrompt.context()` registry with `required: true`. Its provider reads the existing Team membership and renders the member's role, name, and addressing instructions. Optional-context suppression cannot remove it. Fixed collaboration policy stays in the system prompt; roster and task state remain tool queries. Initial tasks contain only their supplied content.
`tool-subagent-control` contributes required `subagent:identity` through `systemPrompt.context()` in ordinary subagent and Team compositions. It derives identity from `ctx.subagents.rootOf(agent)`: the root sees its own Session id, and descendants see their own, direct parent, and root ids. An unavailable root is reported explicitly. Optional-context suppression and Team mode do not remove identity. Agents receive messaging and discovery guidance only while the root is available and each corresponding standard tool is admitted. Team task policy stays in the shared system prompt; task state remains a tool query.
The loop owns logging, retained-snapshot comparison, and re-admission after compaction. A changed role appends the current full runtime-context snapshot after inherited history; unchanged retained context adds no message. No Team-specific retention state, event type, or log migration is introduced. Existing recorded reminders remain historical messages.
This supersedes only the initial-task identity decision in [Agent Teams](../feature/2026-08-05-agent-teams.md#team-identity); its roster, mailbox, and task decisions remain active.
This supersedes only the initial-task identity decision in [Agent Teams](../feature/2026-08-05-agent-teams.md#team-identity). [Task-board wait coordination](2026-10-19-team-task-board-inbox.md) keeps ordinary inbox waiting independent of Team-only model guidance.
## Alternatives considered
@@ -28,4 +30,4 @@ This supersedes only the initial-task identity decision in [Agent Teams](../feat
Members pay for identity within the full runtime-context snapshot. Other context changes repeat it, while stable retained context adds no new message. Forks preserve inherited history and append current identity; actual provider cache hits remain best-effort. Existing Team authorization remains in tool execution.
Verification covers fresh and fork teammates, an independent fork from a teammate, optional-context suppression, compaction restoration, cold resume, and registration disposal. The recorded `team-targets` scenario pins Lead context; the keyless headless Team workflow pins teammate context through the shipped profile.
Verification covers fresh and fork children, an independent Session fork, optional-context suppression, compaction restoration, unavailable roots, cold resume, and registration disposal. Recorded ordinary subagent and Team workflows pin identity through their shipped compositions; delegated tasks retain final-result return guidance.
@@ -1,20 +1,22 @@
# Agent Note: 必需 runtime context 中的 Team 身份
# Agent Note: 必需 runtime context 中的 subagent 身份
Status: implemented
[English](2026-10-13-team-identity-runtime-context.md) | 中文
[目录名称](2026-11-02-subagent-catalog-names.zh.md)取代下文以 Session id 表述身份的文本:agent 看到自身、直接父级和根的名称。runtime context 保留决策仍然有效。
## Problem
嵌入初始任务的 teammate 身份依赖普通历史保留。压缩可能移除它;从 teammate 创建的普通 fork 即使已成为独立 Lead,也可能保留旧角色。身份必须反映当前 membership,同时保持共享系统指令一致。
嵌入初始任务的 teammate 身份依赖普通历史保留。压缩可能移除它;从 teammate 创建的普通 fork 即使已成为独立 Lead,也可能保留旧角色。身份必须反映当前 subagent 树,同时保持共享系统指令一致。
## Decision
`tool-agent-team` 通过 Agent 作用域的 `systemPrompt.context()` 注册 `team:identity`,并设置 `required: true`。提供方读取现有 Team membership,渲染成员角色、名字与寻址说明。关闭可选 context 不能移除它。固定协作策略保留在 system prompt;roster 和 task 状态仍通过工具查询。初始任务只包含传入的正文。
`tool-subagent-control` 在普通 subagent 和 Team 组合中通过 `systemPrompt.context()` 提供必需的 `subagent:identity`。它从 `ctx.subagents.rootOf(agent)` 派生身份:根看到自身 Session id,后代看到自身、直接父级和根 id。根不可用时会明确报告。可选上下文抑制和 Team 模式都不会移除身份。只有根可用时,agent 才会分别根据对应标准工具的获准情况收到通信与发现指导。Team 任务策略保留在共享系统提示中;任务状态仍通过工具查询。
Loop 负责日志记录、保留快照比较和压缩后的重新接纳。角色变化会在继承历史之后追加当前完整 runtime-context 快照;保留的 context 未变化时不追加消息。不引入 Team 专属的保留状态、事件类型或日志迁移。已有身份提醒仍作为历史消息保留。
本决策仅取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md#team-identity) 中的初始任务身份决策;其 roster、mailbox 和 task 决策仍然有效。
这仅取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md#team-identity) 中将身份放入初始任务的决策。[任务板等待协调](2026-10-19-team-task-board-inbox.zh.md)使普通收件箱等待独立于 Team 专用模型指导。
## Alternatives considered
@@ -28,4 +30,4 @@ Loop 负责日志记录、保留快照比较和压缩后的重新接纳。角色
成员承担完整 runtime-context 快照中的身份 token 成本。其他 context 变化会重复身份,保留的 context 稳定时则不追加消息。Fork 保留继承历史并追加当前身份;提供方实际缓存命中仍为尽力而为。现有 Team 授权仍在工具执行时落实。
验证覆盖 fresh 和 fork teammate、从 teammate 创建的独立 fork、关闭可选 context、压缩后恢复、冷恢复和注册释放。录制的 `team-targets` 场景固定 Lead context;无密钥 headless Team 工作流通过内置 profile 固定 teammate context。
验证覆盖新建和 fork 子级、独立 Session fork、可选上下文抑制、压缩恢复、不可用根、冷恢复和注册释放。录制的普通 subagent 和 Team 工作流通过各自的分发组合固定身份行为;委派任务保留最终结果返回指导。
@@ -0,0 +1,22 @@
# Bilingual-pair consistency record for 2026-10-15-agent-teams-as-subagents.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-10-15-agent-teams-as-subagents.md
/agent-note-agent-teams-teammates-are-subagents:
en: f49f9e7ef588e612
zh: 7da804056221af51
/agent-note-agent-teams-teammates-are-subagents/problem:
en: ca2848e3bbc3a009
zh: 0551e32ff6d187d0
/agent-note-agent-teams-teammates-are-subagents/decision:
en: 2c29c42cc02a9a05
zh: bc542e3a27cfd03d
/agent-note-agent-teams-teammates-are-subagents/alternatives-considered:
en: ef0cf648ff54784f
zh: 2a0b3745a54e7072
/agent-note-agent-teams-teammates-are-subagents/consequences:
en: 3ff718671be076fa
zh: 38a367077ce95d52
/agent-note-agent-teams-teammates-are-subagents/testing:
en: a0ec8546a783bfd9
zh: 8920b46970e3a2de
@@ -0,0 +1,41 @@
# Agent Note: Agent Teams teammates are subagents
Status: implemented
English | [中文](2026-10-15-agent-teams-as-subagents.zh.md)
The adjacency and parent-lifetime restrictions are superseded by [tree-wide communication](2026-10-16-tree-wide-agent-communication.md). [Task-board inbox coordination](2026-10-19-team-task-board-inbox.md) supersedes the separate roster and Team-owned wait; the single subagent identity decision remains active. [Catalog names](2026-11-02-subagent-catalog-names.md) supersede the statement that creation accepts no member name and the rejection of unique names below.
## Problem
Teammates already use the subagent runtime for creation, Session identity, catalog membership, capacity, cold resume, settlement, and interruption. A separate Team roster duplicates identity, provisioning recovery, and teardown. Team-scoped messaging and control tools shadow the standard subagent tools, forcing profiles to disable ordinary delegation and making fork tool schemas diverge from their parent.
## Decision
Use one subagent identity: the Session id. The existing `description` tool argument supplies the catalog label and display text; descriptions may repeat and never address an agent. Creation accepts no member name, reserves no names, and adds no catalog payload version.
[Tree-wide communication](2026-10-16-tree-wide-agent-communication.md) authorizes an exact live sender within one root tree and preserves the actual sender’s id. Interruption retains ancestor authority and id addressing.
Every root Agent and its descendants form the Team identified by the root Session id. Team creates no agents and contributes task tools and task policy. Ordinary subagent controls supply required identity, discovery, messaging, interruption, and inbox waiting. Parent and fork children receive identical tool schemas and shared policies.
Task ownership uses `ownerId`; root reassignment accepts itself or any reachable continuable descendant. Historical roster records remain decode-only. Team persists task changes without ending inbox waits or starting idle turns; [inbox-only waiting](../simplification/2026-10-29-inbox-only-agent-wait.md) owns wake behavior. Historical `team/message/*` records remain readable but are never delivered or acknowledged.
This supersedes the roster, provisioning, recovery, and messaging decisions in [Agent Teams](../feature/2026-08-05-agent-teams.md) and the [Team direct inbox decision](../../archived/simplification/2026-09-26-team-direct-inbox.md). The task-board and shared-checkout decisions remain active. [Adjacent Agent messaging](2026-08-27-adjacent-agent-steer-messaging.md) includes sibling routing, while [required runtime-context identity](2026-10-13-team-identity-runtime-context.md) owns the role statement. The [upgrade guide](../../../../docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.md) lists removed tools, methods, and configuration.
## Alternatives considered
**Separate Team authority and tools.** This retains a second roster and recovery lifecycle for Agents already owned by the subagent runtime, plus same-name tools and a private delivery adapter. The shared parent relation supplies the required authority.
**Unique names as sibling-routing opt-in.** Names add validation, reservation, persistence, and ambiguous resolution beside existing Session ids. Descriptions already explain responsibilities; stable ids distinguish repeated descriptions without another identity system.
**Pluggable address books.** A registry would support groups spanning unrelated parents but add another authorization input. No current consumer needs this; revisit when collaboration must cross parent trees.
## Consequences
Tree members discover and message one another through ordinary controls. Team retains task ownership, dependencies, and task policy. Subagent capacity and lifecycle apply; failed starts create no Team roster row. Existing durable task generations remain unchanged.
Descendants at any depth share the board and can be task owners. Historical members grant no authority. Separate working directories remain explicit caller choices; cross-process task coordination is unsupported.
## Testing
Verification covers tree delivery and cold resume with actual sender attribution, task CAS and owner discovery, historical decoding, inbox wait isolation, and registration disposal. Recorded sessions cover Team policy and identity, plus ordinary subagent return guidance.
@@ -0,0 +1,41 @@
# Agent Note: Agent Teams 队友是子代理
Status: implemented
[English](2026-10-15-agent-teams-as-subagents.md) | 中文
邻接和父级生命周期限制由[整树通信](2026-10-16-tree-wide-agent-communication.zh.md)取代。[任务板收件箱协调](2026-10-19-team-task-board-inbox.zh.md)取代独立名册和 Team 所有的等待;单一 subagent 身份决策仍有效。[目录名称](2026-11-02-subagent-catalog-names.zh.md)取代下文“创建不接收成员名称”的表述及对唯一名称的否决。
## Problem
队友已经使用子代理运行时完成创建、Session 身份、目录成员关系、容量、冷恢复、结算和中断。独立 Team 成员列表重复了身份、创建恢复和释放职责。Team 作用域内的消息及控制工具覆盖标准子代理工具,迫使 profile 禁用普通委派,并使分叉工具模式偏离父级。
## Decision
统一使用子代理的 Session id 作为身份。现有 `description` 工具参数提供目录标签和展示文本;描述可以重复,不用于寻址代理。创建不接收成员名称、不预留名称,也不增加目录载荷版本。
[整树通信](2026-10-16-tree-wide-agent-communication.zh.md)在同一根树内授权当前在线发送方,并保留实际发送方 id。中断保留祖先权限和 id 寻址。
每个根 Agent 及其后代组成以根 Session id 标识的 Team。Team 不创建 agent,提供任务工具和任务策略。普通 subagent 控制工具提供必需身份、发现、通信、中断和收件箱等待。父级和 fork 子级接收相同工具 schema 和共享策略。
任务归属使用 `ownerId`;根重新分配可指定自身或任意可达的可继续执行后代。历史名册记录仅供解码。Team 持久化任务变更,不结束收件箱等待,也不启动空闲轮次;[仅等待收件箱](../simplification/2026-10-29-inbox-only-agent-wait.zh.md)负责唤醒行为。历史 `team/message/*` 记录仍可读取,但绝不再投递或确认。
本决策取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 的成员列表、创建、恢复和消息决策,以及 [Team 直接收件箱决策](../../archived/simplification/2026-09-26-team-direct-inbox.md)。任务板和共享工作树决策仍有效。[相邻 Agent 消息](2026-08-27-adjacent-agent-steer-messaging.zh.md)纳入同级路由,[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)负责角色声明。[升级指南](../../../../docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.zh.md)列出移除的工具、方法和配置。
## Alternatives considered
**独立 Team 权限和工具。** 这会为子代理运行时已拥有的 Agent 保留第二套成员列表与恢复生命周期,以及同名工具和私有投递适配器。共同父级关系已经提供所需权限。
**以唯一名称选择性启用同级路由。** 名称会在现有 Session id 之外增加校验、预留、持久化和歧义解析。描述已能说明职责;稳定 id 可以区分重复描述,无需另一套身份系统。
**可插拔地址簿。** 注册表可支持跨不同父级的组,但会增加另一种授权输入。当前没有消费者需要此能力;协作必须跨父级树时再考虑。
## Consequences
树内成员通过普通控制工具发现和联系彼此。Team 保留任务归属、依赖和任务策略。容量和生命周期遵循 subagent 规则;启动失败不会创建 Team 名册行。现有持久化任务代次保持不变。
任意深度后代共享任务板,均可成为任务所有者。历史成员不授予权限。独立工作目录仍由调用者显式选择;不支持跨进程任务协调。
## Testing
验证覆盖具有实际发送方归属的整树投递和冷恢复、任务 CAS 与所有者发现、历史解码、收件箱等待隔离和注册释放。录制会话覆盖 Team 策略和身份,以及普通 subagent 返回指导。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-16-tree-wide-agent-communication.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-10-16-tree-wide-agent-communication.md
/agent-note-communication-follows-the-subagent-tree:
en: 849d806b901e9f04
zh: 649b2984f844d510
/agent-note-communication-follows-the-subagent-tree/problem:
en: c583c3cdb894e417
zh: 6edc0354b79f4f26
/agent-note-communication-follows-the-subagent-tree/decision:
en: 767b4b4dfd84f72e
zh: 36617ec0dab777ee
/agent-note-communication-follows-the-subagent-tree/alternatives-considered:
en: 470f9cf8c3e32af9
zh: 73bbe29767c5d02a
/agent-note-communication-follows-the-subagent-tree/consequences:
en: 6d409833d762d473
zh: 000cd93577ec6b34
@@ -0,0 +1,29 @@
# Agent Note: Communication follows the subagent tree
Status: implemented
English | [中文](2026-10-16-tree-wide-agent-communication.zh.md)
## Problem
A Team shares one task board across the root's entire subagent tree. Restricting discovery and messages to adjacent relatives forces deeper members to relay work through parents despite sharing tasks.
## Decision
`list_agents` starts at the caller's live root and lists that root and all reachable continuable descendants, including the caller. Full names remain relative to the root. The existing parent catalogs remain authoritative; no Team directory is stored separately.
`send_message` accepts another root or continuable member of the same tree. The sender must be the exact live root or a resident continuable subagent. Cross-branch delivery verifies the target descriptor and creation catalogs, then restores only the target under the Team root. Final inbox admission rechecks the sender and root. Cancellation or rejection before acceptance releases the new target instance; accepted input follows normal Agent persistence.
The existing Agent registry records runtime ownership by the Team root. Creation parent ids and catalogs preserve ancestry for discovery and interrupt authority. A member finishing or being released affects only its own instance; Team-root shutdown joins all startup and cleanup. This removes ancestor restoration, parent retention, and recursive disposal without adding a Team runtime registry. Saved target presets and permission events supply restoration configuration.
This supersedes the adjacency, discovery, and parent-lifetime restrictions in [adjacent messaging](2026-08-27-adjacent-agent-steer-messaging.md) and [subagent teammates](2026-10-15-agent-teams-as-subagents.md). Their shared inbox, scheduling, attribution, and Team reuse decisions remain active. Members address one another by the names that [catalog names](2026-11-02-subagent-catalog-names.md) define, or by Session id; ordinary Session forks lead independent Teams.
## Alternatives considered
**Relay through parents.** This preserves the narrower routing implementation but makes the creation hierarchy determine who can collaborate. Shared work requires direct communication across branches.
**Separate Team directory and delivery.** A second member list and message path would duplicate the existing catalogs, inbox, and restoration lifecycle. Tree membership supplies the required communication scope without another registry.
## Consequences
Discovery output grows with the whole tree. Cold delivery requires one capacity slot and readable creation history. Intermediate creators may be offline. Completion notices reach only online creators, so work that requires descendant results must explicitly wait. Listing is a snapshot and does not promise that a later send succeeds. External and one-shot executions cannot receive follow-ups.
@@ -0,0 +1,29 @@
# Agent Note: 通信范围由子代理树决定
Status: implemented
[English](2026-10-16-tree-wide-agent-communication.md) | 中文
## Problem
一个 Team 在根的整棵子代理树中共享任务板。把发现和消息限定在相邻成员之间,会迫使深层成员在共享任务的同时,仍通过父级转发工作。
## Decision
`list_agents` 从调用方的在线根开始,列出该根与所有可达的可继续后代,包括调用方。完整名称始终相对根计算。现有父级目录仍是权威来源;不单独存储 Team 目录。
`send_message` 接受同一棵树中的其他根或可继续成员。发送者必须是确切在线根或驻留的可继续子代理。跨分支投递验证目标描述符和创建目录后,仅在 Team 根下恢复目标。最终收件箱准入重新检查发送者与根。接受前的取消或拒绝释放新建目标实例;已接受输入遵循普通 Agent 持久化。
现有 Agent 注册表记录 Team 根对运行时实例的所有权。创建父级 id 和目录保留用于发现及中断授权的祖先关系。成员完成或释放只影响自身实例;关闭 Team 根会等待全部启动和清理。这删除了祖先恢复、父级保留和递归释放,无需增加 Team 运行时注册表。目标保存的 preset 和权限事件提供恢复配置。
本决策替代[相邻消息](2026-08-27-adjacent-agent-steer-messaging.zh.md)与[子代理队友](2026-10-15-agent-teams-as-subagents.zh.md)中的邻接、发现及父子生命周期限制。它们的共享收件箱、调度、发送者归属和 Team 复用决策仍有效。成员使用[目录名称](2026-11-02-subagent-catalog-names.zh.md)定义的名称或 Session id 互相寻址;普通 Session 分叉领导独立 Team。
## Alternatives considered
**通过父级转发。** 这保留了更窄的路由实现,却让创建层级决定谁能协作。共享工作需要跨分支直接通信。
**单独的 Team 目录与投递。** 第二份成员列表和消息路径会重复现有目录、收件箱与恢复生命周期。树成员身份已经提供所需通信范围,无需另一个注册表。
## Consequences
发现输出随整棵树增长。冷投递需要一个容量名额和可读的创建历史。中间创建者可以离线。完成通知仅投递给在线创建者,因此依赖后代结果的工作必须显式等待。列表是快照,不保证后续发送成功。外部执行和一次性执行不能接收后续消息。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-19-team-task-board-inbox.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-10-19-team-task-board-inbox.md
/agent-note-team-task-boards-share-ordinary-subagent-waits:
en: 7f19734715f5d50f
zh: 95af09c9d855d1af
/agent-note-team-task-boards-share-ordinary-subagent-waits/problem:
en: 6fdc766a348d8659
zh: 8a84748e147deed5
/agent-note-team-task-boards-share-ordinary-subagent-waits/decision:
en: 154da8a8a5146c89
zh: 76a7c921153d238d
/agent-note-team-task-boards-share-ordinary-subagent-waits/alternatives-considered:
en: eed6f6d4fa0ccd7b
zh: 61271057d6e5d019
/agent-note-team-task-boards-share-ordinary-subagent-waits/consequences:
en: cc250eb21dbb45a1
zh: 9d03d1eb53075fb6
@@ -0,0 +1,31 @@
# Agent Note: Team task boards share ordinary subagent waits
Status: implemented
English | [中文](2026-10-19-team-task-board-inbox.zh.md)
## Problem
The subagent tree already supplies identity, discovery, routing, and lifecycle. A second Team roster can grant assignment authority from stale history and duplicates member navigation. Team-wide status waits observe unrelated activity and miss input already pending for the caller. Persisting task-board wakeups as messages adds unread state and message schemas even though the task journal already owns the state to inspect.
## Decision
The subagent tree is the Team. Ordinary controls own required identity, communication, discovery, interruption, and `wait_agent`, independently of the optional task board. Team tools own task policy, without adding it to ordinary subagent compositions. Team owns task storage, dependencies, and compare-and-set commands. Assignment accepts the root or any discovered continuable descendant. Historical roster records grant no authority; the Team panel shows tasks and ordinary navigation owns members.
The wait-signal decision is superseded by [inbox-only Agent waits](../simplification/2026-10-29-inbox-only-agent-wait.md). Tree ownership, task durability, and historical non-authority remain unchanged.
Task state remains in the existing journal. Ordinary tool results record what the waiting model observes. Historical roster, mailbox, and task generations remain readable and unchanged. This partially supersedes the roster and wait decisions in [Agent Teams](../feature/2026-08-05-agent-teams.md) and [subagent teammates](2026-10-15-agent-teams-as-subagents.md). Their task durability and single-identity rationale remain active; [required runtime identity](2026-10-13-team-identity-runtime-context.md) owns retention.
## Alternatives considered
**Keep Team membership and a separate wait tool.** This duplicates tree authority and wait lifecycle, allowing stale roster rows or unrelated activity to affect current work.
**Persist task-board notices in every inbox.** The task journal already preserves every revision. Durable notices add message types, unread state, coalescing, and delivery-failure recovery solely to interrupt a current wait.
**Poll Agent status or start every idle task owner.** Activity does not identify useful input, and task readiness does not authorize a new turn. Explicit messages continue idle work when needed.
## Consequences
Task completion, inbox availability, current waiting, and Agent lifetime remain separate. Task updates do not end waits; callers inspect current tasks when deciding what to do. The task journal retains committed revisions without storing redundant notifications in recipient Sessions.
Verification distinguishes pending inbox input from task commits, isolates callers, excludes next-turn input and status changes, and covers wait cleanup and task persistence. Assignment coverage retains arbitrary depth and historical non-authority.
@@ -0,0 +1,31 @@
# Agent Note: Team 任务板复用普通 subagent 等待
Status: implemented
[English](2026-10-19-team-task-board-inbox.md) | 中文
## 问题
subagent 树已经提供身份、发现、路由和生命周期。第二份 Team 名册可能根据过期历史授予分配权限,并重复成员导航。Team 范围状态等待会观察无关活动,却漏掉调用者已有的待处理输入。任务日志已经拥有待查询状态,将任务板唤醒持久化为消息仍会增加未读状态和消息 schema。
## 决策
subagent 树就是 Team。普通控制工具独立于可选任务板,负责必需身份、通信、发现、中断和 `wait_agent`。Team 工具负责任务策略,不向普通 subagent 组合添加该策略。Team 负责任务存储、依赖和比较并交换命令。分配接受根或任意已发现的可继续执行后代。历史名册不授予权限;Team 面板显示任务,普通导航负责成员。
等待信号决策已由[仅等待收件箱的 Agent 等待](../simplification/2026-10-29-inbox-only-agent-wait.zh.md)取代。树归属、任务持久性和历史记录不授予权限的决策保持不变。
任务状态保留在现有日志中。普通工具结果记录等待中的模型所观察到的结果。历史名册、邮箱和任务代次保持可读且不变。这部分取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 和 [subagent 队友](2026-10-15-agent-teams-as-subagents.zh.md) 中的名册与等待决策。其任务持久性和单一身份理由继续生效;[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)负责保留规则。
## 考虑过的替代方案
**保留 Team 成员关系与独立等待工具。** 这会重复树权限和等待生命周期,让过期名册行或无关活动影响当前工作。
**将任务板通知持久化到每个收件箱。** 任务日志已保留每个修订。持久通知仅为打断当前等待,就增加消息类型、未读状态、合并和投递失败恢复。
**轮询 Agent 状态或启动每个空闲任务所有者。** 活动状态不能标识有用输入,任务就绪也不授权新轮次。需要时通过显式消息继续空闲成员的工作。
## 后果
任务完成、收件箱可用性、当前等待和 Agent 生命周期保持独立。任务更新不会结束等待;调用者在决定下一步工作时查询当前任务。任务日志保留已提交修订,不在接收者 Session 中存储重复通知。
验证区分已有收件箱输入和任务提交,隔离调用者,排除下一轮输入及状态变化,并覆盖等待清理和任务持久性。分配验证保留任意深度和历史记录无权限场景。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-20-session-team-mode.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-10-20-session-team-mode.md
/agent-note-team-availability-is-selected-per-session:
en: 5f36d81bd414ea67
zh: 71883b0fe2afd1b6
/agent-note-team-availability-is-selected-per-session/problem:
en: db7a81ab452fdd0f
zh: a14fdcdb8ff4cd47
/agent-note-team-availability-is-selected-per-session/decision:
en: d1174db5157b91ee
zh: ceb6dc5a89a0a8d6
/agent-note-team-availability-is-selected-per-session/alternatives-considered:
en: d50ed45e8f902281
zh: c01d9be4b6e5df06
/agent-note-team-availability-is-selected-per-session/consequences:
en: b56a6a1c8ad1aa7a
zh: 43bdd8a5bccce8bf
@@ -0,0 +1,33 @@
# Agent Note: Team availability is selected per Session
Status: implemented
English | [中文](2026-10-20-session-team-mode.zh.md)
## Problem
A plugin-level Team switch requires users to change the shared profile before choosing collaboration for one conversation. It can disable another Session’s capability and leaves a second control beside the Session selection. Ordinary tasks still need their existing tool catalog, and disabling collaboration must not discard task history or children.
## Decision
The base bundle loads the Team service and task tools, and the Web bundle loads the Team UI. The packages belong to the subagent and client product groups. A saved root-Session selection gates task tools and Team guidance throughout the tree, and the shared board in the root conversation; new Sessions default off. Ordinary subagent creation, communication, and navigation remain available.
The model and reasoning menu owns the user’s selection. The root conversation’s Subagent panel displays members above shared tasks. Disabling Team keeps the journal and children; re-enabling restores the board. Historical task boards without a saved selection stay enabled. Profile loading removes the retired optional bundle selection, retaining row overrides.
Headless deployments may set `agent-team.defaultEnabled` to save an initial selection for a new root during startup creation. Saved selections take precedence, and resumed Sessions retain their mode even when no selection exists. Ordinary forks initialize independently; subagents share their live root’s selection. Creation identifies subagents by their Session origin because activation ownership is registered only after creation listeners settle. Task access still requires the live runtime owner.
This supersedes the explicit-composition choice in [Agent Teams](../feature/2026-08-05-agent-teams.md). Its durability, ownership, and lifecycle decisions remain active. [Optional bundles](../process/2026-09-15-shipped-optional-bundles.md) still governs other experimental features.
## Alternatives considered
**Keep both switches.** Capability installation remains a prerequisite that the conversation control cannot explain or resolve; users must manage shared profile state for a Session decision.
**Apply the deployment default on resume or at read time.** Changing a profile would change an existing conversation’s tools without a saved selection. Persisting the default once at creation keeps subsequent capability decisions reconstructable from the Session log.
**Hide the plugin card without loading Team.** New profiles would have no way to obtain the capability. Loading experimental packages in the default profile would violate product isolation; moving them to their owning product groups makes the delivered support explicit.
## Consequences
Default profiles carry the Team implementation while exposing no task tools until enabled. The removed package names require custom compositions to migrate; durable event names and task data remain unchanged. [The upgrade guide](../../../../docs/upgrade-guide/v0.2.1-rc.2/session-team-mode/guide.md) owns those steps.
Verification covers default startup, new-Session opt-in, independent roots, persisted selections, retained tasks, old profile migration, and absence of the plugin switch. Product-isolation checks retain an unrelated experimental plugin as their negative control.
@@ -0,0 +1,33 @@
# Agent Note: 按 Session 选择 Team 能力
Status: implemented
[English](2026-10-20-session-team-mode.md) | 中文
## Problem
插件级 Team 开关要求用户先修改共享 profile,才能为一个会话选择协作。它可能关闭另一个 Session 的能力,并在会话选项之外留下第二个控件。普通任务仍需要原有工具目录,关闭协作也不能丢弃任务历史或子智能体。
## Decision
基础 bundle 加载 Team 服务与任务工具,Web bundle 加载 Team UI。相关包归属 subagent 与 client 产品组。根 Session 保存的选择控制整棵树的任务工具与 Team 指引,以及根会话中的共享任务板;新 Session 默认关闭。普通 subagent 创建、通信与导航继续可用。
模型与推理等级菜单承载用户选择。根会话的 Subagent 面板在成员下方显示共享任务。关闭 Team 保留日志与子智能体;重新开启恢复任务板。没有保存选择的历史任务板保持开启。profile 加载时移除已停用的可选 bundle 选择,保留配置行覆盖。
无界面部署可设置 `agent-team.defaultEnabled`,在新根会话的启动创建阶段保存初始选择。已保存的选择优先,恢复的 Session 即使没有保存选择也保留原模式。普通分叉独立初始化;子智能体共享当前运行时根的选择。创建阶段通过 Session origin 识别子智能体,因为激活归属在创建监听器结束后才登记。任务访问仍要求当前运行时归属有效。
本决策替代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 中显式选择组合的部分;其持久性、归属与生命周期决策继续有效。[可选 bundle](../process/2026-09-15-shipped-optional-bundles.zh.md)仍约束其他实验性功能。
## Alternatives considered
**保留两个开关。** 安装能力仍是会话控件无法解释或解决的前置条件;用户必须为 Session 决策管理共享 profile 状态。
**在恢复或读取时应用部署默认值。** 修改 profile 将在没有保存选择的情况下改变现有会话的工具。在创建时一次性持久化默认值,可让后续能力选择从 Session 日志重建。
**隐藏插件卡片但不加载 Team。** 新 profile 将无法获得该能力。默认 profile 加载实验性包会违反产品隔离;将相关包移入所属产品组可明确随产品提供的支持。
## Consequences
默认 profile 包含 Team 实现,但开启前不暴露任务工具。自定义组合必须迁移已移除的包名;持久事件名称与任务数据保持不变。[升级指南](../../../../docs/upgrade-guide/v0.2.1-rc.2/session-team-mode/guide.zh.md)负责具体步骤。
验证覆盖默认启动、新 Session 手动开启、独立根、选择持久化、任务保留、旧 profile 迁移和插件开关移除。产品隔离检查使用另一个无关实验性插件作为负对照。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-22-subagent-target-user-controls.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-10-22-subagent-target-user-controls.md
/agent-note-user-controls-address-the-target-subagent:
en: d7dcf54ddb311881
zh: 5f388447737c5414
/agent-note-user-controls-address-the-target-subagent/problem:
en: 962c75771235903c
zh: 203b651c0830310e
/agent-note-user-controls-address-the-target-subagent/decision:
en: 13a2617e28445744
zh: 75d193ef7d8c7804
/agent-note-user-controls-address-the-target-subagent/alternatives-considered:
en: 447adb24280bf2bb
zh: 08d95a93b14aa5c7
/agent-note-user-controls-address-the-target-subagent/consequences:
en: 98e784f55775247d
zh: 1c62d2ebc71b658f
@@ -0,0 +1,33 @@
# Agent Note: User controls address the target subagent
Status: implemented
English | [中文](2026-10-22-subagent-target-user-controls.zh.md)
## Problem
A browser knows the subagent it is displaying, but requiring its direct creator and a continuable marker makes every control caller repeat history metadata. A caller-supplied parent id checks address consistency without establishing the caller's identity or permission. Calls that resolve only the global access resource also cannot express a grant on one target Session.
## Decision
Browser prompts and `interruptByUser` address only the target child Session. Each Remote method explicitly declares that Session as its access resource; the Gateway checks the invocation's principal before attachment admission, restoration, or cancellation. A global or parent Session grant does not imply a child Session grant. Policies decide whether to grant both actions, either action, or inherited access. Same-process user interruption trusts its caller and does not accept a wire-supplied principal.
Prompt delivery derives the live root from the resident Activation or verified persisted creation edges. Only the cold target resumes; its creator can remain offline. Shared delivery retains exact live root checks, shutdown admission, cancellation, capacity, and final inbox acceptance. A root Session is not a target of the browser subagent prompt endpoint. User interruption addresses a resident Activation without persistence reads or restoration; an absent target remains a no-op, and interruption preserves parked input.
Model messages retain the exact live sender and same-tree rule from [tree-wide communication](2026-10-16-tree-wide-agent-communication.md). Model interruption retains its stricter ancestor rule. Creation parents still determine catalog edges, history-address consistency, ancestry, and completion recipients; they are not browser control credentials.
This supersedes the direct-parent addressing requirement for human continuation in [continuable subagents](../feature/2026-07-28-continuable-subagent-conversations.md). That record retains its inbox and durable continuation rationale; tree-wide communication owns its superseded model adjacency and runtime ownership restrictions. No Session format changes.
## Alternatives considered
**Keep the parent check as authorization.** The client supplies the parent id and can obtain it from history. Matching that id does not authorize the principal, and requiring it couples controls to discovery metadata.
**Require a root id from the browser.** The server already derives the runtime owner from the target. Another supplied id adds a consistency check without granting permission; Agent senders still need an independently resolved root to enforce same-tree messaging.
**Unify user and Agent interruption permissions.** A user with a target grant can stop that target, while an Agent may stop only its descendants. Same-tree collaboration does not grant members control over each other's execution.
## Consequences
Remote callers update their prompt fields, interrupt endpoint, and non-owner policy actions and resources; the [upgrade guide](../../../../docs/upgrade-guide/v0.2.1-rc.2/subagent-user-controls/guide.md) owns the migration. History URLs retain parent ids for consistency checks. Cold host prompts may read creation ancestry twice, once to find the live root and again during restoration; no verified-ancestry cache is retained across asynchronous work.
Gateway tests cover target grants, global-only and parent-only refusal, separate send and interrupt actions, owner access, and refusal before side effects. Continuation tests cover nested and cold targets with online or offline creators, unavailable roots, and rejection of a root as a subagent target. The Web interrupt scenario exercises the renamed endpoint and parked-input behavior through the shipped composition.
@@ -0,0 +1,33 @@
# Agent Note: 用户控制按目标子代理寻址
Status: implemented
[English](2026-10-22-subagent-target-user-controls.md) | 中文
## 问题
浏览器知道当前展示的子代理,但要求提供直接创建者和 continuable 标记,使每个控制调用方都要重复历史元数据。调用方提供的父级 id 只能检查地址一致性,不能确定调用者身份或权限。仅解析出 global 访问资源的调用也无法表达对单个目标 Session 的授权。
## 决策
浏览器提示词和 `interruptByUser` 仅按目标子 Session 寻址。每个 Remote 方法显式声明该 Session 为访问资源;Gateway 在附件接纳、恢复或取消之前检查调用的 principal。global 或父 Session 授权不意味着子 Session 授权。策略决定授予两个操作、其中一个操作,或继承访问权。同进程用户中断信任其调用方,不接受从线上传入的 principal。
提示词投递从驻留 Activation 或经过验证的持久化创建关系中解析在线根。仅恢复冷目标,创建者可以保持离线。共享投递保留精确在线根检查、关闭准入、取消、容量和最终 inbox 接纳。根 Session 不是浏览器子代理提示词端点的目标。用户中断直接寻址驻留 Activation,不读取持久化数据,也不恢复目标;目标缺席仍为无操作,中断保留停放的输入。
模型消息保留[整树通信](2026-10-16-tree-wide-agent-communication.zh.md)中的精确在线发送者和同树规则。模型中断保留更严格的祖先规则。创建父级仍决定目录关系、历史地址一致性、祖先关系和完成通知接收者;它们不是浏览器控制凭证。
本决策取代[可继续的 subagent](../feature/2026-07-28-continuable-subagent-conversations.zh.md)对人工继续执行要求直接父级地址的部分。旧记录保留 inbox 和持久化继续执行的理由;整树通信决策拥有其中已取代的模型相邻关系和运行时所有权限制。Session 格式不变。
## 考虑过的替代方案
**保留父级检查作为授权。** 客户端提供父级 id,也能从历史中取得它。匹配该 id 不能授权 principal,还使控制依赖于发现元数据。
**要求浏览器提供根 id。** 服务端已经能从目标解析运行时所有者。再提供一个 id 只会增加一致性检查,不会授予权限;Agent 发送者仍需要独立解析的根来约束同树消息。
**统一用户和 Agent 的中断权限。** 用户获得目标授权后可以停止该目标,而 Agent 只能停止自己的后代。同树协作不赋予成员控制彼此执行的权限。
## 影响
Remote 调用方需要更新提示词字段、中断端点,以及非 owner 策略的操作和资源;迁移步骤由[升级指南](../../../../docs/upgrade-guide/v0.2.1-rc.2/subagent-user-controls/guide.zh.md)拥有。历史 URL 保留父级 id 用于一致性检查。冷目标的宿主提示词可能读取两次创建祖先:先查找在线根,再在恢复期间验证;不跨异步操作保留已验证祖先缓存。
Gateway 测试覆盖目标授权、仅 global 和仅父级授权的拒绝、独立的发送和中断操作、owner 访问,以及副作用前拒绝。继续执行测试覆盖创建者在线或离线时的嵌套和冷目标、根不可用,以及拒绝把根当作子代理目标。Web 中断场景通过交付组合验证重命名后的端点和输入停放行为。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-22-subagent-tool-owned-communication.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-10-22-subagent-tool-owned-communication.md
/agent-note-subagent-tools-own-communication-guidance:
en: 0bd031aa81fbc18d
zh: c530e980826698ed
/agent-note-subagent-tools-own-communication-guidance/problem:
en: ac391b569d2bc7e9
zh: a95f613aa2b330b8
/agent-note-subagent-tools-own-communication-guidance/decision:
en: d83310e85ffcb3cf
zh: eb1f069948399d93
/agent-note-subagent-tools-own-communication-guidance/alternatives-considered:
en: c55d44c6414e0c4c
zh: 9fa2ab31843e259b
/agent-note-subagent-tools-own-communication-guidance/consequences:
en: 52f7234664c515ec
zh: 55ec7b54c044e9af
@@ -0,0 +1,27 @@
# Agent Note: Subagent tools own communication guidance
Status: implemented
English | [中文](2026-10-22-subagent-tool-owned-communication.zh.md)
## Problem
The continuation manager inspected a Symbol on the standard messaging tool before adding tool instructions and parent addressing to the initial task. This coupled orchestration to one tool implementation and made result-delivery guidance depend on tool installation. A published internal entry point carried both that marker and a host prompt adapter used only by tests.
## Decision
`tool-subagent-control` owns messaging instructions and parent addressing. Its logged runtime context gives delegated children their direct creator’s name from `ctx.subagents.namesOf()`, explains when to send findings, and states that sending does not finish a turn. The context is emitted only for delegated children when the plugin’s own tool definition is admitted in the current scope and step; roots and ordinary Session forks receive none. Communication guidance follows inherited history without changing the shared system prompt or tool schema.
The manager owns result delivery. Local activations with `delivery: 'parent'` receive automatic-result guidance even without messaging tools; caller-owned activations receive no parent-delivery promise. Settlement timing, recipients, output, and suppression of closing text after a final message to the parent remain unchanged. Tool visibility grants no messaging authority.
This supersedes the Symbol identity and initial-task communication-guidance choices in [adjacent messaging](2026-08-27-adjacent-agent-steer-messaging.md). Its message attribution, fixed Steer delivery, and rejection of a separate report operation remain active. The internal entry point and prompt adapter are removed; the [upgrade guide](../../../../docs/upgrade-guide/v0.2.1-rc.2/subagent-user-controls/guide.md) owns caller migration.
## Alternatives considered
**Keep a cross-package marker or add a service hook.** Tool registration already gives its owner the exact definition, and prompt assembly already supplies the current Agent. Another protocol or stored identity has no independent consumer.
**Move all return guidance into the tool.** Automatic parent notification depends on activation delivery, not on messaging capability. The tool must not promise it to caller-owned activations or withhold it from children without tools.
## Consequences
Tests cover visible, hidden and replaced definitions, logged identity, plugin disposal, fork prefix reuse, and parent versus caller result delivery. Recorded-session snapshots cover the assembled prompt and durable runtime context. Tests needing host input use the existing private manager test helper; no production adapter exists solely to expose that helper.
@@ -0,0 +1,27 @@
# Agent Note: Subagent 工具拥有通信指导
Status: implemented
[English](2026-10-22-subagent-tool-owned-communication.md) | 中文
## 问题
继续执行管理器检查标准通信工具上的 Symbol,随后在初始任务中加入工具说明和父级地址。这让编排依赖一个工具实现,也让结果交付指导取决于是否安装工具。已发布的内部入口同时承载该标记和仅供测试使用的宿主 prompt 适配器。
## 决策
`tool-subagent-control` 拥有通信说明和父级寻址。持久记录的运行时上下文通过 `ctx.subagents.namesOf()` 提供委派子级的直接创建者名称,说明何时发送发现,并指出发送不会结束轮次。仅当本插件的工具定义在当前作用域和 step 中可用时,才向委派子级提供上下文;根代理和普通 Session fork 不接收。通信指导位于继承历史之后,不改变共享系统提示词或工具 schema。
管理器拥有结果交付。采用 `delivery: 'parent'` 的本地 activation 即使没有通信工具,也会收到自动结果交付指导;调用方拥有的 activation 不会收到父级交付承诺。结算时序、接收方、输出,以及最后向父级发送消息后省略结束文本的规则保持不变。工具可见性不授予通信权限。
本决策取代[相邻代理通信](2026-08-27-adjacent-agent-steer-messaging.zh.md)中的 Symbol 身份和初始任务通信指导选择。该记录的消息来源归属、固定 Steer 投递,以及不另设 report 操作的决策继续有效。内部入口和 prompt 适配器已删除;调用方迁移由[升级指南](../../../../docs/upgrade-guide/v0.2.1-rc.2/subagent-user-controls/guide.zh.md)说明。
## 考虑过的替代方案
**保留跨包标记或增加 service hook。** 工具注册已经向所有者提供确切定义,提示词组装也已提供当前 Agent。另一套协议或身份存储没有独立消费者。
**把全部返回指导移入工具。** 自动父级通知取决于 activation 的交付方式,而非通信能力。工具不能向调用方拥有的 activation 承诺自动通知,也不能让没有工具的子级缺少交付说明。
## 后果
测试覆盖可见、隐藏及替换的定义、持久记录的身份、插件释放、fork 前缀复用,以及父级与调用方结果交付。录制会话快照覆盖组装后的提示词和持久运行时上下文。需要宿主输入的测试使用现有的私有 manager 测试帮助函数;生产代码不再仅为暴露该帮助函数而保留适配器。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-29-cua-dev-signing-identity.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-10-29-cua-dev-signing-identity.md
/agent-note-development-signing-identity-for-source-built-cua-helpers:
en: 472c4efd47184a32
zh: 49f64628531970e9
/agent-note-development-signing-identity-for-source-built-cua-helpers/problem:
en: a24857a312482724
zh: dbac75d363670e86
/agent-note-development-signing-identity-for-source-built-cua-helpers/decision:
en: d2265f9ec5fb7a79
zh: 05da2479b024a4bb
/agent-note-development-signing-identity-for-source-built-cua-helpers/alternatives-considered:
en: e30db5bc9c7aab43
zh: fa84883b5527334e
/agent-note-development-signing-identity-for-source-built-cua-helpers/consequences:
en: fb0e8fe44890bdf9
zh: d9e9ace1a4c10060
@@ -0,0 +1,35 @@
# Agent Note: Development signing identity for source-built CUA helpers
Status: implemented
English | [中文](2026-10-29-cua-dev-signing-identity.zh.md)
## Problem
Source checkouts compile `DSH Computer Use.app` with an ad-hoc signature. macOS stores Accessibility and Screen Recording grants for a bundle identifier with the designated requirement of the granted code, and an ad-hoc requirement is the code hash. Every changed build and every other worktree therefore needs new grants, and these builds shared the release helper's bundle identifier and replaced its grant entry.
## Decision
The Host builds source checkouts as a development variant with bundle identifier `com.deepseek.harness.computer-use.dev` and name `DSH Computer Use Dev`, in `DSH Computer Use Dev.app`. The macOS privacy list and the helper’s reported launch application use the bundle filename, so that filename also includes Dev. The release variant and its packaging are unchanged. Grants stay with the helper application launched through LaunchServices, as the [shared helper decision](2026-10-09-cua-shared-native-helper.md) requires.
`sourceSigning` belongs to the macOS provider and accepts only default `ad-hoc` or `{ identity, keychain }` selecting an existing certificate and absolute keychain path. Builds do not create, unlock or remove identities. Developer credentials stay separate from the user-facing DSH home, which stores only the certificate selection; the shared `cua` package owns no macOS signing configuration.
The [developer signing skill](../../../skills/dsh-cua-macos-signing/SKILL.md) guides Agents through creating, reusing and unlocking an isolated identity under `~/.cua-macos-keys/`, then selecting its fingerprint and keychain path in user configuration. The tool stores a random password in a `0600` file, permits codesign access, and adds the dedicated keychain to the user search list. A file lock serializes setup and removal for that directory; a missing keychain with an existing record is preserved and rejected. A directory marker limits identity management to explicitly claimed, initially empty directories. Unexpected files or symlinked identity entries stop setup and removal before changes, so a developer-selected path cannot erase unrelated contents.
Certificate signatures embed a signing time even without a timestamp server. Development publication records which generation each unsigned bundle and identity produced, then selects that generation again instead of re-signing, so identical sources keep one executable digest and one shared service. The cache uses certificate fingerprints; explicit certificate names are not reused because renewal can change the certificate behind a name. Signing failures stop publication. Developer setup diagnostics omit passwords and complete command arguments. The build CLI defaults to `--identity -` and accepts an existing certificate with `--identity` and optional `--keychain` for release packaging.
## Alternatives considered
**Grant the launching terminal.** Launching the service directly makes macOS attribute permissions to the terminal or editor that started DSH. Every program that application runs, including commands run by the model, then has the permissions.
**Automatically create a fixed identity during source builds.** This makes ordinary startup create developer private keys and ties their lifetime to user data. An explicit developer workflow preserves the option to reduce repeated grants while keeping identity creation, removal and selection under developer control.
**Self-signed identity in the login keychain.** The login keychain is already unlocked and on the search list, but allowing `codesign` to use a new key there without a dialog requires the user's login password.
**Add the keychain to the search list only while signing.** The release packaging script does this for one invocation. Concurrent builds in several worktrees would read and restore the user list over each other, and a stopped build would leave the entry behind anyway.
**A stable launcher that spawns each worktree's helper.** Grants would follow the launcher, but any local process could ask it to run an arbitrary executable with those grants, which is the same exposure as the stored key without a signature check.
## Consequences
Development builds signed by the same certificate share a signing requirement; actual permission retention after grants, rebuilds and worktree changes still requires verification. Other processes of the same user can read the stored password and sign with that identity; the release helper has a different bundle identifier. Deleting DSH home removes its selection, not the developer identity; deleting the identity requires clearing every configuration that references it. The real-signing e2e checks reuse, unlocking, requirements across output directories and search-list restoration after removal.
@@ -0,0 +1,35 @@
# Agent Note: 源码构建的 CUA helper 使用开发签名身份
Status: implemented
[English](2026-10-29-cua-dev-signing-identity.md) | 中文
## 问题
源码检出用临时签名编译 `DSH Computer Use.app`。macOS 按 Bundle ID 和被授权代码的指定要求保存辅助功能与屏幕录制授权,临时签名的指定要求就是代码哈希。因此每次构建内容变化、每换一个 worktree,都需要重新授权;这些构建还与正式 helper 共用 Bundle ID,会替换正式版的授权条目。
## 决策
Host 以开发版构建源码检出,Bundle ID 为 `com.deepseek.harness.computer-use.dev`,名称为 `DSH Computer Use Dev`,应用包文件名为 `DSH Computer Use Dev.app`。macOS 隐私列表和 helper 返回的启动应用名称使用应用包文件名,因此文件名也需包含 Dev。正式版及其打包流程不变。按[共享 helper 决策](2026-10-09-cua-shared-native-helper.zh.md)的要求,授权仍归经 LaunchServices 启动的 helper 应用。
`sourceSigning` 由 macOS provider 定义,只接受默认的 `ad-hoc` 和指定已有证书及钥匙串绝对路径的 `{ identity, keychain }`。构建不创建、解锁或移除身份。开发者凭据与面向用户的 DSH home 分开,后者只保存选择证书的配置;共享 `cua` 包不持有 macOS 签名配置。
[开发者签名 skill](../../../skills/dsh-cua-macos-signing/SKILL.md)指导 Agent 在 `~/.cua-macos-keys/` 创建、复用和解锁独立身份,将证书指纹和钥匙串路径写入用户配置。工具把随机密码存入 `0600` 文件,允许 codesign 访问私钥,并把专用钥匙串加入用户搜索列表。文件锁让同一目录的初始化和移除串行执行;已有记录但钥匙串缺失时保留记录并报错。目录标记将身份管理限制在明确占用的初始空目录中。发现无关文件或身份文件软链接时,初始化和移除都会在修改前停止,避免开发者指定的路径导致无关内容被删除。
即使不使用时间戳服务器,证书签名也包含签名时间。开发构建记录每份未签名内容与身份对应的已签名目录,再次构建相同内容时复用该目录,保持可执行文件摘要和共享服务一致。缓存使用证书指纹;按证书名称选择时每次签名,避免续期后复用旧证书。签名失败会中止发布。开发者工具的诊断不包含密码或完整命令参数;构建 CLI 默认 `--identity -`,支持指定证书,正式打包也可省略 `--keychain`。
## 考虑过的替代方案
**把权限交给启动终端。** 直接启动服务时,macOS 把权限归属到启动 DSH 的终端或编辑器。该应用运行的所有程序,包括模型执行的命令,都会带上这些权限。
**源码构建自动创建固定身份。** 这会让普通启动生成开发者私钥,并把身份生命周期绑定到用户数据目录。显式开发者操作保留了减少重新授权的选择,也让证书的创建、移除和使用范围由开发者决定。
**把自签名身份放进登录钥匙串。** 登录钥匙串已解锁且在搜索列表中,但要让 `codesign` 不弹窗地使用其中的新私钥,需要用户的登录密码。
**只在签名期间把钥匙串加入搜索列表。** 正式打包脚本对单次调用这样处理。多个 worktree 并发构建时会互相覆盖读取和恢复的列表,构建被中断时条目也会残留。
**用固定的启动器运行各 worktree 的 helper。** 授权会跟随启动器,但任何本机进程都能让它带着这些授权运行任意可执行文件,暴露面与保存的私钥相同,还少了签名检查。
## 后果
同一证书签出的开发构建共用签名要求;授权后重建和跨 worktree 的实际权限保持仍需验证。同一用户的进程可读取保存的密码并使用该身份签名,正式 helper 使用不同的 Bundle ID。删除 DSH home 只移除配置,不删除开发者身份;删除该身份后应清理所有引用它的配置。真实签名 e2e 验证复用、解锁、不同输出目录的签名要求和移除后的搜索列表恢复。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-10-29-retained-ptc-interruption.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-10-29-retained-ptc-interruption.md
/agent-note-retained-ptc-interruption:
en: 482adc6f938bc494
zh: 2bcc4856df4fb39e
/agent-note-retained-ptc-interruption/problem:
en: 1d2ac1280f14aa80
zh: c5875c914cf23d73
/agent-note-retained-ptc-interruption/decision:
en: 77f44a7ce832934c
zh: 4744934c60ca6f3d
/agent-note-retained-ptc-interruption/alternatives-considered:
en: 8c57872ffaf660b2
zh: 4765cbf561ea5dc9
/agent-note-retained-ptc-interruption/consequences:
en: f679322d48a740e9
zh: 4a255df6e019a32c
@@ -0,0 +1,23 @@
# Agent Note: Retained PTC interruption
Status: implemented
English | [中文](2026-10-29-retained-ptc-interruption.zh.md)
## Problem
Terminating a retained Node process on every timeout or cancellation loses unrelated application objects and imported modules. A control handler on the evaluation thread cannot stop a synchronous loop. The [in-memory execution decision](2026-09-23-best-effort-persistent-ptc.md) still owns disposable state and Session behavior; this decision replaces its timeout and cancellation cleanup policy.
## Decision
The host revokes execution authority immediately and requests cooperative stop over the existing control channel. The runtime rejects pending bindings; the original `run()` response confirms that evaluation has settled. Revocation is a one-way notification. The ordered pipe and single active evaluation determine notification ownership; RPC requests associate replies with callers. Acknowledgment permits reuse of the VM and retained objects; earlier mutations remain. Callers await acknowledgment or fallback process cleanup before starting another evaluation.
The host bounds acknowledgment with `stopGraceMs`. Missing or failed acknowledgment terminates the managed process range and awaits cleanup. Results retain the original timeout or cancellation kind and report whether variables survived. Confirmed termination allows a fresh environment on the next call; failed cleanup blocks reuse until host release succeeds. Explicit reset, disposal and policy invalidation terminate immediately. Reset uses the existing release path and exposes no additional model tool.
## Alternatives considered
Unconditional termination unnecessarily discards state during ordinary asynchronous waits. Cooperative stop alone cannot bound synchronous loops. Inspector termination is unsuitable as a state-preserving default: local probes on Node 24.9 and 26.6 crashed native async-context handling when terminating an awaited VM loop with an enabled async hook; Node 24 REPL also crashed without an explicit user hook. A successful Inspector command does not establish that the runtime remains reusable. Any future forced in-process interruption must demonstrate safe recovery with the supported Node versions and async hooks.
## Consequences
Canceled tools throw normally, allowing user `catch` and `finally` handlers to run. A blocked handler or unresolved user Promise prevents acknowledgment and triggers fallback termination. Timers, arbitrary user Promises, I/O callbacks and user-created Workers or subprocesses have no per-evaluation cancellation guarantee and can still mutate retained state after acknowledgment. Acknowledgment confirms revocation of managed execution, not cancellation of all Node activity. Fallback termination loses all state in that process; neither path rolls back external effects. Host reset remains available when retained resources cannot be trusted.
@@ -0,0 +1,23 @@
# Agent Note: 保留状态的 PTC 中断
Status: implemented
[English](2026-10-29-retained-ptc-interruption.md) | 中文
## 问题
每次超时或取消都终止保留的 Node 进程,会丢失无关的应用对象与导入模块。执行线程上的控制处理器无法停止同步死循环。[内存执行决策](2026-09-23-best-effort-persistent-ptc.zh.md)仍负责可丢弃状态与 Session 行为;本决策替代其中的超时与取消清理策略。
## 决策
宿主立即撤销本次执行的权限,并通过现有控制通道请求协作式停止。运行时拒绝未完成的绑定,由原 `run()` 响应确认该次执行已结束。撤销是单向通知。有序管道和唯一的活动执行确定通知归属;RPC 请求将回复关联到调用方。收到确认后可复用 VM 和保留对象;此前发生的修改继续存在。调用方等待确认或兜底进程清理完成后,才能开始下一次执行。
宿主通过 `stopGraceMs` 限制确认等待时间。未收到确认或确认失败时,终止受管进程范围并等待清理。结果保留原来的超时或取消类型,并报告变量是否保留。确认终止后,下一次调用可以创建新环境;清理失败则阻止复用,直到宿主释放成功。显式重置、销毁和策略失效立即终止进程。重置复用现有释放路径,不向模型增加工具。
## 考虑过的替代方案
无条件终止会在普通异步等待期间丢弃本可保留的状态。仅靠协作式停止无法限制同步死循环。Inspector 终止不适合作为保留状态的默认机制:本地探针在 Node 24.9 和 26.6 启用 async hook 后,终止 await 后的 VM 死循环会导致原生异步上下文处理崩溃;Node 24 REPL 在未显式启用用户 hook 时也会崩溃。Inspector 命令成功不能说明运行时仍可复用。未来若引入进程内强制中断,必须验证支持的 Node 版本及 async hooks 下能安全恢复。
## 影响
取消的工具正常抛错,允许用户的 `catch` 和 `finally` 运行。处理器阻塞或用户 Promise 无法完成时,不能确认停止,将触发兜底终止。定时器、任意用户 Promise、I/O 回调及用户创建的 Worker 或子进程不承诺按执行取消,收到确认后仍可能修改保留状态。确认表示受管执行已撤销,不表示所有 Node 活动均已取消。兜底终止会丢失该进程中的全部状态;两条路径均不回滚外部效果。保留资源不可信时,仍可使用宿主重置。
@@ -0,0 +1,37 @@
# Bilingual-pair consistency record for 2026-10-29-web-provider-selection.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-10-29-web-provider-selection.md
/agent-note-web-provider-selection-independent-menus-over-registered-providers:
en: 2a2fa1def8a81625
zh: 317d8b115f34e677
/agent-note-web-provider-selection-independent-menus-over-registered-providers/problem:
en: 3f5dacf86015cebb
zh: cf0d5bceb9941554
/agent-note-web-provider-selection-independent-menus-over-registered-providers/decision:
en: 0682ebef17f6a5e7
zh: 6ea399459262eb0c
/agent-note-web-provider-selection-independent-menus-over-registered-providers/decision/registered-providers-are-observable-availability-is-not:
en: 38ce796dea91fa35
zh: d312a9cd29fd5cc9
/agent-note-web-provider-selection-independent-menus-over-registered-providers/decision/independent-selection:
en: 9d68ffca5c8be681
zh: 36bb661f98de0ebb
/agent-note-web-provider-selection-independent-menus-over-registered-providers/decision/credential-resolution:
en: 32f59c35d71a4832
zh: e9288313bb18b78d
/agent-note-web-provider-selection-independent-menus-over-registered-providers/decision/defaults-and-stored-selections:
en: 5fcd3319fbceed1d
zh: 20771646b9c36fa6
/agent-note-web-provider-selection-independent-menus-over-registered-providers/decision/legacy-messages-search:
en: 2c7c84f8fa1e8745
zh: 5d0244e4f17ba12a
/agent-note-web-provider-selection-independent-menus-over-registered-providers/alternatives-considered:
en: 0c227e270894cd71
zh: 114484a3e69f5c0a
/agent-note-web-provider-selection-independent-menus-over-registered-providers/consequences:
en: 50f046bf81a7783d
zh: 5c0e27bdcaf9f12e
/agent-note-web-provider-selection-independent-menus-over-registered-providers/verification:
en: 26ade0695094e9bc
zh: 7b265c43b1886da5
@@ -0,0 +1,85 @@
# Agent Note: Web provider selection - independent menus over registered providers
Status: implemented
English | [中文](2026-10-29-web-provider-selection.zh.md)
## Problem
The web service selects search and fetch providers through two independent fields, `web.searchProvider` and `web.fetchProvider`. A settings control that writes both at once ties fetch billing and target access policy to the search choice, and a control that offers only the DeepSeek choices it knows cannot select a provider that another plugin registers. A picker over registered providers needs the Host to report them, and the [web capability seam](2026-06-24-web-capability-seam.md) has no availability observation for it to reuse. Messages search starts an auxiliary model request for each query, whereas the [Search API](../feature/2026-10-15-deepseek-search-fetch-api.md) returns structured sources directly. The Search and Fetch APIs also need a DeepSeek credential, and a credential setting of their own makes every user reconcile it with the model provider they chat with.
## Decision
Users choose search and fetch providers independently from the Host's registered provider lists. The shipped composition selects the Search API for search and local HTTP fetch for fetch, and the DeepSeek Search and Fetch APIs follow the conversation's DeepSeek credential.
### Registered providers are observable; availability is not
`WebSearchProvider` and `WebFetchProvider` carry an optional `title`, the provider's display name, and an optional localized `description` of what the provider suits and what it costs or cannot do. `ctx.web.listProviders()` returns `{ search, fetch }`, each a registration-ordered list of `{ id, title?, description? }` entries, and the `web` Remote namespace exposes it as `web/listProviders`. Every registration and disposal emits the payload-free `web/providers-changed` event after the registry changes; the web service contains listener failures, so an observer cannot veto a registry change. [dsh-api-remotes](../../../../packages/api/remotes/README.md) mounts the namespace and forwards the event, and Clients re-read the list after each notification and reconnect.
The lists include providers whose `available()` answers false. A picker shows what can be pinned, including a provider whose credential the user has not configured yet, whereas availability depends on per-request facts such as the Session's model route and credential state. Availability therefore stays an execution result: `search()` and `fetch()` resolve the configured provider at call time and throw the structured `WebError`, as the seam note records. The tool layer still never enumerates providers.
### Independent selection
The Web search and fetch page writes `web.searchProvider` from its Search menu and `web.fetchProvider` from its Fetch menu. Each menu lists every registered provider for its capability by `title` (or `id` when `title` is absent), so plugin providers appear without page changes, and a selected id that the Host does not register stays visible as such. Official providers register no `title`; the page names them from its own locale dictionary and lists them first in a fixed order: the Search API (`deepseek-search-api`) followed by Messages search (`deepseek-official`) for search, and local HTTP fetch (`http`) followed by the Fetch API (`deepseek-fetch-api`) for fetch. Other providers follow in Host registration order. Selecting the deployment's composed provider clears the stored override instead of storing a duplicate value. The [settings page README](../../../../packages/client/ui-settings-web-search/README.md) owns the page's controls.
Every registered provider is listed; a provider has no flag that keeps it out of the menus. This note supersedes the [Search and Fetch API decision](../feature/2026-10-15-deepseek-search-fetch-api.md)'s rejection of listing every registered provider in a settings dropdown. Independent menus must offer providers that other plugins register, and the display metadata that listing needs is only the optional `title` and `description`. The description lives on the registration rather than in the page's dictionaries so that official and plugin providers explain themselves through one field, and a new provider needs no page change to describe its trade-offs. The rejected generic editor still stands: the page edits only the DeepSeek providers' settings, and other providers keep their own settings pages.
The Search and Fetch APIs read one `web-deepseek-api` namespace, and each resolves its own credential and endpoint, as the next section records. Messages search reads `web-search-deepseek` for its independent endpoint and key reference.
### Credential resolution
Search and Fetch follow the DeepSeek credential of the conversation, and the settings page shows no credential control for them. The conversation's route is the `provider` of the initiating Session's latest request context, which account sign-out also reads; `ready()` uses the route of the model request being prepared, so readiness and execution resolve the same plan.
On the `deepseek-account` route, with an active, valid `@deepseek-ai/dsh-llm-deepseek-account` Loader entry at the entry root that the model provider directory lists, the operation sends only the account token that `ctx.deepseekAccount` releases for the dispatch endpoint under that route's origin with `/v1/tools`; an HTTP 401 invalidates that token. Every other operation, including one in an account conversation without a sign-in or a token, sends the key under the `apiKeyEnv` of the active `@deepseek-ai/dsh-llm-deepseek-api-key` entry for `deepseek-official`, resolved with that package's own resolver (including `DEEPSEEK_BASE_URL`), to the origin of that route's base URL with `/v1/tools`; without that entry, `DEEPSEEK_API_KEY` goes to `https://api.deepseek.com/v1/tools`. Sending the API key in a signed-out account conversation keeps web tools available there. A user with only `DEEPSEEK_API_KEY`, including every headless, SDK, and ACP run, therefore searches and fetches by default, and a gateway or internal deployment set through the model provider's base URL serves web access at the same origin. A missing key makes the capability not ready, and an operation fails with `WEB_PROVIDER_CREDENTIAL_MISSING` and guidance naming the Models page, the reference, and the account sign-in. A mounted route entry whose settings its resolver rejects is not treated as absent: an operation that needs that route is not ready and fails with `WEB_PROVIDER_MISCONFIGURED` without reading a credential or sending the request, so a broken gateway setting never redirects requests to the official endpoint.
A capability whose YAML sets a non-empty `searchBaseURL` or `searchApiKeyEnv` (`fetchBaseURL` or `fetchApiKeyEnv` for Fetch) instead always sends the key under its `*ApiKeyEnv` (`DEEPSEEK_API_KEY` when unset) to its `*BaseURL` (the official base when unset) and never the account token, so an intranet or third-party endpoint receives only the key chosen for it. Each capability has its own fields because a deployment can serve search and fetch from different gateways. These fields are YAML-only, and an empty value counts as unset. Stored values of fields outside the schema, including `baseURL`, `apiKeyEnv`, and `apiKey`, load without schema errors and have no effect.
The [credential helpers](../../../../packages/llm/llm-deepseek/src/credential.ts) of `@deepseek-ai/dsh-llm-deepseek` read and validate API keys, check key presence, and release account tokens for the DeepSeek model routes, Messages search, and the Search and Fetch APIs, so every DeepSeek request reads the credential store the same way; the account model route, Messages search, and the Search and Fetch APIs also invalidate the token that an HTTP 401 rejects. Shipped profiles need no per-profile credential override, because the API-key path works wherever `DEEPSEEK_API_KEY` or the DeepSeek API-key route has a key.
### Defaults and stored selections
The [base composition](../../../../packages/bundle/base/cordis.patch.yml) sets `searchProvider: deepseek-search-api` and `fetchProvider: http`. Search defaults to the Search API because it returns sources without Harness starting an auxiliary model request. Fetch defaults to local HTTP fetch because it has no per-request API charge, applies Harness's own URL validation and resource limits, and reaches intranet and local addresses. The Fetch API remains one menu choice away.
A stored selection, whether in the settings document or in a deployment's own composition, wins over the shipped default, and an upgrade does not rewrite it. A selection is never replaced at execution: when the selected provider is missing, unavailable, or fails, the operation fails with its structured error and does not fall back to another search or fetch provider. A fallback would send the query and credential to an endpoint the user did not choose and change billing without notice.
### Legacy Messages search
The DeepSeek Messages search provider remains available as **DeepSeek (legacy)**. A stored `web.searchProvider: deepseek-official` (or `DSH_WEB_SEARCH_PROVIDER=deepseek-official`) selects it, so users whose gateways serve Messages search but not `/v1/tools` can retain their working provider.
The Messages provider owns and writes `web/deepseek-search-llm-request`. The declaration and payload retain compatibility with released Sessions; the Search and Fetch API package neither declares nor writes this auxiliary event.
## Alternatives considered
**Keep one switch for both APIs.** It lets Fetch API adoption need no configuration edit, but it ties fetch billing and target access policy to the search choice, and it cannot express a provider that another plugin registers.
**Filter the lists by availability, or add availability to each entry.** A filtered list hides a provider while its credential is missing, right when the user needs to choose and configure it. Availability also depends on the request's model route and credential state, so a listed boolean could disagree with execution. The archived [observation-surface decision](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md) allows only the smallest signal a consumer reads; the menus read registration only.
**Keep a fixed provider catalog in the Client.** The Client would show ids the Host does not register as normal choices and would never show a provider that a plugin registers at runtime.
**Carry the provider list in the change event.** A payload-free event leaves one read path for initial load, notifications, and reconnects, so the Client never has to order an event payload against a concurrent read.
**Keep display names in the Client or in a separate Host registry.** The registering plugin knows its provider's name; a separate map would let a provider and its label register or dispose independently.
**Fall back to another provider when the selected one fails.** A fallback hides gateway and credential misconfiguration, sends the query to a second service, and changes what the request costs without the user choosing it.
**Remove Messages search and let a stored `deepseek-official` select the Search API.** Gateways can support Messages search without Search API routes. Retaining the legacy provider preserves that choice instead of changing its protocol and billing.
**Default fetch to the Fetch API as well.** It adds a per-request charge to every fetch and delegates page retrieval and target access policy to the service, which fetches from its own side rather than from the user's machine.
**Choose a credential source per capability on the settings page**, from a menu listing the configured DeepSeek model providers plus a custom key and base URL, without fallback between sources. The visible setting would state the whole rule, but the configuration surface is too large for users to understand: each capability adds a credential menu that the user must keep in step with the model provider they chat with, and a signed-in user chatting on the account route gets no web tools until they change it. Following the conversation's credential needs no setting, and the YAML-only own fields cover endpoints that differ from the model provider.
**Share one custom key and base URL between Search and Fetch.** It cannot express a deployment that serves the two APIs from different gateways.
**Fail an account conversation without a sign-in instead of sending the API key.** It would keep the account route from ever billing the API key, but signed-out account conversations would lose web tools even when a DeepSeek API key is configured.
**Send the account token whenever the user is signed in, whatever the conversation's route.** It would bill web access to the account during conversations on the API-key route or on another provider, which the user did not choose for them.
## Consequences
Users without a stored search selection search through the Search API: their queries go to the Search endpoint the conversation's DeepSeek credential selects, cost a Search API request each, and send up to 128 characters of the latest human prompt as `ori_question` unless `sendOriginalQuestion` is off. Deployments whose gateway has no `/v1/tools` routes get search failures until they select Messages search or set `searchBaseURL` to a base that serves them. A stored `deepseek-official` selection uses Messages search. Users without a stored fetch selection fetch through local HTTP fetch. A signed-in user without an API key has DeepSeek web search only in conversations on the account route; the [upgrade guide](../../../../docs/upgrade-guide/v0.2.2-alpha.2/web-search-credential-modes/guide.md) lists each affected setup.
A plugin provider, including one written in creator mode, appears in the matching menu as soon as it registers, under its `title`. The menus can offer a provider that cannot run for the current request; the failure appears when a search or fetch runs. Each registry change costs one forwarded event and one list read in each Client that observes it.
## Verification
[web.spec.ts](../../../../packages/web/web/tests/web.spec.ts) covers list contents and order, titles, events after registration and disposal, no event for a rejected duplicate, and contained listener failures. [deepseek.spec.ts](../../../../packages/web/web-search-deepseek/tests/deepseek.spec.ts) covers the independent Messages provider, and [credential-resolution.spec.ts](../../../../packages/web/web-deepseek-api/tests/credential-resolution.spec.ts) covers, for both capabilities, the account token on the account route and its invalidation after HTTP 401, the API-key fallback without a sign-in, the route key and endpoint in official, non-DeepSeek, and Session-less conversations, `DEEPSEEK_API_KEY` and the official base without that route, YAML-only own fields that never send the token, independent capabilities, readiness matching each operation, and ignored removed fields; [composition.spec.ts](../../../../packages/web/web-deepseek-api/tests/composition.spec.ts) boots the real model routes through the Loader. [remote-events.host.spec.ts](../../../../packages/api/remotes/tests/remote-events.host.spec.ts) covers event forwarding, and [base.spec.ts](../../../../packages/bundle/base/tests/base.spec.ts) pins the shipped defaults. The settings page's controller and card specs and the [plugin configuration browser scenario](../../../../apps/web/tests/plugin-config.e2e.ts) cover menu contents, independent Search and Fetch selection, the absence of credential controls for the DeepSeek APIs, and the settings each provider keeps.
@@ -0,0 +1,85 @@
# Agent Note: Web 提供方选择——基于已注册提供方的独立下拉框
Status: implemented
[English](2026-10-29-web-provider-selection.md) | 中文
## 问题
Web 服务通过 `web.searchProvider` 和 `web.fetchProvider` 两个独立字段分别选择搜索与抓取提供方。用一个设置控件同时写入两者,会把抓取的计费和目标访问策略绑在搜索选择上;只提供已知 DeepSeek 选项的控件,也无法选用其他插件注册的提供方。基于已注册提供方的选择器需要 Host 报告这些提供方,而 [Web 能力 seam](2026-06-24-web-capability-seam.zh.md) 没有可供复用的可用性观察接口。Messages 搜索每次查询都会发起一次辅助模型请求,而 [Search API](../feature/2026-10-15-deepseek-search-fetch-api.zh.md) 能直接返回结构化来源。Search 与 Fetch API 还需要 DeepSeek 凭据;为它们单独设置凭据,会让每个用户都要把它与自己对话所用的模型提供方对齐。
## 决策
用户从 Host 已注册的提供方列表中分别选择搜索和抓取提供方。内置组合将搜索设为 Search API,将抓取设为本地 HTTP 抓取;DeepSeek Search 与 Fetch API 跟随会话的 DeepSeek 凭据。
### 已注册的提供方可观察,可用性不可观察
`WebSearchProvider` 和 `WebFetchProvider` 带有可选的 `title`(提供方的显示名称)和可选的多语言 `description`(说明该提供方适合什么、有什么代价或做不到什么)。`ctx.web.listProviders()` 返回 `{ search, fetch }`,二者均为按注册顺序排列的 `{ id, title?, description? }` 列表,`web` Remote namespace 将其暴露为 `web/listProviders`。每次注册和注销都会在注册表变更后触发无载荷的 `web/providers-changed` 事件;Web 服务会隔离监听器异常,因此观察者无法阻断注册表变更。[dsh-api-remotes](../../../../packages/api/remotes/README.zh.md) 挂载该 namespace 并转发该事件,Client 在收到通知和重连后重新读取列表。
列表包含 `available()` 返回 false 的提供方。选择器展示的是可固定选用的项,包括用户尚未配置凭据的提供方;而可用性取决于会话的模型路由、凭据状态等单次请求条件。因此可用性仍是执行期结果:`search()` 和 `fetch()` 在调用时解析所配置的提供方并抛出结构化 `WebError`,与 seam 记录一致。工具层仍从不枚举提供方。
### 分别选择
「网页搜索与获取」页由 Search 下拉框写入 `web.searchProvider`,由 Fetch 下拉框写入 `web.fetchProvider`。每个下拉框以 `title`(缺省时用 `id`)列出该能力的所有已注册提供方,因此插件提供方无需修改页面即可出现,且 Host 未注册的已选 id 仍会照常显示。官方提供方不注册 `title`,由页面用自己的语言字典命名,并按固定顺序排在最前:搜索依次为 Search API(`deepseek-search-api`)与 Messages 搜索(`deepseek-official`),抓取依次为本地 HTTP 抓取(`http`)和 Fetch API(`deepseek-fetch-api`)。其他提供方按 Host 注册顺序排列。选择部署组合的默认提供方会清除已保存的覆盖值,而非冗余保存一份副本。[设置页 README](../../../../packages/client/ui-settings-web-search/README.zh.md) 负责页面控件说明。
所有已注册的提供方都会列出;提供方没有可以把自己排除在下拉框之外的标志。本 Note 取代了 [Search 与 Fetch API 决策](../feature/2026-10-15-deepseek-search-fetch-api.zh.md)对「在设置页用下拉框列出所有已注册的提供方」的否决。独立的下拉框必须提供其他插件注册的提供方,而列出提供方所需的展示元数据只是可选的 `title` 和 `description`。说明放在注册信息里而不是页面词典里,这样官方与插件提供方都通过同一个字段说明自己,新提供方无需修改页面即可说明其取舍。被否决的通用编辑表单仍然成立:该页面只编辑 DeepSeek 提供方的设置,其他提供方保留各自的设置页。
Search 与 Fetch API 读取同一个 `web-deepseek-api` namespace,并各自解析自己的凭据和端点,见下一节。Messages 搜索从 `web-search-deepseek` 读取独立的端点和密钥引用。
### 凭据解析
Search 与 Fetch 跟随会话的 DeepSeek 凭据,设置页不为它们显示凭据控件。会话的路由是发起会话最近一次请求上下文的 `provider`,账号退出登录读取的也是它;`ready()` 使用正在准备的模型请求的路由,因此就绪判断与执行解析出相同的方案。
在 `deepseek-account` 路由上,若模型提供方目录列出了入口根部活动且有效的 `@deepseek-ai/dsh-llm-deepseek-account` Loader 条目,操作只发送 `ctx.deepseekAccount` 为该路由 origin 加 `/v1/tools` 下的请求端点给出的账号令牌;HTTP 401 会作废该令牌。其他所有操作,包括账号会话中没有登录或没有令牌的操作,都把 `deepseek-official` 的活动 `@deepseek-ai/dsh-llm-deepseek-api-key` 条目的 `apiKeyEnv` 下的密钥(用该包自己的解析函数解析,包括 `DEEPSEEK_BASE_URL`)发往该路由基础地址的 origin 加 `/v1/tools`;没有该条目时,把 `DEEPSEEK_API_KEY` 发往 `https://api.deepseek.com/v1/tools`。未登录的账号会话发送 API 密钥,网页工具因此在其中仍然可用。因此只有 `DEEPSEEK_API_KEY` 的用户(包括所有 headless、SDK 与 ACP 运行)默认即可搜索和抓取,通过模型提供方基础地址设置的网关或内部部署也在同一 origin 上提供 Web 访问。缺少密钥时,该能力未就绪,操作以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败,指引信息点名模型页面、该引用和账号登录。已挂载、但其解析函数拒绝其设置的路由条目不视为不存在:需要该路由的操作未就绪,并以 `WEB_PROVIDER_MISCONFIGURED` 失败,不读取凭据、不发送请求,因此网关设置出错时请求不会被改发到官方端点。
在 YAML 中设置了非空 `searchBaseURL` 或 `searchApiKeyEnv`(Fetch 为 `fetchBaseURL` 或 `fetchApiKeyEnv`)的能力,则始终把其 `*ApiKeyEnv`(未设置时为 `DEEPSEEK_API_KEY`)下的密钥发往其 `*BaseURL`(未设置时为官方基础地址),从不发送账号令牌,因此内网或第三方端点只收到为它选择的密钥。每个能力有自己的字段,因为一个部署可以由不同网关提供搜索和抓取。这些字段只能在 YAML 中设置,空值视为未设置。schema 之外字段的已保存值,包括 `baseURL`、`apiKeyEnv` 和 `apiKey`,可以加载而不产生 schema 错误,且不生效。
`@deepseek-ai/dsh-llm-deepseek` 的[凭据函数](../../../../packages/llm/llm-deepseek/src/credential.ts)为 DeepSeek 模型路由、Messages 搜索以及 Search 与 Fetch API 读取并校验 API 密钥、检查密钥是否存在、取得账号令牌,因此每个 DeepSeek 请求都以同一方式读取凭据库;账号模型路由、Messages 搜索以及 Search 与 Fetch API 还会作废被 HTTP 401 拒绝的令牌。内置配置不需要按 profile 覆盖凭据,因为只要 `DEEPSEEK_API_KEY` 或 DeepSeek API 密钥路由有密钥,API 密钥路径就能工作。
### 默认值与已保存的选择
[base 组合](../../../../packages/bundle/base/cordis.patch.yml)设置 `searchProvider: deepseek-search-api` 和 `fetchProvider: http`。搜索默认使用 Search API,因为它返回来源时无需 Harness 发起辅助模型请求。抓取默认使用本地 HTTP 抓取,因为它没有按次的 API 费用,执行 Harness 自身的 URL 校验和资源限制,并且可以访问内网和本地地址。Fetch API 仍可在下拉框中直接选择。
已保存的选择——无论位于设置文档还是部署自身的组合中——优先于内置默认值,升级不会改写它。执行时从不替换选择:所选提供方缺失、不可用或失败时,操作以结构化错误失败,不会回退到其他搜索或抓取提供方。回退会把查询和凭据发往用户未选择的端点,并在不提示的情况下改变计费。
### 旧版 Messages 搜索
DeepSeek Messages 搜索提供方仍以 **DeepSeek(旧版)**提供。已保存的 `web.searchProvider: deepseek-official`(或 `DSH_WEB_SEARCH_PROVIDER=deepseek-official`)会选中它,因此网关支持 Messages 搜索但不提供 `/v1/tools` 的用户可以保留可用的提供方。
Messages 提供方负责声明并写入 `web/deepseek-search-llm-request`。声明和载荷保持与已发布会话兼容;Search 与 Fetch API 包既不声明也不写入这一辅助事件。
## 考虑过的替代方案
**保留一个同时切换两个 API 的开关。** 它让使用 Fetch API 无需修改配置,但把抓取的计费和目标访问策略绑在搜索选择上,也无法表达其他插件注册的提供方。
**按可用性过滤列表,或在每个条目中加入可用性。** 过滤后的列表恰好会在凭据缺失时隐藏提供方,而这正是用户需要选择并配置它的时候。可用性还取决于请求的模型路由和凭据状态,列表中的布尔值可能与实际执行结果不一致。已归档的[观察面决策](../../archived/simplification/2026-07-04-drop-unconsumed-web-observation-surface.md)只允许引入消费方实际读取的最小信号;下拉框只读取注册情况。
**在 Client 中维护固定的提供方目录。** Client 会把 Host 未注册的 id 当作正常选项展示,且永远无法展示插件在运行时注册的提供方。
**在变更事件中携带提供方列表。** 无载荷事件让初次加载、变更通知和断线重连共用同一条读取路径,Client 无需为事件载荷与并发读取结果排序。
**把显示名称放在 Client 或单独的 Host 注册表中。** 注册插件最清楚自身提供方的名称;拆成独立映射会让提供方与其标签各自独立注册或注销。
**所选提供方失败时回退到其他提供方。** 回退会掩盖网关和凭据的配置错误,将查询发往第二个服务,并在未经用户选择的情况下改变请求的费用。
**移除 Messages 搜索,让已保存的 `deepseek-official` 选中 Search API。** 网关可能支持 Messages 搜索,却没有 Search API 路由。保留旧版提供方可以保留这一选择,而不改变其协议和计费。
**抓取也默认使用 Fetch API。** 它会给每次抓取增加按次费用,并把页面抓取和目标访问策略交给服务端,由服务端而不是用户的机器发起抓取。
**在设置页为每个能力选择一种凭据来源**,下拉框列出已配置的 DeepSeek 模型提供方,外加自定义密钥和基础地址,来源之间不回退。这样可见的设置就能说明全部规则,但配置面过大,用户难以理解:每个能力都多出一个凭据下拉框,用户必须让它与自己对话所用的模型提供方保持一致;在账号路由上对话的已登录用户在修改设置前没有网页工具。跟随会话的凭据不需要任何设置,与模型提供方不同的端点由只能在 YAML 中设置的单独字段覆盖。
**Search 与 Fetch 共用一个自定义密钥和基础地址。** 它无法表达由不同网关分别提供两个 API 的部署。
**账号会话未登录时直接失败,而不是发送 API 密钥。** 这样账号路由永远不会消耗 API 密钥,但即使配置了 DeepSeek API 密钥,未登录的账号会话也会失去网页工具。
**只要用户已登录就发送账号令牌,不论会话的路由。** 这会让在 API 密钥路由或其他提供方上进行的会话把 Web 访问记到账号上,而用户并没有为这些会话这样选择。
## 影响
没有已保存搜索选择的用户通过 Search API 搜索:查询发往会话的 DeepSeek 凭据所选的 Search 端点,每次计为一次 Search API 请求,并且除非关闭 `sendOriginalQuestion`,还会把最近一条用户提示的至多 128 个字符作为 `ori_question` 发送。网关没有 `/v1/tools` 路由的部署会遇到搜索失败,直到选择 Messages 搜索,或把 `searchBaseURL` 设为提供这些路由的基础地址。已保存的 `deepseek-official` 选择使用 Messages 搜索。没有已保存抓取选择的用户通过本地 HTTP 抓取。已登录但没有 API 密钥的用户只在账号路由上的会话中可以使用 DeepSeek 网页搜索;[升级指南](../../../../docs/upgrade-guide/v0.2.2-alpha.2/web-search-credential-modes/guide.zh.md)列出了每种受影响的配置。
插件提供方(包括在创作模式下编写的提供方)一经注册,就会以其 `title` 出现在对应的下拉框中。下拉框可能提供在当前请求中无法运行的提供方;失败会在执行搜索或抓取时出现。每次注册表变更会在每个观察它的 Client 中产生一次转发事件和一次列表读取。
## 验证
[web.spec.ts](../../../../packages/web/web/tests/web.spec.ts) 覆盖列表内容与顺序、标题、注册与撤销后的事件、重复注册被拒绝时不触发事件,以及监听器失败的隔离。[deepseek.spec.ts](../../../../packages/web/web-search-deepseek/tests/deepseek.spec.ts) 覆盖独立的 Messages 提供方,[credential-resolution.spec.ts](../../../../packages/web/web-deepseek-api/tests/credential-resolution.spec.ts) 针对两个能力覆盖账号路由上的账号令牌及其在 HTTP 401 后被作废、未登录时回退到 API 密钥、官方、非 DeepSeek 和会话之外的操作使用路由密钥与端点、没有该路由时使用 `DEEPSEEK_API_KEY` 与官方基础地址、从不发送令牌的 YAML 单独字段、两个能力相互独立、就绪判断与每次操作一致,以及被忽略的已移除字段;[composition.spec.ts](../../../../packages/web/web-deepseek-api/tests/composition.spec.ts) 通过 Loader 启动真实的模型路由。[remote-events.host.spec.ts](../../../../packages/api/remotes/tests/remote-events.host.spec.ts) 覆盖事件转发,[base.spec.ts](../../../../packages/bundle/base/tests/base.spec.ts) 固定内置默认值。设置页的 controller 与 card 测试以及[插件配置浏览器场景](../../../../apps/web/tests/plugin-config.e2e.ts)覆盖下拉框内容、Search 与 Fetch 的分别选择、DeepSeek API 不显示凭据控件,以及各提供方保留的设置。
@@ -0,0 +1,22 @@
# Bilingual-pair consistency record for 2026-11-02-subagent-catalog-names.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-11-02-subagent-catalog-names.md
/agent-note-subagent-names-live-in-the-creator-s-catalog:
en: 987d2272bb47fe75
zh: 54e701ecbaa60457
/agent-note-subagent-names-live-in-the-creator-s-catalog/problem:
en: d4798e7bbbd5d49d
zh: b388470cf1ff33ac
/agent-note-subagent-names-live-in-the-creator-s-catalog/decision:
en: 8fafb256536d1bc5
zh: f45f9e6d6f9a2407
/agent-note-subagent-names-live-in-the-creator-s-catalog/alternatives-considered:
en: 14b564fd848248da
zh: 5f65c05bbe22d581
/agent-note-subagent-names-live-in-the-creator-s-catalog/consequences:
en: 4baa2557e5415ee1
zh: 8e8d7819c04f9043
/agent-note-subagent-names-live-in-the-creator-s-catalog/testing:
en: a7963eadbfe52c21
zh: 10bfc9fb28f6ae73
@@ -0,0 +1,45 @@
# Agent Note: Subagent names live in the creator's catalog
Status: implemented
English | [中文](2026-11-02-subagent-catalog-names.zh.md)
## Problem
Models coordinate subagents through `send_message`, `interrupt_agent`, `list_agents`, and Team task owners. Those tools addressed every member by a 36-character Session id. Models copy such ids poorly: they cannot recall which id belongs to which task, they mistype ids, and every listing, receipt, notice, and identity statement spends tokens on them. Descriptions explain responsibilities but repeat and cannot address a member. Models need short, readable references that survive restarts and resolve to exactly one member, without a second execution identity.
## Decision
The Session id remains the only execution identity for routing, authorization, persistence, task ownership, and navigation. A name is a model-facing reference to it.
Each new child has a name that is unique among its creator's children. The name is a field of the `subagent/catalog` entry that the creator already appends when it creates the child, using catalog payload version 3. Local and parent-delivered external creation write it; caller-delivered external runs enter no catalog and take no name. A name is 1–32 lower-case letters, digits, or hyphens starting with a letter, and is never `lead`. A 36-character UUID therefore never parses as a name. Omission selects the first free `agent-N`. An explicit name already held by a sibling fails with `DUPLICATE_AGENT_NAME`, and an invalid name fails with `INVALID_AGENT_NAME`.
The creator reserves the name synchronously at admission, before startup awaits anything. The reservation lives in process memory beside the creator's committed catalog names and is released when the catalog entry commits or startup fails. Task assignment copies the immutable full name into optional `ownerName` beside `ownerId` in the same task event. Task reads use this saved value and fall back to `ownerId`, so display does not require catalog traversal.
The root is always `lead`. A descendant's full name is the slash-separated path of names from the root without a `lead/` prefix, such as `reviewer/checker`. Sibling-unique segments make full names unique in the tree. A child whose catalog entry predates names, and every descendant below it, keeps its Session id as its name.
`ctx.subagents.resolveName()` returns the Session id for `lead` or a full name by reading only the catalogs along the path; any other text is returned unchanged as a Session id. Tools resolve before they call messaging, interruption, or Team assignment, and those operations authorize the resulting Session id. Resolution grants no authority. Runtime-authored text names agents by full name, while durable attribution such as `senderSessionId` and Team `ownerId` keeps Session ids. The [subagent runtime](../../../../packages/subagent/subagent/README.md#agent-names) owns the API contract and the [upgrade guide](../../../../docs/upgrade-guide/v0.2.2-alpha.3/subagent-names/guide.md) owns caller migration.
This supersedes the rejection of "unique names as sibling-routing opt-in" and the statement that creation accepts no member name in [subagent teammates](2026-10-15-agent-teams-as-subagents.md), and the Session-id identity text in [required runtime-context identity](2026-10-13-team-identity-runtime-context.md). Their single-identity, Team, and runtime-context retention decisions remain active.
## Alternatives considered
**Optional naming plugin with a root-Session binding event.** A separate plugin would record a name-to-Session binding in the root Session after the child starts. That adds a second write that can fail independently after startup succeeds, so a started child can lack its binding, and binding after start can report errors for children that are already running. The presentation plugin would then own cancellation of a lifecycle it does not control, would rewrite runtime-authored messages that name agents, and would still change model-visible schemas when it is absent, because the core tools must accept and render either form. Recording the name in the creator's existing catalog fact needs no second write, no rollback protocol, and no optional service.
**Flat names unique across the tree.** A tree-wide flat namespace needs one registry at the root that every creation must consult and update, including creations by descendants whose root may be cold. Sibling-unique segments joined into paths are unique without a root registry, and each creator checks only its own catalog.
**Recyclable names.** Releasing a name when its child settles would let a later sibling take it. A model that still remembers the earlier child would then message or interrupt a different agent. A name stays taken for as long as the catalog entry exists.
**Automatic suffixes on conflict.** Turning a duplicate `reviewer` into `reviewer-2` would let creation succeed, but the model would believe it holds a name that the runtime never assigned. A duplicate fails so that the model chooses a name it knows.
## Consequences
Catalog payload version 3 is a same-format addition: `SESSION_FORMAT_VERSION` is unchanged, current readers accept versions 0 through 3, and releases that predate names reject version 3 loudly. The `subagentCatalog` projection state version remains 6 because old checkpoint facts keep their meaning and accept named suffix events; invalidating them would add full catalog folds to cold descendant observations without changing the recovered state. Native admission rejects a repeated sibling name in own catalog facts, so a log that could make name lookup ambiguous fails before any member is resolved. The [persistence acknowledgement](../../../../docs/persistence-changes/2026-11-02-subagent-catalog-names.md) records the type change.
Tool schemas grow by one optional `name` parameter on each delegation tool, and `send_message` and `interrupt_agent` rename `agent_id` to `agent`. These schema changes invalidate cached request prefixes once after upgrading. Identity runtime context, receipts, settlement notices, agent messages, and listing rows become shorter for shallow trees, while full names lengthen with nesting depth. `list_agents` drops parent ids and depth because a full name encodes both.
Names cannot be renamed or recycled. Historical children have no names, and no migration assigns them. Name reservations are process-local, as is subagent residency. Tools still accept Session ids, so recorded Session ids and members below unnamed creators remain addressable.
## Testing
Runtime tests cover requested and default names, duplicate and invalid names rejected before startup, reservation across concurrent startup and release after failed startup, nested paths and `namesOf`, resolution of names, unknown text, and Session ids without granting authority, cold restoration of a member's path from the catalogs along its creation chain, and Session-id addressing below an unnamed historical creator. Catalog tests restore version-3 names beside unnamed historical entries, and the V4 format reader admits native version-3 catalog facts. Control-tool and Team-tool tests cover name-addressed messaging, listing rows, owner names, and historical owners shown by Session id. Recorded sessions pin the tool schemas, identity runtime context, receipts, listing rows, and Team policy text.
@@ -0,0 +1,45 @@
# Agent Note: Subagent 名称记录在创建者目录中
Status: implemented
[English](2026-11-02-subagent-catalog-names.md) | 中文
## Problem
模型通过 `send_message`、`interrupt_agent`、`list_agents` 和 Team 任务所有者协调 subagent(子智能体)。这些工具曾以 36 字符的 Session id 寻址每个成员。模型难以准确复制这类 id:记不清哪个 id 对应哪个任务,会输错 id,而且每次列表、回执、通知和身份声明都要为其消耗 token。描述能说明职责,但可以重复,无法寻址成员。模型需要简短、可读、跨重启有效且只解析到唯一成员的引用,同时不引入第二种执行身份。
## Decision
Session id 仍是路由、授权、持久化、任务归属和导航使用的唯一执行身份。名称是指向它的面向模型的引用。
每个新子级都有一个名称,在其创建者的子级之间唯一。名称是创建者在创建子级时本就会追加的 `subagent/catalog` 条目中的一个字段,使用第 3 版目录载荷。本地创建和父级投递的外部创建写入名称;调用方投递的外部执行不进入目录,也没有名称。名称由 1–32 个小写字母、数字或连字符组成,以字母开头,且绝不能是 `lead`。因此 36 字符的 UUID 永远不会被解析为名称。省略时选择第一个空闲的 `agent-N`。显式请求同级已持有的名称会以 `DUPLICATE_AGENT_NAME` 失败,无效名称以 `INVALID_AGENT_NAME` 失败。
创建者在准入时、启动等待任何操作之前同步预留名称。预留保存在进程内存中,与创建者已提交的目录名称并列,并在目录条目提交或启动失败时释放。任务分配在同一个任务事件中,将不可变的完整名称复制到 `ownerId` 旁的可选 `ownerName`。任务读取使用此保存值,缺失时回退到 `ownerId`,因此展示无需遍历目录。
根的名称始终为 `lead`。后代的完整名称是从根开始、以斜杠分隔的名称路径,不带 `lead/` 前缀,例如 `reviewer/checker`。同级唯一的各段使完整名称在整棵树中唯一。目录条目早于名称机制的子级及其下所有后代继续以 Session id 作为名称。
`ctx.subagents.resolveName()` 只读取路径上的目录,为 `lead` 或完整名称返回 Session id;其他文本原样作为 Session id 返回。工具先解析名称,再调用消息、中断或 Team 分配,由这些操作对解析出的 Session id 授权。解析本身不授予权限。运行时撰写的文本以完整名称指称 agent,而 `senderSessionId` 和 Team `ownerId` 等持久归属仍保留 Session id。[subagent 运行时](../../../../packages/subagent/subagent/README.zh.md#agent-names)定义 API 约定,[升级指南](../../../../docs/upgrade-guide/v0.2.2-alpha.3/subagent-names/guide.zh.md)负责调用方迁移。
本决策取代 [Agent Teams 队友是子代理](2026-10-15-agent-teams-as-subagents.zh.md)中对“以唯一名称选择性启用同级路由”的否决以及“创建不接收成员名称”的表述,并取代[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)中以 Session id 表述身份的文本。它们的单一身份、Team 和运行时上下文保留决策仍然有效。
## Alternatives considered
**带根 Session 绑定事件的可选命名插件。** 独立插件会在子级启动后,在根 Session 中记录名称到 Session 的绑定。这会增加第二次写入,且可能在启动成功后单独失败,导致已启动的子级缺少绑定;启动后再绑定还可能为已在运行的子级报告错误。展示用插件因此要负责取消一个不受它控制的生命周期,要改写指称 agent 的运行时消息;而且即使插件不存在,面向模型的 schema 仍会改变,因为核心工具必须接受并渲染两种形式。把名称记录在创建者已有的目录事实中,不需要第二次写入、回滚协议或可选服务。
**全树唯一的扁平名称。** 全树扁平命名空间需要根上的一个注册表,每次创建都要查询并更新它,包括根可能处于冷状态时由后代发起的创建。同级唯一的各段连接成路径后无需根注册表即可唯一,每个创建者只检查自己的目录。
**可回收名称。** 子级结算后释放名称会让之后的同级取得该名称。仍记得先前子级的模型会因此向另一个 agent 发消息或发出中断。只要目录条目存在,名称就保持占用。
**冲突时自动加后缀。** 把重复的 `reviewer` 改为 `reviewer-2` 能让创建成功,但模型会以为自己持有一个运行时从未分配的名称。重复名称直接失败,模型才会选择一个自己知道的名称。
## Consequences
第 3 版目录载荷是同格式增量:`SESSION_FORMAT_VERSION` 不变,当前读取器接受第 0 至第 3 版,早于名称机制的发布会明确拒绝第 3 版。`subagentCatalog` 投影状态版本保持为 6,因为旧检查点事实的含义不变,且能继续处理具名后缀事件;使其失效会在冷后代观察时增加完整目录折叠,却不改变恢复结果。原生准入会拒绝自身目录事实中重复的同级名称,因此会让名称查找产生歧义的日志在解析任何成员之前就失败。[持久化确认记录](../../../../docs/persistence-changes/2026-11-02-subagent-catalog-names.zh.md)记录了该类型变更。
每个委派工具的 schema 增加一个可选 `name` 参数,`send_message` 和 `interrupt_agent` 把 `agent_id` 改名为 `agent`。这些 schema 变更会在升级后使缓存的请求前缀失效一次。对浅层树而言,身份运行时上下文、回执、结算通知、agent 消息和列表行都变短,完整名称则随嵌套深度变长。`list_agents` 不再携带父级 id 和深度,因为完整名称已包含这两项信息。
名称不能修改或回收。历史子级没有名称,也没有迁移为其分配名称。名称预留与 subagent 驻留一样仅限进程内。工具仍接受 Session id,因此已记录的 Session id 和无名创建者下的成员仍可寻址。
## Testing
运行时测试覆盖请求名称与默认名称、启动前拒绝重复和无效名称、并发启动期间的预留和启动失败后的释放、嵌套路径与 `namesOf`、在不授予权限的前提下解析名称、未知文本和 Session id、从创建链上的目录冷恢复成员路径,以及无名历史创建者之下以 Session id 寻址。目录测试在无名历史条目旁恢复第 3 版名称,V4 格式读取器接纳原生第 3 版目录事实。控制工具和 Team 工具测试覆盖按名称发消息、列表行、所有者名称,以及以 Session id 显示的历史所有者。录制会话固定工具 schema、身份运行时上下文、回执、列表行和 Team 策略文本。
@@ -0,0 +1,25 @@
# Bilingual-pair consistency record for 2026-11-03-hoc-slot-kind.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-11-03-hoc-slot-kind.md
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers:
en: 8292b8f9884b8802
zh: 3a4f34ca79313c9d
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers/problem:
en: d16c3bfef49e16be
zh: 6714da1c9c984fe9
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers/decision:
en: 3a4380324cfc1679
zh: 44db368e091cbd46
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers/decision/implementation:
en: 01189e457c91deeb
zh: 94f7e8865b332a07
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers/decision/first-consumer:
en: 1afdf4ceeef7a3b3
zh: be01cf39850db0ca
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers/alternatives-considered:
en: b4824619c502b422
zh: 610364f2dc8cb2b7
/agent-note-add-the-hoc-slot-kind-so-plugins-can-wrap-owner-content-in-layers/consequences:
en: 7542ee2bc2c70258
zh: bc0dd1dc6e50de66
@@ -0,0 +1,72 @@
# Agent Note: Add the `hoc` slot kind so plugins can wrap owner content in layers
Status: implemented
English | [中文](2026-11-03-hoc-slot-kind.zh.md)
## Problem
The home whale plays when blank space is clicked. Its canvas must paint below the content, and `.scrollBody` fills the whole body, so the canvas itself never receives clicks; only an ancestor of the content does.
No existing slot kind lets a plugin become an ancestor of the content:
| Kind | Behavior | Why it is not enough |
|---|---|---|
| `single` / `list` / `keyed` | Render the occupant at an owner-chosen position | The occupant is a sibling of the content |
| `chain` | The elected entry replaces the fallback; with `overlay` it renders beside the fallback | It does not wrap |
| Factory local slot | The caller replaces a local Component per occurrence | Only the caller chooses; plugins cannot stack |
The previous approach had the Conversation classify blank clicks on its body and notify the background plugin through `onBlankClick(listener) => dispose`. The Conversation thereby maintained click classification and subscription forwarding that served only a decorative plugin.
## Decision
Add a fifth kind: `hoc`.
| Item | Contract |
|---|---|
| Declaration | The parent entry's `children` lists `{ kind: 'hoc', scope }`; `SlotMap` declares `kind: 'hoc'` |
| Rendering | `renderSlot(key, owner, { children })`; `entryKey` / `only` / `fallback` / `hookContext` are unavailable; omitted `children` renders the layers around nothing |
| Registration | Same as `list`: required `id`, optional `order` and `priority`; the same `id` at the same `priority` throws, a different `priority` shadows |
| Nesting order | Ascending `order`, then `priority`, then registration order; lowest outermost |
| Layer props | Every layer receives the same owner props plus the framework-supplied `children` (the next inner layer; the innermost is the owner content); `children` is not an owner prop, `HocShare` types it, and owner props must not use that name |
| Layer obligation | Render `children` exactly once without creating a box of its own; do not change the props passed inward |
| No entry | `children` renders unchanged |
| Layer crash | Each layer has its own error boundary; a crashed layer renders its `children` and abdicates, so the next render omits it |
| Content error | An error thrown by the wrapped content passes every layer as its original error and is reported against no layer |
| Remounting | Adding or removing a layer remounts the content inside it; a `session` or `session-maybe` layer also remounts it when its Session binding changes |
### Implementation
| Package | Realization |
|---|---|
| `ui-slots` | `SlotKind` includes `'hoc'`; `HocRenderOpts`, `HocKeysOf`, `HocShare`; `RenderSlotFn` has a hoc-key overload taking optional `{ children }`; `KindOptions` has a hoc branch; `SlotCore.register` / `entriesOfSlot` key hoc cells by `id` |
| `ui-renderer` | `HocOutlet` calls `composeHocLayers` when the slot's ledger version or declaration changes (`useMemo` on the outlet instance) and nests the layers into one render function. Each layer is one Component that fixes its scope-specific Entry (`StrictSessionEntry` / `SessionMaybeEntry` / `RootEntry`), entry, slot inject, and crash reporting at composition, and receives only `owner` and the next inner layer when rendering. With no layer the outlet returns the owner content directly. `HocContentBoundary` wraps each layer's inner content and rethrows its errors as `HocContentError`, which layer boundaries pass through and `HocOutletBoundary` unwraps to the original error. The Entry chain passes `children` to the registered Component as JSX children; non-hoc entries receive `undefined`. `SlotErrorBoundary` takes a `crashed` replacement node |
| `scripts/gen-client-catalog.ts` | Register options, a `children`-rendering example, and the `wraps-owner-content` replace risk for hoc slots |
### First consumer
| Location | Realization |
|---|---|
| `ui-conversation` | `conversation.hero.frame` (hoc, root) with owner props `{ hero: boolean }` wraps `.scrollBody` in every phase, so the composer does not remount when the home ends. On the home (`.root[data-phase='hero']`), the Conversation root, which paints the background, sets `isolation: isolate` as part of the frame contract, so layer decorations at negative `z-index` paint above that background and below all Conversation content. The Conversation performs no click classification for its layers |
| `ui-brand-official` | `PixelWhale` is a frame layer: a React `onClick` on a `display: contents` frame plays the whale for primary clicks on blank space, excluding portaled overlays (`currentTarget.contains(target)`), controls (a selector), and clicks that end a text selection inside the frame. While `hero` is true it paints a canvas at negative `z-index`. The frame atlas loads through the package asset route; the renderer returns `{ play, dispose }` |
## Alternatives considered
- `onBlankClick` subscription forwarding: the Conversation owns click classification that serves only a decorative plugin.
- Canvas below with `pointer-events: none` on `.scrollBody`: blank space cannot scroll when the home overflows.
- A canvas with no `z-index`: it needs no stacking context, but it paints over every unpositioned Conversation content outside the `z-index: 1` home composer stack, such as composer takeovers, and depends on that unrelated rule.
- A layer box that establishes the stacking context itself: the layer has to mirror the body's flex column layout.
- A separate `renderNestedSlot` or a JSX `<HocSlot>` component: one more set of bindings; `hookContext` already shows that `renderSlot` can constrain its third argument per key.
- Precompiling every slot kind per ledger version: no measurable end-to-end time difference, see #7838.
## Consequences
- Adding or removing a layer remounts everything inside it, including the composer on the home.
- Every non-hoc registered Component receives `children: undefined`, which overrides any owner prop of that name.
- Each layer adds one Component and two error boundaries to the tree; each hoc slot with layers adds one outlet boundary.
- The outlet's `[data-slot]` element sits between the owner and its content, so owner selectors cannot assume the content is a direct child.
- React 18 recollects the component stack where an error is rethrown, so a content error reaching boundaries above a hoc slot with layers carries a component stack that starts at `HocOutletBoundary`.
- A layer that wraps its `children` in its own error boundary catches the internal `HocContentError` rather than the original content error.
- The frame contract holds only on the home: other phases leave the Conversation root out of the stacking order, so unportaled Conversation popovers still rise above AppFrame chrome that floats over the column, while on the home they stack only within the root.
- `HocOutlet` keeps its layer Components through `useMemo`, as Factory local Components keep theirs; if React ever discards memoized values, both need an explicit cache, because these Components are the remount boundary of the content inside them.
- [The slot system standard](2026-07-22-slot-type-chain-implementation.md) remains authoritative for the other kinds; this note adds the fifth.
@@ -0,0 +1,72 @@
# Agent Note: 新增 `hoc` 插槽类型,让插件层层包裹宿主内容
Status: implemented
[English](2026-11-03-hoc-slot-kind.md) | 中文
## 问题
首页鲸鱼需要在空白处被点击时播放。canvas 必须画在内容下面,`.scrollBody` 铺满整个 body,所以 canvas 自己收不到点击;能收到点击的只有内容的祖先节点。
现有插槽类型都不能让插件成为内容的祖先:
| 类型 | 行为 | 为什么不够 |
|---|---|---|
| `single` / `list` / `keyed` | 在宿主指定位置渲染 occupant | occupant 和内容是兄弟节点 |
| `chain` | 选中的 entry 替换 fallback;`overlay` 时与 fallback 并排 | 不包裹 |
| Factory 局部 slot | 调用方按次替换局部组件 | 只有调用方能选,插件无法叠加 |
此前的做法是由 Conversation 在 body 上判定空白点击,再通过 `onBlankClick(listener) => dispose` 通知背景插件。这让 Conversation 维护一套只服务于装饰插件的点击分类和订阅转发。
## Decision
新增第五种 kind:`hoc`。
| 项 | 约定 |
|---|---|
| 声明 | 父 entry 的 `children` 写 `{ kind: 'hoc', scope }`;`SlotMap` 中 `kind: 'hoc'` |
| 渲染 | `renderSlot(key, owner, { children })`;`entryKey` / `only` / `fallback` / `hookContext` 不可用;省略 `children` 时各层包裹空内容 |
| 注册 | 与 `list` 相同:必填 `id`,可选 `order`、`priority`;同 `id` 同 `priority` 抛错,不同 `priority` 遮蔽 |
| 嵌套顺序 | 依次按 `order`、`priority`、注册顺序升序,最小者在最外层 |
| 层的 props | 每层收到同一份 owner props,加上框架注入的 `children`(内一层,最内层是宿主内容);`children` 不属于 owner props,类型由 `HocShare` 提供,owner props 不得使用这个名字 |
| 层的义务 | 恰好渲染一次 `children`,自身不产生盒子;不能修改传给内层的 props |
| 无 entry | 原样渲染 `children` |
| 层崩溃 | 每层有独立 error boundary;崩溃时该层渲染自己的 `children` 并 abdicate,下一次渲染不再包含该层 |
| 内容出错 | 被包裹内容抛出的错误以原始错误穿过所有层,不记到任何层名下 |
| 重新挂载 | 增减一层会让其内部内容重新挂载;`session` 或 `session-maybe` 作用域的层在 Session binding 变化时也会让内部内容重新挂载 |
### 实现
| 包 | 实现 |
|---|---|
| `ui-slots` | `SlotKind` 包含 `'hoc'`;`HocRenderOpts`、`HocKeysOf`、`HocShare`;`RenderSlotFn` 为 hoc key 提供接受可选 `{ children }` 的重载;`KindOptions` 有 hoc 分支;`SlotCore.register` / `entriesOfSlot` 按 `id` 划分 hoc cell |
| `ui-renderer` | `HocOutlet` 在插槽 ledger 版本或声明变化时调用 `composeHocLayers`(`useMemo` 缓存在 outlet 实例上),把各层组合成一个嵌套渲染函数。每层是一个组件,在组合时固定按 scope 选定的 Entry(`StrictSessionEntry` / `SessionMaybeEntry` / `RootEntry`)、entry、slot inject 与崩溃上报,渲染时只接收 `owner` 与内一层。没有层时 outlet 直接返回宿主内容。`HocContentBoundary` 包住每层的内层内容,把其中的错误包装为 `HocContentError` 重新抛出;层的边界放行这种错误,`HocOutletBoundary` 再解开为原始错误。Entry 链以 JSX children 把 `children` 交给注册组件;非 hoc entry 收到 `undefined`。`SlotErrorBoundary` 接受 `crashed` 替换节点 |
| `scripts/gen-client-catalog.ts` | hoc 插槽的注册选项、渲染 `children` 的示例,以及 `wraps-owner-content` 替换风险 |
### 首个使用者
| 位置 | 实现 |
|---|---|
| `ui-conversation` | `conversation.hero.frame`(hoc,root),owner props 为 `{ hero: boolean }`,在所有阶段包裹 `.scrollBody`,首页结束时输入框不重新挂载。首页阶段(`.root[data-phase='hero']`),绘制背景的 Conversation 根元素设置 `isolation: isolate`,这是 frame 契约的一部分:层中负 `z-index` 的装饰画在该背景之上、所有 Conversation 内容之下。Conversation 不为层做任何点击判定 |
| `ui-brand-official` | `PixelWhale` 是 frame 层:`display: contents` 外框上的 React `onClick` 在主按钮点击留白时播放,排除 portal 浮层(`currentTarget.contains(target)`)、控件(选择器)和结束外框内文字选择的点击。`hero` 为 true 时以负 `z-index` 绘制 canvas。帧图经包 asset 路由加载;渲染器返回 `{ play, dispose }` |
## Alternatives considered
- `onBlankClick` 订阅转发:Conversation 承担只为装饰插件服务的点击分类。
- canvas 放底下、`.scrollBody` 设 `pointer-events: none`:首页溢出时空白处无法滚动。
- 不设 `z-index` 的 canvas:不需要层叠上下文,但会画在 `z-index: 1` 的首页输入区以外的所有非定位 Conversation 内容之上(例如输入区接管面板),并依赖那条无关的规则。
- 由层自己的盒子建立层叠上下文:层必须复刻 body 的 flex 列布局。
- 单独的 `renderNestedSlot` / JSX 组件 `<HocSlot>`:多一套绑定;`hookContext` 已证明可以按 key 约束 `renderSlot` 的第三个参数。
- 按 ledger 版本预编译所有插槽类型:端到端测不出耗时差异,见 #7838。
## Consequences
- 增减一层会让其内部所有内容重新挂载,包括首页的输入框。
- 所有非 hoc 的注册组件都会收到 `children: undefined`,会覆盖同名的 owner prop。
- 每层在组件树中多一个组件和两个 error boundary;有层的 hoc 插槽另多一个 outlet 边界。
- outlet 的 `[data-slot]` 元素位于 owner 与内容之间,owner 侧选择器不能假定内容是直接子元素。
- React 18 会在错误被重新抛出的位置重新采集组件栈,因此有层的 hoc 插槽之上的边界收到的内容错误,组件栈从 `HocOutletBoundary` 开始。
- 层如果自己在 `children` 外包错误边界,捕获到的是内部的 `HocContentError`,而不是原始的内容错误。
- frame 契约只在首页阶段成立:其他阶段根元素不参与层叠隔离,未经 portal 的 Conversation 浮层仍能盖住悬浮在中栏上方的 AppFrame 元素;首页阶段它们只在根元素内部比较层级。
- `HocOutlet` 与 Factory 局部组件一样用 `useMemo` 保持层组件身份;如果 React 将来会丢弃 memo 缓存,两处都需要改成显式缓存,因为这些组件是其内部内容的重新挂载边界。
- 其他 kind 仍以 [slot 体系标准](2026-07-22-slot-type-chain-implementation.zh.md) 为准;本 Note 新增第五种。
@@ -0,0 +1,19 @@
# Bilingual-pair consistency record for 2026-11-04-unprefixed-experimental-package-names.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-11-04-unprefixed-experimental-package-names.md
/agent-note-name-new-experimental-packages-without-the-experimental-prefix:
en: b84bbfa015f35105
zh: dff2c32694314996
/agent-note-name-new-experimental-packages-without-the-experimental-prefix/problem:
en: 84638e34c7b1d291
zh: 0c3928b3424db016
/agent-note-name-new-experimental-packages-without-the-experimental-prefix/decision:
en: 0f13619da3b05747
zh: fac563c2a8b61270
/agent-note-name-new-experimental-packages-without-the-experimental-prefix/alternatives-considered:
en: 1c6a08a813dcb3a7
zh: 16bf31184377c80b
/agent-note-name-new-experimental-packages-without-the-experimental-prefix/consequences:
en: 2b536c42672e8850
zh: f6a551964855308e
@@ -0,0 +1,29 @@
# Agent Note: Name new experimental packages without the experimental prefix
Status: implemented
English | [中文](2026-11-04-unprefixed-experimental-package-names.zh.md)
## Problem
An experimental package named `@deepseek-ai/dsh-experimental-<name>` must be renamed when it moves to a product group. The rename changes every import, dependency entry, module declaration, and profile file that names the package, so promotion carries a migration that is unrelated to the capability's behavior.
## Decision
New experimental packages use `@deepseek-ai/dsh-<name>` and never the `experimental` prefix. Each one is declared in `EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS` in the [experimental package policy](../../../../scripts/experimental-package-policy.ts), because isolation checks and plugin-record ownership recognize an unprefixed experimental name only through that map. Promotion moves the package and removes its map entry; its npm name stays the same.
Existing packages with the prefix are listed in `LEGACY_PREFIXED_EXPERIMENTAL_PACKAGE_DIRECTORIES`. `pnpm run constraints` rejects the prefix in any other directory and rejects a listed directory that no longer holds a prefixed package, so the list can only shrink. A legacy package is renamed when it is promoted; renaming it earlier needs the release and migration evidence that the [plugin identity decision](2026-10-06-web-desktop-plugin-identity-compatibility.md) requires.
This decision replaces the deferred cleanup that the plugin identity decision recorded, which gave every experimental package the prefix. The [package rules](../../../../packages/experimental/AGENTS.md) state the naming rule for authors.
## Alternatives considered
**Keep the prefix as the default.** The name shows experimental status, but each promotion then renames the package and migrates its consumers and installed profiles.
**Rename the legacy packages now.** One rename removes the legacy list, but it changes published names that existing dependencies and profiles use, without a promotion that needs the change.
**Document the rule without a gate.** A new package copied from a legacy one would keep the prefix with every check passing.
## Consequences
The unprefixed-name map grows with every new experimental package; the legacy list only shrinks. Directory placement and the map, not the npm name, identify an unprefixed package as experimental, and an unprefixed name does not grant admission to the default product.
@@ -0,0 +1,29 @@
# Agent Note: 新实验包命名不使用 experimental 前缀
Status: implemented
[English](2026-11-04-unprefixed-experimental-package-names.md) | 中文
## 问题
名为 `@deepseek-ai/dsh-experimental-<name>` 的实验包迁入产品分组时必须重命名。重命名会改变引用该包名的每个导入、依赖项、模块声明和 profile 文件,因此晋升会附带一次与能力行为无关的迁移。
## 决策
新实验包使用 `@deepseek-ai/dsh-<name>`,不使用 `experimental` 前缀。每个新实验包都在[实验包命名策略](../../../../scripts/experimental-package-policy.ts)的 `EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS` 中声明,因为隔离检查和插件记录归属只通过这个映射识别不带前缀的实验包名。晋升时移动该包并删除其映射条目;其 npm 名称保持不变。
带前缀的既有包列在 `LEGACY_PREFIXED_EXPERIMENTAL_PACKAGE_DIRECTORIES` 中。`pnpm run constraints` 拒绝其他目录中出现该前缀,也拒绝已列出但不再包含带前缀包的目录,因此该清单只能缩减。遗留包在晋升时重命名;提前重命名需要[插件身份决策](2026-10-06-web-desktop-plugin-identity-compatibility.zh.md)要求的发布与迁移依据。
本决策取代插件身份决策记录的暂缓清理,该清理为每个实验包加上前缀。[包规则](../../../../packages/experimental/AGENTS.md)向作者说明命名规则。
## 考虑过的替代方案
**默认保留前缀。** 名称能体现实验状态,但每次晋升都要重命名该包,并迁移其消费方和已安装 profile。
**现在重命名遗留包。** 一次重命名即可删除遗留清单,但会在没有晋升需要的情况下改变既有依赖和 profile 使用的已发布名称。
**只记录规则,不设门禁。** 从遗留包复制的新包会保留前缀,而所有检查仍会通过。
## 后果
不带前缀包名映射随每个新实验包增长;遗留清单只会缩减。识别不带前缀的包是否为实验包依据的是目录位置和该映射,而不是 npm 名称;不带前缀的名称也不因此获准进入默认产品。
@@ -12,8 +12,8 @@
en: 47b44ed9313c2ccf
zh: 7ad254f8ef88351d
/agent-note-subagent-end-turn-guidance-stays-in-the-delegation-paragraph/alternatives-considered:
en: d52fa1af379dea5b
zh: 4e404994f7a607dc
en: 3153c1e7563a3357
zh: 9841db2b0156075b
/agent-note-subagent-end-turn-guidance-stays-in-the-delegation-paragraph/verification:
en: be076216381ea64d
zh: 2da8d9819c20e2fe
@@ -29,7 +29,7 @@ The sentence stays one literal line. Rewording `the next result starts a new tur
**Move the guidance into the tool description.** The paragraph is assembled from the set of visible delegation tools and lists their names; the tool description describes one tool. Moving the sentence changes ownership without changing the behavior it prevents.
**Restore every prompt phrase reverted together with this sentence.** The read guidance and the `run_code` argument order are separate decisions with their own evidence; this note records only the delegation paragraph.
**Restore every prompt phrase reverted together with this sentence.** The read guidance ([2026-10-26 note](2026-10-26-read-guidance-line-ranges.md)) and the `run_code` argument order are separate decisions with their own evidence; this note records only the delegation paragraph.
## Verification
@@ -29,7 +29,7 @@ Status: implemented
**把说明移进工具描述。** 这段指导按可见委派工具集合组装并列出工具名,工具描述只描述单个工具。移动这句会改变归属,但不改变它阻止的行为。
**把与这句一起被回退的所有提示词都恢复。** read 指导与 `run_code` 参数顺序是各自有证据的独立决定;本记录只覆盖委派段。
**把与这句一起被回退的所有提示词都恢复。** read 指导([2026-10-26 note](2026-10-26-read-guidance-line-ranges.zh.md))与 `run_code` 参数顺序是各自有证据的独立决定;本记录只覆盖委派段。
## 验证
@@ -0,0 +1,22 @@
# Bilingual-pair consistency record for 2026-10-26-read-guidance-line-ranges.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/bug-fix/2026-10-26-read-guidance-line-ranges.md
/agent-note-read-guidance-names-shell-readers-and-line-ranges:
en: 1926710614598539
zh: b0106d4a6a2d648d
/agent-note-read-guidance-names-shell-readers-and-line-ranges/problem:
en: 4189d516419d8d12
zh: 30d8496064dd44e3
/agent-note-read-guidance-names-shell-readers-and-line-ranges/decision:
en: 34f18307f7ad87af
zh: 21c466c6ce6c598d
/agent-note-read-guidance-names-shell-readers-and-line-ranges/alternatives-considered:
en: aea7ffb1159ab81a
zh: 4842ca79dc30654d
/agent-note-read-guidance-names-shell-readers-and-line-ranges/verification:
en: cbc16bfba4abe862
zh: 9a71e6c020c01f69
/agent-note-read-guidance-names-shell-readers-and-line-ranges/consequences:
en: cf55161ea197a7a9
zh: fa10ca413f8fd726
@@ -0,0 +1,35 @@
# Agent Note: Read guidance names shell readers and line ranges
Status: implemented
English | [中文](2026-10-26-read-guidance-line-ranges.zh.md)
## Problem
The read guidance named only `cat` as the shell command the read tool replaces, and described `offset`/`limit` as continuing a large file rather than reading a line range. #6057 recorded a long Session reading ranges with `sed -n X,Yp` 60 times. #6054 added `sed`, `head`, `tail`, and the line-range wording; a later prompt revert removed them together with unrelated changes.
The 2026-10-23 evaluation of the SDK profile (native tools, six benchmarks) measured this sentence as the only change: Pass@1 mean −0.03pp and Pass@3 mean +0.70pp over the five benchmarks that report Pass@3 (ProgramBench-New has none) versus the base commit, with mixed step and token deltas (ProgramBench-New −15.5 steps, Terminal-Bench-v4 +13.5 steps). The PTC mode runs did not include this variant; the section renders in every request where `read` is visible.
## Decision
Restore the #6054 wording, byte-identical: `Use the read tool — not shell commands like cat, sed, head, or tail — to inspect text files. Use offset and limit to read a line range or continue a large file.` The read tool and its guidance are owned by `@deepseek-ai/dsh-tool-fs`; write and edit guidance are unchanged.
## Alternatives considered
**Rely on the read tool schema.** The schema already documents `offset` and `limit`, but #6057 shows the model chose `sed` while that schema existed; the guidance is the only model-visible place that names `sed`, `head`, and `tail`.
**Restore every prompt phrase reverted together with this sentence.** The subagent end-turn sentence, the `run_code` argument order, the PTC syntax ownership, and the SDK persona are separate decisions; the first is already restored ([subagent end-turn note](2026-10-21-subagent-end-turn-guidance.md)), and the others carry their own evidence.
**Say "line range" without naming shell commands.** The observed failure was choosing shell commands, so the sentence names them.
## Verification
- `packages/fs/tool-fs/tests/tools.spec.ts` asserts the rendered guidance text.
- Every recorded `system-prompt*.expected.md` sidecar that renders the read guidance carries the restored sentence: `test:snapshot` replays the session and SDK set, and `pnpm run test:web:ci` replays the `snapshots/web` set (the `node 24 / snapshots and artifacts` lane runs both). Two pinned historical sidecars (`web/cordis-tool-round`, `web/schedule-catalog`) retain their recorded text.
- The evaluation above measured this single change on the native SDK profile.
## Consequences
- Every request with the read tool carries three more command names and a line-range clause, about 10 tokens.
- The model is directed to the read tool for line ranges instead of `cat`, `sed`, `head`, or `tail`.
- The benchmark measured this sentence as neutral; keeping it rests on the #6057 production observation, not on a measured quality gain.
@@ -0,0 +1,35 @@
# Agent Note: read 指导点名 shell 读取命令与行区间
Status: implemented
[English](2026-10-26-read-guidance-line-ranges.md) | 中文
## 问题
read 指导只把 `cat` 点名为 read 工具替代的 shell 命令,并把 `offset`/`limit` 描述成继续读大文件,而不是读行区间。#6057 记录了一个长 Session 用 `sed -n X,Yp` 读行区间 60 次。#6054 增加了 `sed`、`head`、`tail` 和行区间措辞;后来的一次提示词回退把它们和不相关的改动一起删掉了。
2026-10-23 对 SDK profile(native 工具、六个 benchmark)的评测里,这句是唯一改动:相对 base commit,Pass@1 均值 −0.03pp、Pass@3 均值 +0.70pp(该均值取报告 Pass@3 的五个 benchmark;ProgramBench-New 没有 Pass@3),步数与 token 变化方向不一(ProgramBench-New −15.5 步、Terminal-Bench-v4 +13.5 步)。PTC 模式的运行没有包含这个变体;只要 `read` 可见,这段就会进入每个请求。
## 决策
逐字恢复 #6054 的措辞:`Use the read tool — not shell commands like cat, sed, head, or tail — to inspect text files. Use offset and limit to read a line range or continue a large file.` read 工具及其指导归 `@deepseek-ai/dsh-tool-fs` 所有;write 与 edit 的指导不变。
## 考虑过的替代方案
**只依赖 read 工具 schema。** schema 已经写明 `offset` 与 `limit`,但 #6057 显示在那个 schema 存在时模型仍选择 `sed`;指导是唯一点名 `sed`、`head`、`tail` 的模型可见位置。
**把与这句一起被回退的所有提示词都恢复。** subagent 结束本轮句、`run_code` 参数顺序、PTC 语法归属、SDK persona 是各自独立的决定;第一项已经恢复([subagent 结束本轮 note](2026-10-21-subagent-end-turn-guidance.zh.md)),其余各有自己的证据。
**只说“行区间”,不点名 shell 命令。** 观测到的失败就是模型选择 shell 命令,所以这句点名它们。
## 验证
- `packages/fs/tool-fs/tests/tools.spec.ts` 断言渲染出的指导文本。
- 所有渲染 read 指导的 `system-prompt*.expected.md` sidecar 都带有恢复后的句子:`test:snapshot` 回放 session 与 SDK 集合,`pnpm run test:web:ci` 回放 `snapshots/web` 集合(`node 24 / snapshots and artifacts` lane 两条都跑)。两个显式 pin 的历史 sidecar(`web/cordis-tool-round`、`web/schedule-catalog`)保留录制文本。
- 上面的评测在 native SDK profile 上只改了这一句进行测量。
## 影响
- 只要 read 工具存在,每个请求多三个命令名和一句行区间说明,约 10 token。
- 模型读行区间时被引导到 read 工具,而不是 `cat`、`sed`、`head`、`tail`。
- benchmark 上这句测得为中性;保留它依据的是 #6057 的生产观测,而不是测到的质量收益。
Loaded 100 of 2559 files, more files were not shown because too many files have changed in this diff. Show more