Quellcode durchsuchen

Merge Computer Use parent into Auto review for PR stack

Tianyi Cui vor 6 Tagen
Ursprung
Commit
66734e344a
100 geänderte Dateien mit 1318 neuen und 497 gelöschten Zeilen
  1. 2 2
      .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.i18n.yaml
  2. 2 2
      .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md
  3. 2 2
      .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md
  4. 2 2
      .agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.i18n.yaml
  5. 4 2
      .agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.md
  6. 4 2
      .agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.zh.md
  7. 3 3
      .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.i18n.yaml
  8. 6 6
      .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.md
  9. 6 6
      .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.zh.md
  10. 2 2
      .agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.i18n.yaml
  11. 2 2
      .agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md
  12. 2 2
      .agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md
  13. 2 2
      .agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.i18n.yaml
  14. 1 1
      .agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.md
  15. 1 1
      .agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.zh.md
  16. 2 2
      .agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.i18n.yaml
  17. 20 20
      .agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.md
  18. 20 20
      .agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.zh.md
  19. 2 2
      .agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.i18n.yaml
  20. 2 2
      .agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.md
  21. 2 2
      .agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.zh.md
  22. 6 0
      .agents/notes/implemented/architecture/2026-09-11-posix-ssh-runtime.i18n.yaml
  23. 57 0
      .agents/notes/implemented/architecture/2026-09-11-posix-ssh-runtime.md
  24. 57 0
      .agents/notes/implemented/architecture/2026-09-11-posix-ssh-runtime.zh.md
  25. 6 0
      .agents/notes/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.i18n.yaml
  26. 56 0
      .agents/notes/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.md
  27. 56 0
      .agents/notes/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.zh.md
  28. 6 0
      .agents/notes/implemented/architecture/2026-09-11-subprocess-control-pipe.i18n.yaml
  29. 37 0
      .agents/notes/implemented/architecture/2026-09-11-subprocess-control-pipe.md
  30. 37 0
      .agents/notes/implemented/architecture/2026-09-11-subprocess-control-pipe.zh.md
  31. 6 0
      .agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.i18n.yaml
  32. 35 0
      .agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.md
  33. 35 0
      .agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.zh.md
  34. 6 0
      .agents/notes/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.i18n.yaml
  35. 29 0
      .agents/notes/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.md
  36. 29 0
      .agents/notes/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.zh.md
  37. 2 2
      .agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.i18n.yaml
  38. 2 2
      .agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.md
  39. 2 2
      .agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.zh.md
  40. 2 2
      .agents/notes/implemented/feature/2026-06-15-ptc.i18n.yaml
  41. 27 30
      .agents/notes/implemented/feature/2026-06-15-ptc.md
  42. 27 30
      .agents/notes/implemented/feature/2026-06-15-ptc.zh.md
  43. 2 2
      .agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
  44. 10 4
      .agents/notes/implemented/feature/2026-07-06-sandbox.md
  45. 11 5
      .agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
  46. 2 2
      .agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.i18n.yaml
  47. 1 1
      .agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md
  48. 1 1
      .agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.zh.md
  49. 2 2
      .agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.i18n.yaml
  50. 2 2
      .agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.md
  51. 2 2
      .agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.zh.md
  52. 2 2
      .agents/notes/implemented/feature/2026-08-28-auto-review.i18n.yaml
  53. 2 2
      .agents/notes/implemented/feature/2026-08-28-auto-review.md
  54. 2 2
      .agents/notes/implemented/feature/2026-08-28-auto-review.zh.md
  55. 2 2
      .agents/notes/implemented/process/2026-06-11-quality-gates.i18n.yaml
  56. 1 1
      .agents/notes/implemented/process/2026-06-11-quality-gates.md
  57. 1 1
      .agents/notes/implemented/process/2026-06-11-quality-gates.zh.md
  58. 2 2
      .agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.i18n.yaml
  59. 1 1
      .agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.md
  60. 1 1
      .agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.zh.md
  61. 2 2
      .agents/notes/implemented/testing/2026-09-08-ci-completion-observations.i18n.yaml
  62. 2 2
      .agents/notes/implemented/testing/2026-09-08-ci-completion-observations.md
  63. 2 2
      .agents/notes/implemented/testing/2026-09-08-ci-completion-observations.zh.md
  64. 2 2
      .agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.i18n.yaml
  65. 1 1
      .agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.md
  66. 1 1
      .agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.zh.md
  67. 2 2
      .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.i18n.yaml
  68. 2 2
      .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md
  69. 2 2
      .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.zh.md
  70. 6 0
      .agents/notes/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.i18n.yaml
  71. 27 0
      .agents/notes/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.md
  72. 27 0
      .agents/notes/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.zh.md
  73. 2 2
      .agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.i18n.yaml
  74. 1 1
      .agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.md
  75. 1 1
      .agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.zh.md
  76. 2 2
      .agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.i18n.yaml
  77. 3 3
      .agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.md
  78. 3 3
      .agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.zh.md
  79. 2 2
      .agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.i18n.yaml
  80. 1 1
      .agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.md
  81. 1 1
      .agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md
  82. 2 1
      AGENTS.md
  83. 1 0
      THIRD_PARTY_NOTICES.md
  84. 1 1
      apps/cli/package.json
  85. 20 6
      apps/cli/tests/profiles/headless/tests/compaction.e2e.ts
  86. 26 13
      apps/cli/tests/profiles/headless/tests/ptc.e2e.ts
  87. 8 8
      apps/web/tests/lifecycle-chrome.e2e.ts
  88. 95 0
      apps/web/tests/ptc-escalation.e2e.ts
  89. 1 1
      apps/web/tests/scaffold.ts
  90. 1 0
      apps/web/tsconfig.json
  91. 2 2
      docs/capability-seams.i18n.yaml
  92. 35 12
      docs/capability-seams.md
  93. 35 12
      docs/capability-seams.zh.md
  94. 2 2
      docs/config-catalog.i18n.yaml
  95. 166 105
      docs/config-catalog.md
  96. 169 108
      docs/config-catalog.zh.md
  97. 2 2
      docs/cookbook/adding-a-tool.i18n.yaml
  98. 1 1
      docs/cookbook/adding-a-tool.md
  99. 1 1
      docs/cookbook/adding-a-tool.zh.md
  100. 2 2
      docs/event-producer-consumer.i18n.yaml

+ 2 - 2
.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md
-2026-07-10-single-file-executable-sdk-runtime-distribution.md: e8c38a7b96cbff9feae2271fcd9a5c41087c62c4
-2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md: 52ca2e13901d9469e2f9663936acb766a934e8f7
+2026-07-10-single-file-executable-sdk-runtime-distribution.md: 7a3d9e2fd5a7f61decdd91da687e652d6be1e6f1
+2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md: 04d705925788ada2f7e59d85614c75d594d0ba5a

Datei-Diff unterdrückt, da er zu groß ist
+ 2 - 2
.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md


Datei-Diff unterdrückt, da er zu groß ist
+ 2 - 2
.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md


+ 2 - 2
.agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.md
-2026-07-28-portable-execution-world-consumers.md: 3375a38d41724aee9b3f7591b014d6eea6979857
-2026-07-28-portable-execution-world-consumers.zh.md: 62d7338d237358b05c0c52f97720312474b74717
+2026-07-28-portable-execution-world-consumers.md: 5a687a552f322f3929b3f3937caf36c3373c05e7
+2026-07-28-portable-execution-world-consumers.zh.md: 6bc0e20bd29d0b0a01068c004655cc90ac3b6699

+ 4 - 2
.agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.md

@@ -16,6 +16,8 @@ Ordinary pipes do not cover one requirement. A persistent terminal needs PTY all
 
 `ctx.fs` and `ctx.subprocess` together define one execution world. Providers mounted together must describe the same path namespace, executables, processes, and terminal sessions; higher capabilities consume those two interfaces rather than name the provider.
 
+Foreground Bash and PowerShell calls use one executor deadline for asynchronous confinement preparation and process execution. Preparation expiry returns an outcome without process-exit or signal facts, and late argv cannot start a process; background preparation follows caller cancellation only. The protected execution result tells subclasses whether the subprocess provider was called, so enforcement facts are attached only after publication.
+
 The filesystem interface owns the path facts that another capability needs without exposing its opaque target identity: a canonical process path, canonical `file:` URI, and containment. Existing whole and streaming text operations remain filesystem-owned; protocol consumers enforce their own retention limits while consuming the stream.
 
 The subprocess interface owns executable lookup and process primitives: ordinary raw or collected process spawning and `spawnTerminal()`. An ordinary handle keeps target identity private: `.done` reports the direct target, while `terminate()` and `waitForExit()` control and observe the same provider-managed range. The [native-containment decision](2026-08-28-subprocess-native-containment.md) owns local Linux scopes, Windows Jobs, and their disclosed fallbacks. The terminal operation is one deep primitive whose handle owns text I/O, foreground groups, signalling, and one awaited TERM-to-KILL operation that settles in-flight handle calls and reaches quiescence for every member of its provider-owned range; an observational fallback limits that range to identities it can still observe. Its signal cancels allocation only; the published handle owns its lifetime. Prompt detection, idle inference, scrollback, sandbox policy, and owner lifecycle remain in the PTY consumer.
@@ -28,7 +30,7 @@ Generic consumers use that execution world:
 
 ## Remote provider ownership
 
-The [E2B provider removal](../simplification/2026-09-11-remove-e2b-providers.md) supersedes the E2B realization of this decision. The filesystem/subprocess agreement and asynchronous terminal contracts remain in force for remote implementations.
+The [E2B provider removal](../simplification/2026-09-11-remove-e2b-providers.md) supersedes the E2B realization of this decision. The filesystem/subprocess agreement and asynchronous terminal contracts remain in force for remote implementations. The [POSIX SSH providers](2026-09-11-posix-ssh-runtime.md) realize them through one installed helper and independent stream channels.
 
 A remote provider owns mutable files, command and terminal processes, language-server processes, and provider-private runtime files. The host owns Cordis and plugin objects, the agent loop, agent/session/goal state, session logs and persistence, model transport, authority, skills, subagent orchestration, terminal readiness and LSP protocol state. Moving execution does not imply workspace synchronization or durable remote handles.
 
@@ -60,7 +62,7 @@ The local filesystem, subprocess, terminal and LSP suites cover path identity, e
 
 ## Consequences
 
-A remote execution provider implements only its shared sandbox owner plus filesystem and subprocess adapters. Bash, PTY, and LSP compose above them, so fixes to those capabilities remain provider-neutral.
+A remote execution family supplies its shared connection owner and matching filesystem, subprocess and, for confined calls, sandbox providers. Bash, PTY, and LSP compose above them, so fixes to those capabilities remain provider-neutral.
 
 The fundamental interfaces are wider, and a filesystem/subprocess pair must agree on one execution world. The added operations are limited to facts and lifecycle mechanics that current generic consumers require; model schemas, protocol framing, readiness policy, and presentation do not leak into the providers.
 

+ 4 - 2
.agents/notes/implemented/architecture/2026-07-28-portable-execution-world-consumers.zh.md

@@ -16,6 +16,8 @@ Status: implemented
 
 `ctx.fs` 与 `ctx.subprocess` 共同定义一个执行世界。共同挂载的提供方必须描述相同的路径命名空间、可执行文件、进程和终端会话;上层能力消费这两个接口,而不引用具体提供方。
 
+前台 Bash 和 PowerShell 调用由同一执行器 deadline 覆盖异步 confinement 准备与进程执行。准备超时返回没有进程退出或信号事实的结果,晚到的 argv 不能启动进程;后台准备仍只跟随调用方取消。子类通过受保护的执行结果区分是否已调用 subprocess provider,只有发布后才附加 enforcement 事实。
+
 文件系统接口负责其他能力需要的路径事实,同时不公开其不透明目标身份:规范化进程路径、规范化 `file:` URI 和包含关系。现有完整文本与流式文本操作仍归文件系统负责;协议消费方在消费流时执行各自的保留上限。
 
 进程管理接口负责可执行文件查找与进程原语:以原始或收集模式 spawn 普通进程,以及 `spawnTerminal()`。普通句柄把 target identity 保持为私有事实:`.done` 报告 direct target,`terminate()` 与 `waitForExit()` 则控制并观察同一个由提供方管理的范围。[原生 containment 决策](2026-08-28-subprocess-native-containment.zh.md)负责本地 Linux scope、Windows Job 及其已声明的 fallback。终端操作是一项深层原语,其句柄负责文本 I/O、前台进程组、信号发送,以及一项须等待的 TERM→KILL 操作;该操作会结算所有在途句柄调用,并使提供方拥有的范围中每个成员完全停稳;观察型 fallback 只能把该范围限制为它仍可观察到的 identity。其信号只取消分配;句柄一经发布,便负责自身生命周期。提示符检测、空闲推断、scrollback、沙箱策略和所有者生命周期仍由 PTY 消费方负责。
@@ -28,7 +30,7 @@ Status: implemented
 
 ## 远程提供方的职责
 
-[E2B 提供方移除决策](../simplification/2026-09-11-remove-e2b-providers.zh.md)取代本决策中的 E2B 实现。文件系统/子进程约定与异步终端约定继续适用于远程实现。
+[E2B 提供方移除决策](../simplification/2026-09-11-remove-e2b-providers.zh.md)取代本决策中的 E2B 实现。文件系统/子进程约定与异步终端约定继续适用于远程实现。[POSIX SSH 提供方](2026-09-11-posix-ssh-runtime.zh.md)通过一个已安装辅助程序与独立流通道实现这些约定。
 
 远程提供方负责可变文件、命令与终端进程、语言服务器进程,以及提供方私有运行时文件。宿主负责 Cordis 与插件对象、智能体循环、智能体/会话/目标状态、会话日志与持久化、模型传输、权限、技能、子智能体编排、终端就绪判断和 LSP 协议状态。移动执行位置不意味着工作区同步或持久远程句柄。
 
@@ -60,7 +62,7 @@ Status: implemented
 
 ## 后果
 
-远程执行提供方只需实现共享沙箱所有者,以及文件系统与进程管理适配器。Bash、PTY 与 LSP 组合在这些适配器之上,因此对这些能力的修复仍与提供方无关。
+远端执行家族提供共享连接管理器及配套文件系统、子进程提供方;受限调用还需要沙箱提供方。Bash、PTY 与 LSP 组合在这些适配器之上,因此对这些能力的修复仍与提供方无关。
 
 基础接口更宽,一对文件系统/进程管理提供方必须在同一个执行世界上保持一致。新增操作仅限当前通用消费方所需的事实与生命周期机制;模型 schema、协议分帧、就绪策略和呈现不会渗入提供方。
 

+ 3 - 3
.agents/notes/implemented/architecture/2026-07-31-code-runtime-python-fd3-protocol.i18n.yaml → .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.i18n.yaml

@@ -1,6 +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/implemented/architecture/2026-07-31-code-runtime-python-fd3-protocol.md
-2026-07-31-code-runtime-python-fd3-protocol.md: 7f928b9cc996b545538e86a67399d5441509471a
-2026-07-31-code-runtime-python-fd3-protocol.zh.md: 568b6ff3d7025ba55080e9677375c4b88303f621
+#   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.md
+2026-07-31-ptc-runtime-python-fd3-protocol.md: 18ce7b44b116eee3a11528a5b515c4e02e955ee6
+2026-07-31-ptc-runtime-python-fd3-protocol.zh.md: d5eea4cfd80f10dc1d99c7c43090ca6b0ff6e19e

+ 6 - 6
.agents/notes/implemented/architecture/2026-07-31-code-runtime-python-fd3-protocol.md → .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.md

@@ -1,16 +1,16 @@
-# Agent Note: the code-runtime-python fd-3 frame protocol
+# Agent Note: the ptc-runtime-python fd-3 frame protocol
 
 Status: implemented
 
-The CPython code runtime now lives at `packages/experimental/code-runtime-python` (private, npm name `@deepseek-ai/dsh-experimental-code-runtime-python`); promotion to a released package follows the experimental-packages decision.
+The CPython PTC runtime now lives at `packages/experimental/ptc-runtime-python` (private, npm name `@deepseek-ai/dsh-experimental-ptc-runtime-python`); promotion to a released package follows the experimental-packages decision.
 
-English | [中文](2026-07-31-code-runtime-python-fd3-protocol.zh.md)
+English | [中文](2026-07-31-ptc-runtime-python-fd3-protocol.zh.md)
 
 ## Problem
 
-`@deepseek-ai/dsh-experimental-code-runtime-python` owns the wire protocol intended for a CPython code-runtime provider. Such a provider runs each model program in a fresh `python3 -I` subprocess and bridges binding calls and completion values over the child's fd 3. The host cannot trust that channel: model code has full access to fd 3 and can forge any frame, so every inbound frame is hostile input that the host must validate and rebuild before reading. The protocol also has to carry lossless JSON without the depth limit `JSON.stringify` and `json.dumps` impose, because the seam's `CodeJsonValue` is depth-unbounded.
+`@deepseek-ai/dsh-experimental-ptc-runtime-python` owns the wire protocol intended for a CPython ptc-runtime provider. Such a provider runs each model program in a fresh `python3 -I` subprocess and bridges binding calls and completion values over the child's fd 3. The host cannot trust that channel: model code has full access to fd 3 and can forge any frame, so every inbound frame is hostile input that the host must validate and rebuild before reading. The protocol also has to carry lossless JSON without the depth limit `JSON.stringify` and `json.dumps` impose, because the seam's `PtcJsonValue` is depth-unbounded.
 
-The private experimental package contains both the protocol and runtime implementation: `PythonCodeRuntime` (the plugin's default export), the `python3 -I` subprocess path, and the Python-side JSON codec all live in `@deepseek-ai/dsh-experimental-code-runtime-python`. The protocol builds on the [portable identifier seam](../../archived/architecture/2026-07-31-code-runtime-portable-identifier-seam.md).
+The private experimental package contains both the protocol and runtime implementation: `PythonPtcRuntime` (the plugin's default export), the `python3 -I` subprocess path, and the Python-side JSON codec all live in `@deepseek-ai/dsh-experimental-ptc-runtime-python`. The protocol builds on the [portable identifier seam](../../archived/architecture/2026-07-31-code-runtime-portable-identifier-seam.md).
 
 ## Decision
 
@@ -42,4 +42,4 @@ Frames are JSON-lines on fd 3, one object per line, leaving stdout/stderr free f
 
 Bought: the fd-3 protocol and its hostile-input codec form a self-contained, fully unit-covered layer, with an executing guard against TypeScript/Python field-set drift. The runtime built on it (`bootstrap.py`) consumes the reviewed wire contract.
 
-Cost: the package name denotes a Python runtime family and `src/index.ts` exports the full `PythonCodeRuntime` implementation, so the protocol vocabulary is only one part of the package surface. The mirror e2e compares field names and required/optional status across the two sides but not field types; comparing type declarations across TypeScript and Python has no mechanical equivalent, so review and the runtime's real-subprocess suite retain that responsibility.
+Cost: the package name denotes a Python runtime family and `src/index.ts` exports the full `PythonPtcRuntime` implementation, so the protocol vocabulary is only one part of the package surface. The mirror e2e compares field names and required/optional status across the two sides but not field types; comparing type declarations across TypeScript and Python has no mechanical equivalent, so review and the runtime's real-subprocess suite retain that responsibility.

+ 6 - 6
.agents/notes/implemented/architecture/2026-07-31-code-runtime-python-fd3-protocol.zh.md → .agents/notes/implemented/architecture/2026-07-31-ptc-runtime-python-fd3-protocol.zh.md

@@ -1,16 +1,16 @@
-# Agent Note: the code-runtime-python fd-3 frame protocol
+# Agent Note: the ptc-runtime-python fd-3 frame protocol
 
 Status: implemented
 
-CPython 代码运行时现在位于 `packages/experimental/code-runtime-python`(私有,npm 名 `@deepseek-ai/dsh-experimental-code-runtime-python`);提升为发布包遵循 experimental-packages 决策。
+CPython PTC 运行时现在位于 `packages/experimental/ptc-runtime-python`(私有,npm 名 `@deepseek-ai/dsh-experimental-ptc-runtime-python`);提升为发布包遵循 experimental-packages 决策。
 
-[English](2026-07-31-code-runtime-python-fd3-protocol.md) | 中文
+[English](2026-07-31-ptc-runtime-python-fd3-protocol.md) | 中文
 
 ## Problem
 
-`@deepseek-ai/dsh-experimental-code-runtime-python` 负责供 CPython code-runtime 提供方使用的 wire protocol。这样的提供方会在全新的 `python3 -I` 子进程中运行每个模型程序,并通过子进程 fd 3 桥接 binding 调用与完成值。Host 不能信任这条通道:模型代码可以完全访问 fd 3 并伪造任意帧,因此 host 必须把每个入站帧视为敌意输入,先校验并重建后才能读取。协议还必须承载无深度限制的 lossless JSON,因为 seam 的 `CodeJsonValue` 深度无界,而 `JSON.stringify` 和 `json.dumps` 都有递归深度限制。
+`@deepseek-ai/dsh-experimental-ptc-runtime-python` 负责供 CPython ptc-runtime 提供方使用的 wire protocol。这样的提供方会在全新的 `python3 -I` 子进程中运行每个模型程序,并通过子进程 fd 3 桥接 binding 调用与完成值。Host 不能信任这条通道:模型代码可以完全访问 fd 3 并伪造任意帧,因此 host 必须把每个入站帧视为敌意输入,先校验并重建后才能读取。协议还必须承载无深度限制的 lossless JSON,因为 seam 的 `PtcJsonValue` 深度无界,而 `JSON.stringify` 和 `json.dumps` 都有递归深度限制。
 
-这个私有实验包同时包含协议与 runtime 实现:`PythonCodeRuntime`(插件的默认导出)、`python3 -I` 子进程路径与 Python 侧 JSON codec 都在 `@deepseek-ai/dsh-experimental-code-runtime-python` 中。协议建立在[可移植标识符 seam](../../archived/architecture/2026-07-31-code-runtime-portable-identifier-seam.md)之上。
+这个私有实验包同时包含协议与 runtime 实现:`PythonPtcRuntime`(插件的默认导出)、`python3 -I` 子进程路径与 Python 侧 JSON codec 都在 `@deepseek-ai/dsh-experimental-ptc-runtime-python` 中。协议建立在[可移植标识符 seam](../../archived/architecture/2026-07-31-code-runtime-portable-identifier-seam.md)之上。
 
 ## Decision
 
@@ -42,4 +42,4 @@ CPython 代码运行时现在位于 `packages/experimental/code-runtime-python`
 
 收获:fd-3 协议及其敌意输入 codec 构成自包含、unit 全覆盖的一层,并由执行中的 guard 防止 TypeScript/Python 字段集漂移。基于它构建的 runtime(`bootstrap.py`)消费经过评审的 wire contract。
 
-代价:包名表示 Python runtime 家族,而 `src/index.ts` 导出完整的 `PythonCodeRuntime` 实现,协议 vocabulary 只是包表面的一部分。mirror e2e 会比较两侧字段名与必填/可选状态,但不比较字段类型;跨 TypeScript 与 Python 比较类型声明没有机械等价物,因此评审与 runtime 的真实子进程套件继续负责这项检查。
+代价:包名表示 Python runtime 家族,而 `src/index.ts` 导出完整的 `PythonPtcRuntime` 实现,协议 vocabulary 只是包表面的一部分。mirror e2e 会比较两侧字段名与必填/可选状态,但不比较字段类型;跨 TypeScript 与 Python 比较类型声明没有机械等价物,因此评审与 runtime 的真实子进程套件继续负责这项检查。

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md
-2026-08-09-client-conversation-node-assembly.md: 842dfb0218478591f975c97064f101a35ea2f211
-2026-08-09-client-conversation-node-assembly.zh.md: 6ab64c25957d33487461fe56e122faedb19f9421
+2026-08-09-client-conversation-node-assembly.md: 43f9f0359822f7d7c5b52c09646fd3ffbf80f783
+2026-08-09-client-conversation-node-assembly.zh.md: d7475f5ff8218419743448d455d5d5ffe3cc4091

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.md

@@ -263,7 +263,7 @@ Page size, record packing, the number of history loads, and RAF coalescing affec
 | Message / `input-message` | Message ID | Append-surface `user/message` | None | Use source for a context message, or read the nearest next-step Inbox to distinguish user from steering |
 | Request Prompt / `request-prompt` | Header Event seq | Each `request/header` | None | Read the preceding Request Prompt through Reader, retain the full prompt state, and classify system/tool changes |
 | Assistant / `assistant-step` | `turn:step` | `step/start` | Live `assistant/live-chunk`, durable `assistant/message` or `assistant/attempt`, and same-step Retry | Aggregate blocks, usage, first-token time, settlement evidence, and retry-hidden state, then publish same-key Step data |
-| Tool / `tool-call` | Root call ID | Root `tool/call` | Root result and Code Dispatch start/result | Aggregate the root, children, and parent Map; Dispatch Events route exactly through `rootCallId` |
+| Tool / `tool-call` | Root call ID | Root `tool/call` | Root result and PTC dispatch start/result | Aggregate the root, children, and parent Map; Dispatch Events route exactly through `rootCallId` |
 | Command / `command` | Command ID | `command/run` | `command/done` and compact lifecycle/checkpoint Events carrying a source command ID | Aggregate command outcome and manual-compaction evidence |
 | Automatic Compaction / `compaction` | Compaction ID | `compaction/start` without a source command ID | Summary, end, and replacement checkpoint | Aggregate summary/checkpoint; sufficient checkpoint evidence supports fallback without a start |
 | Retry / `model-retry` | Retry ID | Attempt 1 `llm/retry` | Later `llm/retry` and `llm/retry-started` | Aggregate one RetryId's attempts and scheduled/started state |
@@ -360,7 +360,7 @@ SessionEventLike window
 
 Runtime tests pin Definition lifecycle registration, exact-ID append, update-before-start collection followed by forward replay after start, prepend identity, Reader window-gap repair, transitive dependencies, Location closure, Step→Turn data phase order, Location data replacement, publication cadence, illegal withdrawal, first-subscription activation, monotonic active targets, and per-target Builders.
 
-Conversation tests cover every built-in Chat Definition, Assistant Step data, Turn Tail and Deliverables Turn data, Chat ordering and structural sharing, selector isolation, Assistant and Tool running-to-settled identity, nested Code Dispatch, steering, Compaction, Retry, interruption, load-older anchoring, and slot dispatch. Trajectory tests cover its independently registered Message, Assistant, Tool, Compaction, Request-header, and boundary Definitions together with the preserved stage-oriented view model.
+Conversation tests cover every built-in Chat Definition, Assistant Step data, Turn Tail and Deliverables Turn data, Chat ordering and structural sharing, selector isolation, Assistant and Tool running-to-settled identity, nested PTC dispatch, steering, Compaction, Retry, interruption, load-older anchoring, and slot dispatch. Trajectory tests cover its independently registered Message, Assistant, Tool, Compaction, Request-header, and boundary Definitions together with the preserved stage-oriented view model.
 
 Slot type/runtime tests pin required parent-provided common inject, the `hookContext` type, Hook isolation across Node contexts, stable factory/Hook identity, and the absence of business-renderer rerenders for unrelated Session publications. Existing entry-owned Observable Hook tests continue to pin the path that does not use a contextual factory.
 

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-09-client-conversation-node-assembly.zh.md

@@ -263,7 +263,7 @@ Chat `order` 的结构性变化仍可能重排当前可见 key;纯 data 更新
 | Message / `input-message` | message ID | append-surface `user/message` | 无 | 根据 source 生成 context message,或读取最近 next-step Inbox 判断 user/steering |
 | Request Prompt / `request-prompt` | header Event seq | 每条 `request/header` | 无 | 通过 Reader 读取前一条 Request Prompt,保留完整 prompt 状态,并判定 system/tool 变化 |
 | Assistant / `assistant-step` | `turn:step` | `step/start` | Live `assistant/live-chunk`、持久 `assistant/message` 或 `assistant/attempt`、同 step Retry | 聚合 block、usage、首 token 时间、settlement 证据与 retry-hidden state,再发布同 key Step data |
-| Tool / `tool-call` | root call ID | root `tool/call` | root result、Code Dispatch start/result | 聚合 root、children 和 parent Map;Dispatch Event 用 `rootCallId` 精确路由 |
+| Tool / `tool-call` | root call ID | root `tool/call` | root result、PTC dispatch start/result | 聚合 root、children 和 parent Map;Dispatch Event 用 `rootCallId` 精确路由 |
 | Command / `command` | command ID | `command/run` | `command/done`、带 source command ID 的 compact lifecycle/checkpoint | 聚合 command outcome 和手动压缩证据 |
 | Automatic Compaction / `compaction` | compaction ID | 无 source command ID 的 `compaction/start` | summary、end、replacement checkpoint | 聚合 summary/checkpoint;checkpoint 足够时可在缺 start 下 fallback |
 | Retry / `model-retry` | retry ID | attempt 1 的 `llm/retry` | 后续 `llm/retry` 与 `llm/retry-started` | 聚合同一 RetryId 的 attempts 与 scheduled/started 状态 |
@@ -360,7 +360,7 @@ SessionEventLike window
 
 Runtime tests 固定 Definition 生命周期注册、exact-ID append、update-before-start 收集与 start 后正序 replay、prepend identity、Reader window-gap 修复、传递依赖、Location closure、Step→Turn data phase order、Location data replacement、publication cadence、非法撤回、首次订阅 activation、单调 active target 和 per-target Builder。
 
-Conversation tests 覆盖全部内建 Chat Definition、Assistant Step data、Turn Tail 与 Deliverables Turn data、Chat 排序和结构共享、selector isolation、Assistant/Tool running-to-settled identity、nested Code Dispatch、steering、Compaction、Retry、interruption、load-older anchoring 和 slot dispatch。Trajectory tests 则覆盖它独立注册的 Message、Assistant、Tool、Compaction、Request-header 与 boundary Definition,以及继续保留的 stage-oriented view model。
+Conversation tests 覆盖全部内建 Chat Definition、Assistant Step data、Turn Tail 与 Deliverables Turn data、Chat 排序和结构共享、selector isolation、Assistant/Tool running-to-settled identity、nested PTC dispatch、steering、Compaction、Retry、interruption、load-older anchoring 和 slot dispatch。Trajectory tests 则覆盖它独立注册的 Message、Assistant、Tool、Compaction、Request-header 与 boundary Definition,以及继续保留的 stage-oriented view model。
 
 Slot type/runtime tests 固定父注册必须提供声明的 common inject、`hookContext` 类型、不同 Node context 的 Hook 隔离、factory/Hook identity 稳定,以及无关 Session publication 不重渲染业务 renderer。原 entry-owned Observable Hook 测试继续固定未使用 contextual factory 的路径。
 

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.md
-2026-08-18-experimental-agent-teams-packages.md: 8ccbd690882cac0a4dc844d253656681e600fb74
-2026-08-18-experimental-agent-teams-packages.zh.md: dd79d8f2171b545977b7b7e776463da0087724bc
+2026-08-18-experimental-agent-teams-packages.md: c36760587979b234ffd7990035178b0acee53e35
+2026-08-18-experimental-agent-teams-packages.zh.md: 9502ad15ee776564423ccd398d5f77a98a51b730

+ 1 - 1
.agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.md

@@ -14,7 +14,7 @@ Moving the packages into product-role groups would remove their experimental nam
 
 `packages/experimental/agent-team`, `packages/experimental/tool-agent-team`, `packages/experimental/agent-team-profile`, `packages/experimental/client-ui-agent-team`, and `packages/experimental/agent-team-web-profile` are public workspace packages. They retain their existing `@deepseek-ai/dsh-experimental-*` names and join the dsh release family. The [experimental package rules](../../../../packages/experimental/AGENTS.md) own the private default, this exception, and later promotion.
 
-The dsh pack and publish set and the local baseline publisher include exactly these five experimental package directories. Workspace constraints require them to omit `private`, set `publishConfig.access` to `public`, and keep the experimental npm prefix. Every other experimental package remains private and excluded from publication by default. Release packages and apps outside the experimental group, plus the Python runtime, cannot name experimental packages in `dependencies`, `optionalDependencies`, or `peerDependencies`; experimental packages may depend on release packages and each other.
+The dsh pack and publish set and the local baseline publisher include the explicit experimental allowlist. These five Agent Teams directories and the [Cua Driver provider exceptions](2026-09-12-computer-use-provider-registration.md) omit `private`, set `publishConfig.access` to `public`, and keep the experimental npm prefix. Unlisted experimental packages remain private and excluded from publication by default. Release packages and apps outside the experimental group, plus the Python runtime, cannot name experimental packages in `dependencies`, `optionalDependencies`, or `peerDependencies`; experimental packages may depend on release packages and each other.
 
 The generic caller-reserved continuable child identity and selective direct-child drain remain in the stable Subagent service. They own Subagent identity and Activation lifecycle without importing or naming Agent Teams; the experimental Team service consumes them in the permitted direction.
 

+ 1 - 1
.agents/notes/implemented/architecture/2026-08-18-experimental-agent-teams-packages.zh.md

@@ -14,7 +14,7 @@ Agent Teams 的服务与工具约定仍在变化,但它需要使用真实 Sess
 
 `packages/experimental/agent-team`、`packages/experimental/tool-agent-team`、`packages/experimental/agent-team-profile`、`packages/experimental/client-ui-agent-team` 与 `packages/experimental/agent-team-web-profile` 是公开 workspace 包。它们保留现有 `@deepseek-ai/dsh-experimental-*` 名称并加入 dsh 发布系列。[实验性包规则](../../../../packages/experimental/AGENTS.md)负责默认私有原则、本例外与后续 promotion。
 
-dsh pack 与 publish 集合以及本地 baseline 发布器只会纳入这五个实验性包目录。workspace 约束要求它们省略 `private`、设置 `publishConfig.access` 为 `public`,并保留实验性 npm 前缀。其他实验性包默认仍为私有且不发布。实验组外的发布包与 app 以及 Python runtime 不得通过 `dependencies`、`optionalDependencies` 或 `peerDependencies` 引用实验性包;实验性包可以依赖发布包和其他实验性包
+dsh 打包与发布集合以及本地基线发布器包含显式实验包允许列表。这五个 Agent Teams 目录以及 [Cua Driver 提供方例外](2026-09-12-computer-use-provider-registration.zh.md)省略 `private`、将 `publishConfig.access` 设为 `public`,并保留实验性 npm 前缀。未列出的实验包默认保持私有且不参与发布。实验组之外的发布包、应用和 Python 运行时不能在 `dependencies`、`optionalDependencies` 或 `peerDependencies` 中引用实验包;实验包可以依赖发布包和彼此
 
 通用的调用方预留 continuable child 身份和精确 direct-child drain 仍属于稳定 Subagent 服务。它们负责 Subagent 身份与 Activation 生命周期,不 import 或命名 Agent Teams;实验性 Team 服务沿允许的方向消费这些能力。
 

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.md
-2026-08-23-client-derived-tool-presentation.md: 6b19dc881d572bfece345cbbd5eca688b2e05aab
-2026-08-23-client-derived-tool-presentation.zh.md: 98ada31627398f317e4c41336af56e8c6cf0837e
+2026-08-23-client-derived-tool-presentation.md: 895618099c6669058d23fc0a58335a90d5fbe4ca
+2026-08-23-client-derived-tool-presentation.zh.md: 38572f17c41ea9b68dc8c8c8cdc26a74d9dbb757

+ 20 - 20
.agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.md

@@ -12,13 +12,13 @@ A `tool/result` does not repeat the tool name or arguments. Host-side result pre
 
 Host projection would also duplicate structured data. Read, diff, search, and web results already persist bounded facts in `tool/result.data.meta`; another view object increases Remote payload size and Client decoding without adding durable meaning.
 
-The Client already owns a complete tool-presentation entry point. `ui-chat` assembles `tool/call`, `tool/result`, and Code Dispatch events into stable `ToolCallBlock` values. `ui-tool` owns the recursive call tree, the `tool.call.toolview` keyed slot dispatched by tool name, the Generic fallback, card models, and details output. A business Client plugin can register a renderer for its own tool names.
+The Client already owns a complete tool-presentation entry point. `ui-chat` assembles `tool/call`, `tool/result`, and PTC dispatch events into stable `ToolCallBlock` values. `ui-tool` owns the recursive call tree, the `tool.call.toolview` keyed slot dispatched by tool name, the Generic fallback, card models, and details output. A business Client plugin can register a renderer for its own tool names.
 
 Splitting presentation between Host presenters and Client keyed renderers creates two interpretations of the same event. The keyed renderer is the Web extension point, so an intermediate Host view provides no independent Web capability.
 
 `ToolDefinition.presentCall` and `presentResult` remain useful Host APIs even though ACP is automation-only and the repository has no production TUI consumer. Removing their definitions is a separate decision from keeping Session reads independent of presentation.
 
-The required result is one raw Session journal and one Client presentation owner without visual degradation or incidental enhancement. Specialized cards, interactions, and Code Dispatch topology remain stable while the transport stops carrying transient views.
+The required result is one raw Session journal and one Client presentation owner without visual degradation or incidental enhancement. Specialized cards, interactions, and PTC dispatch topology remain stable while the transport stops carrying transient views.
 
 ## Decision
 
@@ -26,7 +26,7 @@ The visual-equivalence requirements below exclude the separately approved [neste
 
 The Session Remote journal sends only raw, validated, persistable Session events. `session.page` and `session.follow` do not parse tool arguments, query the Tools registry, restore a presenter scope, execute `presentCall` or `presentResult`, or construct or clone any tool view.
 
-The Client Conversation layer continues to own tool call/result identity, pairing, lifecycle, Code Dispatch topology, and stable Chat Nodes. It does not interpret individual tool names or produce terminal, diff, read, search, or web component props.
+The Client Conversation layer continues to own tool call/result identity, pairing, lifecycle, PTC dispatch topology, and stable Chat Nodes. It does not interpret individual tool names or produce terminal, diff, read, search, or web component props.
 
 Client `ui-tool` continues to own card models and concrete renderers. Each card model directly reads the tool name, raw arguments, result content, error, durable metadata, Session cwd, and Host home from `ToolCallBlock`, and produces the same component props as the current page.
 
@@ -51,7 +51,7 @@ The Host `ToolDefinition.presentCall`, `ToolDefinition.presentResult`, `ToolCall
 | Retained | the Session log format, Remote journal lifecycle, and Conversation identity/topology |
 | Retained | the existing keyed slot, Generic fallback, and Chat, Details, and Trajectory structure |
 | Forbidden | a new Client presenter service, parallel registry, or wire renderer id |
-| Forbidden | new cards, visual redesign, interaction redesign, or Code Dispatch rich-card enhancements except the [nested terminal-card exception](../bug-fix/2026-09-05-nested-terminal-cards.md) |
+| Forbidden | new cards, visual redesign, interaction redesign, or PTC dispatch rich-card enhancements except the [nested terminal-card exception](../bug-fix/2026-09-05-nested-terminal-cards.md) |
 | Forbidden | compatibility dual-writing, version negotiation, or retention of the old `view` field |
 
 ## Terminology
@@ -96,7 +96,7 @@ The Host `ToolDefinition.presentCall`, `ToolDefinition.presentResult`, `ToolCall
 1. The Client Session stores one contiguous raw event window.
 2. `SessionEventSource` publishes `SessionEventEntry` values containing only events.
 3. `ui-conversation` folds each event without a presentation companion.
-4. The Chat and Trajectory Tool Definitions pair top-level calls and results by callId and assemble Code Dispatch subtrees.
+4. The Chat and Trajectory Tool Definitions pair top-level calls and results by callId and assemble PTC dispatch subtrees.
 5. `RunningToolCall` and `ToolResultNode` retain raw facts, metadata, and existing parent identity.
 6. `ToolCallTree` dispatches `tool.call.toolview` by wire tool name.
 7. `ui-tool` derives card component props from the block at the render site.
@@ -132,7 +132,7 @@ Session page/follow
 
 Client SessionEventSource
   -> Conversation Tool Definition
-  -> root call/result pairing + Code Dispatch topology
+  -> root call/result pairing + PTC dispatch topology
   -> ToolCallBlock(name, argsRaw, content, error, meta)
   -> tool.call.toolview keyed dispatch
   -> Client card model
@@ -242,11 +242,11 @@ The Chat and Trajectory Tool Definitions read no views. They derive the followin
 
 `ToolCallBlock` does not gain a generic `view`, `card`, `kind`, or `locations` field to replace the deleted fields. Concrete presentation remains the responsibility of `ui-tool` and keyed renderers.
 
-### Root and Code Dispatch subcalls
+### Root and PTC dispatch subcalls
 
-Host presenter APIs describe top-level calls and results. Code Dispatch subcalls retain Generic, flattened presentation for the diff, read, search, and web models covered here; supported terminal calls use the same eligibility rules as roots.
+Host presenter APIs describe top-level calls and results. PTC dispatch subcalls retain Generic, flattened presentation for the diff, read, search, and web models covered here; supported terminal calls use the same eligibility rules as roots.
 
-Code Dispatch start and result events already carry `parentCallId`. Conversation preserves that existing fact on each child `ToolCallBlock`; root Session calls omit it. The diff, read, search, and web models accept only blocks without `parentCallId`; the terminal model and existing renderers that intentionally support nested calls accept child blocks.
+PTC dispatch start and result events already carry `parentCallId`. Conversation preserves that existing fact on each child `ToolCallBlock`; root Session calls omit it. The diff, read, search, and web models accept only blocks without `parentCallId`; the terminal model and existing renderers that intentionally support nested calls accept child blocks.
 
 Shared card models apply the same terminal eligibility and nonterminal child restrictions wherever a block renders, so no second presentation surface needs a placement field; the details panel that once delegated a selected block was removed with the right-hand details column ([decision](../feature/2026-09-04-right-sidebar-docking-infrastructure.md)).
 
@@ -308,7 +308,7 @@ The Client terminal model derives existing `TerminalBlock` props from the tool n
 | settled persistent `bash`/`pwsh` | Generic flattened result, with no new exit card |
 | foreground `terminal_send` | terminal prompt and output |
 | background/error `terminal_send` | Generic result |
-| Code Dispatch child | same terminal eligibility and fallback rules as a root call |
+| PTC dispatch child | same terminal eligibility and fallback rules as a root call |
 
 Standard shell results parse trailing `[exit code: N]` and `[killed by signal: X]` markers. A final recognized spill-policy notice selects Generic output instead: expandable in shell rows and raw in Details, because the exit marker may be displaced or omitted. A parsed marker is removed from the terminal body; timeout, sandbox denial, and markers without a pill remain in the body.
 
@@ -331,7 +331,7 @@ Standard and persistent providers sharing the same tool name are a special compa
 | successful settled `write`/`edit` | applied contextual hunks from `meta.diffs` |
 | settled `str_replace_editor` | Generic, because the tool defines no result presenter |
 | write create or missing/malformed/empty applied metadata | current argument fallback |
-| error, malformed arguments, edit with malformed metadata, or Code Dispatch child | Generic |
+| error, malformed arguments, edit with malformed metadata, or PTC dispatch child | Generic |
 
 Paths, `oldText:null`, `newText`, result-over-call diff precedence, the eight-line Chat limit, full-height Details presentation, and file-opening behavior remain unchanged.
 
@@ -339,7 +339,7 @@ Paths, `oldText:null`, `newText`, result-over-call diff precedence, the eight-li
 
 A running `read` continues to show only the summary row. A successful settled `read` reads path, offset, lines, totalLines, and lang from result metadata and confirms that the result is one text block matching the read envelope.
 
-Missing metadata, malformed fields, a mismatched result envelope, an error, a missing call head, or a Code Dispatch child all use Generic. Cwd-relative path labels, home abbreviation, syntax language, total line count, the eight-line Chat limit, and full-height Details presentation remain unchanged.
+Missing metadata, malformed fields, a mismatched result envelope, an error, a missing call head, or a PTC dispatch child all use Generic. Cwd-relative path labels, home abbreviation, syntax language, total line count, the eight-line Chat limit, and full-height Details presentation remain unchanged.
 
 The Client does not need to construct Host `ReadResultView.content`; Generic fallback can always read raw result content directly.
 
@@ -347,7 +347,7 @@ The Client does not need to construct Host `ReadResultView.content`; Generic fal
 
 A running `grep` or `glob` continues to show only the argument summary. Successful results produce grouped matches or a path list from `meta.shape:'matches'` and `meta.shape:'paths'`, respectively.
 
-The Client validates path, lineNumber, line, truncated, and total. Empty matches or paths form a valid card. Missing or malformed metadata, an unknown shape, an error, a missing call head, or a Code Dispatch child uses Generic.
+The Client validates path, lineNumber, line, truncated, and total. Empty matches or paths form a valid card. Missing or malformed metadata, an unknown shape, an error, a missing call head, or a PTC dispatch child uses Generic.
 
 When `truncated:true`, the card continues to show a recovery locator from raw result content. It does not show one when untruncated. The eight-line Chat limit, full-height Details presentation, and expansion behavior remain unchanged.
 
@@ -355,7 +355,7 @@ When `truncated:true`, the card continues to show a recovery locator from raw re
 
 A running `web_search` or `web_fetch` continues to show only the summary row. A successful search builds the card from `meta.sources`, `meta.answer`, and `meta.truncated`; a successful fetch builds it from `meta.url`, `meta.statusCode`, and `meta.truncated`.
 
-The Client validates every source's url, title, snippet, and publishedAt, and continues rendering only http/https URLs as links. Missing or malformed metadata, an error, a missing call head, or a Code Dispatch child uses Generic.
+The Client validates every source's url, title, snippet, and publishedAt, and continues rendering only http/https URLs as links. Missing or malformed metadata, an error, a missing call head, or a PTC dispatch child uses Generic.
 
 Search answer text, source ordering, label fallback, and truncation notice remain unchanged. The fetch final URL, status, truncation notice, and raw body below Details remain unchanged.
 
@@ -420,7 +420,7 @@ The fixture does not import Host tool packages to compute page presentation and
 | grep/glob | current grouped/path card, truncation, and recovery |
 | web_search/web_fetch | current source/summary card and raw body |
 | Todo/Question/Skill/Cordis | current specialized rows |
-| Code Dispatch subcall | terminal cards when eligible; diff/read/search/web remain Generic/flattened |
+| PTC dispatch subcall | terminal cards when eligible; diff/read/search/web remain Generic/flattened |
 | Chat and Details | identical card fields for the same call |
 | Trajectory | current identity, tree, selection, and details |
 | Deliverables | current successful-mutation chips and links |
@@ -478,7 +478,7 @@ This change does not promise to preserve differences expressed only through a Ho
 - Conversation input and Tool blocks contain no view fields.
 - Chat and Trajectory Tool Definitions read raw events.
 - Event pairing, Context replay, trees, and target snapshots remain unchanged.
-- Child Tool blocks preserve the existing Code Dispatch `parentCallId`; row and Details slot owner props add no separate placement field.
+- Child Tool blocks preserve the existing PTC dispatch `parentCallId`; row and Details slot owner props add no separate placement field.
 
 ### UI Tool and Deliverables
 
@@ -515,7 +515,7 @@ This change does not promise to preserve differences expressed only through a Ho
 
 - replace, prepend, and append accept entries without views.
 - Chat and Trajectory root call/result pairing remains unchanged.
-- The Code Dispatch tree remains unchanged.
+- The PTC dispatch tree remains unchanged.
 - Result-only fallback remains unchanged.
 - A synthetic interruption result copies no view.
 - Node identity across registry rebuild, older prepend, and live append remains unchanged.
@@ -589,7 +589,7 @@ Changes to this decision use `dsh-pre-push-checks` to select commands for the fi
 - Deliverables does not depend on render intent and preserves current paths.
 - Text, components, expanded content, states, links, and ordering for all first-party top-level tools remain unchanged.
 - Malformed, missing-metadata, error, orphan, and unknown-tool cases continue to fall back safely.
-- Code Dispatch diff, read, search, and web subcalls remain Generic and flattened; terminal subcalls follow root eligibility.
+- PTC dispatch diff, read, search, and web subcalls remain Generic and flattened; terminal subcalls follow root eligibility.
 - Chat, Details, and Trajectory behavior remains unchanged.
 - Existing Web browser expected outputs pass without refresh.
 - Host presenter APIs, implementations, and direct tests remain unchanged.
@@ -634,7 +634,7 @@ An on-demand RPC would turn one page read into N network calls and would still r
 
 ### Allow presentation enhancements
 
-Bundling richer Code Dispatch cards, missing-call-head inference, or other historical presentation enhancements with the ownership change would prevent snapshots from proving equivalence. This decision rejects that coupling; the [nested terminal-card exception](../bug-fix/2026-09-05-nested-terminal-cards.md) does not relax nonterminal child restrictions.
+Bundling richer PTC dispatch cards, missing-call-head inference, or other historical presentation enhancements with the ownership change would prevent snapshots from proving equivalence. This decision rejects that coupling; the [nested terminal-card exception](../bug-fix/2026-09-05-nested-terminal-cards.md) does not relax nonterminal child restrictions.
 
 ### Accept temporary Generic degradation
 
@@ -703,7 +703,7 @@ This note preserves result metadata from the [canonical tool output contract](20
 ## Deferred
 
 - A separate explicit decision may evaluate deleting Host presenters if they remain without production consumers; this decision does not prejudge it.
-- Specialized diff, read, search, and web cards for Code Dispatch subcalls require a separate design and visible-snapshot updates; terminal calls are covered by the linked partial supersession.
+- Specialized diff, read, search, and web cards for PTC dispatch subcalls require a separate design and visible-snapshot updates; terminal calls are covered by the linked partial supersession.
 - A third-party mutation tool that joins Deliverables requires a new Client-owned contribution; this decision does not create a registry for an absent consumer.
 - Distinct Client presentation for same-named providers first requires a stable, non-presentational identity; it must not restore per-page Host views.
 - If Client card-model performance needs measurement, an immutable-block microbenchmark can be added; the shipped architecture already prohibits scanning the Session window.

+ 20 - 20
.agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.zh.md

@@ -12,13 +12,13 @@ Session 历史是持久 journal 接口,工具卡片属于 Client 展示。在
 
 Host 投影还会重复结构化数据。read、diff、search 与 web 结果已在 `tool/result.data.meta` 中持久化有界事实;另一份 view 只增加 Remote payload 与 Client 解码成本,不增加持久语义。
 
-Client 已经拥有完整的工具展示入口。`ui-chat` 将 `tool/call`、`tool/result` 与 Code Dispatch 事件组装成稳定的 `ToolCallBlock`;`ui-tool` 拥有递归调用树、按工具名称分发的 `tool.call.toolview` keyed slot、Generic fallback、卡片模型和 details output;业务 Client 插件可以为自己的工具名称注册 renderer。
+Client 已经拥有完整的工具展示入口。`ui-chat` 将 `tool/call`、`tool/result` 与 PTC dispatch 事件组装成稳定的 `ToolCallBlock`;`ui-tool` 拥有递归调用树、按工具名称分发的 `tool.call.toolview` keyed slot、Generic fallback、卡片模型和 details output;业务 Client 插件可以为自己的工具名称注册 renderer。
 
 Host presenter 与 Client keyed renderer 分担展示会形成对同一事件的两套解释。keyed renderer 是 Web 扩展点,因此中间 Host view 不提供独立 Web 能力。
 
 `ToolDefinition.presentCall`/`presentResult` 仍是保留的 Host API;ACP 采用 automation-only 协议,仓库也没有生产 TUI consumer。是否删除这些定义与 Session 读取是否独立于展示是两个决定。
 
-所需结果是一条原始 Session journal 和一个 Client 展示 owner,且不发生可见退化或顺带增强。专用卡片、交互和 Code Dispatch 拓扑保持稳定,transport 不再携带临时 view。
+所需结果是一条原始 Session journal 和一个 Client 展示 owner,且不发生可见退化或顺带增强。专用卡片、交互和 PTC dispatch 拓扑保持稳定,transport 不再携带临时 view。
 
 ## Decision
 
@@ -26,7 +26,7 @@ Host presenter 与 Client keyed renderer 分担展示会形成对同一事件的
 
 Session Remote journal 只下发原始、已验证、可持久化的 Session event。`session.page` 和 `session.follow` 不解析工具参数,不查询 Tools registry,不恢复 presenter scope,不执行 `presentCall`/`presentResult`,也不构造或克隆任何 tool view。
 
-Client Conversation 层继续负责工具调用与结果的 identity、配对、生命周期、Code Dispatch 拓扑和稳定 Chat Node。它不解释具体工具名称,也不生成 terminal、diff、read、search 或 web 组件 props。
+Client Conversation 层继续负责工具调用与结果的 identity、配对、生命周期、PTC dispatch 拓扑和稳定 Chat Node。它不解释具体工具名称,也不生成 terminal、diff、read、search 或 web 组件 props。
 
 Client `ui-tool` 继续负责 card model 和具体 renderer。每个 card model 改为直接读取 `ToolCallBlock` 中的工具名称、原始参数、结果内容、错误、持久 metadata、Session cwd 与 Host home,并生成与现有页面相同的组件 props。
 
@@ -51,7 +51,7 @@ Host 的 `ToolDefinition.presentCall`、`ToolDefinition.presentResult`、`ToolCa
 | 保留 | Session 日志格式、Remote journal 生命周期与 Conversation identity/topology |
 | 保留 | 现有 keyed slot、Generic fallback、Chat、Details 与 Trajectory 结构 |
 | 禁止 | 新 Client presenter service、平行 registry 或 wire renderer id |
-| 禁止 | 新卡片、视觉改版、交互改版或 Code Dispatch rich-card 增强,[嵌套 terminal 卡片例外](../bug-fix/2026-09-05-nested-terminal-cards.zh.md)除外 |
+| 禁止 | 新卡片、视觉改版、交互改版或 PTC dispatch rich-card 增强,[嵌套 terminal 卡片例外](../bug-fix/2026-09-05-nested-terminal-cards.zh.md)除外 |
 | 禁止 | 为兼容保留双写、版本协商或旧 `view` 字段 |
 
 ## 术语
@@ -96,7 +96,7 @@ Host 的 `ToolDefinition.presentCall`、`ToolDefinition.presentResult`、`ToolCa
 1. Client Session 保存一个连续 raw event window。
 2. `SessionEventSource` 发布只含 event 的 `SessionEventEntry`。
 3. `ui-conversation` 在没有 presentation companion 的情况下 fold 每个事件。
-4. Chat 与 Trajectory Tool Definition 按 callId 配对顶层 call/result,并组装 Code Dispatch 子树。
+4. Chat 与 Trajectory Tool Definition 按 callId 配对顶层 call/result,并组装 PTC dispatch 子树。
 5. `RunningToolCall` 与 `ToolResultNode` 保存 raw facts、metadata 与既有 parent identity。
 6. `ToolCallTree` 按 wire tool name 分发 `tool.call.toolview`。
 7. `ui-tool` 在 render site 从 block 派生 card component props。
@@ -132,7 +132,7 @@ Session page/follow
 
 Client SessionEventSource
   -> Conversation Tool Definition
-  -> root call/result pairing + Code Dispatch topology
+  -> root call/result pairing + PTC dispatch topology
   -> ToolCallBlock(name, argsRaw, content, error, meta)
   -> tool.call.toolview keyed dispatch
   -> Client card model
@@ -242,11 +242,11 @@ Chat 和 Trajectory 的 Tool Definition 都不读取 view,而从事件生成
 
 `ToolCallBlock` 不新增通用 `view`、`card`、`kind` 或 `locations` 字段替代被删除字段。具体展示仍只属于 `ui-tool` 与 keyed renderer。
 
-### Root 与 Code Dispatch 子调用
+### Root 与 PTC dispatch 子调用
 
-Host presenter API 描述顶层 call/result。本决定覆盖的 diff、read、search 和 web model 对 Code Dispatch 子调用保留 Generic/flattened 展示;受支持的 terminal 调用使用与根调用相同的适用规则。
+Host presenter API 描述顶层 call/result。本决定覆盖的 diff、read、search 和 web model 对 PTC dispatch 子调用保留 Generic/flattened 展示;受支持的 terminal 调用使用与根调用相同的适用规则。
 
-Code Dispatch start 与 result event 已经携带 `parentCallId`。Conversation 在每个 child `ToolCallBlock` 上保留这项现有事实,root Session call 则不携带它。diff、read、search 和 web model 只接受没有 `parentCallId` 的 block;terminal model 与原本有意支持嵌套调用的 renderer 接受 child block。
+PTC dispatch start 与 result event 已经携带 `parentCallId`。Conversation 在每个 child `ToolCallBlock` 上保留这项现有事实,root Session call 则不携带它。diff、read、search 和 web model 只接受没有 `parentCallId` 的 block;terminal model 与原本有意支持嵌套调用的 renderer 接受 child block。
 
 共享的 card model 在 block 渲染到哪里都施加同样的终端资格与非终端子调用限制,因此不需要第二个展示面带 placement 字段;曾经原样委托选中 block 的详情面板已随右侧详情列一并删除([决策](../feature/2026-09-04-right-sidebar-docking-infrastructure.zh.md))。
 
@@ -308,7 +308,7 @@ Client terminal model 从工具名称、调用参数、结果 content、error 
 | persistent `bash`/`pwsh` settled | Generic flattened result,不新增 exit card |
 | `terminal_send` 前台 | terminal prompt 与 output |
 | `terminal_send` background/error | Generic 结果 |
-| Code Dispatch child | 与根调用相同的 terminal 适用规则与 fallback 规则 |
+| PTC dispatch child | 与根调用相同的 terminal 适用规则与 fallback 规则 |
 
 标准 shell 结果解析末尾 `[exit code: N]` 与 `[killed by signal: X]`。末尾已识别的 spill 策略提示会改用 Generic 输出:在 shell 行中可展开,在 Details 中显示原文,因为退出标记可能被移位或省略。已解析的 marker 从 terminal 正文移除;timeout、sandbox denial 与没有 pill 的 marker 留在正文。
 
@@ -331,7 +331,7 @@ TerminalBlock 的 ANSI、光标重放、宽字符、行数上限、展开、复
 | settled `write`/`edit` success | 从 `meta.diffs` 生成 applied contextual hunks |
 | settled `str_replace_editor` | Generic,因为该工具没有 result presenter |
 | write create 或 applied metadata 缺失、畸形、为空 | 当前 args fallback |
-| error、畸形 args、edit 的 metadata 畸形、Code Dispatch child | Generic |
+| error、畸形 args、edit 的 metadata 畸形、PTC dispatch child | Generic |
 
 路径、`oldText:null`、`newText`、结果覆盖调用时 diff、Chat 8 行上限、Details 全高显示和文件打开行为不变。
 
@@ -339,7 +339,7 @@ TerminalBlock 的 ANSI、光标重放、宽字符、行数上限、展开、复
 
 running `read` 继续只有摘要行。成功 settled `read` 从 result meta 读取 path、offset、lines、totalLines 与 lang,并确认结果是单个文本块且符合 read envelope。
 
-meta 缺失、字段畸形、result envelope 不匹配、error、缺失 call head 或 Code Dispatch child 都走 Generic。路径 label 的 cwd 相对化、home 缩写、语法语言、总行数、Chat 8 行上限与 Details 全高显示不变。
+meta 缺失、字段畸形、result envelope 不匹配、error、缺失 call head 或 PTC dispatch child 都走 Generic。路径 label 的 cwd 相对化、home 缩写、语法语言、总行数、Chat 8 行上限与 Details 全高显示不变。
 
 Client 不需要构造 Host `ReadResultView.content`;Generic fallback 始终可直接读取原始 result content。
 
@@ -347,7 +347,7 @@ Client 不需要构造 Host `ReadResultView.content`;Generic fallback 始终
 
 running `grep`/`glob` 继续只有参数摘要。成功结果分别从 `meta.shape:'matches'` 与 `meta.shape:'paths'` 生成 grouped matches 或 path list。
 
-Client 校验 path、lineNumber、line、truncated 与 total。空 matches/paths 是有效卡片;缺失/畸形 meta、未知 shape、error、缺失 call head 与 Code Dispatch child 走 Generic。
+Client 校验 path、lineNumber、line、truncated 与 total。空 matches/paths 是有效卡片;缺失/畸形 meta、未知 shape、error、缺失 call head 与 PTC dispatch child 走 Generic。
 
 `truncated:true` 时继续从原始 result content 显示 recovery locator;未截断时不显示。Chat 8 行上限、Details 全高显示和展开行为不变。
 
@@ -355,7 +355,7 @@ Client 校验 path、lineNumber、line、truncated 与 total。空 matches/paths
 
 running `web_search`/`web_fetch` 继续只有摘要行。成功 search 从 `meta.sources`、`meta.answer`、`meta.truncated` 生成卡片;成功 fetch 从 `meta.url`、`meta.statusCode`、`meta.truncated` 生成卡片。
 
-Client 校验每个 source 的 url、title、snippet 与 publishedAt,并继续只把 http/https URL 渲染为链接。meta 缺失或畸形、error、缺失 call head 与 Code Dispatch child 走 Generic。
+Client 校验每个 source 的 url、title、snippet 与 publishedAt,并继续只把 http/https URL 渲染为链接。meta 缺失或畸形、error、缺失 call head 与 PTC dispatch child 走 Generic。
 
 search 的 answer、来源顺序、label fallback 与截断提示不变;fetch 的最终 URL、状态、截断提示与 Details 下方原始正文不变。
 
@@ -420,7 +420,7 @@ fixture 不导入 Host 工具包来计算页面展示,也不保留 presenter 
 | grep/glob | 当前 grouped/path card、截断与 recovery |
 | web_search/web_fetch | 当前来源/摘要 card 与原始正文 |
 | Todo/Question/Skill/Cordis | 当前专用行 |
-| Code Dispatch subcall | 满足条件时显示 terminal 卡片;diff/read/search/web 保持 Generic/flattened |
+| PTC dispatch subcall | 满足条件时显示 terminal 卡片;diff/read/search/web 保持 Generic/flattened |
 | Chat 与 Details | 同一调用使用相同 card fields |
 | Trajectory | 当前 identity、树、选择和 details |
 | Deliverables | 当前成功 mutation chips 与链接 |
@@ -478,7 +478,7 @@ Host registry 允许不同 scope 为同一 tool name 提供不同定义;Sessio
 - Conversation input 与 Tool block 不含 view 字段。
 - Chat/Trajectory Tool Definition 读取 raw event。
 - event pairing、Context replay、树与 target snapshot 保持不变。
-- child Tool block 保留现有 Code Dispatch `parentCallId`;row 与 Details slot owner props 都不增加独立 placement 字段。
+- child Tool block 保留现有 PTC dispatch `parentCallId`;row 与 Details slot owner props 都不增加独立 placement 字段。
 
 ### UI Tool 与 Deliverables
 
@@ -515,7 +515,7 @@ Host registry 允许不同 scope 为同一 tool name 提供不同定义;Sessio
 
 - replace、prepend 与 append 接受无 view entry。
 - Chat 与 Trajectory root call/result 配对不变。
-- Code Dispatch 树不变。
+- PTC dispatch 树不变。
 - result-only fallback 不变。
 - interruption synthetic result 不复制 view。
 - registry rebuild、older prepend 与 live append 的 Node identity 不变。
@@ -589,7 +589,7 @@ Host registry 允许不同 scope 为同一 tool name 提供不同定义;Sessio
 - Deliverables 不依赖 render intent 且保持当前 paths。
 - 所有第一方顶层工具的文本、组件、展开内容、状态、链接与排序不变。
 - malformed、missing-meta、error、orphan 与 unknown-tool 继续安全 fallback。
-- Code Dispatch 的 diff、read、search 和 web 子调用保持 Generic/flattened;terminal 子调用遵循根调用适用规则。
+- PTC dispatch 的 diff、read、search 和 web 子调用保持 Generic/flattened;terminal 子调用遵循根调用适用规则。
 - Chat、Details 与 Trajectory 行为不变。
 - 现有 Web browser expected 无需刷新即可通过。
 - Host presenter API、实现与直接测试不变。
@@ -634,7 +634,7 @@ read 行结构、applied diff、search 分组、web sources 和有效 truncation
 
 ### 允许展示增强
 
-将更丰富的 Code Dispatch 卡片、缺失 call head 的推断或其他历史展示增强与所有权变更捆绑,会使快照无法证明对等。本决定拒绝这种捆绑;[嵌套 terminal 卡片例外](../bug-fix/2026-09-05-nested-terminal-cards.zh.md)不放宽非 terminal 子调用限制。
+将更丰富的 PTC dispatch 卡片、缺失 call head 的推断或其他历史展示增强与所有权变更捆绑,会使快照无法证明对等。本决定拒绝这种捆绑;[嵌套 terminal 卡片例外](../bug-fix/2026-09-05-nested-terminal-cards.zh.md)不放宽非 terminal 子调用限制。
 
 ### 接受临时 Generic 退化
 
@@ -703,7 +703,7 @@ optional `view` 的缺失是所有 consumer 共同遵守的预发布 wire 类型
 ## Deferred
 
 - Host presenter 若长期没有生产消费者,可由另一项明确决策评估删除;本决定不预判。
-- Code Dispatch 子调用的 diff、read、search 和 web 专用卡片仍需独立设计并更新可见快照;terminal 调用由链接的部分取代决策负责。
+- PTC dispatch 子调用的 diff、read、search 和 web 专用卡片仍需独立设计并更新可见快照;terminal 调用由链接的部分取代决策负责。
 - 第三方 mutation tool 若要加入 Deliverables,需新增 Client-owned 贡献;本决定不为尚无消费者的扩展性建 registry。
 - 同名 provider 若要不同 Client 展示,需先定义稳定、非展示性的 identity;不得恢复按页 Host view。
 - Client card model 若需量化性能,可以增加 immutable-block 微基准;已交付架构禁止扫描 Session window。

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.md
-2026-08-27-outbound-proxy-policy.md: 015b6edd3f4153f29db4084c92e8e8da8cad70e2
-2026-08-27-outbound-proxy-policy.zh.md: c7bdcaee138ac41a55733d76da1063b25f9ba633
+2026-08-27-outbound-proxy-policy.md: 9db894c45bd1c816e7394ac621e4be18a8a94e74
+2026-08-27-outbound-proxy-policy.zh.md: 30e30e725ade9d6daf94129fa45b81a6aeb97f96

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.md

@@ -40,7 +40,7 @@ This keeps `proxyForUrl()` and the dispatcher answering from one set of values.
 
 The URL-level policy is untouched: `http(s)` only, no embedded credentials, the length cap, and the cross-origin redirect refusal all still apply on every hop.
 
-**A spawned child gets the policy through its environment; a model-executing worker gets nothing.** `proxyEnvironmentForChild()` merges into `scrubbedParentEnv()`, the one function every spawner already shares. The workflow worker does NOT receive it: it executes the model-authored script body, and a proxy URL may carry `user:password`. That is the same containment the code runtime keeps and `docs/defensive-patterns.md` requires, so a workflow's own requests go direct.
+**A spawned child gets the policy through its environment; a model-executing worker gets nothing.** `proxyEnvironmentForChild()` merges into `scrubbedParentEnv()`, the one function every spawner already shares. The workflow worker does NOT receive it: it executes the model-authored script body, and a proxy URL may carry `user:password`. That is the same containment the PTC runtime keeps and `docs/defensive-patterns.md` requires, so a workflow's own requests go direct.
 
 The child keeps the user's own values, and that is what once broke it. Node parses `HTTP_PROXY` and `HTTPS_PROXY` under `NODE_USE_ENV_PROXY` before running the program and exits on any scheme other than `http:` or `https:`; a `socks4://` kept for `curl` therefore ended every Node child — MCP servers, subagent CLIs, `npm` — before its first line, while this process had reported only that the scheme stayed direct. Measured on Node 24.17: `socks4://`, `ftp://`, and a malformed value all exit 1; `socks5://` happens to be accepted there. The flag is now withheld whenever a value the child receives is one this package refused, so such a child connects directly and `curl` still reads the value it was kept for. Handing the child the resolved value instead would have kept Node proxied at the price of silently rewriting what the user set for another tool.
 
@@ -70,7 +70,7 @@ Weighed against that, telemetry is the one outbound channel whose loss costs the
 
 **Read the operating system's proxy settings.** Rejected for this change. Only Codex and Reasonix among six surveyed products do it, and Codex keeps it behind a default-off flag. Measured on the author's machine, it would have found nothing: the proxy application had written the setting to the Wi-Fi service while the primary interface was a USB ethernet adapter with no proxy, so `scutil --proxy` reported none while the exported variables worked. It also needs its own bypass matcher, because an operating system list carries CIDR entries that neither undici nor Node matches.
 
-**Give the `code-runtime` worker the proxy too.** Rejected. Model-authored programs run there with no ambient environment at all — a stronger containment than the scrubbed environment spawned commands get — and a proxy URL may carry credentials. Handing model code a credentialed URL to reach the network is the wrong trade; the exclusion is recorded in that package's limitations.
+**Give model-authored code the proxy too.** Rejected because a proxy URL may carry credentials. Node ptc-runtime processes and workflow workers keep those settings outside the program environment; direct network use remains subject to the program's execution policy.
 
 ## Consequences
 

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-27-outbound-proxy-policy.zh.md

@@ -40,7 +40,7 @@ Node 内置的 `fetch` 会忽略 `HTTP_PROXY` 与 `HTTPS_PROXY`。开发者运
 
 URL 层策略未受影响:仅 `http(s)`、禁止内嵌凭据、长度上限与跨域重定向拒绝在每一跳上依然生效。
 
-**派生的子进程通过环境获得策略;执行模型代码的 worker 什么也不获得。** `proxyEnvironmentForChild()` 并入 `scrubbedParentEnv()`——每个 spawner 本就共享的那一个函数。workflow worker **不**接收它:它执行的是模型编写的脚本体,而代理 URL 可能携带 `user:password`。这与 code runtime 保持的隔离相同,也是 `docs/defensive-patterns.md` 的要求,因此 workflow 自身的请求直连。
+**派生的子进程通过环境获得策略;执行模型代码的 worker 什么也不获得。** `proxyEnvironmentForChild()` 并入 `scrubbedParentEnv()`——每个 spawner 本就共享的那一个函数。workflow worker **不**接收它:它执行的是模型编写的脚本体,而代理 URL 可能携带 `user:password`。这与 PTC runtime 保持的隔离相同,也是 `docs/defensive-patterns.md` 的要求,因此 workflow 自身的请求直连。
 
 子进程拿到的是用户自己的值,而这恰恰曾把它弄坏。Node 在 `NODE_USE_ENV_PROXY` 下会在运行程序之前先解析 `HTTP_PROXY` 与 `HTTPS_PROXY`,遇到 `http:`/`https:` 之外的协议直接退出;于是一个为 `curl` 保留的 `socks4://` 会让每个 Node 子进程——MCP server、subagent CLI、`npm`——在第一行之前就终结,而本进程此前只报告过该协议保持直连。在 Node 24.17 上实测:`socks4://`、`ftp://` 与畸形值均以 1 退出;`socks5://` 恰好在该版本被接受。现在只要子进程收到的某个值是本包拒绝过的,就扣下该标志,这样的子进程直连,`curl` 仍读到为它保留的值。若改为把解析后的值交给子进程,Node 固然能继续走代理,代价却是悄悄改写用户为另一工具设置的值。
 
@@ -70,7 +70,7 @@ URL 层策略未受影响:仅 `http(s)`、禁止内嵌凭据、长度上限与
 
 **读取操作系统的代理设置。** 本次变更中被否决。所调研的六个产品中只有 Codex 与 Reasonix 这样做,且 Codex 把它放在默认关闭的开关之后。在作者机器上实测,它什么也读不到:代理软件把设置写在了 Wi-Fi 服务上,而主接口是一块没有代理的 USB 以太网卡,因此 `scutil --proxy` 报告无代理,而导出的环境变量却工作正常。它还需要自带的绕过匹配器,因为操作系统的列表含有 undici 与 Node 都不匹配的 CIDR 条目。
 
-**也把代理给 `code-runtime` worker。** 被否决。模型编写的程序在那里运行时完全没有环境变量——这比派生命令得到的 scrubbed 环境更严——而代理 URL 可能携带凭据。把带凭据的 URL 交给模型代码去访问网络是错误的取舍;该排除已记入那个包的限制清单
+**也把代理配置交给模型编写的代码。** 不采纳,因为代理 URL 可能携带凭据。Node ptc-runtime 进程与 workflow worker 不在程序环境中提供这些设置;直接网络访问仍受程序执行策略约束
 
 ## Consequences
 

+ 6 - 0
.agents/notes/implemented/architecture/2026-09-11-posix-ssh-runtime.i18n.yaml

@@ -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/implemented/architecture/2026-09-11-posix-ssh-runtime.md
+2026-09-11-posix-ssh-runtime.md: d54600faa68524b569f219627537ce250158d9b1
+2026-09-11-posix-ssh-runtime.zh.md: a6b3a84c37b0d7b4438e8c766ef4e010443fe744

+ 57 - 0
.agents/notes/implemented/architecture/2026-09-11-posix-ssh-runtime.md

@@ -0,0 +1,57 @@
+# Agent Note: POSIX SSH execution providers
+
+Status: implemented
+
+English | [中文](2026-09-11-posix-ssh-runtime.zh.md)
+
+## Problem
+
+Remote coding requires file tools, Bash, terminals, language servers and Node programs to see one filesystem and process world. The [portable-consumer decision](2026-07-28-portable-execution-world-consumers.md) provides those interfaces. The [E2B retirement](../simplification/2026-09-11-remove-e2b-providers.md) preserves their asynchronous semantics while removing an integration whose standard-stream SDK needs a separate transport project to carry bidirectional PTC control traffic.
+
+SSH supplies authenticated byte channels and per-channel flow control, but an ordinary exec request does not map arbitrary child descriptors. Treating SSH as a replacement for a process owner would leave control-stream bridging, launch publication, EOF, cancellation and disconnected cleanup without an implementation.
+
+## Decision
+
+A deployment-owned OpenSSH alias connects the local Harness to an installed POSIX helper. Filesystem, subprocess and sandbox providers share that helper; the Harness retains Cordis objects, model transport, permissions, callbacks and Session persistence. Terminal methods remain asynchronous. Provider paths describe the execution world without a separate local/remote flag.
+
+Confinement is asynchronous and cancellable: the running helper resolves each policy through its loaded sandbox provider before the subprocess provider receives literal argv. `ShellExecutor.start()` resolves a `Promise<ShellProcess>` after preparation. Generic job admission remains synchronous; tool-owned `JobHooks` begin asynchronous shell preparation after preflight, cancel pending preparation and join any late process handle.
+
+Private administrative RPC and each program stream use independent SSH channels. A remote reservation creates the requested stream endpoints before the payload starts. Stdout and stderr cannot carry administrative replies, and pausing one output channel does not consume fd 7’s flow-control window. This trades a larger remote helper for explicit binary transport and bounded stream retention.
+
+Each stream receives a fresh 256-bit TLS pre-shared key through private administrative RPC. TLS 1.2 with `PSK-AES256-GCM-SHA384` authenticates both endpoints and protects subsequent bytes before the stream can publish. The key never travels as a stream preface. Socket permissions alone are insufficient: file-effect confinement can permit same-user connections or pathname replacement in writable temporary directories. No administrative Unix listener or stream secret is exposed to the payload. Cancellation transfers to the TLS wrapper after wrapping; cleanup closes that wrapper before the underlying socket so native TLS reads cannot outlive their transport.
+
+Collected stdout and stderr carry bounded tail snapshots through handlers that only update output observations. Capture continues while a snapshot is waiting for transport. Final snapshots preserve raw-byte offsets, and completed spill files use the local provider’s retained-output storage after connection disposal.
+
+Readiness verifies the installed helper digest and, when PTC is configured, the installed Node bootstrap digest. These checks pin expected deployment artifacts; they do not authenticate a malicious remote operating system. The helper disables Node debugger activation through `SIGUSR1`: file-effect confinement can still permit same-user signals, which must not expose the helper's unrestricted filesystem and process services. File-effect confinement delegates to the remote local sandbox provider and retains its full/partial disclosure and platform limitations.
+
+Path canonicalization belongs where the files exist. The shared policy resolver preserves absolute execution-world spelling; enforcing providers resolve symlinks and `..` on their own filesystem. Headless records and validates cwd through `ctx.fs`. Host-path projection remains unavailable for SSH, so Node execution requires an explicitly installed remote bootstrap.
+
+A lost connection invalidates pending operations without reconnect or replay. Helper EOF, signals and a heartbeat lease initiate remote native cleanup. The client cannot turn lease expiry into an observed successful termination: an interrupted mutation or launch can have an unknown outcome. Administrative request deadlines, consumer execution deadlines and cleanup remain separate owners.
+
+## Alternatives considered
+
+**Frame every stream over SSH exec stdout/stdin.** This avoids Unix-socket forwarding, but requires per-stream credits, queue bounds and independent EOF semantics inside the application protocol. Independent SSH channels use the transport’s existing flow control and keep program output away from administrative framing.
+
+**Replace remote file operations with SFTP alone.** SFTP supplies file transport but does not directly preserve the existing guarded atomic-write, edit, policy and error semantics. Reusing the remote local filesystem providers keeps those mechanisms with their current owners.
+
+**Expose TCP or HTTP endpoints for program streams.** This permits independent transports but introduces remote port exposure, endpoint authentication and server deployment beyond the existing SSH connection. Forwarded Unix sockets use the authenticated SSH session and additionally authenticate both stream endpoints with TLS-PSK against same-user connections or pathname replacement.
+
+**Move the complete Harness to the remote host.** That is a separate deployment model. It moves model credentials, Session storage and plugin state with execution rather than supplying remote implementations of existing capabilities.
+
+## Consequences
+
+Remote providers add transport, reservation and disconnection responsibilities even though they reuse local file and process mechanisms. Raw streams, collected tails and remote spill files retain distinct lifetimes. Consumers must release their streams and handles; a helper’s cleanup result cannot be reconstructed after transport loss.
+
+The initial composition scope is POSIX headless and custom profiles. Web workspace consumers with host-filesystem assumptions require their own integration. Network restrictions, process-visibility isolation, hostile-host attestation, persistent remote handles and automatic artifact provisioning are outside this provider family.
+
+The portable-consumer decision remains active; this note supplies its SSH realization. The E2B retirement remains active for the removed integration and its maintenance tradeoff. Neither note is fully superseded.
+
+## Verification
+
+Required protocol and lifecycle evidence covers malformed frames, bounds, reservation cancellation, authenticated stream publication and disconnect errors. Native SSH acceptance must exercise remote file guards and symlink identity, Bash confinement, fd 7 binary traffic, control progress under paused output, terminal operations, LSP and Node execution. Live checks require an explicitly configured disposable remote workspace; keyless tests do not provision one.
+
+Security evidence requires both a same-user connector and a replaced socket listener that cannot claim a reserved stream, learn its key, alter another run or receive its plaintext output. Process-lifecycle evidence distinguishes direct exit from managed-range quiescence and tests cancellation both before launch publication and after the payload starts. Source and built profile checks verify the installed helper/bootstrap arrangement without transferring host credentials to program environments.
+
+## Deferred work
+
+Persistent remote reconnection needs a separate operation-identity and recovery design; it cannot reuse live callback handles after disconnect. Broader Web support needs provider-owned workspace resources. Stronger resource accounting or revised PTC yield/wait and timeout policy belongs to the [Node runtime reference](../../../../packages/ptc-runtime/ptc-runtime-node/README.md), not the SSH administrative request deadline.

+ 57 - 0
.agents/notes/implemented/architecture/2026-09-11-posix-ssh-runtime.zh.md

@@ -0,0 +1,57 @@
+# Agent Note: POSIX SSH 执行提供方
+
+Status: implemented
+
+[English](2026-09-11-posix-ssh-runtime.md) | 中文
+
+## 问题
+
+远端编码需要文件工具、Bash、终端、语言服务器与 Node 程序看到同一个文件系统与进程环境。[可移植消费方决策](2026-07-28-portable-execution-world-consumers.zh.md)提供这些接口。[E2B 退役决策](../simplification/2026-09-11-remove-e2b-providers.zh.md)保留其异步语义,同时移除一个需要单独传输项目才能通过标准流库承载双向 PTC 控制流的集成。
+
+SSH 提供经过认证的字节通道及逐通道流量控制,但普通 exec 请求不映射任意子进程描述符。若把 SSH 当作进程管理器的替代品,控制流桥接、启动发布、EOF、取消及断连清理就会缺少实现。
+
+## 决策
+
+部署方持有的 OpenSSH 主机别名将本地 Harness 连接到已安装的 POSIX 辅助程序。文件系统、子进程及沙箱提供方共享该辅助程序;Harness 保留 Cordis 对象、模型传输、权限、回调与 Session 持久化。终端方法保持异步。提供方路径描述执行环境,无需额外的本地/远端标记。
+
+限制解析支持异步与取消:正在运行的辅助程序通过已加载的沙箱提供方解析每次策略,然后子进程提供方取得可直接执行的 argv。`ShellExecutor.start()` 在准备完成后返回 `Promise<ShellProcess>`。通用任务准入保持同步;由工具负责的 `JobHooks` 在预检后启动异步 shell 准备,取消待完成的准备并等待任何延迟出现的进程句柄结束。
+
+私有管理 RPC 与各条程序流使用独立 SSH 通道。远端预留操作在程序启动前创建请求的流端点。stdout 与 stderr 无法承载管理回复,暂停一条输出通道也不会占用 fd 7 的流控窗口。这用更大的远端辅助程序换取显式二进制传输与有界流保留。
+
+每条流通过私有管理 RPC 获得新的 256 位 TLS 预共享密钥。TLS 1.2 配合 `PSK-AES256-GCM-SHA384` 在流发布前认证两端并保护后续字节。密钥绝不作为流前缀传输。仅靠套接字权限不足:文件效果限制可能允许同用户连接,或允许在可写临时目录中替换路径名。程序不会获得管理 Unix 监听端点或流密钥。包装完成后,取消责任转移到 TLS 包装流;清理先关闭该流,再关闭底层套接字,确保原生 TLS 读取不会超过传输层的生命周期。
+
+收集的 stdout 与 stderr 通过仅更新输出观测的处理器传递有界尾部快照。快照等待传输时,输出捕获继续进行。最终快照保留原始字节偏移,已完成的 spill 文件在连接释放后使用本地提供方的保留输出存储。
+
+就绪流程验证已安装辅助程序的摘要,并在配置 PTC 时验证已安装 Node 引导程序的摘要。这些检查固定预期部署产物,不用于认证恶意远端操作系统。辅助程序禁用通过 `SIGUSR1` 启动 Node 调试器:文件效果限制仍可能允许同用户信号,这些信号不得暴露辅助程序不受限的文件系统和进程服务。文件效果限制委托给远端本地沙箱提供方,保留其完整/部分执行披露及平台限制。
+
+路径规范化属于文件实际存在的位置。共享策略解析器保留执行环境中的绝对路径写法;执行限制的提供方在自己的文件系统上解析符号链接与 `..`。headless 通过 `ctx.fs` 记录和验证 cwd。SSH 不提供主机路径投影,因此 Node 执行需要显式安装的远端引导程序。
+
+连接丢失会使待处理操作失效,不进行重连或重放。辅助进程 EOF、信号及心跳租期会启动远端原生清理。客户端不能把租期到期当作观察到的成功终止:被中断的修改或启动可能结果未知。管理请求截止时限、消费方执行截止时限及清理分别由各自归属方负责。
+
+## 考虑过的替代方案
+
+**在 SSH exec stdout/stdin 上为所有流分帧。** 这避免了 Unix 套接字转发,但要求应用协议自行实现逐流额度、队列上限及独立 EOF 语义。独立 SSH 通道使用传输层既有流控,并将程序输出与管理帧分开。
+
+**仅用 SFTP 替代远端文件操作。** SFTP 提供文件传输,但不直接保留既有的带保护原子写入、编辑、策略及错误语义。复用远端本地文件系统提供方,使这些机制继续归属现有实现。
+
+**通过 TCP 或 HTTP 端点提供程序流。** 这允许独立传输,但会在现有 SSH 连接之外引入远端端口暴露、端点认证及服务器部署。转发 Unix 套接字使用已认证的 SSH 会话,并通过 TLS-PSK 额外认证流的两端,以防御同用户连接或路径名替换。
+
+**把完整 Harness 移到远端主机。** 这是另一种部署模型,会将模型凭据、Session 存储及插件状态随执行一起迁移,而不是提供现有能力的远端实现。
+
+## 影响
+
+远端提供方虽然复用本地文件与进程机制,仍增加传输、预留及断连责任。原始流、收集尾部及远端 spill 文件保留各自生命周期。消费方必须释放流与句柄;传输丢失后无法重建辅助进程的清理结果。
+
+初始组合范围为 POSIX headless 与自定义配置组合。假定可访问主机文件系统的 Web 工作区消费方需要独立集成。网络限制、进程可见性隔离、恶意主机证明、持久远端句柄及自动产物配置不属于本提供方家族。
+
+可移植消费方决策继续有效;本文提供其 SSH 实现。E2B 退役决策仍负责已移除集成及维护取舍。两篇记录均未被完全取代。
+
+## 验证
+
+必要的协议及生命周期证据覆盖畸形消息、上限、预留取消、经过认证的流发布及断连错误。原生 SSH 验收必须覆盖远端文件保护与符号链接身份、Bash 限制、fd 7 二进制流、输出暂停时的控制进展、终端操作、LSP 及 Node 执行。实时检查要求显式配置的可丢弃远端工作区;无密钥测试不创建远端环境。
+
+安全证据要求同时尝试同用户连接与替换套接字监听端点,并确认其无法占用预留流、获知流密钥、修改另一执行或收到其明文输出。进程生命周期证据区分直接退出与托管进程范围静止,并测试启动发布前及程序启动后的取消。源码与构建后配置组合检查验证已安装辅助程序/引导安排,不向程序环境传递主机凭据。
+
+## 延后工作
+
+持久远端重连需要独立的操作身份与恢复设计,不能在断连后复用活跃回调句柄。更广泛的 Web 支持需要由提供方负责的工作区资源。更强的资源计量或修订后的 PTC yield/wait 与超时策略属于 [Node 运行时参考](../../../../packages/ptc-runtime/ptc-runtime-node/README.zh.md),不属于 SSH 管理请求截止时限。

+ 6 - 0
.agents/notes/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.i18n.yaml

@@ -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/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.md
+2026-09-11-sandboxed-node-ptc-runtime.md: 02d76bd607ab8352e208d78f3cf0442fc0794917
+2026-09-11-sandboxed-node-ptc-runtime.zh.md: 40390243b6a15404a2bd9fd2f9d5f9700ad883e6

+ 56 - 0
.agents/notes/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.md

@@ -0,0 +1,56 @@
+# Agent Note: Sandboxed Node execution for PTC
+
+Status: implemented
+
+English | [中文](2026-09-11-sandboxed-node-ptc-runtime.zh.md)
+
+## Problem
+
+A Node worker isolates JavaScript state but does not apply the calling Session's OS sandbox policy. Model code can import filesystem and subprocess APIs directly, bypassing the tool-policy path even when nested `tools.*` calls receive the correct checks. Terminating the worker also does not establish that its child processes have stopped.
+
+The [PTC foundation](../feature/2026-06-15-ptc.md) remains responsible for registry presentation, generated bindings, dispatch logging and one-shot settlement. This decision supersedes its worker-based execution, trust and budget realization while preserving those consumer rules.
+
+## Decision
+
+`dsh-ptc-runtime-node` runs each program in one fresh Node process. The host resolves execution choices, confines the launch through the same `ctx.sandbox` provider as Bash, and gives process lifetime to `ctx.subprocess`. The child evaluates erasable TypeScript with direct Node APIs, an empty model environment and host-provided asynchronous bindings. No worker or persistent kernel remains inside this provider.
+
+### Resolved inputs and policy
+
+`PtcRuntime.resolve(request)` validates supported options and supplies a complete `PtcRunSpec`; `run(spec)` does not introduce defaults. PTC passes the calling Session's cwd and resolved standing policy. Direct runtime callers receive deployment defaults through the same resolver. The filesystem and subprocess providers share one execution world, and bootstrap paths cross through the filesystem's explicit host-file mapping or a configured preinstalled bootstrap.
+
+File mode, observed denial and enforcement completeness travel in `PtcRunResult.sandbox` separately from the program outcome. Restricted execution fails if the required sandbox backend cannot launch. Full access is an explicit policy mode. Program success does not prove full enforcement, and neither the `process` descriptor nor the extra control pipe claims multi-tenant isolation.
+
+The private Python provider keeps its existing execution implementation and configured wall deadline. Its resolver accepts cwd but rejects an explicit file policy or per-call timeout override; a capability descriptor never silently grants unsupported protection.
+
+### Control and lifetime
+
+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.
+
+### Resource limits
+
+The default elapsed deadline is 120 seconds, capped at 600 seconds by default. It includes runtime setup and nested tool or approval waits. V8 old-generation memory, serialized outer output and control traffic have separate configured bounds. The heap limit excludes native allocations and descendant memory, and elapsed time is not a process-tree CPU budget.
+
+## Alternatives considered
+
+**Keep the worker and add tool checks.** Tool checks cannot intercept direct Node imports or establish OS confinement. Keeping a worker inside a confined supervisor process preserves worker metering but adds another execution lifetime without supplying a process-tree CPU limit.
+
+**Use an in-process JavaScript realm.** A language-level realm does not enforce the filesystem and process policy required for direct Node APIs. OS confinement and a host-owned process lifetime are the required protections.
+
+**Copy Codex Code Mode's execution model.** [Codex Code Mode](https://github.com/openai/codex/blob/02a8f038b87ad34d4a1dc5058eda26972ed7aa6c/codex-rs/code-mode-protocol/src/description.rs) exposes fresh raw-JavaScript isolates and host tool callbacks, with yield/wait observations separate from execution lifetime. Removing Node APIs or adding resumable cells would change PTC's programming and logging model. The retained design keeps direct Node access under OS policy and one-shot results.
+
+**Treat worker active time as a CPU limit.** Event-loop utilization counts active wall time in one worker, not CPU consumed by Node and its descendants. The process provider uses a host-owned elapsed deadline and does not make that stronger claim.
+
+## Consequences
+
+Each `run_code` pays for a Node process launch and OS sandbox setup. Long nested tools and approvals consume the same elapsed budget as direct program work. Cleanup can extend the caller's wait beyond that deadline. Stronger descendant containment remains dependent on the selected subprocess backend; its fallback limitations remain visible rather than being upgraded by the runtime label.
+
+`run_code` requires the program and its description. Service callers can resolve supported execution choices; model-facing timeout and approval controls have a separate consumer owner. Every nested tool still passes through its normal policy and logging path.
+
+<a id="deferred-timeout-design"></a>
+## Deferred timeout design
+
+The elapsed defaults are a revisitable deployment choice. Further design must separate responsiveness from termination: yielding output to a model does not itself stop a program, and adding waitable cells introduces ownership, cancellation, partial-output logging, turn-end and resume obligations.
+
+Open questions include whether approval waits consume the program budget, how sequential long-running tools compose, whether a separate total-lifetime backstop is needed, and which process-tree CPU/RSS limits can be enforced consistently. A persistent kernel additionally needs a Session-log representation of retained state. These questions do not silently pause or extend the shipped elapsed timer.

+ 56 - 0
.agents/notes/implemented/architecture/2026-09-11-sandboxed-node-ptc-runtime.zh.md

@@ -0,0 +1,56 @@
+# Agent Note: PTC 的沙箱 Node 执行
+
+Status: implemented
+
+[English](2026-09-11-sandboxed-node-ptc-runtime.md) | 中文
+
+## 问题
+
+Node worker 隔离 JavaScript 状态,但不应用调用 Session 的 OS 沙箱策略。模型代码可以直接导入文件系统与子进程 API,绕过工具策略路径,即使嵌套 `tools.*` 调用受到正确检查。终止 worker 也不能证明其子进程已停止。
+
+[PTC 基础](../feature/2026-06-15-ptc.zh.md)继续负责注册表呈现、生成绑定、分派日志和一次性结算。本决策取代其中基于 worker 的执行、信任与预算实现,同时保留消费方规则。
+
+## 决策
+
+`dsh-ptc-runtime-node` 在一个全新 Node 进程中运行每个程序。Host 解析执行选择,通过与 Bash 相同的 `ctx.sandbox` 提供方约束启动,并将进程生命周期交给 `ctx.subprocess`。子进程以直接 Node API、空模型环境和 Host 提供的异步绑定求值可擦除 TypeScript。本提供方不保留 worker 或持久内核。
+
+### 已解析输入与策略
+
+`PtcRuntime.resolve(request)` 验证支持的选项并补全 `PtcRunSpec`;`run(spec)` 不引入默认值。PTC 传入调用 Session 的 cwd 与已解析常设策略。直接运行时调用方通过同一解析器取得部署默认值。文件系统与子进程提供方共享一个执行世界,bootstrap 路径通过文件系统的显式宿主文件映射或配置的预安装 bootstrap 传递。
+
+文件模式、观察到的拒绝与强制完整性通过 `PtcRunResult.sandbox` 独立于程序结果传递。所需沙箱后端无法启动时,受限执行失败。完整访问是显式策略模式。程序成功不证明完整强制能力,`process` 描述符与额外控制管道也不声明多租户隔离。
+
+私有 Python 提供方保留现有执行实现与配置的经过时间截止。其解析器接受 cwd,但拒绝显式文件策略或单次 timeout 覆盖;能力描述符不会静默授予不受支持的保护。
+
+### 控制与生命周期
+
+子进程所有者提供专用的继承式二进制控制通道,与程序 stdout/stderr 及 launcher 生命周期 IPC 分开。Host 限制帧、排队写入、待处理调用和未完成参数字节,然后在分派前验证调用身份与绑定允许列表。模型代码可以写入该通道,因此其中字节仍不可信。
+
+程序完成、超时、取消与协议失败都通过受管进程所有者关闭执行。选择结果后停止执行计时器;清理随后等待直接结果与受管范围停稳。PTC 桥接在外层工具结果结算前,另行取消并排空嵌套工具分派。拒绝或传输失败绝不自动重放可能已经产生副作用的程序。
+
+### 资源限制
+
+默认经过时间截止为 120 秒,默认上限为 600 秒。它包括运行时准备以及嵌套工具或审批等待。V8 老生代内存、序列化外层输出与控制通信具有独立配置的上限。堆限制不包含原生分配和后代内存,经过时间也不是进程树 CPU 预算。
+
+## 考虑过的替代方案
+
+**保留 worker 并增加工具检查。** 工具检查不能拦截直接 Node 导入,也不能建立 OS 约束。在受限监督进程中保留 worker 可以保留 worker 计量,但会增加一层执行生命周期,仍不能提供进程树 CPU 限制。
+
+**使用同进程 JavaScript realm。** 语言级 realm 不强制直接 Node API 所需的文件系统与进程策略。所需保护是 OS 约束与 Host 拥有的进程生命周期。
+
+**复制 Codex Code Mode 的执行模型。** [Codex Code Mode](https://github.com/openai/codex/blob/02a8f038b87ad34d4a1dc5058eda26972ed7aa6c/codex-rs/code-mode-protocol/src/description.rs) 提供全新原始 JavaScript isolate 与 Host 工具回调,yield/wait 观察与执行生命周期分开。移除 Node API 或增加可恢复单元会改变 PTC 的编程与日志模型。保留的设计在 OS 策略下提供直接 Node 访问,并返回一次性结果。
+
+**把 worker active time 当作 CPU 限制。** 事件循环占用率计量单个 worker 的活跃经过时间,不是 Node 及其后代消耗的 CPU。进程提供方使用 Host 拥有的经过时间截止,不作更强声明。
+
+## 后果
+
+每次 `run_code` 都承担 Node 进程启动与 OS 沙箱准备成本。较长的嵌套工具和审批消耗与直接程序工作相同的经过时间预算。清理可能让调用方在截止后继续等待。更强的后代约束仍取决于所选子进程后端;其 fallback 限制保持可见,不会被运行时标签提升。
+
+`run_code` 要求程序及其描述。服务调用方可以解析支持的执行选择;面向模型的 timeout 与审批控制由独立消费方负责。每个嵌套工具仍经过正常策略与日志路径。
+
+<a id="deferred-timeout-design"></a>
+## 延后评估 timeout 设计
+
+经过时间默认值是可以重新评估的部署选择。后续设计必须将响应及时性与终止分开:向模型交出输出本身不会停止程序;增加可等待单元会引入所有权、取消、部分输出日志、轮次结束和恢复义务。
+
+开放问题包括审批等待是否消耗程序预算、顺序执行的长任务工具如何组合、是否需要独立的总生命周期兜底,以及哪些进程树 CPU/RSS 上限可以一致强制执行。持久内核还需要在 Session 日志中表示保留状态。这些问题不会静默暂停或延长已发布的经过时间计时器。

+ 6 - 0
.agents/notes/implemented/architecture/2026-09-11-subprocess-control-pipe.i18n.yaml

@@ -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/implemented/architecture/2026-09-11-subprocess-control-pipe.md
+2026-09-11-subprocess-control-pipe.md: 438f840958cb2f7f576b899335602ecff1e8a5ef
+2026-09-11-subprocess-control-pipe.zh.md: f4305d279342bb6c91162db5748ed49743d352c6

+ 37 - 0
.agents/notes/implemented/architecture/2026-09-11-subprocess-control-pipe.md

@@ -0,0 +1,37 @@
+# Agent Note: Subprocess control pipe
+
+Status: implemented
+
+English | [中文](2026-09-11-subprocess-control-pipe.zh.md)
+
+## Problem
+
+A managed Node program can write arbitrary bytes to stdout and stderr. A host protocol sharing either stream cannot distinguish those bytes from program diagnostics without restricting ordinary Node behavior. Windows process wrappers also require explicit descriptor inheritance before the child runtime allocates its own descriptors.
+
+## Decision
+
+Ordinary subprocess requests optionally set `stdio.control: 'pipe'` and receive a raw `Duplex` as `handle.control`. The target opens fd 7 through `@deepseek-ai/dsh-subprocess/control`. The provider owns the environment marker; the child helper consumes it. Consumers own bounded framing, message validation, backpressure, and endpoint closure. Standard output collection and managed-range lifetime retain their existing semantics. The provider tracks open control endpoints independently until they close, including after their managed range exits, and disposal closes remaining endpoints after attempting range teardown.
+
+POSIX launchers preserve fd 7 across exec. Windows ordinary Job and restricted-token launchers place the pipe at slot 7 in the CRT startup descriptor table, preserve standard handles, and leave slots 3–6 closed in the payload. Each wrapper closes its carrier after transferring ownership. Handle inheritance is enabled only around process creation. This channel grants no host capability: the child remains untrusted, and every host tool request requires its usual dispatch and approval checks.
+
+Control pipes use Node's `overlapped` stdio disposition, which equals `pipe` on POSIX and creates Windows handles with `FILE_FLAG_OVERLAPPED`. Reads and writes can then proceed independently, including a child sending its first message before the host sends anything.
+
+Windows managed-range proof observes the runner process exit and its private IPC result independently of caller stream drains. A clean runner exit with a received result confirms its range; a clean exit without a result remains pending only until the IPC channel closes. Paused control output cannot delay this proof or prevent provider disposal from closing the endpoint.
+
+The filesystem and subprocess services remain replaceable together by remote providers. Neither the public handle nor its request exposes a host path, process identifier, execution-world flag, or transport negotiation catalogue. Terminal allocation remains asynchronous and does not gain an extra descriptor.
+
+## Alternatives considered
+
+**Stdout framing.** Native code and ordinary `process.stdout.write` can emit arbitrary bytes, so protocol integrity would depend on intercepting program output.
+
+**Synchronous Windows pipes.** A blocking read on an inherited synchronous pipe can prevent a concurrent write on the same handle from progressing. A parent-first echo does not expose this deadlock; child-first readiness and teardown require overlapped handles.
+
+**Node IPC.** The Windows process supervisor already uses a private IPC channel. Coupling payload requests to that management protocol would expose supervisor operations and complicate remote transport.
+
+**A late Windows descriptor replacement.** Replacing fd 7 after Node starts can overwrite an internal descriptor. The CRT startup table reserves it before runtime initialization and keeps the child API identical across hosts.
+
+**A different descriptor per platform.** Per-platform numbers would add bootstrap branching without removing the native startup work. One fixed slot also allows remote providers to preserve the same child API.
+
+## Consequences
+
+Native wrappers must preserve and close one extra pipe explicitly. The subprocess service does not interpret control messages or buffer them for callers, so protocol consumers must bound their own retained input and output. The OS sandbox and managed process owner remain responsible for confinement and teardown; a dedicated transport is not a JavaScript security boundary.

+ 37 - 0
.agents/notes/implemented/architecture/2026-09-11-subprocess-control-pipe.zh.md

@@ -0,0 +1,37 @@
+# Agent Note: Subprocess control pipe
+
+Status: implemented
+
+[English](2026-09-11-subprocess-control-pipe.md) | 中文
+
+## 问题
+
+受管 Node 程序可以向 stdout 和 stderr 写入任意字节。若宿主协议共用其中任一流,就无法在不限制普通 Node 行为的前提下区分协议字节和程序诊断。Windows 进程包装器还要求在子运行时分配自身描述符前显式设置描述符继承。
+
+## 决策
+
+普通 subprocess 请求可以设置 `stdio.control: 'pipe'`,并通过 `handle.control` 收到原始 `Duplex`。目标通过 `@deepseek-ai/dsh-subprocess/control` 打开 fd 7。提供方拥有环境标记;子进程辅助函数会消费它。消费方负责有界分帧、消息校验、背压和端点关闭。标准输出收集与受管范围生命周期保留现有语义。提供方独立跟踪打开的控制端点,直到它们关闭,包括受管范围退出之后;销毁时先尝试受管范围拆卸,再关闭剩余端点。
+
+POSIX 启动器在 exec 时保留 fd 7。Windows 普通 Job 与受限令牌启动器把管道放入 CRT 启动描述符表的槽 7,保留标准句柄,并让负载中的槽 3–6 保持关闭。每层包装器在转移所有权后关闭自身承载端。句柄继承仅在进程创建期间启用。该通道不授予任何宿主能力:子进程仍不可信,每次宿主工具请求都需要通常的分发与审批检查。
+
+控制管道使用 Node 的 `overlapped` stdio 处置方式:它在 POSIX 上等同于 `pipe`,在 Windows 上创建带有 `FILE_FLAG_OVERLAPPED` 的句柄。因此读写可以独立进行,包括子进程在宿主发送任何内容前发出第一条消息。
+
+Windows 受管范围证明独立观察 runner 进程退出及其私有 IPC 结果,不依赖调用方排空流。收到结果且 runner 正常退出即可确认范围结束;正常退出但未收到结果时,最多等待到 IPC 通道关闭。暂停的控制输出不会延迟该证明,也不会阻止提供方销毁时关闭端点。
+
+文件系统与 subprocess 服务仍可由远程提供方成对替换。公共句柄和请求均不公开宿主路径、进程标识、执行世界标志或传输协商目录。终端分配保持异步,且不增加额外描述符。
+
+## 考虑过的替代方案
+
+**Stdout 分帧。** 原生代码和普通 `process.stdout.write` 可以输出任意字节,因此协议完整性将依赖于拦截程序输出。
+
+**同步 Windows 管道。** 继承的同步管道上的阻塞读取可能阻止同一句柄上的并发写入继续执行。宿主先发送的回显无法暴露该死锁;子进程先报告就绪以及拆卸都需要重叠 I/O 句柄。
+
+**Node IPC。** Windows 进程监督器已经使用私有 IPC 通道。把负载请求耦合到该管理协议会暴露监督器操作,并使远程传输复杂化。
+
+**在 Windows 启动后替换描述符。** Node 启动后替换 fd 7 可能覆盖内部描述符。CRT 启动表在运行时初始化前保留它,并保持各宿主的子进程 API 一致。
+
+**各平台使用不同描述符。** 不同平台的编号会增加引导分支,却不能省去原生启动工作。固定槽位也让远程提供方可以保留相同的子进程 API。
+
+## 后果
+
+原生包装器必须显式保留并关闭一条额外管道。subprocess 服务不解释控制消息,也不替调用方缓冲消息,因此协议消费方必须自行限制保留的输入和输出。操作系统沙箱与受管进程拥有者仍负责限制和拆卸;独立传输不是 JavaScript 安全边界。

+ 6 - 0
.agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.i18n.yaml

@@ -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/implemented/architecture/2026-09-12-computer-use-provider-registration.md
+2026-09-12-computer-use-provider-registration.md: 52e51ac9c4f89b6ad730e647280ba3f0c04ea3c9
+2026-09-12-computer-use-provider-registration.zh.md: 2d8802e1d8cb3c5262cbc2ba6185262f949bca01

+ 35 - 0
.agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.md

@@ -0,0 +1,35 @@
+# Agent Note: Computer-use provider registration
+
+Status: implemented
+
+English | [中文](2026-09-12-computer-use-provider-registration.zh.md)
+
+## Problem
+
+Desktop providers expose different operations, observation formats, and platform facilities. DSH needs to prevent accidentally enabling two providers in one composition while allowing provider-specific integrations to work without committing to a common action API.
+
+## Decision
+
+The DSH capability is named **computer use**. [`dsh-computer-use`](../../../../packages/computer-use/computer-use/README.md) owns `ctx.computerUse`, which registers one provider-owned name and returns its effect disposer. A second registration fails regardless of its name. The service contains no provider object, shared operation type, dispatch method, Session lock, or runtime selector.
+
+**Cua Driver** names the upstream implementation. The [MCP provider](../../../../packages/experimental/computer-use-cua-driver-mcp/README.md) connects an installed executable. The [native provider](../../../../packages/experimental/computer-use-cua-driver-native/README.md) installs the upstream native npm dependency. Both remain experimental and join the explicit public-release allowlist; neither is enabled by default.
+
+Each integration exposes the upstream tool catalog. MCP result conversion stays in `dsh-mcp-client`, whose callback-based tool adapter also converts native Cua Driver results. The computer-use service has no dependency on that adapter or either provider.
+
+Provider teardown retains the registration until tool admission stops and owned work and resources close. A grouped Cordis effect orders that cleanup; separate effects may dispose concurrently. The native provider uses `tools/execute` to share cancellation across native calls and screenshot admission while preserving execution identity. Concurrent Sessions remain caller-coordinated because a provider registration does not own an observe, act, and verify workflow.
+
+## Alternatives considered
+
+**Unified action API.** A common screenshot, input, and window vocabulary would require translating provider-specific semantics without a current consumer that needs portability. Provider-owned tools preserve those semantics.
+
+**Only external MCP.** This reuses an installed driver and its process identity but leaves a separate installation prerequisite. The native provider supplies a one-package runtime installation.
+
+**Only embedded native runtime.** Native integration makes DSH own runtime lifecycle and shares native failures with its backend process. The MCP provider remains available for independently installed drivers.
+
+**Session ownership broker.** Reserving a desktop across a whole workflow requires an explicit acquisition and release policy. The current service enforces provider registration only, leaving workflow coordination to callers.
+
+## Consequences
+
+The service remains independent of experimental packages. The public-release allowlist admits the two provider packages without promoting their support status. Configuration selects a provider, and switching requires unloading the current provider first.
+
+Native platform support and host permissions remain upstream and deployment responsibilities. macOS cursor-overlay hosting and dedicated Desktop permission UI are deferred. Cancellation stops waiting and propagates to the driver; it does not promise rollback of delivered desktop input.

+ 35 - 0
.agents/notes/implemented/architecture/2026-09-12-computer-use-provider-registration.zh.md

@@ -0,0 +1,35 @@
+# Agent Note: Computer-use provider registration
+
+Status: implemented
+
+[English](2026-09-12-computer-use-provider-registration.md) | 中文
+
+## Problem
+
+桌面提供方暴露不同的操作、观测格式和平台设施。DSH 需要防止在一个组合中意外启用两个提供方,同时让各提供方的集成正常工作,而不承诺通用操作 API。
+
+## Decision
+
+DSH 能力称为 **computer use(计算机操作)**。[`dsh-computer-use`](../../../../packages/computer-use/computer-use/README.zh.md) 拥有 `ctx.computerUse`,注册一个提供方自定的名称并返回其 effect 清理函数。第二次注册无论名称为何都会失败。服务不包含提供方对象、共享操作类型、分派方法、Session 锁或运行时选择器。
+
+**Cua Driver** 是上游实现的名称。[MCP 提供方](../../../../packages/experimental/computer-use-cua-driver-mcp/README.zh.md)连接已安装的可执行文件。[原生提供方](../../../../packages/experimental/computer-use-cua-driver-native/README.zh.md)安装上游原生 npm 依赖。两者均保持实验性并加入显式公开发布允许列表;均不默认启用。
+
+各集成暴露上游工具目录。MCP 结果转换保留在 `dsh-mcp-client` 中,其基于回调的工具适配函数也转换原生 Cua Driver 结果。计算机操作服务不依赖该适配函数或任一提供方。
+
+提供方卸载时保留注册,直到停止接收工具调用且自有工作和资源关闭。分组 Cordis effect 为此清理排序;独立 effect 可能并发清理。原生提供方通过 `tools/execute` 在原生调用和截图准入之间共享取消信号,同时保留执行标识。并发 Session 由调用方协调,因为提供方注册不拥有观察、操作和验证流程。
+
+## Alternatives considered
+
+**统一操作 API。** 通用截图、输入和窗口术语需要转换提供方特有的语义,而当前没有需要可移植性的消费者。由提供方拥有工具可保留这些语义。
+
+**仅外部 MCP。** 此方案复用已安装的驱动及其进程身份,但保留独立安装的前提。原生提供方提供单包运行时安装。
+
+**仅嵌入原生运行时。** 原生集成让 DSH 拥有运行时生命周期,并与后端进程共享原生故障。MCP 提供方保留独立安装驱动的选项。
+
+**Session 所有权代理。** 在完整流程期间预留桌面需要显式获取和释放策略。当前服务仅约束提供方注册,将流程协调留给调用方。
+
+## Consequences
+
+服务保持独立于实验性包。公开发布允许列表接纳两个提供方包,但不提升其支持状态。配置选择提供方,切换需要先卸载当前提供方。
+
+原生平台支持和宿主权限仍由上游和部署负责。macOS 光标叠加层托管和专用 Desktop 权限界面暂缓实现。取消会停止等待并传播到驱动;不承诺回滚已交付的桌面输入。

+ 6 - 0
.agents/notes/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.i18n.yaml

@@ -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/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.md
+2026-09-12-ptc-runtime-vocabulary.md: 86d2132b735166fb0ee7c4a4929d5ca64ba0880a
+2026-09-12-ptc-runtime-vocabulary.zh.md: 5951c43b9554a6266d96cc60d99c11e84acc138f

+ 29 - 0
.agents/notes/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.md

@@ -0,0 +1,29 @@
+# Agent Note: PTC runtime vocabulary
+
+Status: implemented
+
+English | [中文](2026-09-12-ptc-runtime-vocabulary.zh.md)
+
+## Problem
+
+PTC mode and its execution providers need one searchable name across package manifests, service lookup, public types, configuration, and documentation. Mixed runtime prefixes make it difficult to trace a provider from a profile to its implementation and packaged bootstrap.
+
+## Decision
+
+The execution capability uses the `ptc-runtime` package family, `PtcRuntime` types, and `ctx.ptcRuntime`. The Node and private experimental Python providers share this vocabulary. Profile entry identifiers, internal bootstrap selectors, compiler references, package exports, and generated catalogs use the same names; no compatibility package or second service registration is supplied.
+
+The PTC names for runtime packages and SDK language types supersede the exceptions recorded in [the earlier naming decision](../../archived/architecture/2026-08-25-rename-code-mode-to-ptc.md), giving providers and callers one searchable vocabulary.
+
+The model-facing `run_code` operation, its `code` source argument, and its stable failure identity keep their descriptive names. General source-code terminology, error codes, external project names and URLs, historical migration identifiers, and sealed Agent Notes retain their meanings and recorded spelling. The naming decision does not change program execution, sandbox authority, deadlines, bindings, or Session formats.
+
+## Alternatives considered
+
+**Keep a separate generic runtime prefix.** The providers remain independent of tool and Session ownership, but their package and service names identify the PTC execution capability. A separate prefix adds a second name without separating an independently evolving feature.
+
+**Replace every occurrence of “code.”** Program source, operation names, external references, and historical records describe different subjects. Replacing them would change public operations or recorded facts beyond the runtime naming decision.
+
+**Publish old-name aliases.** The APIs are pre-stable and every repository consumer moves together. Aliases would preserve duplicate package and service identities and make subsequent discovery ambiguous.
+
+## Consequences
+
+Deployments and source consumers use the PTC package names and configuration identifiers together. Existing `run_code` transcripts and error routing remain readable. Mechanical audits compare renamed source tokens, preserve external URLs and frozen records, and inspect every residual old runtime name; built profile, package, and snapshot checks exercise the consumers that static imports cannot cover.

+ 29 - 0
.agents/notes/implemented/architecture/2026-09-12-ptc-runtime-vocabulary.zh.md

@@ -0,0 +1,29 @@
+# Agent Note: PTC 运行时词汇
+
+Status: implemented
+
+[English](2026-09-12-ptc-runtime-vocabulary.md) | 中文
+
+## Problem
+
+PTC 模式及其执行提供方需要在包清单、服务查找、公共类型、配置和文档中使用同一个可搜索的名称。混合的运行时前缀使维护者难以从 profile 追踪到提供方实现及打包后的引导程序。
+
+## Decision
+
+执行能力使用 `ptc-runtime` 包族、`PtcRuntime` 类型和 `ctx.ptcRuntime`。Node 与私有实验性 Python 提供方共享这套词汇。Profile 条目标识符、内部引导选择器、编译器引用、包导出和生成目录使用相同名称;不提供兼容包或第二份服务注册。
+
+运行时包与 SDK 语言类型的 PTC 名称取代[早期命名决定](../../archived/architecture/2026-08-25-rename-code-mode-to-ptc.md)记录的例外,使提供方与调用方使用同一套可搜索的词汇。
+
+面向模型的 `run_code` 操作、其 `code` 源码参数和稳定失败标识保留描述性名称。一般源码术语、错误码、外部项目名称与 URL、历史迁移标识符以及封存 Agent Note 保留原有含义和记录拼写。这一命名决定不改变程序执行、沙箱权限、期限、绑定或 Session 格式。
+
+## Alternatives considered
+
+**保留单独的通用运行时前缀。** 提供方仍独立于工具和 Session 的所有权,但其包名与服务名标识 PTC 执行能力。单独的前缀会增加第二个名称,却没有分离出独立演进的功能。
+
+**替换每一处“code”。** 程序源码、操作名称、外部引用和历史记录描述不同对象。替换这些内容会改变运行时命名决定之外的公共操作或已记录事实。
+
+**发布旧名称别名。** API 尚未稳定,仓库中的每个消费方共同迁移。别名会保留重复的包和服务身份,使后续查找含糊不清。
+
+## Consequences
+
+部署配置和源码消费方共同使用 PTC 包名与配置标识符。已有的 `run_code` 记录与错误路由仍可读取。机械审计比较重命名前后的源码 token,保留外部 URL 和冻结记录,并检查每一处残留旧运行时名称;构建后的 profile、包和 snapshot 检查覆盖静态导入无法验证的消费方。

+ 2 - 2
.agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.md
-2026-09-05-nested-terminal-cards.md: 03b80382a00c2302d25d2b572c3b06aedff0e1b3
-2026-09-05-nested-terminal-cards.zh.md: bf97fffa2a8d92562e982d498d17b830e1e25f48
+2026-09-05-nested-terminal-cards.md: c5946a332f2879588cb6ee334404fdaa92a8cb28
+2026-09-05-nested-terminal-cards.zh.md: 74056014ce5b9ac38574c5551420a4e25808b9b4

+ 2 - 2
.agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.md

@@ -10,7 +10,7 @@ A shell command dispatched through `run_code` carries the arguments and rendered
 
 ## Decision
 
-`terminalCardModel` applies the same eligibility checks to root and Code Dispatch calls, without rejecting `parentCallId`. Supported running and settled `bash`, `pwsh`, and `terminal_send` calls use the existing terminal card. Background calls, tool errors, malformed inputs, missing call heads, and unsupported result content retain generic fallback. Persistent shells remain eligible while running and generic when settled; a nonzero process exit remains terminal result data rather than a tool error.
+`terminalCardModel` applies the same eligibility checks to root and PTC dispatch calls, without rejecting `parentCallId`. Supported running and settled `bash`, `pwsh`, and `terminal_send` calls use the existing terminal card. Background calls, tool errors, malformed inputs, missing call heads, and unsupported result content retain generic fallback. Persistent shells remain eligible while running and generic when settled; a nonzero process exit remains terminal result data rather than a tool error.
 
 This partially supersedes only the terminal child-card prohibition in [Client-derived tool presentation](../architecture/2026-08-23-client-derived-tool-presentation.md). That note remains active for Client presentation ownership and the diff/read/search/web child restrictions. No Host presenter, event, schema, metadata, call-tree, or model-context change is required. The metadata and execution-local value decisions in [canonical tool output](../architecture/2026-07-20-canonical-tool-output-contract.md) and [PTC typed returns](../feature/2026-07-20-ptc-typed-tool-returns.md) remain intact; metadata omission does not prohibit Client-derived terminal cards.
 
@@ -32,4 +32,4 @@ Rows and Details share terminal derivation for nested calls without a second ren
 
 ## Verification
 
-The [terminal card specs](../../../../packages/client/ui-tool/tests/terminal-card.client.spec.tsx) cover root/child eligibility, running and settled Details, and fallback cases. The [assembled Code Dispatch specs](../../../../packages/client/ui-tool/tests/chat-code-subcalls.client.spec.tsx) cover nested terminal rendering through the conversation tree. The [notice specs](../../../../packages/spill/spill-policy/tests/notice.spec.ts) pin the historical spelling with a literal fixture independent of the formatter. The [spill-policy-to-UI specs](../../../../packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts) exercise actual root and PTC spill production, unchanged full text and programmatic values, byte caps, notice-only output, and terminal fallback. Browser replay owns the visible nested-card change; nonterminal child behavior remains outside this fix.
+The [terminal card specs](../../../../packages/client/ui-tool/tests/terminal-card.client.spec.tsx) cover root/child eligibility, running and settled Details, and fallback cases. The [assembled PTC dispatch specs](../../../../packages/client/ui-tool/tests/chat-ptc-subcalls.client.spec.tsx) cover nested terminal rendering through the conversation tree. The [notice specs](../../../../packages/spill/spill-policy/tests/notice.spec.ts) pin the historical spelling with a literal fixture independent of the formatter. The [spill-policy-to-UI specs](../../../../packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts) exercise actual root and PTC spill production, unchanged full text and programmatic values, byte caps, notice-only output, and terminal fallback. Browser replay owns the visible nested-card change; nonterminal child behavior remains outside this fix.

+ 2 - 2
.agents/notes/implemented/bug-fix/2026-09-05-nested-terminal-cards.zh.md

@@ -10,7 +10,7 @@ Status: implemented
 
 ## 决策
 
-`terminalCardModel` 对根调用与 Code Dispatch 调用应用相同的适用检查,不因 `parentCallId` 拒绝调用。受支持的运行中与已完成的 `bash`、`pwsh` 和 `terminal_send` 调用使用现有 terminal 卡片。后台调用、工具错误、格式错误的输入、缺失的调用头和不受支持的结果内容保留通用回退。持久 shell 在运行中仍可使用 terminal,完成后使用通用展示;非零进程退出仍是 terminal 结果数据,而非工具错误。
+`terminalCardModel` 对根调用与 PTC dispatch 调用应用相同的适用检查,不因 `parentCallId` 拒绝调用。受支持的运行中与已完成的 `bash`、`pwsh` 和 `terminal_send` 调用使用现有 terminal 卡片。后台调用、工具错误、格式错误的输入、缺失的调用头和不受支持的结果内容保留通用回退。持久 shell 在运行中仍可使用 terminal,完成后使用通用展示;非零进程退出仍是 terminal 结果数据,而非工具错误。
 
 本文仅部分取代 [Client 派生工具展示](../architecture/2026-08-23-client-derived-tool-presentation.zh.md)中的 terminal 子调用卡片禁令。该文继续负责 Client 展示所有权及 diff/read/search/web 子调用限制。无需更改 Host 展示转换器、事件、schema、元数据、调用树或模型上下文。[规范工具输出](../architecture/2026-07-20-canonical-tool-output-contract.zh.md)与 [PTC 类型化返回值](../feature/2026-07-20-ptc-typed-tool-returns.zh.md)中的元数据和执行期值决策保持不变;省略元数据不禁止 Client 派生 terminal 卡片。
 
@@ -32,4 +32,4 @@ Status: implemented
 
 ## 验证
 
-[Terminal 卡片测试](../../../../packages/client/ui-tool/tests/terminal-card.client.spec.tsx)覆盖根/子调用适用性、运行中与已完成的 Details 以及回退情况。[组装后的 Code Dispatch 测试](../../../../packages/client/ui-tool/tests/chat-code-subcalls.client.spec.tsx)覆盖经对话树渲染的嵌套 terminal。[通知测试](../../../../packages/spill/spill-policy/tests/notice.spec.ts)使用独立于格式化函数的字面量 fixture(测试前置数据)固定历史拼写。[spill-policy 到 UI 的测试](../../../../packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts)覆盖真实的根调用与 PTC spill 生成、保持不变的完整文本和程序化值、字节上限、仅含通知的输出以及 terminal 回退。浏览器回放负责验证可见的嵌套卡片变化;非 terminal 子调用行为不属于本修复。
+[Terminal 卡片测试](../../../../packages/client/ui-tool/tests/terminal-card.client.spec.tsx)覆盖根/子调用适用性、运行中与已完成的 Details 以及回退情况。[组装后的 PTC dispatch 测试](../../../../packages/client/ui-tool/tests/chat-ptc-subcalls.client.spec.tsx)覆盖经对话树渲染的嵌套 terminal。[通知测试](../../../../packages/spill/spill-policy/tests/notice.spec.ts)使用独立于格式化函数的字面量 fixture(测试前置数据)固定历史拼写。[spill-policy 到 UI 的测试](../../../../packages/client/ui-tool/tests/spill-policy-terminal.client.spec.ts)覆盖真实的根调用与 PTC spill 生成、保持不变的完整文本和程序化值、字节上限、仅含通知的输出以及 terminal 回退。浏览器回放负责验证可见的嵌套卡片变化;非 terminal 子调用行为不属于本修复。

+ 2 - 2
.agents/notes/implemented/feature/2026-06-15-ptc.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-06-15-ptc.md
-2026-06-15-ptc.md: 43bd4a4fd5c49a449ceccb7f1889b80b0844214d
-2026-06-15-ptc.zh.md: a6aaf203186ad3023ce39a9df04fc1b233db88eb
+2026-06-15-ptc.md: d5ed3fc79d4586d3754b7273e08970fb066fe6e9
+2026-06-15-ptc.zh.md: 1434db5c352cdb14b857dc5d4ca3bb2f5332e98e

+ 27 - 30
.agents/notes/implemented/feature/2026-06-15-ptc.md

@@ -12,17 +12,23 @@ For multi-step tool work this is token-heavy and serial. The model cannot compos
 
 Cloudflare's [Code Mode](https://blog.cloudflare.com/code-mode/) proposes an alternative grounded in a simple observation: LLMs are better at writing code than at emitting tool calls, because they have seen millions of lines of real code and comparatively few contrived tool-calling traces. Instead of one tool call per step, the model writes a TypeScript program against a generated API over the tools, the program executes in a sandboxed runtime, and the model curates what comes back — only what it prints or returns — instead of every intermediate result.
 
-Tool presentation belongs to the registry that owns tool visibility: implementing a second presentation as an after-the-fact waterfall transform would make correctness depend on listener order and fight [reconstructable requests](../architecture/2026-07-05-reconstructable-requests.md). The execution substrate is also part of the foundation rather than a placeholder: Node `worker_threads` provides a separate isolate, an empty environment, heap caps, and termination of a hot synchronous loop, while fitting the harness's existing trust model (§Trust posture).
+Tool presentation belongs to the registry that owns tool visibility: implementing another presentation as an after-the-fact waterfall transform would depend on listener order and fight [reconstructable requests](../architecture/2026-07-05-reconstructable-requests.md). Execution must preserve fresh program state, host-owned bindings and cancellable settlement independently of the presentation mode.
 
 ## Decision
 
 Three decisions, each elaborated in its own section below:
 
 1. **PTC mode is a first-class presentation mode of `ToolRuntime`** (`dsh-tools`), selected by a validated `mode` config: `'native'` (the default, contributing the visible capability schemas), `'ptc'` (the registry contributes only its reserved `run_code` transport plus a generated SDK `.d.ts` in the system prompt), or `'both'` (native schemas and the transport + SDK). The registry constructs its canonical contribution at the source; the cooperative prompt-assembly result remains authoritative, and the logged request header records exactly that returned presentation.
-2. **Code execution is a capability seam** — `packages/code-runtime/` contains the Service Definition package `@deepseek-ai/dsh-code-runtime`, which owns `ctx.codeRuntime` ([capability seams](../architecture/2026-06-13-capability-seams.md); Consumer = `dsh-tools`, with core-consumes-a-seam precedent in `agent-loop` → `dsh-llm`). The runtime knows nothing about tools: it is handed a program and named async bindings, runs the program, and reports `{ value, logs, error? }`. Language and substrate are backend properties, so a future Python or container backend is another Service Provider package, not a redesign.
-3. **The shipped implementation is `@deepseek-ai/dsh-code-runtime-worker-thread`**: one fresh Node worker thread per run, executing the model's TypeScript after type-strip, with bindings bridged over the message port, an empty environment, configurable heap/output/time caps, and hard termination. Its trust posture is bash-equivalent by design — no unsafe-acknowledgement flags — because the harness already ships `dsh-bash-local`, which executes arbitrary model-written shell commands with strictly *more* ambient authority.
+2. **Code execution is a capability seam** — `packages/ptc-runtime/` contains the Service Definition package `@deepseek-ai/dsh-ptc-runtime`, which owns `ctx.ptcRuntime` ([capability seams](../architecture/2026-06-13-capability-seams.md); Consumer = `dsh-tools`, with core-consumes-a-seam precedent in `agent-loop` → `dsh-llm`). The runtime knows nothing about tools: it is handed a program and named async bindings, runs the program, and reports `{ value, logs, error? }`. Language and substrate are backend properties, so a future Python or container backend is another Service Provider package, not a redesign.
+3. **The shipped implementation is `@deepseek-ai/dsh-ptc-runtime-node`**: each program runs in a fresh managed Node process under the calling Session's resolved sandbox policy. Direct Node APIs remain available within that policy; named host bindings cross a separately validated control channel.
 
-This note owns PTC mode's presentation, composition, isolation, and settlement foundation. The later [typed tool-return Agent Note](2026-07-20-ptc-typed-tool-returns.md) owns the generated output map, canonical binding values, `ToolCallError`, and the lossless outer-output boundary.
+This note owns PTC presentation, composition and settlement. The [typed tool-return decision](2026-07-20-ptc-typed-tool-returns.md) owns generated outputs, canonical binding values, `ToolCallError` and lossless outer output. The [sandboxed Node decision](../architecture/2026-09-11-sandboxed-node-ptc-runtime.md) supersedes the worker execution, trust posture and budget realization while preserving this note's consumer rules.
+
+### Per-program execution controls
+
+The consumer advertises `timeoutMs` from the runtime's readonly `timeout` descriptor and passes explicit values through its resolver. Unsupported providers expose no timeout field and reject an explicit request. Sandbox escalation uses the shared strict-widening approval helper before runtime resolution and launch; replacing only the resolved policy's mode keeps the grant local to one program. Nested dispatches retain the original Agent and Session, so their approvals and file policy remain independent. No failed program is replayed implicitly because earlier effects may already have committed.
+
+The [Node execution decision](../architecture/2026-09-11-sandboxed-node-ptc-runtime.md#deferred-timeout-design) owns elapsed-budget accounting and deferred lifetime questions.
 
 ### The registry owns the mode
 
@@ -32,7 +38,7 @@ This note owns PTC mode's presentation, composition, isolation, and settlement f
 
 **Interaction with `toolOrder`:** a configured `systemPrompt.toolOrder` naming native capabilities rejects every assembly under `mode: 'ptc'`, because those names are outside that mode's wire-validation universe. This is correct behavior, not a bug: a deployment using PTC mode updates its order config or drops it.
 
-**SDK prompt section.** In `'ptc'` and `'both'`, the lazy `tools:sdk` section in the tool-guidance order band renders the loaded runtime's language declarations plus fixed usage instructions for the scope's visible capabilities (TypeScript by default; the [language-dispatch note](../../archived/feature/2026-07-31-ptc-language-dispatch.md) added Python and the `ctx.codeRuntime.language` renderer table). It shares lookup and execution visibility, excludes `run_code`, and sorts tools lexicographically for byte-stable output.
+**SDK prompt section.** In `'ptc'` and `'both'`, the lazy `tools:sdk` section in the tool-guidance order band renders the loaded runtime's language declarations plus fixed usage instructions for the scope's visible capabilities (TypeScript by default; the [language-dispatch note](../../archived/feature/2026-07-31-ptc-language-dispatch.md) added Python and the `ctx.ptcRuntime.language` renderer table). It shares lookup and execution visibility, excludes `run_code`, and sorts tools lexicographically for byte-stable output.
 
 **Assembly ownership.** `run_code` and `tools:sdk` enter the trusted `system-prompt/assemble` waterfall as normal assembly inputs. A scoped `tools:sdk` section may shadow the global default before dispatch, and a listener may remove or replace either contribution. The waterfall's returned assembly is final, so whoever changes these inputs owns preserving a viable PTC mode protocol when the deployment expects PTC mode to remain usable; no restoration pass overrides deliberate composition.
 
@@ -43,7 +49,7 @@ This note owns PTC mode's presentation, composition, isolation, and settlement f
 Under `'ptc'` and `'both'` the registry owns `run_code` as a reserved presentation transport with two required parameters, `{ code: string; description: string }` (the description labels the call in UIs, the bash precedent). It is represented by a normal `ToolDefinition` for dispatch but stays outside the filterable capability layers, so restrictions cannot accidentally remove PTC mode's only entry point. Calls traverse the complete tool pipeline — `tools/pre-execute` → monotonic guards → `tools/execute` around dispatch → `tools/post-execute` → optional definition-owned `finalizeContent` → immutable `tools/result` notification — exactly like native calls; a permission plugin can inspect the program text before it runs, and final-result observers see the normalized outer outcome. Its `execute(args, exec)`:
 
 1. **Build bindings.** One run-scoped signal follows outer cancellation and is aborted whenever the run settles. Each visible tool binding snapshots lossless-JSON arguments, enters the native-contract dispatch pool (the [live-parallel note](2026-07-26-ptc-live-parallel-dispatch.md) owns the scheduling design), executes with a deterministic call id and the outer token as `parent`, defers returned contexts through the outer execution, and logs the `tool/ptc-dispatch-start`/`tool/ptc-dispatch` pair, the settle side carrying the full rendered result content. Success returns the tool's final canonical JSON value; failure becomes the program-visible `ToolCallError`. Every sub-call retains its own immutable execution identity and traverses the full tool pipeline.
-2. **Runs the program**: `ctx.codeRuntime.run({ program: args.code, bindings: [{ global: 'tools', functions }], signal: runController.signal })`. The runtime receives the run-scoped signal, not only the caller's outer signal, so any way the outer run settles also aborts work inside the runtime.
+2. **Runs resolved inputs:** `ctx.ptcRuntime.run(ctx.ptcRuntime.resolve(request))`. The request carries the program, bindings, run-scoped signal, calling Session cwd and applicable resolved file policy. The run-scoped signal connects outer settlement to execution cancellation.
 3. **Settle after quiescence.** When the runtime settles, the bridge aborts outstanding work and drains the dispatch queue before returning. Success returns captured logs and the completion value as canonical output; the registry renders that value into durable `tool/result.content`, which the result card reads directly. A runtime failure becomes `CodeRunFailedError`; backend rejection uses the registry's normal error boundary. Both produce structured error results, and no sub-call can append after `run_code` settles.
 
 **Sub-call contexts are deferred through the parent.** Injecting inside `run_code` would break parent call/result adjacency, so `ToolRunContext.deferContext()` collects every sub-result `additionalContexts` entry in dispatch order. The registry carries that array even when the program later throws, and the loop appends each entry only after the outer result and every sibling result in the step. An outer post-execute block discards tool-deferred entries and exposes only contexts explicitly attached by the blocking decision.
@@ -60,32 +66,23 @@ New sub-calls use `<parent>:ptc:<n>` ids, numbered in submission order. All call
 
 The [V2-to-V3 PTC specification](../../../../packages/session/session-format-v2-to-v3/README.md#ptc-vocabulary) owns exact historical tag and attribution conversion; [native V3 admission](../../../../packages/session/session-format-v2-to-v3/README.md#native-v3-admission) owns predecessor-tag refusal. These are not runtime aliases: an opaque extension must not acquire PTC lifecycle meaning merely through a version change.
 
-### The code-runtime seam
+### The ptc-runtime seam
 
-`packages/code-runtime/code-runtime/` — `@deepseek-ai/dsh-code-runtime`, depending only on `cordis`. An abstract `CodeRuntime extends Service` (`super(ctx, 'codeRuntime')`) plus the vocabulary:
+`dsh-ptc-runtime` owns `PtcRuntime` and the [request, resolved-spec, binding and result types](../../../../docs/subsystems/ptc-runtime.md). `resolve(request)` validates supported cwd, timeout and policy choices and supplies deployment defaults; `run(spec)` executes complete inputs. Named async bindings and optional rejection constructors keep registry-specific tool names outside the runtime.
 
-- `CodeRunRequest = { program: string; bindings: CodeBindingNamespace[]; signal?: AbortSignal }`
-- `CodeBindingNamespace = { global: string; functions: Record<string, (args: unknown) => Promise<CodeJsonValue>>; errorClass?: { name: string; memberNameProperty: string } }` — the runtime exposes each namespace as a global object of async functions inside the program; the optional descriptor asks the runtime to inject a real program-visible rejection class without teaching the seam consumer-specific names. `CodeJsonValue` is this dependency-light seam's structural lossless-JSON type, so binding arguments and resolutions cross the implementation's serialization boundary whole.
-- `CodeRunResult = { value?: CodeJsonValue; logs: string[]; error?: CodeRunFailure }` — program execution outcomes resolve as the `error` field. `run()` may reject only for caller/seam misuse (for example a duplicate binding namespace); consumers still contain a non-conforming backend rejection at their own error boundary.
-- `CodeRunFailure = { kind: 'exception' | 'timeout' | 'abort' | 'worker-exit' | 'invalid-output' | 'output-limit'; message: string }` — orthogonal outcomes reported independently per [defensive patterns](../../../../docs/defensive-patterns.md); a timed-out run is not an exception, an abort is not a timeout, a lossy completion is not an overflow, and a substrate exit is none of them.
-- Two readonly backend descriptors, informational not gating: `language` (what the program must be written in — `'typescript'` for the first backend; a Python backend says `'python'` and pairs with its own SDK generator on the presentation side) and `isolation` (`'worker-thread'` for the shipped backend; `'process'`, `'container'`, … for future ones). `dsh-tools` accepts any `language` with a registered SDK renderer and `run_code` flavor (TypeScript and Python ship; see the [language-dispatch note](../../archived/feature/2026-07-31-ptc-language-dispatch.md)) and fails the assembly loudly otherwise, the same misconfiguration idiom as `toolOrder` violations (as when `mode` is non-native with no `ctx.codeRuntime` loaded at all).
+`language` selects the SDK renderer and `run_code` flavor; an unsupported language fails assembly. `isolation` identifies the substrate, not its authority. `sandboxMode` indicates whether the consumer can pass a resolved Session file policy. Missing runtimes fail PTC assembly, while native presentation does not require one.
 
-Requests contain every runtime input; implementations own validated timeout and cap defaults. The registry looks up the optional runtime only when PTC mode is assembled, so native mode does not depend on one. Missing or language-incompatible runtimes fail loudly. Alternate substrates or languages can replace the implementation behind the same seam, paired with the appropriate SDK generator.
+Program outcomes resolve as result fields; caller misuse may reject. Lossless binding values cross under provider-owned transport limits. The sandbox mode, denial and enforcement facts are independent from success or failure.
 
-### The worker-thread runtime
+### The Node process runtime
 
-`@deepseek-ai/dsh-code-runtime-worker-thread`, the second package of the `packages/code-runtime/` group. Per `run()`:
+The Node provider strips erasable TypeScript, resolves its executable/bootstrap in the configured filesystem/subprocess world, and launches through the shared sandbox and managed process owners. Each child has an empty model environment and fresh program globals. Typed rejection constructors and the capturing console remain program-visible.
 
-1. **Type-strip host-side** with Node's built-in `stripTypeScriptTypes` (`node:module`; present across the repo's whole engines range, `^22.19.0 || >=24.0.0`, and position-preserving, so runtime error line numbers match the model's source). Strip-only mode rejects non-erasable syntax (`enum`, namespaces) — that rejection returns as `error.kind: 'exception'` with Node's message, the SDK instructions say "erasable TypeScript only", and the model self-corrects like any other program error. A syntax-level failure never spawns a worker.
-2. **Spawn one fresh `Worker` per run** from the package's own bootstrap module: `env: {}` (truly empty — stronger than the scrubbed-env rule for spawned commands), `resourceLimits` from config, `stdout`/`stderr` captured into `logs` rather than inherited. No pooling and no cross-run state: a program's world dies with its worker, which keeps runs reconstructable from the log alone and makes state bleed unrepresentable.
-3. **Execute** in the bootstrap: the stripped program becomes the body of an `AsyncFunction` whose parameters are the binding globals, any consumer-declared rejection classes, and a capturing `console` shim, so top-level `await` and `return` work. PTC mode declares `ToolCallError` with member property `toolName`; the runtime materializes that real constructor without hardcoding tools. A lossless JSON completion crosses exactly; `undefined` remains absence, a lossy value is `invalid-output`, and an oversized outer result is `output-limit` rather than an inspected-string substitute.
-4. **Bridge bindings over the message port**: each binding function in the worker posts `{ id, global, name, args }` and awaits the reply; the host validates the name against the request's bindings, invokes, and replies `{ id, ok, value }` or `{ id, ok: false, message }` (a host-side binding rejection becomes a program-side rejection). The worker-side namespace objects are built null-prototype via `defineProperty`, so a binding named `__proto__`, `constructor`, or `toString` is an ordinary own property, not a prototype collision. Unknown names, duplicate ids, and post-settlement messages are rejected or ignored — the port protocol assumes a hostile peer, because the peer runs model code.
-5. **Enforce independent budgets.** `computeMs` meters worker busy time, allowing slow awaited tools without excusing a hot loop. `maxWallMs` bounds total elapsed time, including unresolved waits. `maxOutputBytes` bounds only the combined serialized outer logs, completion, or diagnostic; intermediate binding values have no byte cap. Expiry, cancellation, and completion terminate the worker, and heap exits or outer overflow are explicit failures.
-6. **Dispose to quiescence**: the service's own disposal terminates in-flight workers and *awaits* their exits before resolving, per [defensive patterns](../../../../docs/defensive-patterns.md).
+Binding traffic uses a dedicated control channel separate from stdout/stderr. The host validates frames, call identities and allowed bindings before dispatch. Output, pending calls and channel buffers have configured limits; completion, deadline, cancellation and protocol failure terminate and await the managed range. The provider README and sandboxed Node decision own exact bounds and platform limits.
 
 ### Trust posture
 
-The worker runtime provides containment, not a security boundary: model code can reach Node APIs and has authority comparable to the bash tool. `worker.terminate()` stops the thread but not OS processes it spawned. PTC mode uses the same `tools/pre-execute` policy gate as bash and adds an empty environment, heap limits, a separate isolate, and hard termination of the program itself. Deployments that need a hard multi-tenant boundary need a container-class backend for both code and bash; the runtime's isolation descriptor lets them distinguish that backend.
+The selected OS backend constrains direct Node effects under the same policy used by Bash. Nested tools retain their registry policy and approval path. Full or partial enforcement is reported separately, and an unavailable required backend fails closed. Cleanup inherits the subprocess provider's documented range; a process descriptor does not imply a stronger multi-tenant guarantee.
 
 ### What the model sees
 
@@ -99,16 +96,16 @@ Deployments switching to `'ptc'` must update any native-only `toolOrder`. Assemb
 
 ## Testing
 
-- **Worker runtime:** Real-worker tests cover typed binding values and failures, every lossless JSON completion root, invalid and over-limit output, exact combined ledger boundaries, compute and wall budgets, hostile binding traffic, empty environment, and disposal to quiescence. A built-package test runs the worker entry under plain Node.
+- **Node runtime:** Provider tests cover direct Node execution, resolved cwd/policy, empty environment, typed bindings, hostile control frames, output limits, deadlines, cancellation and managed cleanup. Bootstrap tests cover source and built execution.
 - **Registry integration:** Tests cover code generation, all presentation modes, reserved-name and restriction rules, scoped visibility, authoritative assembly rewrites, `toolOrder`, runtime compatibility failures, full-pipeline sub-dispatch, parent-token correlation, serialization, cancellation and queue drain, JSON normalization, error propagation, log events, ordered context deferral across successful and failed programs, outer-block suppression, and HMR cleanup.
 - **With-key e2e:** A real model composes two bash calls in one program; another discovers nested workspace instructions through a PTC mode fs dispatch. The tests verify collapsed request headers, correlated dispatch events, resulting files, deferred context, and model behavior.
-- **Snapshot:** The `ptc-turn`, `both-mode-turn`, and `ptc-workspace-context` fixtures pin SDK text, header tool lists, dispatch events, deferred context, and result cards. The TypeScript SDK PTC scenario mounts its worker runtime through an explicit test-owned profile patch and pins Session events and JSON-RPC notifications; its expected response and completed-turn checks run before refresh writes.
+- **Snapshot:** The `ptc-turn`, `both-mode-turn`, and `ptc-workspace-context` fixtures pin SDK text, header tool lists, dispatch events, deferred context, and result cards. The TypeScript SDK PTC scenario mounts its Node runtime through an explicit test-owned profile patch and pins Session events and JSON-RPC notifications; its expected response and completed-turn checks run before refresh writes.
 
 ## Alternatives considered
 
 **An add-on consumer plugin with zero core changes.** Rejected because `agent/request` is call-config-only under [reconstructable requests](../architecture/2026-07-05-reconstructable-requests.md), while transforming an assembled tool list would have to undo `toolOrder` canonicalization without owning its config and would depend on listener order. Which tools the model is offered, and in which representation, is the registry's single concern: native schemas and the SDK are two projections of one visible store.
 
-**`node:vm` as the reference runtime, with hardening deferred.** Rejected: `node:vm` is not isolation (prototype-chain escapes reach the host realm) and cannot interrupt a hot loop. A worker thread provides a separate isolate, empty environment, `resourceLimits`, and reliable `terminate()` at bash-equivalent trust, so the reference and production implementation are one package without an unsafe-acknowledgement ceremony.
+**Use `node:vm` as the runtime security mechanism.** A language realm does not enforce OS file/process policy on direct Node APIs. The provider therefore uses the shared sandbox and managed process owners; a separate realm is not a substitute for those protections.
 
 **Result elision / summarization over native tool-calling.** Addresses only the context-bloat half of the problem: trimming old `tool-result`s is cheap to add as a logged surface replacement under reconstructable requests, but still pays one model round-trip per call and cannot express loops, branches, or joins. Complementary, not competing; it can layer under PTC mode for residual native calls.
 
@@ -124,16 +121,16 @@ Deployments switching to `'ptc'` must update any native-only `toolOrder`. Assemb
 
 ## Risks
 
-**The worker is not a hard security boundary.** Deliberate and documented (§Trust posture): posture equals the existing bash tool, containment exceeds it, gating uses the same approval and sandbox policies. Deployments needing more need a future `isolation: 'container'` backend — tracked as the seam's designed extension, not a TODO on this design.
+**Platform enforcement and cleanup vary.** The runtime reports the selected sandbox backend's completeness and inherits the managed process owner's range. Unsupported or partial platform guarantees remain explicit rather than being hidden behind the isolation descriptor.
 
 **`stripTypeScriptTypes` is marked experimental.** It is the same engine (amaro/swc) behind Node's own native `.ts` execution, exposed as an API across this repo's whole engines range. Mitigations: the runtime's unit suite checks position preservation and the required parts of the erasable-only rejection message, the call sits behind one private function, and `amaro`/`sucrase` are direct replacements if the API shifts. The erasable-only subset is a model-facing input restriction, and the error tells the model how to correct the program.
 
 **Prompt cost of the SDK, especially under `'both'`.** The `.d.ts` can rival the native schemas it complements; `'both'` carries two representations. Prefix stability + provider caching amortize per-session cost; the mode is per-deployment; the Agent Note makes no unconditional-savings claim. Measured guidance (when to prefer which mode) is explicitly post-ship learning.
 
-**Registry scope growth.** `dsh-tools` absorbs codegen, a tool, a bridge, and an event. Package modules separate these responsibilities (`ts-types.ts` and `ptc.ts` beside `schema.ts`, `json-schema.ts`, and `presentation.ts`), while `ctx.codeRuntime` owns all code-runtime-specific implementation.
+**Registry scope growth.** `dsh-tools` absorbs codegen, a tool, a bridge, and an event. Package modules separate these responsibilities (`ts-types.ts` and `ptc.ts` beside `schema.ts`, `json-schema.ts`, and `presentation.ts`), while `ctx.ptcRuntime` owns all ptc-runtime-specific implementation.
 
-**Large lossless JSON values can exhaust memory.** Tool bindings snapshot lossless JSON before dispatch and return canonical JSON resolutions whole. The runtime validates both sides of the worker port and applies no per-binding byte cap; structured-clone cost and process or worker memory are the practical bounds. The combined outer-output ledger for logs, the completion value, and a failure diagnostic is the only byte-capped boundary.
+**Large binding values can still exhaust memory.** The Node provider bounds control frames, outstanding arguments and queued writes, while a host binding can allocate its result before those checks. Lossless JSON transfer and the outer output ledger do not create a process-wide allocation quota.
 
 **Sub-dispatch overlap is bounded by tool safety claims, not by the caller.** A program's `Promise.all` or `asyncio.gather` buys wall-clock parallelism only across calls the tool itself classifies concurrency-safe; a run of exclusive calls still costs its round-trips in sequence, and models may over-expect. Both flavors' SDK instructions state the real contract. This note shipped the serialized placeholder that made the risk absolute; the [live-parallel Agent Note](2026-07-26-ptc-live-parallel-dispatch.md) owns the scheduler and its overlap cap.
 
-**Budget metering reads the event loop, not a flag.** Busy-time polling (`eventLoopUtilization()`) is coarser than an exact CPU meter — a budget expires up to one poll interval late — and its correctness claim ("a pending dispatch cannot pause it") is load-bearing against a hostile program. Both sides are unit-tested (hot loop with a pending decoy dispatch dies at `computeMs`; idle-on-slow-binding survives to `maxWallMs`), and the poll interval is an internal constant, not config — nothing a deployment could mis-tune into a bypass. `maxWallMs` is config, and it reaches `setTimeout`, which clamps a delay above `MAX_TIMER_DELAY_MS` (2^31-1 ms) to 1 ms; a positivity check alone therefore accepts a 25-day ceiling that expires on the first tick and times out every run. The worker runtime range-checks the field at load for that reason. `computeMs` needs no upper bound because it is compared against measured utilization instead of being handed to a timer.
+**Elapsed deadlines include awaited work.** Nested tools and approval waits consume the program's elapsed budget. The host stops execution on expiry and awaits managed cleanup; it does not meter process-tree CPU consumption. The sandboxed Node decision records current defaults and deferred timeout questions.

+ 27 - 30
.agents/notes/implemented/feature/2026-06-15-ptc.zh.md

@@ -12,17 +12,23 @@ Status: implemented
 
 Cloudflare 的 [Code Mode](https://blog.cloudflare.com/code-mode/) 提出了一种替代方案,基于一个简单的观察:LLM(大语言模型)编写代码的能力优于发出工具调用,因为它们见过数百万行真实代码,而人为构造的工具调用 trace 相对很少。模型不再每步发出一次工具调用,而是针对工具生成的 API 编写一段 TypeScript 程序,程序在沙箱运行时中执行,模型只筛选返回的内容——仅限它 print 或 return 的部分——而非所有中间结果。
 
-工具呈现属于掌管工具可见性的注册表:如果把第二种呈现方式实现为事后的 waterfall(瀑布式事件)变换,正确性将依赖监听器顺序,并与[可重建请求](../architecture/2026-07-05-reconstructable-requests.zh.md)冲突。执行基底同样属于基础设施而非占位实现:Node `worker_threads` 提供独立 isolate、空环境、堆上限以及对热同步循环的终止能力,同时契合 harness 既有的信任模型(§信任姿态)
+工具呈现属于掌管工具可见性的注册表:将另一种呈现实现为事后的 waterfall(瀑布式事件)变换会依赖监听器顺序,并与[可重建请求](../architecture/2026-07-05-reconstructable-requests.zh.md)冲突。执行必须独立于呈现模式,保留全新程序状态、Host 拥有的绑定和可取消结算
 
 ## 决策
 
 三项决策,各自在下方独立小节中展开:
 
 1. **PTC mode 是 `ToolRuntime`(`dsh-tools`)的一等呈现模式**,通过经校验的 `mode` 配置选择:`'native'`(默认,贡献可见能力 schema)、`'ptc'`(注册表仅贡献其保留的 `run_code` 传输通道加一份生成的 SDK `.d.ts` 到系统提示词中)或 `'both'`(原生 schema 加传输通道 + SDK)。注册表在源头构建其规范贡献;协作式提示词组装的结果仍具权威性,记录在日志中的请求头精确反映该返回的呈现。
-2. **代码执行是一个能力 seam**——`packages/code-runtime/` 包含 Service Definition 包 `@deepseek-ai/dsh-code-runtime`,拥有 `ctx.codeRuntime`([能力 seam](../architecture/2026-06-13-capability-seams.zh.md);消费方 = `dsh-tools`,core 消费 seam 的先例见 `agent-loop` → `dsh-llm`)。运行时对工具一无所知:它接收一段程序和命名的异步绑定,执行程序,报告 `{ value, logs, error? }`。语言和基底是后端属性,因此未来的 Python 或容器后端只是另一个 Service Provider 包,而非重新设计。
-3. **交付的实现是 `@deepseek-ai/dsh-code-runtime-worker-thread`**:每次运行 spawn 一个全新的 Node worker 线程,对模型的 TypeScript 进行 type-strip 后执行,绑定通过消息端口桥接,环境为空,堆/输出/时间上限可配置,并支持硬终止。其信任姿态在设计上等同于 bash——无需 unsafe-acknowledgement flag——因为 harness 已经交付了 `dsh-bash-local`,后者以严格*更高*的环境权限执行模型编写的任意 shell 命令
+2. **代码执行是一个能力 seam**——`packages/ptc-runtime/` 包含 Service Definition 包 `@deepseek-ai/dsh-ptc-runtime`,拥有 `ctx.ptcRuntime`([能力 seam](../architecture/2026-06-13-capability-seams.zh.md);消费方 = `dsh-tools`,core 消费 seam 的先例见 `agent-loop` → `dsh-llm`)。运行时对工具一无所知:它接收一段程序和命名的异步绑定,执行程序,报告 `{ value, logs, error? }`。语言和基底是后端属性,因此未来的 Python 或容器后端只是另一个 Service Provider 包,而非重新设计。
+3. **交付的实现是 `@deepseek-ai/dsh-ptc-runtime-node`**:每个程序在全新的受管 Node 进程中,按调用 Session 的已解析沙箱策略运行。直接 Node API 在该策略内仍可使用;具名 Host 绑定通过单独验证的控制通道调用
 
-本说明负责定义 PTC mode 的呈现、组合、隔离与结算基础。后续的[类型化工具返回值 Agent Note](2026-07-20-ptc-typed-tool-returns.zh.md)负责定义生成的输出映射、规范绑定值、`ToolCallError` 和无损外层输出边界。
+本说明负责 PTC 呈现、组合与结算。[类型化工具返回决策](2026-07-20-ptc-typed-tool-returns.zh.md)负责生成输出、规范绑定值、`ToolCallError` 与无损外层输出。[沙箱 Node 决策](../architecture/2026-09-11-sandboxed-node-ptc-runtime.zh.md)取代 worker 执行、信任姿态与预算实现,同时保留本说明的消费方规则。
+
+### 每段程序的执行控制
+
+消费方根据运行时只读 `timeout` 描述符通告 `timeoutMs`,并把显式值交给其解析器。不支持的提供方不公开超时字段,并拒绝显式请求。沙箱提升在运行时解析和启动前使用共享的严格扩大权限审批辅助函数;仅替换已解析策略的模式,使授权只作用于一段程序。嵌套分发保留原始 Agent 和 Session,因此其审批与文件策略保持独立。失败程序不会隐式重放,因为先前效果可能已提交。
+
+[Node 执行决策](../architecture/2026-09-11-sandboxed-node-ptc-runtime.zh.md#deferred-timeout-design) 负责经过时间预算核算与延期的生命周期问题。
 
 ### 注册表拥有模式
 
@@ -32,7 +38,7 @@ Cloudflare 的 [Code Mode](https://blog.cloudflare.com/code-mode/) 提出了一
 
 **与 `toolOrder` 的交互:** 如果配置的 `systemPrompt.toolOrder` 引用了原生能力名称,在 `mode: 'ptc'` 下会拒绝所有组装,因为那些名称不在该模式的协议校验范围内。这是正确行为而非 bug:使用 PTC mode 的部署需要更新其 order 配置或移除它。
 
-**SDK 提示词段。** 在 `'ptc'` 和 `'both'` 下,tool-guidance order band 中的惰性 `tools:sdk` 段为当前 scope 的可见能力渲染所加载运行时语言的声明加固定的使用说明(默认 TypeScript;[语言分发 note](../../archived/feature/2026-07-31-ptc-language-dispatch.md) 加入了 Python 与按 `ctx.codeRuntime.language` 选择的渲染器表)。它共享查找和执行可见性,排除 `run_code`,并按字典序排列工具以获得字节稳定的输出。
+**SDK 提示词段。** 在 `'ptc'` 和 `'both'` 下,tool-guidance order band 中的惰性 `tools:sdk` 段为当前 scope 的可见能力渲染所加载运行时语言的声明加固定的使用说明(默认 TypeScript;[语言分发 note](../../archived/feature/2026-07-31-ptc-language-dispatch.md) 加入了 Python 与按 `ctx.ptcRuntime.language` 选择的渲染器表)。它共享查找和执行可见性,排除 `run_code`,并按字典序排列工具以获得字节稳定的输出。
 
 **组装所有权。** `run_code` 和 `tools:sdk` 作为正常的组装输入进入受信任的 `system-prompt/assemble` waterfall。一个 scoped 的 `tools:sdk` 段可以在分发前遮蔽全局默认值,监听器也可以移除或替换任一贡献。waterfall 返回的组装结果是最终的,因此修改这些输入的人有责任在部署期望 PTC mode 可用时保持协议面的完整性;没有恢复 pass 会覆盖有意的组合。
 
@@ -43,7 +49,7 @@ Cloudflare 的 [Code Mode](https://blog.cloudflare.com/code-mode/) 提出了一
 在 `'ptc'` 和 `'both'` 下,注册表拥有 `run_code` 作为保留的呈现传输通道,带两个必需参数 `{ code: string; description: string }`(description 为 UI 标注该调用,沿用 bash 的先例)。它由一个正常的 `ToolDefinition` 表示以供分发,但位于可过滤的能力层之外,因此限制规则不会意外移除 PTC mode 的唯一入口。调用遍历完整的工具流水线——`tools/pre-execute` → 单调性守卫 → `tools/execute` 包裹分发 → `tools/post-execute` → 由定义拥有的可选 `finalizeContent` → 不可变的 `tools/result` 通知——与原生调用完全一致;权限插件可以在程序运行前检查程序文本,最终结果观察者看到的是规范化的外层结果。其 `execute(args, exec)`:
 
 1. **构建绑定。** 一个 run 级别的 signal 跟随外层取消,并在 run 结算时被 abort。每个可见工具绑定都会对无损 JSON 参数创建快照,进入原生约定的分发池(调度设计由[实时并行 Agent Note](2026-07-26-ptc-live-parallel-dispatch.zh.md) 负责),以确定性的 call id 和外层 token 作为 `parent` 执行,通过外层 execution 延后返回的上下文,并记录 `tool/ptc-dispatch-start`/`tool/ptc-dispatch` 事件对,其中结算侧携带完整渲染后的结果内容。成功时返回工具最终的规范 JSON 值;失败则变为程序可见的 `ToolCallError`。每个子调用保留自己不可变的执行标识,并遍历完整的工具流水线。
-2. **运行程序**:`ctx.codeRuntime.run({ program: args.code, bindings: [{ global: 'tools', functions }], signal: runController.signal })`。运行时接收的是 run 级别的 signal 而非仅调用方的外层 signal,因此外层 run 以任何方式结算都会同时 abort 运行时内部的工作
+2. **运行已解析输入:** `ctx.ptcRuntime.run(ctx.ptcRuntime.resolve(request))`。请求携带程序、绑定、运行级信号、调用 Session 的 cwd 与适用的已解析文件策略。运行级信号将外层结算与执行取消关联
 3. **完全停稳后结算。** 运行时结算后,桥 abort 未完成的工作并排空分发队列后再返回。成功时返回捕获的日志和完成值,将其作为规范输出;注册表再把该值渲染为持久化的 `tool/result.content`,供结果卡片直接读取。运行时失败变为 `CodeRunFailedError`;后端拒绝使用注册表的正常错误边界。两者都产生结构化的错误结果,且 `run_code` 结算后不允许子调用追加。
 
 **子调用上下文通过父调用延后。** 在 `run_code` 内部注入会破坏父调用/结果的相邻性,因此 `ToolRunContext.deferContext()` 按分发顺序收集每个子结果的 `additionalContexts` 条目。即使程序后来抛出异常,注册表仍携带该数组;循环只在外层结果与步骤中所有兄弟结果之后追加每个条目。外层 post-execute 阻止会丢弃工具延后的条目,只暴露阻止 decision 显式附加的上下文。
@@ -60,32 +66,23 @@ Cloudflare 的 [Code Mode](https://blog.cloudflare.com/code-mode/) 提出了一
 
 [V2 到 V3 PTC 规范](../../../../packages/session/session-format-v2-to-v3/README.zh.md#ptc-vocabulary)负责精确的历史标签与归属转换;[原生 V3 准入](../../../../packages/session/session-format-v2-to-v3/README.zh.md#native-v3-admission)负责前代标签拒绝。这些不是运行时别名:不透明扩展不能仅因版本变化就获得 PTC 生命周期含义。
 
-### code-runtime seam
+### ptc-runtime seam
 
-`packages/code-runtime/code-runtime/`——`@deepseek-ai/dsh-code-runtime`,仅依赖 `cordis`。一个抽象的 `CodeRuntime extends Service`(`super(ctx, 'codeRuntime')`)加上词汇:
+`dsh-ptc-runtime` 负责 `PtcRuntime` 与[请求、已解析 spec、绑定及结果类型](../../../../docs/subsystems/ptc-runtime.zh.md)。`resolve(request)` 验证支持的 cwd、timeout 与策略选择并补全部署默认值;`run(spec)` 执行完整输入。具名异步绑定和可选拒绝构造器使运行时不必了解注册表特定的工具名称。
 
-- `CodeRunRequest = { program: string; bindings: CodeBindingNamespace[]; signal?: AbortSignal }`
-- `CodeBindingNamespace = { global: string; functions: Record<string, (args: unknown) => Promise<CodeJsonValue>>; errorClass?: { name: string; memberNameProperty: string } }`——运行时将每个命名空间作为程序内部的全局异步函数对象暴露;可选描述符要求运行时注入真正的、程序可见的 reject 类,而无需让 seam 获知消费方专用名称。`CodeJsonValue` 是这个低依赖 seam 的结构化无损 JSON 类型,因此绑定参数与返回值可以完整跨越实现的序列化边界。
-- `CodeRunResult = { value?: CodeJsonValue; logs: string[]; error?: CodeRunFailure }`——程序执行失败时,执行 promise 仍会 fulfill,并通过 `error` 字段返回失败结果。只有调用方/seam 误用(例如重复的绑定命名空间)时,`run()` 才会 reject;消费方仍在自己的错误边界处理不合规后端的拒绝。
-- `CodeRunFailure = { kind: 'exception' | 'timeout' | 'abort' | 'worker-exit' | 'invalid-output' | 'output-limit'; message: string }`——按[防御性模式](../../../../docs/defensive-patterns.zh.md)独立报告的正交结果;超时的 run 不是异常,abort 不是超时,有损完成值不是溢出,基底退出也与上述情况相互独立。
-- 两个只读的后端描述符,仅供信息参考而非门禁判定:`language`(程序必须使用的语言——首个后端为 `'typescript'`;Python 后端声明 `'python'`,并在呈现侧配对自己的 SDK 生成器)和 `isolation`(交付的后端为 `'worker-thread'`;未来可为 `'process'`、`'container'` 等)。`dsh-tools` 接受任何注册了 SDK 渲染器与 `run_code` flavor 的 `language`(TypeScript 与 Python 已交付;见[语言分发 note](../../archived/feature/2026-07-31-ptc-language-dispatch.md)),否则组装会显式失败,与 `toolOrder` 违规时的配置错误惯用法相同(如 `mode` 为非 native 但根本没有加载 `ctx.codeRuntime`)。
+`language` 选择 SDK 渲染器与 `run_code` flavor;不支持的语言使组装失败。`isolation` 标识执行基底,不标识权限。`sandboxMode` 表示消费方能否传入已解析 Session 文件策略。缺少运行时使 PTC 组装失败,native 呈现则不需要运行时。
 
-请求包含所有运行时输入;实现方拥有经校验的超时和上限默认值。注册表仅在组装 PTC mode 时查找可选的运行时,因此 native 模式不依赖它。缺失或语言不兼容的运行时会显式失败。替代基底或语言可以在同一 seam 背后替换实现,配对相应的 SDK 生成器
+程序结果以字段返回;调用方误用可以拒绝。无损绑定值在提供方自己的传输限制下跨越。沙箱模式、拒绝与强制事实独立于成功或失败。
 
-### worker-thread 运行时
+### Node 进程运行时
 
-`@deepseek-ai/dsh-code-runtime-worker-thread`,`packages/code-runtime/` 组的第二个包。每次 `run()`:
+Node 提供方擦除可擦除 TypeScript,在配置的文件系统/子进程世界中解析可执行文件与 bootstrap,并通过共享沙箱与受管进程所有者启动。每个子进程都有空模型环境和全新程序全局对象。类型化拒绝构造器与捕获 console 仍对程序可见。
 
-1. **宿主侧 type-strip**,使用 Node 内置的 `stripTypeScriptTypes`(`node:module`;在本仓库的整个引擎范围 `^22.19.0 || >=24.0.0` 内可用,且会保留源码位置,因此运行时错误行号与模型源码一致)。仅剥离模式拒绝不可擦除的语法(`enum`、namespaces)——该拒绝以 `error.kind: 'exception'` 加 Node 的消息返回,SDK 说明写明「仅限可擦除 TypeScript」,模型像处理其他程序错误一样自我修正。语法级失败不会 spawn worker。
-2. **每次 run spawn 一个全新 `Worker`**,来自包自身的 bootstrap 模块:`env: {}`(真正为空——比 spawn 命令的 scrubbed-env 规则更严格),`resourceLimits` 来自配置,`stdout`/`stderr` 捕获到 `logs` 而非继承。不做池化,不跨 run 保留状态:程序的世界随 worker 消亡,这使得 run 仅从日志即可重建,状态泄漏不可表达。
-3. **在 bootstrap 中执行**:剥离后的程序成为一个 `AsyncFunction` 的函数体,其参数是绑定全局变量、消费方声明的 reject 类和一个捕获式 `console` shim,因此顶层 `await` 和 `return` 可用。PTC mode 声明 `ToolCallError`,成员属性为 `toolName`;运行时无需硬编码工具即可实体化真正的构造函数。无损 JSON 完成值会精确跨越边界;`undefined` 仍表示缺席,有损值产生 `invalid-output`,过大的外层结果产生 `output-limit`,而不会退化为检查格式化后的字符串替代品。
-4. **通过消息端口桥接绑定**:worker 中的每个绑定函数发送 `{ id, global, name, args }` 并等待回复;宿主根据请求的绑定校验名称、调用、并回复 `{ id, ok, value }` 或 `{ id, ok: false, message }`(宿主侧绑定拒绝变为程序侧 rejection)。worker 侧的命名空间对象通过 `defineProperty` 构建为 null-prototype,因此名为 `__proto__`、`constructor` 或 `toString` 的绑定是普通自有属性,而非原型链碰撞。未知名称、重复 id 和结算后消息被拒绝或忽略——端口协议假设对端是恶意的,因为对端运行的是模型代码。
-5. **强制独立预算。** `computeMs` 计量 worker 忙碌时间,允许慢速的 awaited 工具而不放过热循环。`maxWallMs` 约束总经过时间,包括未解析的等待。`maxOutputBytes` 只约束序列化后的外层日志、完成值或诊断的组合;中间绑定值没有字节数上限。到期、取消和完成都终止 worker,堆退出或外层溢出会作为显式失败报告。
-6. **dispose(资源释放)至完全停稳**:服务自身的 dispose 终止进行中的 worker 并*等待*其退出后再 resolve,遵循[防御性模式](../../../../docs/defensive-patterns.zh.md)。
+绑定通信使用与 stdout/stderr 分开的专用控制通道。Host 在分派前验证帧、调用身份与允许的绑定。输出、待处理调用与通道缓冲有配置上限;完成、截止、取消和协议失败会终止并等待受管范围。提供方 README 与沙箱 Node 决策负责确切上限和平台限制。
 
 ### 信任姿态
 
-worker 运行时只能约束程序的运行,而不构成安全边界:模型代码可以访问 Node API,权限与 bash 工具相当。`worker.terminate()` 停止线程但不停止它 spawn 的 OS 进程。PTC mode 使用与 bash 相同的 `tools/pre-execute` 策略门禁,并额外提供空环境、堆限制、独立 isolate 和对程序本身的硬终止。需要硬多租户边界的部署需要为代码和 bash 都使用容器级后端;运行时的 isolation 描述符让它们能区分该后端
+所选 OS 后端按照与 Bash 相同的策略约束直接 Node 副作用。嵌套工具保留注册表策略与审批路径。完整或部分强制能力独立报告;所需后端不可用时以失败关闭。清理继承子进程提供方声明的范围;进程描述符不意味着更强的多租户保证。
 
 ### 模型看到的内容
 
@@ -99,16 +96,16 @@ SDK 指示模型编写一个所加载运行时语言的异步函数体(默认
 
 ## 测试
 
-- **Worker 运行时:** 真实 worker 测试覆盖类型化的绑定值与失败、每一种无损 JSON 根类型的完成值、无效和超限输出、精确的组合账本边界、compute 和 wall 预算、恶意绑定流量、空环境以及 dispose 至完全停稳。一个构建后包测试在纯 Node 下运行 worker 入口
+- **Node 运行时:** 提供方测试覆盖直接 Node 执行、已解析 cwd/策略、空环境、类型化绑定、恶意控制帧、输出上限、截止、取消与受管清理。Bootstrap 测试覆盖源代码与构建后执行
 - **注册表集成:** 测试覆盖代码生成、所有呈现模式、保留名称和限制规则、scoped 可见性、权威组装重写、`toolOrder`、运行时兼容性失败、完整流水线子分发、parent-token 关联、序列化、取消和队列排空、JSON 规范化、错误传播、日志事件、成功与失败程序中的有序上下文延后、外层阻止抑制以及 HMR(热模块替换)清理。
 - **带密钥 e2e:** 真实模型在一个程序中组合两次 bash 调用;另一个模型通过 PTC mode fs 分发发现嵌套的工作区指令。测试验证折叠的请求头、关联的分发事件、结果文件、延后上下文和模型行为。
-- **快照:** `ptc-turn`、`both-mode-turn` 和 `ptc-workspace-context` fixture(测试前置数据)固定 SDK 文本、请求头工具列表、分发事件、延后上下文和结果卡片。TypeScript SDK PTC 场景通过测试拥有的显式 profile patch 挂载 worker 运行时,并固定 Session 事件与 JSON-RPC 通知;预期回复和完成轮次检查在 refresh 写入前执行。
+- **快照:** `ptc-turn`、`both-mode-turn` 和 `ptc-workspace-context` fixture(测试前置数据)固定 SDK 文本、请求头工具列表、分发事件、延后上下文和结果卡片。TypeScript SDK PTC 场景通过测试拥有的显式 profile patch 挂载 Node 运行时,并固定 Session 事件与 JSON-RPC 通知;预期回复和完成轮次检查在 refresh 写入前执行。
 
 ## 曾考虑的替代方案
 
 **一个零核心改动的附加消费方插件。** 否决,因为 `agent/request` 在[可重建请求](../architecture/2026-07-05-reconstructable-requests.zh.md)下仅限 call-config,而变换已组装的工具列表需要在不拥有其配置的情况下撤销 `toolOrder` 规范化,并依赖监听器顺序。向模型提供哪些工具、以何种表示形式提供,是注册表的单一关注点:原生 schema 和 SDK 是同一个可见存储的两种投影。
 
-**`node:vm` 作为参考运行时,加固推迟。** 否决:`node:vm` 不是隔离(原型链逃逸可达宿主 realm)且无法中断热循环。worker 线程提供独立 isolate、空环境、`resourceLimits` 和可靠的 `terminate()`,信任等级等同于 bash,因此参考实现和生产实现是同一个包,无需 unsafe-acknowledgement 仪式
+**将 `node:vm` 作为运行时安全机制。** 语言 realm 不会对直接 Node API 强制 OS 文件/进程策略。因此提供方使用共享沙箱与受管进程所有者;独立 realm 不能代替这些保护
 
 **在原生工具调用上做结果省略/摘要。** 仅解决问题中上下文膨胀这一半:裁剪旧 `tool-result` 作为可重建请求下的日志化表面替换成本低,但仍需每次调用一次模型往返,且无法表达循环、分支或汇合。互补而非竞争;它可以在 PTC mode 下为残余的原生调用分层。
 
@@ -124,16 +121,16 @@ SDK 指示模型编写一个所加载运行时语言的异步函数体(默认
 
 ## 风险
 
-**Worker 不是硬安全边界。** 有意为之且已文档化(§信任姿态):姿态等同于既有的 bash 工具,约束能力强于它,门禁使用相同的审批与沙箱策略。需要更强隔离的部署需要未来的 `isolation: 'container'` 后端——作为 seam 设计中预留的扩展进行跟踪,而非本设计的 TODO
+**平台强制能力与清理能力不同。** 运行时报告所选沙箱后端的完整性,并继承受管进程所有者的范围。不支持或部分的平台保证保持明确,不隐藏在 isolation 描述符之后
 
 **`stripTypeScriptTypes` 标记为 experimental。** 它与 Node 自身原生 `.ts` 执行背后的引擎(amaro/swc)相同,在本仓库的整个引擎范围内作为 API 暴露。缓解措施:运行时的单元测试套件会检查位置保持和可擦除限制拒绝消息中的必需部分;调用位于一个私有函数之后,且 `amaro`/`sucrase` 可在 API 变化时直接替换它。仅可擦除子集是面向模型的输入限制,错误消息会告诉模型如何修正程序。
 
 **SDK 的提示词成本,尤其在 `'both'` 下。** `.d.ts` 可能与它补充的原生 schema 体量相当;`'both'` 携带两种表示。前缀稳定性 + 提供方缓存摊销了每会话成本;mode 按部署配置;本 Agent Note 不做无条件节省的声明。何时优先使用哪种模式的量化指导明确属于上线后学习。
 
-**注册表 scope 增长。** `dsh-tools` 吸收了代码生成、一个工具、一个桥和一个事件。包内模块把这些职责分开(`ts-types.ts`、`ptc.ts` 与 `schema.ts`、`json-schema.ts`、`presentation.ts` 并列),所有 code-runtime 专用实现都由 `ctx.codeRuntime` 提供。
+**注册表 scope 增长。** `dsh-tools` 吸收了代码生成、一个工具、一个桥和一个事件。包内模块把这些职责分开(`ts-types.ts`、`ptc.ts` 与 `schema.ts`、`json-schema.ts`、`presentation.ts` 并列),所有 ptc-runtime 专用实现都由 `ctx.ptcRuntime` 提供。
 
-**大型无损 JSON 值可能耗尽内存。** 工具绑定会在分发前对无损 JSON 创建快照,并完整返回规范 JSON 返回值。运行时会校验 worker 端口两侧,但不对单次绑定设置字节数上限;结构化克隆成本以及进程或 worker 内存构成实际边界。只有包含日志、完成值和失败诊断的组合外层输出账本受字节数上限约束
+**大型绑定值仍可能耗尽内存。** Node 提供方限制控制帧、未完成参数和排队写入,而 Host 绑定可以在这些检查前分配其结果。无损 JSON 传输与外层输出账本不构成进程级分配配额
 
 **子分发的重叠由工具自身的安全声明限定,而非由调用方决定。** 程序里的 `Promise.all` 或 `asyncio.gather` 只在工具自己分类为并发安全的调用之间换来挂钟并行性;一串 exclusive 调用仍要按顺序付出各自的往返开销,模型可能过度期望。两种 flavor 的 SDK 说明都陈述了真实约定。本 note 交付的是使该风险绝对化的序列化占位实现;调度器及其重叠上限由[实时并行 Agent Note](2026-07-26-ptc-live-parallel-dispatch.zh.md) 负责。
 
-**预算计量读取事件循环,而非 flag。** 忙碌时间轮询(`eventLoopUtilization()`)比精确 CPU 计量更粗糙——预算到期最多延迟一个轮询间隔——且其正确性声明(「pending 的分发不能暂停它」)是抵御恶意程序的关键。两种情况均有单元测试(带 pending 诱饵分发的热循环会在耗尽 `computeMs` 预算时终止;等待慢速绑定的空闲程序则会持续运行至 `maxWallMs`),轮询间隔是内部常量而非配置——部署无法将其误调为绕过手段。`maxWallMs` 是配置项,且会传入 `setTimeout`,后者会把超过 `MAX_TIMER_DELAY_MS`(2^31-1 ms)的延迟夹到 1 ms;因此仅有正数校验会放行一个 25 天的上限,它在第一个 tick 就到期,使每次运行都超时。worker 运行时正因如此在加载时对该字段做范围校验。`computeMs` 不需要上界,因为它对照的是实测占用率,而不是交给定时器
+**经过时间截止包括被等待的工作。** 嵌套工具和审批等待消耗程序的经过时间预算。Host 在截止到期时停止执行并等待受管清理;它不计量进程树 CPU 消耗。沙箱 Node 决策记录当前默认值与延后评估的 timeout 问题

+ 2 - 2
.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-06-sandbox.md
-2026-07-06-sandbox.md: 31d96836ad2f932f2abf7d1d76242a711f0de2f6
-2026-07-06-sandbox.zh.md: fdf5f4b1691b4a55fffbd206847c99307e12c9da
+2026-07-06-sandbox.md: 9f7139c8088df35ac87ddd66120dd8d6dc8e6e35
+2026-07-06-sandbox.zh.md: b8d8b1feb0db91431d0e71bea42299eafbc6856f

+ 10 - 4
.agents/notes/implemented/feature/2026-07-06-sandbox.md

@@ -50,7 +50,7 @@ OS subprocess confinement applies to the bash executor, including hook commands,
 
 #### The seam: `ctx.sandbox`
 
-`dsh-sandbox` owns the vocabulary and the `SandboxProvider` contract: `confine(argv, policy)` returns the argv to spawn INSTEAD of the caller's own — wrapped so the process and everything it spawns run confined — plus the `enforcement` completeness the selected backend achieves, its denial dialect (`denialSignatures`, the stderr substrings that backend's kernel prints on a denied file effect), and its structured runner-failure evidence (`runnerFailureRules`, optional allowed exit codes plus fatal per-line signatures after exact informational-line exclusions); with no usable backend it throws the fail-closed `SANDBOX_UNAVAILABLE` error, never a silent unconfined passthrough. The vocabulary: `SandboxMode` (`read-only` / `workspace-write` / `danger-full-access`, FILE effects only — network and process visibility are not claimed), `SandboxEnforcement` (`full` / `partial`), `SandboxExecutionPolicy` (the complete per-capability-call mode + workspace root), and `SandboxPolicy` (the confined provider subset).
+`dsh-sandbox` owns the vocabulary and the `SandboxProvider` contract: `confine(argv, policy, signal?)` resolves to the argv to spawn INSTEAD of the caller's own — wrapped so the process and everything it spawns run confined — plus the `enforcement` completeness the selected backend achieves, its denial dialect (`denialSignatures`, the stderr substrings that backend's kernel prints on a denied file effect), and its structured runner-failure evidence (`runnerFailureRules`, optional allowed exit codes plus fatal per-line signatures after exact informational-line exclusions); with no usable backend it rejects with the fail-closed `SANDBOX_UNAVAILABLE` error, never a silent unconfined passthrough. The vocabulary: `SandboxMode` (`read-only` / `workspace-write` / `danger-full-access`, FILE effects only — network and process visibility are not claimed), `SandboxEnforcement` (`full` / `partial`), `SandboxExecutionPolicy` (the complete per-capability-call mode + workspace root), and `SandboxPolicy` (the confined provider subset).
 
 Policy rides each CALL, not the provider: two consumers may confine under different policies at the same instant (bash under `read-only` while a confined child agent keeps its state directory writable), and an approved escalated retry is a new call with a wider policy — inexpressible under a config-fixed provider mode.
 
@@ -68,9 +68,15 @@ The Landlock launcher source and package family live at `native/system`, next to
 
 Backend profiles share the mode contract but differ in necessary host grants. Landlock and Seatbelt allow only `/dev/null` in read-only mode; workspace-write also permits their required host temp roots. Each wrap carries backend-specific denial signatures. Landlock reports partial enforcement on older ABIs that cannot govern every operation, while successful bwrap and Seatbelt profiles report full enforcement.
 
+#### Asynchronous preparation
+
+Provider-owned I/O requires asynchronous confinement. `confine(argv, policy, signal?)` returns a promise so a provider can ask an already-running trusted process to prepare an execution while subprocess argv stays literal. Consumers await that preparation and recheck cancellation before launching a payload; a remote provider does not need to execute a mutable helper artifact outside confinement for every command.
+
+`ShellExecutor.start()` publishes a handle only after preparation succeeds. Generic job admission stays synchronous: tool-owned hooks start preparation after job preflight, own its cancellation, and await any late process before settling. This keeps admission failures from starting work and records asynchronous preparation failures as job outcomes.
+
 #### The bash consumer
 
-`dsh-bash-sandbox` extends `LocalBashExecutor`, hands `ctx.sandbox` the exact `['bash', '-c', command]` argv, and directly spawns the provider result. This leaves shell semantics and `BASH_ENV` on the inner Bash after the shipped native runner establishes confinement. A provider error propagates unchanged. An asynchronous rejection counts as a runner failure only when the caller-owned workdir is independently usable and Node reports `ENOENT` or `EACCES` with either an `error.path` equal to provider argv[0] or, when `error.path` is absent, an exact `syscall: 'spawn <runner>'`; a present path also requires `syscall: 'spawn'` or the exact `spawn <runner>`. Other codes, invalid workdirs, resource failures, unrelated syscalls, and unstructured rejections retain stage-neutral provider-failure semantics. Foreground execution converts attributable runner failures to `SANDBOX_UNAVAILABLE` with the original detail; an attributable asynchronous background rejection stamps `runnerFailed: true`, `denied: false`. A `SubprocessRuntime` that synchronously throws the same runner-identifying shape makes background start throw `SANDBOX_UNAVAILABLE`, while other synchronous errors propagate unchanged. After a direct outcome exists, foreground and background use one runner-failure classifier that requires the rule's exit-code check and a remaining fatal line after informational exclusions. A match takes priority over denial: foreground execution throws `SANDBOX_UNAVAILABLE` with that fatal line as detail; a settled `ShellProcess` stamps `sandbox.runnerFailed`, and the bash producer renders it through generic `job_output`.
+`dsh-bash-sandbox` extends `LocalBashExecutor`, awaits `ctx.sandbox` for the exact `['bash', '-c', command]` argv with the execution signal, rechecks cancellation, and directly spawns the provider result. This leaves shell semantics and `BASH_ENV` on the inner Bash after the shipped native runner establishes confinement. A provider error propagates unchanged. An asynchronous rejection counts as a runner failure only when the caller-owned workdir is independently usable and Node reports `ENOENT` or `EACCES` with either an `error.path` equal to provider argv[0] or, when `error.path` is absent, an exact `syscall: 'spawn <runner>'`; a present path also requires `syscall: 'spawn'` or the exact `spawn <runner>`. Other codes, invalid workdirs, resource failures, unrelated syscalls, and unstructured rejections retain stage-neutral provider-failure semantics. Foreground execution converts attributable runner failures to `SANDBOX_UNAVAILABLE` with the original detail; an attributable asynchronous background rejection stamps `runnerFailed: true`, `denied: false`. A `SubprocessRuntime` that synchronously throws the same runner-identifying shape makes background start reject with `SANDBOX_UNAVAILABLE` before handle publication, while other synchronous errors propagate unchanged. After a direct outcome exists, foreground and background use one runner-failure classifier that requires the rule's exit-code check and a remaining fatal line after informational exclusions. A match takes priority over denial: foreground execution throws `SANDBOX_UNAVAILABLE` with that fatal line as detail; a settled `ShellProcess` stamps `sandbox.runnerFailed`, and the bash producer renders it through generic `job_output`.
 
 The model sees the current effective file policy in the owner-derived `sandbox:policy` context, while the static tool description explains the denial marker (`[sandbox: file access denied under <mode> mode]`), encourages attempting commands that may be denied, and forbids retrying around a denial; when the escalation fields are advertised, a denied result additionally carries the escalation hint itself, so the sanctioned same-turn retry is prompted at the decision point rather than depending on the model recalling the description (§ Escalation). [The current-policy decision](../../archived/feature/2026-07-30-current-sandbox-policy-context.md) owns the context's rationale and boundaries.
 
@@ -184,8 +190,8 @@ Costs and accepted limits:
 ## FAQ
 
 - **A command came back with `[sandbox: file access denied under read-only mode]` — did it fail?** It RAN, and the kernel refused a file effect: the denial is a result fact orthogonal to exit code. The teaching forbids retrying around it; the one sanctioned move is the same command retried once with an escalation request.
-- **How is a BROKEN sandbox told apart from a failing command?** An asynchronous subprocess-provider rejection exposes no public execution stage. It identifies a broken confinement runner only when the caller-owned workdir is usable and Node reports attributable `ENOENT` or `EACCES` for that argv[0]; a bare `syscall: 'spawn'` without an exact error path and all other rejections remain stage-neutral provider failures. After a direct outcome exists, runner failure outranks denial only when one `runnerFailureRules` entry matches both its optional exit-code gate and a fatal stderr line after exact informational exclusions. Foreground runner failures throw structured `SANDBOX_UNAVAILABLE` with executable or matched-line detail; an attributable asynchronous background rejection or a matched settled failure stamps `sandbox.runnerFailed`, while every asynchronous rejection renders the local executor's provider-failure note. A `SubprocessRuntime` that synchronously throws the same `ENOENT`/`EACCES` shape with the runner path makes background start throw the structured error; other synchronous errors propagate unchanged. A Landlock partial-enforcement notice plus an ordinary child failure remains a command result.
-- **What happens on a platform with no backend?** `confine()` throws the fail-closed `SANDBOX_UNAVAILABLE`, and the command never spawns.
+- **How is a BROKEN sandbox told apart from a failing command?** An asynchronous subprocess-provider rejection exposes no public execution stage. It identifies a broken confinement runner only when the caller-owned workdir is usable and Node reports attributable `ENOENT` or `EACCES` for that argv[0]; a bare `syscall: 'spawn'` without an exact error path and all other rejections remain stage-neutral provider failures. After a direct outcome exists, runner failure outranks denial only when one `runnerFailureRules` entry matches both its optional exit-code gate and a fatal stderr line after exact informational exclusions. Foreground runner failures throw structured `SANDBOX_UNAVAILABLE` with executable or matched-line detail; an attributable asynchronous background rejection or a matched settled failure stamps `sandbox.runnerFailed`, while every asynchronous rejection renders the local executor's provider-failure note. A `SubprocessRuntime` that synchronously throws the same `ENOENT`/`EACCES` shape with the runner path makes background start reject with the structured error before handle publication; other synchronous errors propagate unchanged. A Landlock partial-enforcement notice plus an ordinary child failure remains a command result.
+- **What happens on a platform with no backend?** `confine()` rejects with the fail-closed `SANDBOX_UNAVAILABLE`, and the command never spawns.
 - **`bwrap` is installed on my host but unusable (disabled unprivileged userns, an LSM denying `mount`) — what happens?** The chain probe is functional — it builds and enforces a real profile rather than checking `--version` — so a present-but-unusable `bwrap` fails its probe, selection falls to the packaged Landlock launcher, and the verdict is cached for the provider's lifetime.
 - **Does the sandbox restrict network or process visibility?** `SandboxMode` claims FILE effects only, and no backend claims network. Process visibility is backend-specific: bwrap unshares PID and mounts matching procfs because host `/proc/<pid>` magic links otherwise bypass file confinement, while Landlock and Seatbelt leave process visibility unchanged ([decision](../bug-fix/2026-08-06-bwrap-private-pid-namespace.md)). Whether network restriction becomes its own knob is left open in § The seam.
 - **Which tools actually run confined?** OS subprocesses through `ctx.shell` — the bash tools, and hook commands transitively — plus the filesystem tools (`read`/`write`/`edit`) through the sandboxed `ctx.fs` provider (the [cross-family fs sandbox RFC](2026-07-14-cross-family-fs-sandbox.md)): bash confines via the OS runner, fs via an in-process path fence, both keying off the same `ctx.sandboxPolicy` mode. web/todo stay in-process and unfenced (web's only effect is network, outside the file-effect mode vocabulary).

+ 11 - 5
.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md

@@ -38,7 +38,7 @@ harness 是一个 SDK,因此约束必须是开发者可组合的能力:是
 
 这一替换对 `ctx.shell` 的所有消费方透明:bash 工具、钩子命令和后台任务照常运行,直接使用提供方返回的已包装 argv 启动。删除 `sandbox` 和 `permission` 条目、将 `bash` 替换为 `@deepseek-ai/dsh-bash-local` 即为退出——执行恢复为无约束,升级字段从工具 schema 中消失,因为它们是基于已挂载执行器的能力门控,而非基于配置。仅省略 `approval` 则保留约束但以自身错误文本关闭每次升级;`permission` 还要求 approval seam 和约束执行器同时存在,因此组合不完整的 preset 层会在加载时明确报错。
 
-配置错误会显式导致失败:`mode` 不在封闭词汇中时在插件加载时被拒绝;主机上没有可用后端时在 `confine()` 阶段抛出结构化的 `SANDBOX_UNAVAILABLE`,而非降级为无约束执行。若可归因的 `ENOENT` 或 `EACCES` 指明所选 runner,就能证明该 executable 未启动,因此消费方会报告同一基础设施错误;其他同步创建错误原样传播。异步 subprocess-provider rejection 不公开 target 阶段,保留本地不声明阶段的语义。`dsh-sandbox-local` 上的 `runnerCommand` 是运维人员对一个 bwrap 兼容 runner 的显式断言(跳过链和探测);它同时充当 keyless 测试的确定性 fake-runner 钩子。
+配置错误会显式导致失败:`mode` 不在封闭词汇中时在插件加载时被拒绝;主机上没有可用后端时在 `confine()` 阶段以结构化的 `SANDBOX_UNAVAILABLE` 拒绝,而非降级为无约束执行。若可归因的 `ENOENT` 或 `EACCES` 指明所选 runner,就能证明该 executable 未启动,因此消费方会报告同一基础设施错误;其他同步创建错误原样传播。异步 subprocess-provider rejection 不公开 target 阶段,保留本地不声明阶段的语义。`dsh-sandbox-local` 上的 `runnerCommand` 是运维人员对一个 bwrap 兼容 runner 的显式断言(跳过链和探测);它同时充当 keyless 测试的确定性 fake-runner 钩子。
 
 被拒绝的文件操作返回 `[sandbox: file access denied under <mode> mode]` 标记,并附带不要绕过拒绝的指令。约束执行器添加配对的 `sandbox_permissions` 和 `justification` 字段,用于一次经批准的重试,该重试必须严格宽于会话的有效模式。授权仅放宽该次重试;拒绝则不执行任何内容,返回 `the user rejected escalating this command to "<mode>"`,且不允许再次请求。由归属方派生的待处理策略上下文会说明当前文件策略,但不会取代这些强制执行边界。当 `dsh-permission-presets` 与某个 UI 适配器一起组合时,一个 preset 同时选定两个旋钮值;不匹配的组合折叠为 `custom`。[ACP(Agent Client Protocol)应用组合包](../../../../packages/bundle/acp-app/README.zh.md)不挂载该 UI 服务,而是显式选定其部署模式。
 
@@ -50,7 +50,7 @@ OS 子进程约束适用于 bash 执行器(包括钩子命令),后续还
 
 #### seam:`ctx.sandbox`
 
-`dsh-sandbox` 负责定义词汇和 `SandboxProvider` 约定:`confine(argv, policy)` 返回调用方应当 spawn 的替代 argv(经过包装,使进程及其所有子进程在约束下运行),加上所选后端达到的 `enforcement` 完整度、其拒绝方言(`denialSignatures`,该后端内核在拒绝文件操作时打印到 stderr 的子串),以及其结构化 runner 失败证据(`runnerFailureRules`,可选的允许退出码加上排除整行精确信息性行后按行匹配的致命签名);没有可用后端时抛出失败关闭的 `SANDBOX_UNAVAILABLE` 错误,绝不静默放行。词汇:`SandboxMode`(`read-only` / `workspace-write` / `danger-full-access`,仅限文件操作——不声称覆盖网络和进程可见性)、`SandboxEnforcement`(`full` / `partial`)、`SandboxExecutionPolicy`(每次能力调用的完整 mode + 工作区根目录)以及 `SandboxPolicy`(提供给约束后端的子集)。
+`dsh-sandbox` 负责定义词汇和 `SandboxProvider` 约定:`confine(argv, policy, signal?)` 的 Promise 完成后提供调用方应当 spawn 的替代 argv(经过包装,使进程及其所有子进程在约束下运行),加上所选后端达到的 `enforcement` 完整度、其拒绝方言(`denialSignatures`,该后端内核在拒绝文件操作时打印到 stderr 的子串),以及其结构化 runner 失败证据(`runnerFailureRules`,可选的允许退出码加上排除整行精确信息性行后按行匹配的致命签名);没有可用后端时以失败关闭的 `SANDBOX_UNAVAILABLE` 错误拒绝,绝不静默放行。词汇:`SandboxMode`(`read-only` / `workspace-write` / `danger-full-access`,仅限文件操作——不声称覆盖网络和进程可见性)、`SandboxEnforcement`(`full` / `partial`)、`SandboxExecutionPolicy`(每次能力调用的完整 mode + 工作区根目录)以及 `SandboxPolicy`(提供给约束后端的子集)。
 
 策略随每次调用而非提供方携带:两个消费方可以在同一时刻以不同策略约束(bash 在 `read-only` 下运行,而一个受约束的子 agent 保持其状态目录可写),且经批准的升级重试是一次带有更宽策略的新调用——在配置固定的提供方模式下无法表达。
 
@@ -68,9 +68,15 @@ Landlock launcher 源码和包家族位于 `native/system`,与 harness 消费
 
 后端 profile 共享模式约定但在必要的主机授权上有所不同。Landlock 和 Seatbelt 在 read-only 模式下仅允许 `/dev/null`;workspace-write 还允许各自所需的主机临时目录根。每次包装携带后端特定的拒绝签名。Landlock 在较旧的 ABI 无法管控所有操作时报告 partial enforcement,而成功的 bwrap 和 Seatbelt profile 报告 full enforcement。
 
+#### 异步准备
+
+提供方拥有的 I/O 要求异步约束。`confine(argv, policy, signal?)` 返回 Promise,使提供方可以请求已运行的受信进程准备执行,同时让 subprocess argv 保持字面含义。消费方等待准备完成,并在启动载荷前再次检查取消状态;远端提供方无需为每条命令在约束之外执行可变的 helper 产物。
+
+`ShellExecutor.start()` 仅在准备成功后发布句柄。通用任务准入保持同步:工具拥有的钩子在任务预检通过后开始准备,拥有准备阶段的取消,并在结算前等待迟到的进程。这使准入失败不会启动工作,并将异步准备失败记录为任务结果。
+
 #### bash 消费方
 
-`dsh-bash-sandbox` 扩展 `LocalBashExecutor`,把精确的 `['bash', '-c', command]` argv 交给 `ctx.sandbox`,并直接 spawn 提供方返回的 argv。这样,随附的原生 runner 建立约束后,shell 语义与 `BASH_ENV` 仍由内层 Bash 处理。提供方错误原样传播。异步 rejection 只有在调用方拥有的 workdir 经独立验证可用,Node 报告 `ENOENT` 或 `EACCES`,并且错误符合以下一种形态时,才判定为 runner 失败:`error.path` 等于提供方返回的 `argv[0]`,同时 `syscall` 为 `'spawn'` 或精确的 `'spawn <runner>'`;或者 `error.path` 不存在,同时 `syscall` 为精确的 `'spawn <runner>'`。其他错误码、无效 workdir、资源失败、无关 syscall 与无结构 rejection 保留不声明阶段的 provider-failure 语义。前台执行会将可归因的 runner failure 转为 `SANDBOX_UNAVAILABLE` 并附上原始详情;可归因的异步后台 rejection 则盖章 `runnerFailed: true`、`denied: false`。如果 `SubprocessRuntime` 同步抛出同样能指明 runner 的形态,后台启动会抛出 `SANDBOX_UNAVAILABLE`;其他同步错误原样传播。direct outcome 已存在后,前台与后台共用一个 runner failure 分类器:先排除信息性行,再要求规则的退出码检查与余下的一行致命诊断同时匹配。匹配结果优先于拒绝:前台执行抛出 `SANDBOX_UNAVAILABLE`,并以该致命行作为详情;结算后的 `ShellProcess` 会盖章 `sandbox.runnerFailed`,bash 生产者再通过通用 `job_output` 渲染它。
+`dsh-bash-sandbox` 扩展 `LocalBashExecutor`,携带执行信号等待 `ctx.sandbox` 为精确的 `['bash', '-c', command]` argv 完成限制准备,重新检查取消后直接 spawn 提供方返回的 argv。这样,随附的原生 runner 建立约束后,shell 语义与 `BASH_ENV` 仍由内层 Bash 处理。提供方错误原样传播。异步 rejection 只有在调用方拥有的 workdir 经独立验证可用,Node 报告 `ENOENT` 或 `EACCES`,并且错误符合以下一种形态时,才判定为 runner 失败:`error.path` 等于提供方返回的 `argv[0]`,同时 `syscall` 为 `'spawn'` 或精确的 `'spawn <runner>'`;或者 `error.path` 不存在,同时 `syscall` 为精确的 `'spawn <runner>'`。其他错误码、无效 workdir、资源失败、无关 syscall 与无结构 rejection 保留不声明阶段的 provider-failure 语义。前台执行会将可归因的 runner failure 转为 `SANDBOX_UNAVAILABLE` 并附上原始详情;可归因的异步后台 rejection 则盖章 `runnerFailed: true`、`denied: false`。如果 `SubprocessRuntime` 同步抛出同样能指明 runner 的形态,后台启动会在发布句柄前以 `SANDBOX_UNAVAILABLE` 拒绝;其他同步错误原样传播。direct outcome 已存在后,前台与后台共用一个 runner failure 分类器:先排除信息性行,再要求规则的退出码检查与余下的一行致命诊断同时匹配。匹配结果优先于拒绝:前台执行抛出 `SANDBOX_UNAVAILABLE`,并以该致命行作为详情;结算后的 `ShellProcess` 会盖章 `sandbox.runnerFailed`,bash 生产者再通过通用 `job_output` 渲染它。
 
 模型会在归属方派生的 `sandbox:policy` 上下文中看到当前有效的文件策略;静态工具描述则解释拒绝标记(`[sandbox: file access denied under <mode> mode]`),鼓励尝试可能被拒绝的命令,并禁止绕过拒绝重试。当升级字段被公布时,被拒绝的结果还会携带升级提示本身,使被认可的同轮次重试在决策点获得提示,而非依赖模型回忆描述(§ 升级机制)。[当前策略决策](../../archived/feature/2026-07-30-current-sandbox-policy-context.md)负责该上下文的理由与边界。
 
@@ -184,8 +190,8 @@ fs/web/todo 在进程内执行,因此它们的沙箱语义是各自能力边
 ## FAQ
 
 - **一个命令返回了 `[sandbox: file access denied under read-only mode]`——它失败了吗?** 它运行了,内核拒绝了一个文件操作:拒绝是与退出码正交的结果事实。相关指令禁止通过绕过限制来重试;唯一被认可的动作是以升级请求重试同一命令一次。
-- **如何区分损坏的沙箱与失败的命令?** 异步 subprocess-provider rejection 不公开执行阶段。只有在调用方拥有的 workdir 可用,且 Node 为该 argv[0] 报告可归因的 `ENOENT` 或 `EACCES` 时,才能据此判定 confinement runner 损坏;没有精确错误路径的裸 `syscall: 'spawn'` 和其他所有 rejection 都保持不声明阶段的 provider failure。direct outcome 已存在后,只有当 `runnerFailureRules` 中某一条目同时匹配其可选退出码门控,以及排除整行精确信息性行后的一行致命 stderr 诊断时,runner 失败才会优先于拒绝。前台 runner failure 会抛出结构化的 `SANDBOX_UNAVAILABLE`,并附带 executable 或匹配行详情;可归因的异步后台 rejection 或匹配到的已结算失败会盖章 `sandbox.runnerFailed`,而所有异步 rejection 都会渲染本地执行器的 provider-failure 提示。如果 `SubprocessRuntime` 同步抛出同样带有 runner 路径的 `ENOENT`/`EACCES` 形态,后台启动会抛出该结构化错误;其他同步错误原样传播。Landlock 部分强制执行通知加上普通子进程失败时,仍返回命令结果。
-- **在没有后端的平台上会发生什么?** `confine()` 抛出失败关闭的 `SANDBOX_UNAVAILABLE`,命令永不 spawn。
+- **如何区分损坏的沙箱与失败的命令?** 异步 subprocess-provider rejection 不公开执行阶段。只有在调用方拥有的 workdir 可用,且 Node 为该 argv[0] 报告可归因的 `ENOENT` 或 `EACCES` 时,才能据此判定 confinement runner 损坏;没有精确错误路径的裸 `syscall: 'spawn'` 和其他所有 rejection 都保持不声明阶段的 provider failure。direct outcome 已存在后,只有当 `runnerFailureRules` 中某一条目同时匹配其可选退出码门控,以及排除整行精确信息性行后的一行致命 stderr 诊断时,runner 失败才会优先于拒绝。前台 runner failure 会抛出结构化的 `SANDBOX_UNAVAILABLE`,并附带 executable 或匹配行详情;可归因的异步后台 rejection 或匹配到的已结算失败会盖章 `sandbox.runnerFailed`,而所有异步 rejection 都会渲染本地执行器的 provider-failure 提示。如果 `SubprocessRuntime` 同步抛出同样带有 runner 路径的 `ENOENT`/`EACCES` 形态,后台启动会在发布句柄前以该结构化错误拒绝;其他同步错误原样传播。Landlock 部分强制执行通知加上普通子进程失败时,仍返回命令结果。
+- **在没有后端的平台上会发生什么?** `confine()` 以失败关闭的 `SANDBOX_UNAVAILABLE` 拒绝,命令永不 spawn。
 - **`bwrap` 已安装在我的主机上但不可用(禁用了非特权 userns、LSM 拒绝 `mount`)——会发生什么?** 链探测是功能性的——它构建并强制一个真实 profile 而非检查 `--version`——因此存在但不可用的 `bwrap` 探测失败,选择落到已打包的 Landlock launcher,结论在提供方生命周期内缓存。
 - **沙箱限制网络或进程可见性吗?** `SandboxMode` 仅声称文件操作,没有后端声称网络。进程可见性取决于后端:bwrap 会 unshare PID 并挂载匹配的 procfs,因为宿主 `/proc/<pid>` 魔法链接会绕过文件约束;Landlock 与 Seatbelt 则保持进程可见性不变([决策](../bug-fix/2026-08-06-bwrap-private-pid-namespace.zh.md))。网络限制是否成为自己的旋钮留在 § seam 中开放。
 - **哪些工具实际在约束下运行?** 通过 `ctx.shell` 的 OS 子进程——bash 工具及传递性的钩子命令——再加上通过沙箱化 `ctx.fs` 提供方运行的文件系统工具(`read`/`write`/`edit`,见[跨工具族 fs 沙箱 RFC](2026-07-14-cross-family-fs-sandbox.zh.md)):bash 通过 OS runner 约束,fs 通过进程内路径围栏约束,二者都以同一个 `ctx.sandboxPolicy` 模式为键。web/todo 仍在进程内且不受限制(web 的唯一效果是网络,不在文件效果模式词汇内)。

+ 2 - 2
.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md
-2026-07-14-cross-family-fs-sandbox.md: 08b95541770b3c150a694a3f0731cdc42fe3c3fd
-2026-07-14-cross-family-fs-sandbox.zh.md: 5ee4f4f5d6b9193f83a243b49bc9ae2627d63920
+2026-07-14-cross-family-fs-sandbox.md: 0fe1d4078797eceb9f3f0f9c319bba62acd6b08c
+2026-07-14-cross-family-fs-sandbox.zh.md: 021923c7e3c4653991377cd9b5bccf18c9a4bf65

+ 1 - 1
.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md

@@ -37,7 +37,7 @@ Three coordinated pieces, all composed from the leaf `cordis.yml`, none touching
 
 A denial is the structured `FS_SANDBOX_DENIED` carrying the effective mode — distinct from `FS_PERMISSION_DENIED` (a host EACCES is the world refusing; this is policy refusing). No text inference: an in-process fence knows exactly what it denied. The per-call carrier is a trailing optional `SandboxExecutionPolicy` on `writeText`/`editText` (the filesystem twin of `ShellExecRequest.sandboxPolicy`); the seam stays session-free, and the bare local backend ignores it. `FileSystem.sandboxMode` is the capability fact (`undefined` on the base and `fs-local`, the default on `SandboxedFileSystem`), so the tool layer advertises escalation from composition truth.
 
-The threat model is stated in the package README: a policy fence in trusted code over model-controlled paths, not a kernel boundary — the operations are the seam's own, only the target path is untrusted, so canonicalize-then-contain is the complete answer to this surface (the `code-runtime` "containment, not a security boundary" precedent). Kernel-grade isolation of untrusted CODE stays `ctx.shell`'s job. The residual resolve-to-syscall race is narrowed by the in-place re-canonicalization and eliminated only by platform primitives (`openat2` `RESOLVE_BENEATH`) not worth their portability cost here.
+The threat model is stated in the package README: a policy fence in trusted code over model-controlled paths, not a kernel boundary — the operations are the seam's own, only the target path is untrusted, so canonicalize-then-contain is the complete answer to this surface (the `ptc-runtime` "containment, not a security boundary" precedent). Kernel-grade isolation of untrusted CODE stays `ctx.shell`'s job. The residual resolve-to-syscall race is narrowed by the in-place re-canonicalization and eliminated only by platform primitives (`openat2` `RESOLVE_BENEATH`) not worth their portability cost here.
 
 ### Tool parity — one denial marker, one escalation flow
 

+ 1 - 1
.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.zh.md

@@ -37,7 +37,7 @@ Status: implemented
 
 拒绝是结构化的 `FS_SANDBOX_DENIED`,携带生效模式——区别于 `FS_PERMISSION_DENIED`(宿主 EACCES 是世界在拒绝;这里是策略在拒绝)。无文本推断:进程内围栏确切知道它拒绝了什么。per-call 载体是 `writeText`/`editText` 上一个末尾可选的 `SandboxExecutionPolicy`(文件系统侧对应 `ShellExecRequest.sandboxPolicy`);该 seam 保持无会话依赖,而裸的本地后端会忽略它。`FileSystem.sandboxMode` 是能力事实(在基类与 `fs-local` 上为 `undefined`,在 `SandboxedFileSystem` 上为默认值),所以工具层按组合真相来宣告升级。
 
-威胁模型写在包 README 里:一道位于可信代码中、针对模型可控路径的策略围栏,而非内核边界——操作是 seam 自身的,只有目标路径不可信,所以「先规范化再判包含」足以完整覆盖这一调用面(`code-runtime` 的「containment, not a security boundary」先例)。对不可信代码的内核级隔离仍是 `ctx.shell` 的职责。resolve 到系统调用之间残留的竞态被就地重新规范化收窄,只有平台原语(`openat2` `RESOLVE_BENEATH`)能彻底消除它,而在此不值得为其付出可移植性代价。
+威胁模型写在包 README 里:一道位于可信代码中、针对模型可控路径的策略围栏,而非内核边界——操作是 seam 自身的,只有目标路径不可信,所以「先规范化再判包含」足以完整覆盖这一调用面(`ptc-runtime` 的「containment, not a security boundary」先例)。对不可信代码的内核级隔离仍是 `ctx.shell` 的职责。resolve 到系统调用之间残留的竞态被就地重新规范化收窄,只有平台原语(`openat2` `RESOLVE_BENEATH`)能彻底消除它,而在此不值得为其付出可移植性代价。
 
 ### 工具对等——一个拒绝标记、一条升级流程
 

+ 2 - 2
.agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.md
-2026-07-20-ptc-typed-tool-returns.md: b7d7cc56210b229d7e36f8face888a34c9d1ac3e
-2026-07-20-ptc-typed-tool-returns.zh.md: 20e14cdac6151ceead084ff6a78eb5f7a6277af1
+2026-07-20-ptc-typed-tool-returns.md: 08e760b95a40386bccc8c7a2722d289da10e3dff
+2026-07-20-ptc-typed-tool-returns.zh.md: c2883740e1eeadbf6fb2413496104ff08a9bd1f7

+ 2 - 2
.agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.md

@@ -53,13 +53,13 @@ Before dispatch the bridge snapshots binding arguments as lossless JSON and snap
 
 PTC mode declares its rejection capability on the runtime request as `{ name: "ToolCallError", memberNameProperty: "toolName" }`. The runtime Service Definition treats those names as data: the worker materializes and injects the actual constructor used for `tools` binding failures, so `error instanceof ToolCallError` works without making a generic runtime know about tools. The worker constructs failures and defines their public fields through module-captured error and property-definition intrinsics plus null-prototype descriptors, so model mutations cannot replace the promised rejection with a worker failure. The error has the standard `Error` message plus the exact `toolName`; it deliberately omits `ToolFailure.info`, error codes, and Native content. This is an exception contract for control flow, not a failure union for programmatic classification.
 
-Binding arguments and resolutions are revalidated as lossless JSON on both sides of the hostile worker protocol and have no byte cap. Before crossing through structured clone, each detached value is encoded as a flat pre-order token stream whose transport nesting is bounded; the receiver rebuilds it iteratively. Valid application nesting therefore has neither a JavaScript call-stack depth cap nor a platform-specific nested structured-clone limit. At module initialization the worker captures its own realm's `Array.prototype` and `Object.prototype` identities, the native function-source intrinsic used only to recognize foreign-realm plain-container prototypes, and every structural and metering intrinsic used by the JSON boundary. Property writes use null-prototype descriptors, while private array and set operations invoke captured methods without consulting mutable global or prototype slots. Model code can therefore replace helpers such as `Object.keys`, `Array.isArray`, collection methods, string methods, or `Buffer.byteLength`, rewrite intrinsic-prototype constructor slots, or add descriptor-shaped fields to `Object.prototype` without changing validation, wire transport, or byte accounting. The foreign-realm native function-source check still rejects user-authored constructors that imitate `Object` or `Array`. The dependency-light runtime Service Definition names its structural equivalent `CodeJsonValue` so it need not depend on the session-owned canonical type; the generated SDK and tool API use `JsonValue`. Intermediate values are not prompt-truncated, context-spilled, or persisted. This preserves full acquired search, workflow, task, filesystem, and MCP values for programmatic filtering while leaving provider and executor acquisition limits truthful.
+Binding arguments and resolutions are revalidated as lossless JSON on both sides of the hostile worker protocol and have no byte cap. Before crossing through structured clone, each detached value is encoded as a flat pre-order token stream whose transport nesting is bounded; the receiver rebuilds it iteratively. Valid application nesting therefore has neither a JavaScript call-stack depth cap nor a platform-specific nested structured-clone limit. At module initialization the worker captures its own realm's `Array.prototype` and `Object.prototype` identities, the native function-source intrinsic used only to recognize foreign-realm plain-container prototypes, and every structural and metering intrinsic used by the JSON boundary. Property writes use null-prototype descriptors, while private array and set operations invoke captured methods without consulting mutable global or prototype slots. Model code can therefore replace helpers such as `Object.keys`, `Array.isArray`, collection methods, string methods, or `Buffer.byteLength`, rewrite intrinsic-prototype constructor slots, or add descriptor-shaped fields to `Object.prototype` without changing validation, wire transport, or byte accounting. The foreign-realm native function-source check still rejects user-authored constructors that imitate `Object` or `Array`. The dependency-light runtime Service Definition names its structural equivalent `PtcJsonValue` so it need not depend on the session-owned canonical type; the generated SDK and tool API use `JsonValue`. Intermediate values are not prompt-truncated, context-spilled, or persisted. This preserves full acquired search, workflow, task, filesystem, and MCP values for programmatic filtering while leaving provider and executor acquisition limits truthful.
 
 ### Outer result and output ledger
 
 The runtime accepts an exact lossless JSON completion of any root. Returning `undefined` omits the completion; returning `null` is an explicit result. `run_code` exposes the canonical outer value `{ logs: string[], result?: JsonValue }`. Its Native renderer emits logs first, renders a string result raw, and renders every other JSON root with an iterative pretty printer. Total indentation is capped at ten characters and deeper subtrees remain compact, preserving the established shallow text while keeping traversal stack-safe and formatted size linear in the canonical JSON size.
 
-`WorkerThreadCodeRuntime` replaces the former independent log and value caps with configurable `maxOutputBytes`, defaulting to `67_108_864` bytes. The worker charges captured logs by their exact JSON-string serialization and preflights the detached completion or program exception against the remaining combined budget before posting a terminal message. A giant thrown string or stack therefore crosses the worker port only as the fixed `output-limit` diagnostic. The host repeats the hostile-peer ledger for forged traffic and native pipe writes the worker cannot observe. Fixed `CodeRunResult` field names, braces, the bounded error-kind tag, and later presentation whitespace are deliberately outside this variable-payload ledger. Neither stage materializes an over-limit serialized completion. A result at or below the cap is exact. A completion that cannot survive lossless JSON snapshotting fails as `invalid-output`; a value, diagnostic, or combined outcome over the cap fails as `output-limit` rather than becoming inspected or truncated text.
+`NodePtcRuntime` replaces the former independent log and value caps with configurable `maxOutputBytes`, defaulting to `67_108_864` bytes. The worker charges captured logs by their exact JSON-string serialization and preflights the detached completion or program exception against the remaining combined budget before posting a terminal message. A giant thrown string or stack therefore crosses the worker port only as the fixed `output-limit` diagnostic. The host repeats the hostile-peer ledger for forged traffic and native pipe writes the worker cannot observe. Fixed `PtcRunResult` field names, braces, the bounded error-kind tag, and later presentation whitespace are deliberately outside this variable-payload ledger. Neither stage materializes an over-limit serialized completion. A result at or below the cap is exact. A completion that cannot survive lossless JSON snapshotting fails as `invalid-output`; a value, diagnostic, or combined outcome over the cap fails as `output-limit` rather than becoming inspected or truncated text.
 
 Logs stream eagerly so a terminated run can retain output already admitted. Native stdout and stderr writes that bypass the worker's patched stream slots use independent pipes, so terminal settlement continues bounded capture until worker termination completes before materializing the result. When the cap is crossed, the runtime returns an explicit bounded failure with the fitting captured prefix. That outer result then traverses the ordinary `run_code` rendering and spill policy, which may save the captured text and expose its configured head/tail preview. The spill layer cannot recover bytes the runtime rejected beyond the hard cap.
 

+ 2 - 2
.agents/notes/implemented/feature/2026-07-20-ptc-typed-tool-returns.zh.md

@@ -53,13 +53,13 @@ declare const tools: {
 
 PTC mode 通过运行时请求中的 `{ name: "ToolCallError", memberNameProperty: "toolName" }` 声明其以异常拒绝 Promise 的能力。运行时 Service Definition 只把这些名称视为数据:worker 会动态生成并注入真正用于 `tools` 绑定失败的构造函数,因此无需让通用运行时了解工具,`error instanceof ToolCallError` 也能成立。worker 使用模块初始化时捕获的 Error 构造函数与属性定义内建方法,配合原型为 null 的属性描述符,构造失败对象并定义其公开字段,因此模型代码的修改不会把约定承诺的 reject 变成 worker 失败。该错误包含标准的 `Error` 消息和确切的 `toolName`,并有意省略 `ToolFailure.info`、错误代码与 Native 内容。这是一项用于控制流的异常约定,而不是供程序分类的失败联合。
 
-绑定参数与绑定返回值会在不可信 worker 协议的两端重新校验为无损 JSON,且不设字节上限。每个分离后的值在通过结构化克隆跨越边界前,都会编码为扁平的前序 token 流,其传输结构的嵌套深度有界;接收方再以迭代方式重建该值。因此,有效应用数据的嵌套深度既不受 JavaScript 调用栈深度上限限制,也不受特定平台对嵌套结构化克隆施加的上限限制。模块初始化时,worker 会捕获自身 JavaScript 运行域中 `Array.prototype` 和 `Object.prototype` 的引用、仅用于识别其他运行域普通容器原型、可获取原生函数源码的内建函数,以及 JSON 边界用于结构处理和计量的全部内建方法。属性写入使用原型为 null 的属性描述符;内部的数组与集合操作直接调用捕获的方法,不会访问可变的全局或原型槽位。因此,即使模型代码替换 `Object.keys`、`Array.isArray`、集合方法、字符串方法或 `Buffer.byteLength` 等辅助方法,重写内建原型的构造函数槽位,或向 `Object.prototype` 添加形如属性描述符的字段,也不会改变校验、协议传输或字节计量。面向其他运行域的原生函数源码检查仍会拒绝由用户编写、冒充 `Object` 或 `Array` 的构造函数。为保持依赖轻量,运行时 Service Definition 将结构等价类型命名为 `CodeJsonValue`,从而无需依赖会话侧拥有的规范类型;生成的 SDK 和工具 API 则使用 `JsonValue`。这些值不会经过提示词截断、上下文 spill 或持久化。因此,程序可以完整筛选已经采集的搜索、工作流、任务、文件系统与 MCP 值,同时提供方和执行器的采集上限仍会实际生效。
+绑定参数与绑定返回值会在不可信 worker 协议的两端重新校验为无损 JSON,且不设字节上限。每个分离后的值在通过结构化克隆跨越边界前,都会编码为扁平的前序 token 流,其传输结构的嵌套深度有界;接收方再以迭代方式重建该值。因此,有效应用数据的嵌套深度既不受 JavaScript 调用栈深度上限限制,也不受特定平台对嵌套结构化克隆施加的上限限制。模块初始化时,worker 会捕获自身 JavaScript 运行域中 `Array.prototype` 和 `Object.prototype` 的引用、仅用于识别其他运行域普通容器原型、可获取原生函数源码的内建函数,以及 JSON 边界用于结构处理和计量的全部内建方法。属性写入使用原型为 null 的属性描述符;内部的数组与集合操作直接调用捕获的方法,不会访问可变的全局或原型槽位。因此,即使模型代码替换 `Object.keys`、`Array.isArray`、集合方法、字符串方法或 `Buffer.byteLength` 等辅助方法,重写内建原型的构造函数槽位,或向 `Object.prototype` 添加形如属性描述符的字段,也不会改变校验、协议传输或字节计量。面向其他运行域的原生函数源码检查仍会拒绝由用户编写、冒充 `Object` 或 `Array` 的构造函数。为保持依赖轻量,运行时 Service Definition 将结构等价类型命名为 `PtcJsonValue`,从而无需依赖会话侧拥有的规范类型;生成的 SDK 和工具 API 则使用 `JsonValue`。这些值不会经过提示词截断、上下文 spill 或持久化。因此,程序可以完整筛选已经采集的搜索、工作流、任务、文件系统与 MCP 值,同时提供方和执行器的采集上限仍会实际生效。
 
 ### 外层结果与输出账本
 
 运行时接受以任意 JSON 类型为根的精确无损完成值。返回 `undefined` 表示省略完成值;返回 `null` 则是显式结果。`run_code` 暴露规范外层值 `{ logs: string[], result?: JsonValue }`。其 Native 渲染器先输出日志;字符串结果保持原文,其他所有 JSON 根值则使用迭代式美化渲染器。总缩进长度上限为 10 个字符,更深的子树保持紧凑格式,既保留既有的浅层文本,又确保遍历不受调用栈深度限制,且格式化输出大小与规范 JSON 大小呈线性关系。
 
-`WorkerThreadCodeRuntime` 以可配置的 `maxOutputBytes` 取代彼此独立的日志与值上限,默认值为 `67_108_864` 字节。worker 会将已捕获日志序列化为 JSON 字符串后的精确字节数计入账本,并在发送终态消息前,根据组合账本的剩余额度预检分离后的完成值或程序异常。因此,即使抛出的字符串或堆栈极大,通过 worker 端口的也只会是固定的 `output-limit` 诊断。宿主侧会针对伪造流量以及 worker 无法观察的原生管道写入,重复执行这套面向不可信对端的账本校验。固定的 `CodeRunResult` 字段名、花括号、有界的错误类型标签及后续展示空白有意不计入这份可变负载账本。这两个阶段都不会实际生成超出上限的完成值序列化结果。结果不超过上限时会保持精确。完成值无法通过无损 JSON 快照时,以 `invalid-output` 失败;值、诊断或包含日志的组合结果超过上限时,以 `output-limit` 失败,而不会变成检查格式化后或截断的文本。
+`NodePtcRuntime` 以可配置的 `maxOutputBytes` 取代彼此独立的日志与值上限,默认值为 `67_108_864` 字节。worker 会将已捕获日志序列化为 JSON 字符串后的精确字节数计入账本,并在发送终态消息前,根据组合账本的剩余额度预检分离后的完成值或程序异常。因此,即使抛出的字符串或堆栈极大,通过 worker 端口的也只会是固定的 `output-limit` 诊断。宿主侧会针对伪造流量以及 worker 无法观察的原生管道写入,重复执行这套面向不可信对端的账本校验。固定的 `PtcRunResult` 字段名、花括号、有界的错误类型标签及后续展示空白有意不计入这份可变负载账本。这两个阶段都不会实际生成超出上限的完成值序列化结果。结果不超过上限时会保持精确。完成值无法通过无损 JSON 快照时,以 `invalid-output` 失败;值、诊断或包含日志的组合结果超过上限时,以 `output-limit` 失败,而不会变成检查格式化后或截断的文本。
 
 日志会在产生时立即流出,因此运行被终止时仍可保留已经纳入额度的输出。绕过 worker 中已改写流写入入口的原生 stdout 和 stderr 写入会经由彼此独立的管道传输,因此运行时在终态结算期间仍会继续在上限内捕获输出,直至 worker 完全终止,然后才组装结果。超过上限后,运行时会返回一个显式的有界失败,并携带可容纳的已捕获前缀。该外层结果随后通过普通的 `run_code` 渲染与 spill 策略;策略可以保存已捕获的文本,并暴露其配置指定的头尾预览。spill 层无法恢复运行时在硬上限之外拒绝的字节。
 

+ 2 - 2
.agents/notes/implemented/feature/2026-08-28-auto-review.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-28-auto-review.md
-2026-08-28-auto-review.md: 06925b9cd69395a980a13fb7307b82ea42bfdf30
-2026-08-28-auto-review.zh.md: f2052766ed89d04ed984cc21bb1cd914ec5c5348
+2026-08-28-auto-review.md: a5a0b94540a1c47a0f8dea988476653b0e60bfae
+2026-08-28-auto-review.zh.md: 076380c638fa09e8ef1a4a9adaa0b2e6e0ea58e9

+ 2 - 2
.agents/notes/implemented/feature/2026-08-28-auto-review.md

@@ -12,7 +12,7 @@ Full access lets useful project work proceed without repeated approvals, but it
 
 [`dsh-experimental-auto-review`](../../../../packages/experimental/auto-review/README.md) is a private, explicitly installed source Web layer. Default Web retains Read Only, Workspace Write, and Full access. The layer contributes current-session `auto`, whose only durable identity is `permission/preset:auto`; it shares Full access's unchanged `danger-full-access + never` knobs and tool definitions. Official payloads, Headless, General settings, and new-session defaults exclude the integration.
 
-Every native call and started PTC `tools.*` inner call receives one review before its body. The outer `run_code` transport and direct Node effects in its worker remain outside this guarantee. There are no tool-name exemptions, cached grants, retries, configurable policy, second authorization check, or manual fallback. A repeated call receives a fresh review.
+Every native call and started PTC `tools.*` inner call receives one review before its body. The outer `run_code` transport and direct Node effects in a PTC program remain outside this guarantee. There are no tool-name exemptions, cached grants, retries, configurable policy, second authorization check, or manual fallback. A repeated call receives a fresh review.
 
 ### Effects and authority
 
@@ -76,6 +76,6 @@ The [delegation-time policy capture](2026-07-25-subagent-policy-inheritance.md)
 
 ## Consequences
 
-Auto adds model latency and token cost and can misclassify effects. Its full-access execution and worker Node limitation make the experimental confirmation necessary. Filtering limits untrusted instruction roles but does not make an LLM classifier a deterministic security boundary.
+Auto adds model latency and token cost and can misclassify effects. Its full-access execution and PTC program limitation make the experimental confirmation necessary. Filtering limits untrusted instruction roles but does not make an LLM classifier a deterministic security boundary.
 
 Focused owner tests pin request filtering, strict response parsing, denial propagation, catalog ordering, cancellation, and post-seed child identity. Real Web composition tests exercise default/experimental menus, confirmation, denial cards, live removal/reinstallation, persisted restoration, and terminal survival. The certification runner uses shipped tools on isolated targets and exactly eight real reviewer calls: Flash covers exact session-created cleanup, unauthorized/authorized pre-existing deletion, and explicitly requested synthetic exfiltration; Pro and Vision each repeat only the medium pair. It records redacted decisions and external effects without retries or skipped cases; deterministic tests provide the same policy cases without credentials.

+ 2 - 2
.agents/notes/implemented/feature/2026-08-28-auto-review.zh.md

@@ -12,7 +12,7 @@ Full access 让有用的项目工作无需反复审批即可继续,但也允
 
 [`dsh-experimental-auto-review`](../../../../packages/experimental/auto-review/README.zh.md)是显式安装的私有源码 Web 层。默认 Web 保持 Read Only、Workspace Write 与 Full access。此层贡献仅限当前会话的 `auto`,唯一持久身份为 `permission/preset:auto`;它共用 Full access 未改变的 `danger-full-access + never` 旋钮与工具定义。正式 payload、Headless、通用设置与新会话默认值都排除此 integration。
 
-每个原生调用与已开始的 PTC `tools.*` inner call 都在 body 前接受一次审查。外层 `run_code` transport 与 worker 内直接 Node 效果不在保证范围内。不提供按工具名豁免、缓存 grant、重试、可配置策略、第二授权检查或人工 fallback。重复调用也重新审查。
+每个原生调用与已开始的 PTC `tools.*` inner call 都在 body 前接受一次审查。外层 `run_code` transport 与 PTC 程序内直接 Node 效果不在保证范围内。不提供按工具名豁免、缓存 grant、重试、可配置策略、第二授权检查或人工 fallback。重复调用也重新审查。
 
 ### 效果与权威
 
@@ -76,6 +76,6 @@ Auto 带右上标 `EXP`。两个可见当前会话选择器都要求实验确认
 
 ## 后果
 
-Auto 增加模型延迟和 token 成本,并可能误判效果。Full access 执行与 worker Node 限制使实验确认成为必要。过滤限制不可信指令角色,但不会把 LLM 分类器变成确定性安全边界。
+Auto 增加模型延迟和 token 成本,并可能误判效果。Full access 执行与 PTC 程序限制使实验确认成为必要。过滤限制不可信指令角色,但不会把 LLM 分类器变成确定性安全边界。
 
 聚焦 owner 测试固定请求过滤、严格响应解析、拒绝传播、目录顺序、取消与 seed 后 child 身份。真实 Web composition 测试覆盖默认/实验菜单、确认、拒绝卡片、live 移除/重装、持久恢复和终端存活。认证 runner 在隔离目标上使用 shipped tools,严格发起八次真实 reviewer 调用:Flash 覆盖精确清理本会话创建对象、未授权/已授权删除既有对象,以及显式请求的合成敏感信息外泄;Pro 与 Vision 各只重复 medium pair。它记录脱敏决定与外部效果,不重试、不跳过 case;确定性测试在无凭据时提供同形策略用例。

+ 2 - 2
.agents/notes/implemented/process/2026-06-11-quality-gates.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-06-11-quality-gates.md
-2026-06-11-quality-gates.md: f3e70f843eca1fb59e10704523d42c200d44de69
-2026-06-11-quality-gates.zh.md: efcf840ad8cb4189c0de268ccf315319c5639453
+2026-06-11-quality-gates.md: b4bcbcbb6e47ccbb3ed193300c4a8b2daa3901c0
+2026-06-11-quality-gates.zh.md: 64179fd7b13bf5de3d82bf9bf8819f6230b46238

+ 1 - 1
.agents/notes/implemented/process/2026-06-11-quality-gates.md

@@ -19,7 +19,7 @@ Every mechanically checkable AGENTS.md promise gets a command that exits non-zer
 - jscpd detects cross-file clones in package production TypeScript and repository scripts; narrow source-range exceptions document deliberately parallel implementations.
 - Per-file 100% coverage on `packages/*/*/src` (v8); unreachable defensive guards carry `/* v8 ignore */ ` with stated reasons instead of deletion.
 - publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM), and a NodeNext consumer typecheck for built package declarations. [The unused-code gate removal](2026-08-19-remove-knip.md) records why static dead-code scanning is outside this suite.
-- lefthook pre-commit applies project-free Oxlint validation and [safe fixes with a bounded retry](../../archived/process/2026-08-09-oxlint-only-fix-workflow.md), rejects staged whitespace, and checks the vendor manifest; pre-push runs incremental typecheck. CI runs the full matrix on node 22.19/24/26 plus built application smokes for the Headless, TUI, ACP, JSON-RPC, workflow, and code-runtime entry paths.
+- lefthook pre-commit applies project-free Oxlint validation and [safe fixes with a bounded retry](../../archived/process/2026-08-09-oxlint-only-fix-workflow.md), rejects staged whitespace, and checks the vendor manifest; pre-push runs incremental typecheck. CI runs the full matrix on node 22.19/24/26 plus built application smokes for the Headless, TUI, ACP, JSON-RPC, workflow, and ptc-runtime entry paths.
 
 ## Consequences
 

+ 1 - 1
.agents/notes/implemented/process/2026-06-11-quality-gates.zh.md

@@ -19,7 +19,7 @@ Status: implemented
 - jscpd 检测包的生产 TypeScript 代码与仓库脚本中的跨文件克隆;窄范围的源码区间例外用于记录有意为之的并行实现。
 - `packages/*/*/src` 下按文件 100% 覆盖率(v8);不可达的防御性守卫使用 `/* v8 ignore */ ` 并注明理由,而非删除。
 - publint(包的正确性)、workspace 约束(workspace 规则:private、cordis peer+dev、统一版本、ESM),以及对构建出的包声明文件进行 NodeNext 消费方类型检查。[移除未使用代码门禁的决策](2026-08-19-remove-knip.zh.md)说明了静态死代码扫描为何不属于这套门禁。
-- lefthook pre-commit 执行不加载项目的 Oxlint 验证,并应用带[一次有界重试](../../archived/process/2026-08-09-oxlint-only-fix-workflow.md)的安全修复,拒绝已暂存的空白问题并检查 vendor manifest(元数据清单);pre-push 运行增量类型检查。CI 在 Node 22.19/24/26 上运行完整矩阵,并对 Headless、TUI、ACP(Agent Client Protocol)、JSON-RPC、工作流和代码运行时入口路径执行已构建应用的冒烟测试。
+- lefthook pre-commit 执行不加载项目的 Oxlint 验证,并应用带[一次有界重试](../../archived/process/2026-08-09-oxlint-only-fix-workflow.md)的安全修复,拒绝已暂存的空白问题并检查 vendor manifest(元数据清单);pre-push 运行增量类型检查。CI 在 Node 22.19/24/26 上运行完整矩阵,并对 Headless、TUI、ACP(Agent Client Protocol)、JSON-RPC、工作流和 PTC 运行时入口路径执行已构建应用的冒烟测试。
 
 ## 后果
 

+ 2 - 2
.agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.md
-2026-09-11-remove-e2b-providers.md: 87bec5b728f64443e7132aa9b40feb684f9a0d0f
-2026-09-11-remove-e2b-providers.zh.md: 3048a61ed28232bda2f86ddc6ba6b003060ff87d
+2026-09-11-remove-e2b-providers.md: 6ff8f51e94875b9496da2c77b1e160357a15add9
+2026-09-11-remove-e2b-providers.zh.md: ebc592fb564d1ed1a0e981dc9b1bd3806de54414

+ 1 - 1
.agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.md

@@ -42,7 +42,7 @@ The [native containment](../architecture/2026-08-28-subprocess-native-containmen
 
 A remote provider needs a concrete execution use case and evidence for shared file/process coordinates, policy enforcement, bounded transport retention, independent control progress, precise channel closure and managed cancellation. Source and built compositions must exercise those behaviors. A connection loss cannot justify replaying a possibly executed program or claiming unobserved cleanup succeeded.
 
-SSH remains a possible transport for those providers: binary channels avoid the E2B command SDK's retained-output path, but ordinary SSH exec does not map arbitrary child descriptors. A remote helper still owns control-stream bridging, file semantics, process lifetime and remote policy enforcement. The retained interfaces permit that work without claiming a replacement is already available.
+The [POSIX SSH providers](../architecture/2026-09-11-posix-ssh-runtime.md) use binary channels outside the E2B command SDK's retained-output path. Ordinary SSH exec does not map arbitrary child descriptors, so their remote helper owns control-stream bridging, file semantics, process lifetime and remote policy enforcement. That implementation uses the retained interfaces; the E2B integration remains retired.
 
 ## Verification
 

+ 1 - 1
.agents/notes/implemented/simplification/2026-09-11-remove-e2b-providers.zh.md

@@ -42,7 +42,7 @@ PTC Node 约束需要独立于任意程序 stdin/stdout/stderr 的双向控制
 
 远程提供方需要具体的执行用例,并证明共享文件/进程坐标、策略强制、有界传输保留、独立控制推进、精确通道关闭和受管取消。源代码与构建后组合必须执行这些行为。连接丢失不能成为重放可能已执行程序的理由,也不能据此声称未观察到的清理已经成功。
 
-SSH 仍可作为这些提供方的传输:二进制通道避开 E2B 命令 SDK 保留输出的路径,但普通 SSH exec 不映射任意子进程描述符。远程辅助进程仍负责控制流桥接、文件语义、进程生命周期和远程策略强制。保留的接口允许开展这项工作,但不声称替代方案已经可用
+[POSIX SSH 提供方](../architecture/2026-09-11-posix-ssh-runtime.zh.md)使用独立于 E2B 命令 SDK 累计输出保留路径的二进制通道。普通 SSH exec 不映射任意子进程描述符,因此其远端辅助程序负责控制流桥接、文件语义、进程生命周期及远端策略执行。该实现使用保留的接口;E2B 集成继续退役
 
 ## 验证
 

+ 2 - 2
.agents/notes/implemented/testing/2026-09-08-ci-completion-observations.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-09-08-ci-completion-observations.md
-2026-09-08-ci-completion-observations.md: e3de87a70419778407b5eb230cec64ce088dd0c0
-2026-09-08-ci-completion-observations.zh.md: 4e95d6fb9dc9b1ecac2e3d0dfa262b819de16261
+2026-09-08-ci-completion-observations.md: 368d3b2f644ca3f7f850dd0ffbd56db19a1dca64
+2026-09-08-ci-completion-observations.zh.md: 1529361fb07b661f7152da9566900e3782cdb67f

+ 2 - 2
.agents/notes/implemented/testing/2026-09-08-ci-completion-observations.md

@@ -22,7 +22,7 @@ The [whole-queue steering test](../../../../apps/web/tests/steering.e2e.ts) wait
 
 The [workspace-management test](../../../../apps/web/tests/workspace-management.e2e.ts) waits for restored composer focus before the next directory-dialog gesture. Its archive case gives the known seed id an explicit user title through the Session controller, then uses that exact title to identify the row across reload. An unrelated restored row cannot satisfy that locator; the durable archive assertion still checks the seed id and retained log.
 
-The [worker budget tests](../../../../packages/code-runtime/code-runtime-worker-thread/tests/budget.spec.ts) retain real worker execution and binding transport while controlling host timers and ELU samples. They acknowledge binding entry before exercising idle, active, and wall-clock decisions, so a bootstrap timeout cannot stand in for a budget decision during a binding. The [real-worker tests](../../../../packages/code-runtime/code-runtime-worker-thread/tests/runtime.spec.ts) independently retain actual ELU, idle-binding, and hot-loop coverage.
+The [Node runtime tests](../../../../packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts) exercise real managed-process execution, binding transport, elapsed deadlines and cancellation. The [sandboxed Node decision](../architecture/2026-09-11-sandboxed-node-ptc-runtime.md) supersedes the worker ELU budget and its controlled-sample tests; real process and transport evidence remains necessary.
 
 The [detached-launch tests](../../../../packages/host/open-in-app/tests/launch-detached.spec.ts) control watch time and deliver late process events through the real launcher's registered callbacks. They check one settlement, one unref, and no child kill. Real-process environment and early-exit cases remain in the [resolver tests](../../../../packages/host/open-in-app/tests/resolver.spec.ts).
 
@@ -38,7 +38,7 @@ The [Node import sweep](../../../../packages/experimental/webworker-runtime/test
 
 **Completion inferred from acceptance or a preview.** HTTP 202 and an optimistic image can precede the operation being asserted.
 
-**Controlled samples replacing measured worker coverage.** Rejected because they omit verification of Node's actual ELU and transport behavior.
+**Controlled samples replacing real execution.** Rejected because timer samples alone cannot verify process launch, control transport or managed cleanup.
 
 ## Consequences
 

+ 2 - 2
.agents/notes/implemented/testing/2026-09-08-ci-completion-observations.zh.md

@@ -22,7 +22,7 @@ Status: implemented
 
 [Workspace 管理测试](../../../../apps/web/tests/workspace-management.e2e.ts)在下一次目录对话框操作前等待恢复后的 composer 焦点。归档用例通过 Session controller 为已知 seed id 设置显式用户标题,再用该精确标题跨重载定位行。无关的恢复行无法匹配该定位器;持久化归档断言仍检查 seed id 和保留的日志。
 
-[Worker 预算测试](../../../../packages/code-runtime/code-runtime-worker-thread/tests/budget.spec.ts)保留真实 worker 执行与绑定传输,只控制 Host 定时器和 ELU 样本。测试先确认绑定已进入,再检验 idle、active 和壁钟决策,使启动超时不能冒充绑定期间的预算决策。[真实 worker 测试](../../../../packages/code-runtime/code-runtime-worker-thread/tests/runtime.spec.ts)独立保留实际 ELU、空闲绑定和热循环覆盖
+[Node 运行时测试](../../../../packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts)执行真实受管进程、绑定传输、经过时间截止与取消。[沙箱 Node 决策](../architecture/2026-09-11-sandboxed-node-ptc-runtime.zh.md)取代 worker ELU 预算及其受控样本测试;真实进程与传输证据仍然必要
 
 [分离启动测试](../../../../packages/host/open-in-app/tests/launch-detached.spec.ts)控制观察时间,并通过真实 launcher 登记的回调发送迟到进程事件。测试检查仅完成一次、仅 unref 一次且不终止子进程。[Resolver 测试](../../../../packages/host/open-in-app/tests/resolver.spec.ts)保留真实进程的环境变量和提前退出用例。
 
@@ -38,7 +38,7 @@ Status: implemented
 
 **从接受或预览推断完成。** HTTP 202 和乐观图片可能早于被断言的操作。
 
-**用受控样本替换实测 worker 覆盖。** 拒绝,因为会遗漏对 Node 实际 ELU 与传输行为的验证
+**用受控样本代替真实执行。** 不采纳,因为仅靠定时器样本无法验证进程启动、控制传输或受管清理
 
 ## 影响
 

+ 2 - 2
.agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.md
-2026-09-08-ci-readiness-and-completion.md: c72048ef90b987e7d27992b1754736b2ce895093
-2026-09-08-ci-readiness-and-completion.zh.md: 1188d66249b3092416435da2e8496a657ad00677
+2026-09-08-ci-readiness-and-completion.md: 9afc8f7ecc3036acdf9ec2d5c87a50ef74ac9b69
+2026-09-08-ci-readiness-and-completion.zh.md: f3d72276a9160dd040e2b332d8579df98afec8fc

+ 1 - 1
.agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.md

@@ -28,7 +28,7 @@ The [ACP disconnect tests](../../../../packages/acp/acp/tests/dispose.spec.ts) a
 
 The [subagent teardown decision](2026-09-07-subagent-teardown-test-budgets.md) owns lifecycle cleanup budgets. The [persistent PowerShell decision](2026-09-07-pwsh-ci-observable-completion.md) owns exact versus inferred terminal readiness; a one-shot process's completion promise has different semantics.
 
-The [worker-runtime binding test](../../../../packages/code-runtime/code-runtime-worker-thread/tests/runtime.spec.ts) allows five seconds of compute for source-worker initialization and delays the binding for 6.5 seconds. Charging that idle delay would still exceed the entire compute allowance. The case retains its 15-second test limit and 30-second wall ceiling, registers Context and reply-timer cleanup, and leaves the hot-loop, decoy-dispatch, wall-ceiling, and abort controls at their existing limits. Production budgets remain unchanged.
+The [sandboxed Node decision](../architecture/2026-09-11-sandboxed-node-ptc-runtime.md) supersedes worker active-time accounting. The [Node runtime suite](../../../../packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts) exercises the replacement elapsed deadline through real managed processes. The other completion observations and fixture lifecycle rules in this note remain in force.
 
 The [SDK subagent protocol-error test](../../../../packages/subagent/subagent-dsh-sdk/tests/subagent-dsh-sdk.spec.ts) uses the provider's normal shutdown and exit grace periods and registers disposal before its assertions. The [Inspector tree tests](../../../../packages/experimental/inspector/tests/cordis-tree.host.spec.ts) pass the active test budget to Worker startup and register cleanup while startup is still pending. A cancelled test cannot receive a late-ready handle; cleanup awaits initialization and closes a successfully started Worker. Failed initialization already terminates the Worker before rejecting. A controlled late-start test verifies cancellation and closure through the real Worker's HTTP endpoint. Production defaults remain unchanged.
 

+ 1 - 1
.agents/notes/implemented/testing/2026-09-08-ci-readiness-and-completion.zh.md

@@ -28,7 +28,7 @@ Status: implemented
 
 [子 Agent 拆卸决策](2026-09-07-subagent-teardown-test-budgets.zh.md)负责生命周期清理预算。[持久 PowerShell 决策](2026-09-07-pwsh-ci-observable-completion.zh.md)负责精确与推断的终端就绪状态;一次性进程的完成 Promise 具有不同语义。
 
-[Worker runtime binding 测试](../../../../packages/code-runtime/code-runtime-worker-thread/tests/runtime.spec.ts)为源码 worker 初始化保留五秒计算额度,并将 binding 延迟设为 6.5 秒。若将该空闲延迟计费,仍会超过整个计算额度。用例保留 15 秒测试期限与 30 秒墙钟上限,登记 Context 和回复定时器的清理,并保持热循环、诱饵 dispatch、墙钟上限及取消控制用例的原有限制。生产预算不变
+[沙箱 Node 决策](../architecture/2026-09-11-sandboxed-node-ptc-runtime.zh.md)取代 worker 活跃时间计量。[Node 运行时套件](../../../../packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts)通过真实受管进程验证替代的经过时间截止。本说明中其他完成观测与测试生命周期规则保持有效
 
 [SDK 子 Agent 协议错误测试](../../../../packages/subagent/subagent-dsh-sdk/tests/subagent-dsh-sdk.spec.ts)使用提供方正常的关闭和退出等待时间,并在断言前登记清理。[Inspector 树测试](../../../../packages/experimental/inspector/tests/cordis-tree.host.spec.ts)将当前测试预算传给 Worker 启动,并在启动尚未完成时登记清理。取消后的测试不会收到随后才就绪的实例;清理等待初始化完成,并关闭成功启动的 Worker。初始化失败时,启动操作会在拒绝前终止 Worker。受控的延迟启动测试通过真实 Worker 的 HTTP 端点验证取消和关闭。生产默认值不变。
 

+ 2 - 2
.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md
-2026-09-10-hosted-image-test-assumptions.md: 5cf3b14503162d67d0fbb27743228cb1a8d7ffe2
-2026-09-10-hosted-image-test-assumptions.zh.md: db82566c11b50b9203d30f7be6aec1378512f94b
+2026-09-10-hosted-image-test-assumptions.md: 1053b43514c87e73a756e8cd5eeb26facc483a51
+2026-09-10-hosted-image-test-assumptions.zh.md: 9061ceb696101c08e644a256c54e2ffaa09d4b29

+ 2 - 2
.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.md

@@ -18,9 +18,9 @@ Terminal cases that drive a mocked PTY exit pin the containment they need (`inte
 
 `plugin-config dispose graces reach the real ACP run` configures 5000ms dispose graces. At 150ms the hosted image escalated while the scope could not take the signal — `systemctl` failed the kill (`Failed to send signal SIGKILL to auxiliary processes: Invalid argument`) and the teardown reported a failure the configuration never asked for. Its mock refuses stdin EOF and `SIGTERM` by design, so the case waits out both graces (~10s) and carries a 30s case budget, above the 5000ms default the local unit entry grants.
 
-Both illegal-UTF-8 residual cases in `packages/experimental/code-runtime-python/tests/runtime.spec.ts` pace their writes with `time.sleep(0.001)`: `os.sched_yield()` lets a loaded reader coalesce the writes into one chunk, and the coalesced chunk is what the wrapped `Buffer.concat` measures (the hosted image measured 2563 against the 2048 bound with a correct implementation). Their payloads stay above that bound — 3200 bytes for the `0xFF` case and 1100 `ED A0 80` sequences, 3300 raw bytes, for the CESU-8 case, past the 3072-byte budget a raw-byte undercount reaches — so the undercount still flushes above 2048. Each carries a 20s case budget for the paced writes plus the interpreter start.
+Both illegal-UTF-8 residual cases in `packages/experimental/ptc-runtime-python/tests/runtime.spec.ts` pace their writes with `time.sleep(0.001)`: `os.sched_yield()` lets a loaded reader coalesce the writes into one chunk, and the coalesced chunk is what the wrapped `Buffer.concat` measures (the hosted image measured 2563 against the 2048 bound with a correct implementation). Their payloads stay above that bound — 3200 bytes for the `0xFF` case and 1100 `ED A0 80` sequences, 3300 raw bytes, for the CESU-8 case, past the 3072-byte budget a raw-byte undercount reaches — so the undercount still flushes above 2048. Each carries a 20s case budget for the paced writes plus the interpreter start.
 
-The stray-output sealing test in `packages/experimental/code-runtime-python/tests/stray-fragments.spec.ts` keeps a real Python child but splits its stdout reads into single-byte events. OS pipe coalescing cannot guarantee the 1024 fragments needed to seal a block: [run 34465259316](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34465259316) passed all assertions but missed that branch. The controlled reads exercise repeated sealing and the final newline merge; exact output and bounded copy volume detect dropped bytes and repeated prefix copies.
+The stray-output sealing test in `packages/experimental/ptc-runtime-python/tests/stray-fragments.spec.ts` keeps a real Python child but splits its stdout reads into single-byte events. OS pipe coalescing cannot guarantee the 1024 fragments needed to seal a block: [run 34465259316](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34465259316) passed all assertions but missed that branch. The controlled reads exercise repeated sealing and the final newline merge; exact output and bounded copy volume detect dropped bytes and repeated prefix copies.
 
 The Linux coverage lane grants `DSH_COVERAGE_TEST_TIMEOUT_MS: '90000'`, matching the Windows coverage lane, because the disposal cases in `subprocess-local` and `bash-sandbox` exceed the 5000ms default when the lane's partitions, workers, and sibling gates share one host.
 

+ 2 - 2
.agents/notes/implemented/testing/2026-09-10-hosted-image-test-assumptions.zh.md

@@ -18,9 +18,9 @@ Status: implemented
 
 `plugin-config dispose graces reach the real ACP run` 配置 5000ms 的 dispose 宽限。在 150ms 时,托管镜像在 scope 还无法接受信号时就升级了信号——`systemctl` 的 kill 失败(`Failed to send signal SIGKILL to auxiliary processes: Invalid argument`),teardown 上报了一个配置从未要求的失败。该用例的 mock 按设计既拒绝 stdin EOF 也拒绝 `SIGTERM`,因此用例会等满两个宽限(约 10s),并自带 30s 的用例预算,高于本地单测入口授予的 5000ms 默认值。
 
-`packages/experimental/code-runtime-python/tests/runtime.spec.ts` 的两个非法 UTF-8 残余用例都用 `time.sleep(0.001)` 控制写入节奏:`os.sched_yield()` 会让被抢占的读端把多次写入合并成一个分块,而被包裹的 `Buffer.concat` 测量的正是该分块(在正确实现下,托管镜像测得 2563,超过了 2048 的界)。两个用例的载荷都保持在该界之上——`0xFF` 用例 3200 字节,CESU-8 用例 1100 个 `ED A0 80` 序列(3300 原始字节,超过按原始字节计费会触及的 3072 字节预算)——因此少计仍然会在 2048 之上触发 flush。两者各自带有 20s 的用例预算,容纳带节奏的写入与解释器启动。
+`packages/experimental/ptc-runtime-python/tests/runtime.spec.ts` 的两个非法 UTF-8 残余用例都用 `time.sleep(0.001)` 控制写入节奏:`os.sched_yield()` 会让被抢占的读端把多次写入合并成一个分块,而被包裹的 `Buffer.concat` 测量的正是该分块(在正确实现下,托管镜像测得 2563,超过了 2048 的界)。两个用例的载荷都保持在该界之上——`0xFF` 用例 3200 字节,CESU-8 用例 1100 个 `ED A0 80` 序列(3300 原始字节,超过按原始字节计费会触及的 3072 字节预算)——因此少计仍然会在 2048 之上触发 flush。两者各自带有 20s 的用例预算,容纳带节奏的写入与解释器启动。
 
-`packages/experimental/code-runtime-python/tests/stray-fragments.spec.ts` 的原生输出分块封存测试保留真实 Python 子进程,但把 stdout 读取拆成单字节事件。操作系统的管道合并无法保证达到封存一块所需的 1024 个片段:[run 34465259316](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34465259316) 的全部断言通过,却未覆盖该分支。可控读取覆盖反复封存和末尾换行合并;精确输出与复制总量上限检测字节丢失和前缀反复复制。
+`packages/experimental/ptc-runtime-python/tests/stray-fragments.spec.ts` 的原生输出分块封存测试保留真实 Python 子进程,但把 stdout 读取拆成单字节事件。操作系统的管道合并无法保证达到封存一块所需的 1024 个片段:[run 34465259316](https://github.com/deepseek-harness/deepseek-harness/actions/runs/34465259316) 的全部断言通过,却未覆盖该分支。可控读取覆盖反复封存和末尾换行合并;精确输出与复制总量上限检测字节丢失和前缀反复复制。
 
 Linux coverage 通道授予 `DSH_COVERAGE_TEST_TIMEOUT_MS: '90000'`,与 Windows coverage 通道一致,因为当该通道的分区、worker 与同级门禁共用一个宿主时,`subprocess-local` 与 `bash-sandbox` 的处置用例会超过 5000ms 默认值。
 

+ 6 - 0
.agents/notes/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.i18n.yaml

@@ -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/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.md
+2026-09-12-connection-and-compaction-fixture-preconditions.md: 9b97657c7742bb37a8a70b04350d5e6b43868f6c
+2026-09-12-connection-and-compaction-fixture-preconditions.zh.md: bc73a0e84ab4f60b7f3091f60a5217dcc950cb20

+ 27 - 0
.agents/notes/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.md

@@ -0,0 +1,27 @@
+# Agent Note: Connection and compaction fixture preconditions
+
+Status: implemented
+
+English | [中文](2026-09-12-connection-and-compaction-fixture-preconditions.zh.md)
+
+## Problem
+
+A reconnect label can appear while its hover color is still transitioning. Compaction pressure can select an initial instruction that is smaller than the required checkpoint framing. Neither observation alone establishes the state its test needs to assert.
+
+## Decision
+
+The [connection recovery test](../../../../apps/web/tests/lifecycle-chrome.e2e.ts) reads the warning color tokens from an independent element, then polls the indicator's computed foreground and background until both equal those tokens. Browser CSS transitions use a different clock from the mocked JavaScript retry timers. This follows the [fixture completion decision](2026-09-08-ci-completion-observations.md): an intermediate visual state cannot satisfy the final assertion.
+
+The [compaction smoke](../../../../apps/cli/tests/profiles/headless/tests/compaction.e2e.ts) completes each file-reading turn before submitting the next. Each file contains 200 repetitions of its numbered sentence (4,600 characters). The 8,000-token synthetic context window triggers compaction at 50% usage, leaving a 4,000-token pressure threshold. In a fixture measurement, one completed read used 2,738 request tokens, including 1,274 tokens outside the conversation surface. A failing calibration run selected about 60 tokens of initial instruction but produced a 475-token framed checkpoint; the runtime correctly rejected that larger replacement. Separate completed turns prevent all four reads from becoming one retained tool group. Failure output includes the recorded compaction errors, while the test still requires a summary, replaced history, and a final answer.
+
+## Alternatives considered
+
+**Sleep or disable browser transitions.** A fixed delay does not observe completion; disabling transitions removes the production behavior involved in the failure.
+
+**Raise timeouts or accept a nonshrinking checkpoint.** More time cannot make a short instruction larger than checkpoint framing. The runtime's nonshrinking rejection remains required.
+
+**Only enlarge the synthetic context window.** A model can still batch the reads into one retained tool group, leaving no substantial older group to summarize.
+
+## Consequences
+
+Product behavior, CSS, compaction acceptance rules, recorded snapshots, retries, and test deadlines remain unchanged. The live-provider smoke uses additional explicit turns to establish older history; the browser fixture observes animation completion through the same exact color assertions.

+ 27 - 0
.agents/notes/implemented/testing/2026-09-12-connection-and-compaction-fixture-preconditions.zh.md

@@ -0,0 +1,27 @@
+# Agent Note: 连接与压缩测试夹具的前置条件
+
+Status: implemented
+
+[English](2026-09-12-connection-and-compaction-fixture-preconditions.md) | 中文
+
+## Problem
+
+重连标签可能在悬停颜色仍处于过渡期间出现。压缩压力可能选中比检查点必需格式还短的初始指令。仅凭其中任一观察,都无法确定测试要断言的状态已经成立。
+
+## Decision
+
+[连接恢复测试](../../../../apps/web/tests/lifecycle-chrome.e2e.ts) 从独立元素读取警告颜色 token,再轮询指示器计算后的前景色与背景色,直到两者都等于相应 token。浏览器 CSS 过渡与被模拟的 JavaScript 重试计时器使用不同的时钟。这遵循[夹具完成条件决策](2026-09-08-ci-completion-observations.zh.md):中间视觉状态不能满足最终断言。
+
+[压缩冒烟测试](../../../../apps/cli/tests/profiles/headless/tests/compaction.e2e.ts) 完成每一轮文件读取后才提交下一轮。每个文件将包含编号的句子重复 200 次,共 4,600 个字符。8,000 token 的合成上下文窗口在用量达到 50% 时触发压缩,因此压力阈值为 4,000 token。一次夹具测量中,完成一次读取后的请求用了 2,738 token,其中 1,274 token 位于对话 surface 之外。一次失败的校准运行选中了约 60 token 的初始指令,却生成了包含格式在内的 475 token 检查点;运行时正确拒绝了这个更大的替换。分开的已完成轮次避免四次读取全部成为一个被保留的工具组。失败输出包含已记录的压缩错误,同时测试仍要求产生摘要、替换历史并给出最终回答。
+
+## Alternatives considered
+
+**等待固定时长或禁用浏览器过渡。** 固定延迟不能观察完成状态;禁用过渡则移除了失败所涉及的生产行为。
+
+**增大超时或接受未缩小的检查点。** 更多时间不能使短指令大于检查点格式。运行时仍必须拒绝未缩小的检查点。
+
+**只增大合成上下文窗口。** 模型仍可能将读取合并到一个被保留的工具组,使更早的历史中没有足够内容可供摘要。
+
+## Consequences
+
+产品行为、CSS、压缩接受规则、已记录快照、重试次数与测试截止时间保持不变。真实提供方冒烟测试通过额外的显式轮次建立较旧历史;浏览器夹具通过相同的精确颜色断言观察动画完成。

+ 2 - 2
.agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.md
-2026-07-19-required-cancellation-through-tool-capability-seams.md: 478cd887d84ceae8a5a0cd455fb7cf4b4d562950
-2026-07-19-required-cancellation-through-tool-capability-seams.zh.md: 0ce5956da11ef5144254325f0499d85b817ccfa6
+2026-07-19-required-cancellation-through-tool-capability-seams.md: 722eefc3c79d3c1c589696bc1876d10ccbce77ea
+2026-07-19-required-cancellation-through-tool-capability-seams.zh.md: 80d751e22cebd16d878f09ae9a5c15996c337603

+ 1 - 1
.agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.md

@@ -18,7 +18,7 @@ Require an `AbortSignal` on every asynchronous same-process capability operation
 
 Each direct caller supplies a signal it owns or propagates from its own required operation context. Implementations may derive a child deadline or cancellation scope, but the derived signal remains linked to the upstream signal for the delegated lifetime. Capability implementations do not synthesize never-abort signals, use ambient async-local cancellation, or validate `AbortSignal` at runtime solely to repeat the typed same-process contract.
 
-The migration begins with an inventory from every first-party `ToolDefinition.execute()` through the capability calls it awaits. It then changes each coherent Service Definition / Service Provider / Consumer seam together, including tests and generated API documentation. Separate PRs may migrate filesystem, shell/task, web/provider, workflow/subagent, code-runtime, and similar families so each change remains reviewable, but no migrated interface keeps an optional compatibility overload under the repository's pre-release policy.
+The migration begins with an inventory from every first-party `ToolDefinition.execute()` through the capability calls it awaits. It then changes each coherent Service Definition / Service Provider / Consumer seam together, including tests and generated API documentation. Separate PRs may migrate filesystem, shell/task, web/provider, workflow/subagent, ptc-runtime, and similar families so each change remains reviewable, but no migrated interface keeps an optional compatibility overload under the repository's pre-release policy.
 
 ### Scope boundary
 

+ 1 - 1
.agents/notes/proposed/architecture/2026-07-19-required-cancellation-through-tool-capability-seams.zh.md

@@ -18,7 +18,7 @@ Status: proposed
 
 每个直接调用方提供自己持有的信号,或从自身必填的操作上下文继续传递信号。实现可以派生子截止时间或取消作用域,但派生信号在委托期间仍须与上游信号关联。能力实现不得生成永不中止信号、使用环境式异步本地取消,也不得仅为重复类型化同进程约定而在运行时校验 `AbortSignal`。
 
-迁移首先从每个第一方 `ToolDefinition.execute()` 出发,沿其等待的能力调用进行清点;随后按内聚的 Service Definition/Service Provider/Consumer seam,将测试与生成的 API 文档一并修改。文件系统、Bash 与任务、Web 与提供方、工作流与 subagent、代码运行时等能力族可以通过独立 PR(Pull Request)迁移,以保持每项变更可审查;但根据仓库的预发布原则,已经迁移的接口不得保留可选兼容重载。
+迁移首先从每个第一方 `ToolDefinition.execute()` 出发,沿其等待的能力调用进行清点;随后按内聚的 Service Definition/Service Provider/Consumer seam,将测试与生成的 API 文档一并修改。文件系统、Bash 与任务、Web 与提供方、工作流与 subagent、PTC 运行时等能力族可以通过独立 PR(Pull Request)迁移,以保持每项变更可审查;但根据仓库的预发布原则,已经迁移的接口不得保留可选兼容重载。
 
 ### 范围边界
 

+ 2 - 2
.agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.md
-2026-07-04-prune-dead-core-spine-api.md: b2f6787e960bb85b25810836bf21476a9abdc0e3
-2026-07-04-prune-dead-core-spine-api.zh.md: 73dd1b94e06002b7b24f3229d8885b964058eed1
+2026-07-04-prune-dead-core-spine-api.md: 35130b1af2a2245ff575604466e098dcae7776cc
+2026-07-04-prune-dead-core-spine-api.zh.md: d2782b733b76a29b48f74e81d4a76a0f90575fdb

+ 3 - 3
.agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.md

@@ -16,7 +16,7 @@ The production corpus is `packages/*/*/src`, example sources/config, and runtime
 | `ToolExecutionResult.callId` | Every hook already receives the immutable `ToolExecution`; the loop and ACP correlate through the call/session event. No consumer reads the duplicate result field. | Remove the field, copy/mismatch guards, and tests that prove the duplicate cannot disagree. |
 | `ReactLoopAgent` root export | Outside-package named imports are tests; production programs against `Agent` and creates/resumes through `ctx.agents`. | Return/interface-type `Agent` and make the concrete loop class package-internal; keep the deliberate synchronous config-only `AgentLoop.create()` path. |
 | `workflow-worker-thread` protocol/runtime/session re-exports and named `WorkerThreadWorkflowEngine` | Every package-name consumer uses the default engine; the workflow Agent Note already defines the worker wire protocol as private. | Keep the default plugin class/config contract; drop the duplicate named class export and keep protocol modules source-private. |
-| `code-runtime-worker` protocol/bootstrap re-exports | Outside-package production/e2e consumers use `WorkerThreadCodeRuntime` and config, not `BootstrapPort`, `PatchableStream`, or worker message/boot types. | Keep the runtime class/config contract and make its wire/bootstrap vocabulary source-private. |
+| `ptc-runtime-worker` protocol/bootstrap re-exports | Outside-package production/e2e consumers use `NodePtcRuntime` and config, not `BootstrapPort`, `PatchableStream`, or worker message/boot types. | Keep the runtime class/config contract and make its wire/bootstrap vocabulary source-private. |
 | ACP `agentOptions` root export | The helper has only same-file and ACP-test consumers; the sole outside-package production consumer mounts the plugin namespace. | Keep `name`, `inject`, `Config`, `AcpConfig`, and `apply`; make `agentOptions` source-private and test it through bridge behavior. |
 | `providerWording` and `completedTurnPrefix` root exports | Each has one same-package production caller; only the balanced-prefix helper has a same-package white-box test. | Make them source-private and test provider behavior. |
 | `depthOf`, `SubagentDepthError`, `waitForExit`, and `exitsWithin` root exports | Production subagent backends consume the in-process runner and subprocess construction/disposal helpers, not these enforcement/test internals. `SENSITIVE_ENV_PATTERN` is excluded because the SDK helper applies it to caller-supplied environments. | Keep depth and exit behavior but make the remaining helpers and error source-private; test through spawn and disposal. Keep the shared credential pattern public. |
@@ -25,8 +25,8 @@ The production corpus is `packages/*/*/src`, example sources/config, and runtime
 | `compactRegion`'s separate `session` argument | The fixed caller passes the same object already present as `agent.session`; the model-visible mount API can also call the method, but accepting two identities permits a mounted plugin to provide an incoherent pair. | Keep the manual-region API while deliberately narrowing it to `agent.session` as the one source of truth. |
 | `CompactionResult.startSeq`, `summarySeq`, `endSeq`, and `summary` | The production consumer reads only shadowed range/seq/token accounting; the durable log owns summary and event identity. | Remove the four result echoes while keeping both shared transcript renderers. |
 | `BasicCompactionEngine` estimation/summarization visibility | No outside production caller invokes the five methods; the implemented Agent Note names only `estimateContentTokens()` and `summarize()` as subclass hooks. | Make those two `protected` and the three orchestration-only estimators private. |
-| `CodeLogEntry.source`/`level` and `RunCodeMeta.dispatches` | Every production consumer maps logs to text; no presenter/model path reads the other fields or the persisted dispatch count. | Make code-runtime logs strings (or text-only entries) and remove result-meta dispatch plumbing; keep the local counter that mints deterministic dispatch ids. |
-| `CodeRuntime.language` and `CodeRuntime.isolation` | The worker backend supplies the only production values, while PTC mode and every other production caller invoke only `run()`. | Remove the unread descriptors while preserving the worker's language, isolation, budgets, cancellation, and disposal behavior. |
+| `CodeLogEntry.source`/`level` and `RunCodeMeta.dispatches` | Every production consumer maps logs to text; no presenter/model path reads the other fields or the persisted dispatch count. | Make ptc-runtime logs strings (or text-only entries) and remove result-meta dispatch plumbing; keep the local counter that mints deterministic dispatch ids. |
+| `PtcRuntime.language` and `PtcRuntime.isolation` | The worker backend supplies the only production values, while PTC mode and every other production caller invoke only `run()`. | Remove the unread descriptors while preserving the worker's language, isolation, budgets, cancellation, and disposal behavior. |
 | `ToolNotFoundError.toolName`, `SystemPrompt.config`, and `BashTask.command` | Each stored public value has no production reader. | Drop the unread field while retaining error messages, resolved configuration behavior, and task lifecycle. |
 | Backend package-root implementation helpers | The exact inventory below is called only through relative same-package imports. Production namespace imports mount the retained plugin contract without reading these properties; named root consumers are tests. | Retain each adapter/provider/service and its config/error contract; stop exporting the listed helper functions/constants at package roots. |
 | Consumer package-root implementation helpers | The exact inventory below has only same-package production callers. Production namespace imports mount plugin contracts without reading helper properties; named root consumers are tests. | Retain plugin contracts and stable error codes; move tests to package-local modules or public behavior and stop exporting the listed helpers at package roots. |

+ 3 - 3
.agents/notes/rejected/simplification/2026-07-04-prune-dead-core-spine-api.zh.md

@@ -16,7 +16,7 @@ Status: rejected — stale 2026-07 inventory: rows were pruned piecemeal or gain
 | `ToolExecutionResult.callId` | 每个钩子已经接收不可变的 `ToolExecution`;循环和 ACP(Agent Client Protocol)通过调用/会话事件关联。没有消费方读取这个重复的结果字段。 | 移除该字段、复制/不匹配守卫,以及证明该重复不可能不一致的测试。 |
 | `ReactLoopAgent` 根导出 | 包外的命名导入都是测试;生产代码面向 `Agent` 编程,通过 `ctx.agents` 创建/恢复。 | 将返回类型和接口类型设为 `Agent`,将具体循环类改为包内部;保留有意设计的同步、仅配置的 `AgentLoop.create()` 路径。 |
 | `workflow-worker-thread` 的 protocol/runtime/session 再导出与命名的 `WorkerThreadWorkflowEngine` | 所有通过包名导入的消费方都使用默认引擎;工作流 Agent Note 已将 worker 协议格式(wire format)定义为私有。 | 保留默认插件类/配置约定;移除重复的命名类导出,将协议模块保持为源码私有。 |
-| `code-runtime-worker` 的 protocol/bootstrap 再导出 | 包外的生产/e2e 消费方使用 `WorkerThreadCodeRuntime` 和配置,而非 `BootstrapPort`、`PatchableStream` 或 worker 消息/启动类型。 | 保留运行时类/配置约定,将其协议格式/bootstrap 词汇改为源码私有。 |
+| `ptc-runtime-worker` 的 protocol/bootstrap 再导出 | 包外的生产/e2e 消费方使用 `NodePtcRuntime` 和配置,而非 `BootstrapPort`、`PatchableStream` 或 worker 消息/启动类型。 | 保留运行时类/配置约定,将其协议格式/bootstrap 词汇改为源码私有。 |
 | ACP 的 `agentOptions` 根导出 | 该辅助函数只有同文件和 ACP 测试消费方;唯一的包外生产消费方挂载的是插件命名空间。 | 保留 `name`、`inject`、`Config`、`AcpConfig` 和 `apply`;将 `agentOptions` 改为源码私有,通过桥接层行为测试。 |
 | `providerWording` 与 `completedTurnPrefix` 根导出 | 各有一个同包生产调用者;只有 balanced-prefix 辅助函数有一个同包白盒测试。 | 改为源码私有,测试提供方行为。 |
 | `depthOf`、`SubagentDepthError`、`waitForExit` 与 `exitsWithin` 根导出 | 生产 subagent 后端消费的是进程内 runner 和子进程构造/dispose(资源释放)辅助函数,而非这些强制机制和测试内部实现。`SENSITIVE_ENV_PATTERN` 不在其中,因为 SDK helper 会将它应用于调用方传入的环境。 | 保留深度与退出行为,但将剩余辅助函数和 error 改为源码私有;通过 spawn 和 dispose 测试。保持共享凭据正则公开。 |
@@ -25,8 +25,8 @@ Status: rejected — stale 2026-07 inventory: rows were pruned piecemeal or gain
 | `compactRegion` 的独立 `session` 参数 | 固定调用方传入的对象就是 `agent.session` 中已有的对象;模型可见的 mount API 也可以调用该方法,但同时接受两个独立对象,会让挂载的插件传入不一致的组合。 | 保留手动 region API,同时有意将其收窄为以 `agent.session` 为唯一真源。 |
 | `CompactionResult.startSeq`、`summarySeq`、`endSeq` 与 `summary` | 生产消费方只读取 shadowed range/seq/token 统计;持久日志拥有 summary 和事件标识。 | 移除四个结果回显,保留两个共享的 transcript(文本记录)渲染器。 |
 | `BasicCompactionEngine` 的估算/摘要方法可见性 | 没有包外生产调用者调用这五个方法;已实现的 Agent Note 只将 `estimateContentTokens()` 和 `summarize()` 命名为子类钩子。 | 将这两个方法改为 `protected`,其余三个编排专用的估算器改为 private。 |
-| `CodeLogEntry.source`/`level` 与 `RunCodeMeta.dispatches` | 每个生产消费方都将日志映射为文本;没有 presenter/模型路径读取其他字段或持久化的 dispatch 计数。 | 将 code-runtime 日志改为字符串(或纯文本条目),移除 result-meta 的 dispatch 管道;保留用于生成确定性 dispatch id 的本地计数器。 |
-| `CodeRuntime.language` 与 `CodeRuntime.isolation` | worker 后端提供唯一的生产值,而 PTC mode 及其他所有生产调用方只调用 `run()`。 | 移除未读描述符,同时保留 worker 的语言、隔离、预算、取消与资源释放行为。 |
+| `CodeLogEntry.source`/`level` 与 `RunCodeMeta.dispatches` | 每个生产消费方都将日志映射为文本;没有 presenter/模型路径读取其他字段或持久化的 dispatch 计数。 | 将 ptc-runtime 日志改为字符串(或纯文本条目),移除 result-meta 的 dispatch 管道;保留用于生成确定性 dispatch id 的本地计数器。 |
+| `PtcRuntime.language` 与 `PtcRuntime.isolation` | worker 后端提供唯一的生产值,而 PTC mode 及其他所有生产调用方只调用 `run()`。 | 移除未读描述符,同时保留 worker 的语言、隔离、预算、取消与资源释放行为。 |
 | `ToolNotFoundError.toolName`、`SystemPrompt.config` 与 `BashTask.command` | 每个存储的公开值都没有生产读取者。 | 移除未读字段,保留错误消息、已解析的配置行为和任务生命周期。 |
 | 后端包根实现辅助函数 | 下方精确清单仅通过相对路径的同包导入调用。生产命名空间导入挂载的是保留的插件约定,不读取这些属性;包根命名导入的消费方都是测试。 | 保留每个适配器/提供方/服务及其配置/错误约定;停止在包根导出所列辅助函数/常量。 |
 | 消费方包根实现辅助函数 | 下方精确清单只有同包生产调用者。生产命名空间导入挂载的是插件约定,不读取辅助属性;包根命名导入的消费方都是测试。 | 保留插件约定和稳定的错误码;将测试迁移到包内模块或公开行为,停止在包根导出所列辅助函数。 |

+ 2 - 2
.agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.md
-2026-07-26-dependency-swaps-rejected-by-nih-audit.md: 06b884d78105c23c5c489a10e0c1489aa5f95eee
-2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: 0cff8b10109919b5e6be96e1f0c26de3e4d3bbf8
+2026-07-26-dependency-swaps-rejected-by-nih-audit.md: 1ea01d96c5e817440c1daca871603d9cec78c06a
+2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md: d7140b88a75d1af045fd32e4b1048554071d68e8

+ 1 - 1
.agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.md

@@ -31,7 +31,7 @@ Adopt the following dependency swaps. Rejected — per-item evidence below; a fu
 **Data and validation:**
 
 - **Ajv for the tools JSON Schema validator**: the [schema-DSL note](../../implemented/architecture/2026-07-20-unified-json-value-schema-dsl.md) explicitly rejected accepting a larger schema language; the validator also does realm-intrinsic prototype checks Ajv does not.
-- **`structuredClone` for session `snapshotJsonValue`/`isJsonValue`**: it is a validator + detacher enforcing the lossless-JSON boundary with single-read-per-getter and cross-realm intrinsic checks; `structuredClone` accepts Map/Date/-0 and enforces nothing. Same for the deliberately dependency-free `code-runtime-worker` mirror hardened against a model-mutated realm.
+- **`structuredClone` for session `snapshotJsonValue`/`isJsonValue`**: it is a validator + detacher enforcing the lossless-JSON boundary with single-read-per-getter and cross-realm intrinsic checks; `structuredClone` accepts Map/Date/-0 and enforces nothing. Same for the deliberately dependency-free `ptc-runtime-worker` mirror hardened against a model-mutated realm.
 - **`fast-deep-equal` for session surface `isDeepEqualJson`** and **`safe-stable-stringify` for repeat-tool-reminder canonicalization**: both swaps work mechanically but each trades ~17–20 commented, tested lines for the first external runtime dependency of a core package — negative net at this size.
 - **zod/valibot for durable-event strict decoders** (goal fold, tool-ralph, session): exact-key fail-loud decoders at durable boundaries with event-specific messages; a second schema library beside repo-standard schemastery is a policy change, not a deletion.
 - **`gpt-tokenizer`/tiktoken for token-meter**: the [replay-token-meter note](../../archived/architecture/2026-07-15-replay-token-meter-service.md) explicitly rejected tokenizer backends; a GPT BPE is also the wrong tokenizer for DeepSeek models, and ~350 of the package's lines are replay-fold bookkeeping no tokenizer covers.

+ 1 - 1
.agents/notes/rejected/simplification/2026-07-26-dependency-swaps-rejected-by-nih-audit.zh.md

@@ -31,7 +31,7 @@ Status: rejected — 下列每一项替换在证据上都未达到净简化门
 **数据与校验:**
 
 - **以 Ajv 承担 tools 的 JSON Schema 校验器**:[schema DSL 决策](../../implemented/architecture/2026-07-20-unified-json-value-schema-dsl.zh.md)已明确否决接纳更大的 schema 语言;这个校验器还会做 Ajv 不做的、针对 realm 内建原型的检查。
-- **以 `structuredClone` 替换会话的 `snapshotJsonValue`/`isJsonValue`**:它是校验器加分离器,以「每个 getter 只读一次」和跨 realm 内建对象检查强制执行无损 JSON 边界;`structuredClone` 接受 Map/Date/-0,什么都不强制。有意保持零依赖、针对被模型篡改的 realm 做过加固的 `code-runtime-worker` 镜像实现同理。
+- **以 `structuredClone` 替换会话的 `snapshotJsonValue`/`isJsonValue`**:它是校验器加分离器,以「每个 getter 只读一次」和跨 realm 内建对象检查强制执行无损 JSON 边界;`structuredClone` 接受 Map/Date/-0,什么都不强制。有意保持零依赖、针对被模型篡改的 realm 做过加固的 `ptc-runtime-worker` 镜像实现同理。
 - **以 `fast-deep-equal` 替换会话接口面的 `isDeepEqualJson`**、**以 `safe-stable-stringify` 承担 repeat-tool-reminder 的规范化**:两项替换在机械层面都可行,但每一项都是拿约 17–20 行带注释、有测试的代码,去换一个核心包的第一个外部运行时依赖——在这个体量上是净亏损。
 - **以 zod/valibot 承担持久事件的严格解码器**(goal fold、tool-ralph、session):它们是位于持久化边界、键集精确匹配、失败即明确报错、带事件专属报错信息的解码器;在仓库标准 schemastery 之外再放一个 schema 库是政策变更,不是删除。
 - **以 `gpt-tokenizer`/tiktoken 替换 token-meter**:[回放 token 计量决策](../../archived/architecture/2026-07-15-replay-token-meter-service.md)已明确否决分词器后端;GPT 的 BPE 对 DeepSeek 模型来说也是错误的分词器,而且这个包约 350 行是回放折叠簿记,任何分词器都覆盖不了。

+ 2 - 1
AGENTS.md

@@ -21,6 +21,7 @@ packages/    @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
   llm/         LLM capability: Service Definition/Consumer + DeepSeek providers
   shell/        bash capability: Service Definition + local/pwsh providers + shell Consumers
   subprocess/  subprocess capability + local process-tree provider + shared Win32 library
+  ssh/         SSH connection + remote filesystem/subprocess/sandbox providers
   terminal/         persistent sessions
   fs/          filesystem capability + policy
   lsp/         language-server capability
@@ -77,7 +78,7 @@ pnpm run duplication    # cross-file TypeScript clone detection
 pnpm run build          # tsc emits lib/types, tsdown bundles runtime
 pnpm run hygiene        # publint + workspace/package/dependency checks + NodeNext consumer check
 pnpm run check:windows-wine  # ONLY when diagnosing a known Windows failure (needs wine); CI owns this signal
-pnpm run doc-sync       # all documentation gates; leaf list in scripts/run-gates.ts
+pnpm run doc-sync       # documentation gates (scripts/run-gates.ts)
 pnpm run test:docs      # quick documentation checks (no build; doc-quick aggregate)
 pnpm run website:build  # VitePress build (doubles as dead-link check)
 pnpm dsh --profile headless "task"  # run one task from source (needs DEEPSEEK_API_KEY)

+ 1 - 0
THIRD_PARTY_NOTICES.md

@@ -55,6 +55,7 @@ External packages installed for runtime use or distributed inside the prebuilt b
 | [`@shikijs/langs`](https://github.com/shikijs/shiki) | MIT |
 | [`@standard-schema/spec`](https://github.com/standard-schema/standard-schema) | MIT |
 | [`@tanstack/react-virtual`](https://github.com/TanStack/virtual) | MIT |
+| [`@trycua/cua-driver`](https://github.com/trycua/cua) | MIT |
 | [`@vscode/ripgrep`](https://github.com/microsoft/vscode-ripgrep) | MIT |
 | [`@xterm/headless`](https://github.com/xtermjs/xterm.js) | MIT |
 | [`@yarnpkg/parsers`](https://github.com/yarnpkg/berry) | BSD-2-Clause |

+ 1 - 1
apps/cli/package.json

@@ -113,7 +113,7 @@
     "@deepseek-ai/dsh-deepseek-llm-api-extensions": "workspace:^",
     "@deepseek-ai/dsh-experimental-agent-team": "workspace:^",
     "@deepseek-ai/dsh-experimental-agent-team-profile": "workspace:^",
-    "@deepseek-ai/dsh-experimental-code-runtime-python": "workspace:^",
+    "@deepseek-ai/dsh-experimental-ptc-runtime-python": "workspace:^",
     "@deepseek-ai/dsh-experimental-tool-agent-team": "workspace:^",
     "@deepseek-ai/dsh-fs-observation-policy": "workspace:^",
     "@deepseek-ai/dsh-fs-sandbox": "workspace:^",

+ 20 - 6
apps/cli/tests/profiles/headless/tests/compaction.e2e.ts

@@ -27,14 +27,15 @@ afterEach(async () => {
 describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compacts mid-flight and keeps running', () => {
   it('summarizes older history into a checkpoint without breaking the task', async () => {
     workdir = await mkdtemp(join(tmpdir(), 'dsh-compaction-'))
+    // An older file result must exceed the framed checkpoint's fixed sections.
     for (let i = 1; i <= 4; i++) {
-      await writeFile(join(workdir, `file${i}.txt`), `This is file number ${i}. `.repeat(50))
+      await writeFile(join(workdir, `file${i}.txt`), `This is file number ${i}. `.repeat(200))
     }
 
     // Reasoning tokens require a larger generation cap than the retained checkpoint.
     ctx = await codingHarness(workdir, {
       personaPrefix: SYSTEM_PROMPT,
-      modelContextWindow: 2000,
+      modelContextWindow: 8000,
       compact: {
         thresholdRatio: 0.5,
         retainTokens: 400,
@@ -47,12 +48,20 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compa
     })
     const agent = await ctx.agentLoop.create(SessionId('e2e-compaction'), { provider: 'deepseek-official', model: 'deepseek-v4-flash' })
 
+    // Completed read turns cannot collapse into one retained parallel tool group.
+    for (let i = 1; i <= 4; i++) {
+      agent.followup(createUserMessage({
+        content: [{
+          type: 'text',
+          text: `Read only file${i}.txt using one bash cat command. Remember its number for my next question.`,
+        }], source: { kind: 'user' } }))
+      await waitForIdle(ctx, agent)
+    }
+
     agent.followup(createUserMessage({
       content: [{
         type: 'text',
-        text: 'Read file1.txt, file2.txt, file3.txt, and file4.txt one at a '
-        + 'time using cat (a separate bash command for each). After reading all four, tell me how '
-        + 'many files you read and the number mentioned in file1.txt.',
+        text: 'How many files have you read, and what number was mentioned in file1.txt? Do not read them again.',
       }], source: { kind: 'user' } }))
     await waitForIdle(ctx, agent)
 
@@ -67,7 +76,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compa
     // It succeeded at least once: a `compaction/summary` event describing the summary and a
     // replace-op user/message (the surface mutation) both landed.
     const summaries = events.filter(e => e.type === 'compaction/summary')
-    expect(summaries.length).toBeGreaterThan(0)
+    expect(summaries.length, JSON.stringify(ends.map(event => event.data.error))).toBeGreaterThan(0)
     const replaceNode = events.find((e) => {
       const se = e as unknown as { type: string; surfaceOp?: unknown }
       return se.type === 'user/message' && typeof se.surfaceOp === 'object' && se.surfaceOp !== null
@@ -78,11 +87,16 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compa
     // message-producing event count).
     const summaryData = summaries[0]!.data as { shadowedSeqs: number[] }
     expect(summaryData.shadowedSeqs.length).toBeGreaterThan(0)
+    expect(events.some(event => event.type === 'tool/result'
+      && summaryData.shadowedSeqs.includes(event.seq)
+      && event.data.message.content[0].content.some(block => block.type === 'text'
+        && block.text.includes('This is file number')))).toBe(true)
 
     // The conversation survived compaction: the agent produced a final answer
     // that reflects the work (it read four files).
     const answer = finalText(events).toLowerCase()
     expect(answer.length).toBeGreaterThan(0)
     expect(answer).toMatch(/\b(4|four)\b/)
+    expect(answer).toMatch(/\b(1|one)\b/)
   }, 240_000)
 })

+ 26 - 13
apps/cli/tests/profiles/headless/tests/ptc.e2e.ts

@@ -1,7 +1,7 @@
 import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
 import { tmpdir } from 'node:os'
 import { join } from 'node:path'
-import { afterEach, describe, expect, it, vi } from 'vitest'
+import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest'
 import { Context } from '@deepseek-ai/cordis'
 import LlmRuntime, { createUserMessage, ToolCallId, HarnessError  } from '@deepseek-ai/dsh-llm'
 import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
@@ -18,7 +18,9 @@ import * as BashEnvPlugin from '@deepseek-ai/dsh-shell-env'
 import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
 import * as ToolBash from '@deepseek-ai/dsh-tool-bash'
 import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
-import { WorkerThreadCodeRuntime } from '@deepseek-ai/dsh-code-runtime-worker-thread'
+import NodeRuntime from '@deepseek-ai/dsh-ptc-runtime-node'
+import Sandbox from '@deepseek-ai/dsh-sandbox-local'
+import SandboxPolicy from '@deepseek-ai/dsh-sandbox-policy'
 import LocalFileSystem from '@deepseek-ai/dsh-fs-local'
 import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
 import * as WorkspaceContext from '@deepseek-ai/dsh-agent-instructions'
@@ -42,7 +44,7 @@ let workdir: string | undefined
 
 afterEach(async () => {
   // Always dispose, even on failure/retry/timeout: agent-loop teardown stops
-  // the loop, the executor kills stray processes, and the code runtime's
+  // the loop, the executor kills stray processes, and the PTC runtime's
   // dispose awaits worker exits.
   await ctx?.fiber.dispose()
   ctx = undefined
@@ -60,11 +62,11 @@ async function ptcModeHarness(cwd: string): Promise<Context> {
   await harness.plugin(AgentRegistry)
   await harness.plugin(AgentLoop, { agents: [] })
   await harness.plugin(LlmDeepSeek)
-  await harness.plugin(LocalSubprocessRuntime)
+  if (harness.get('subprocess') === undefined) await harness.plugin(LocalSubprocessRuntime)
   await harness.plugin(BashEnvPlugin)
   await harness.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 })
   await harness.plugin(ToolBash)
-  await harness.plugin(WorkerThreadCodeRuntime, {})
+  await mountRuntime(harness)
   return harness
 }
 
@@ -81,14 +83,14 @@ async function workspacePtcModeHarness(): Promise<Context> {
   await harness.plugin(WorkspaceContext, { maxBytes: 65536 })
   await harness.plugin(AgentLoop, { agents: [] })
   await harness.plugin(LlmDeepSeek, { models: [{ id: 'deepseek-v4-flash' }] })
-  await harness.plugin(WorkerThreadCodeRuntime, {})
+  await mountRuntime(harness)
   return harness
 }
 
 let keylessCall = 0
 const testToolSignal = new AbortController().signal
 
-/** Execute one outer PTC mode call through the real registry and worker. */
+/** Execute one outer PTC mode call through the real registry and Node process. */
 function runCode(
   harness: Context,
   code: string,
@@ -114,28 +116,39 @@ function completion(result: ToolExecutionResult): unknown {
   return value.result
 }
 
-/** Keyless real-worker harness for direct typed-binding acceptance tests. */
+async function mountRuntime(harness: Context): Promise<void> {
+  onTestFinished(async () => { await harness.fiber.dispose() })
+  if (!harness.get('sessions')) await harness.plugin(SessionStore)
+  if (!harness.get('fs')) await harness.plugin(LocalFileSystem)
+  if (!harness.get('subprocess')) await harness.plugin(LocalSubprocessRuntime)
+  if (!harness.get('sandbox')) await harness.plugin(Sandbox, {})
+  if (!harness.get('sessionProjections')) await harness.plugin(SessionProjectionRegistry)
+  if (!harness.get('sandboxPolicy')) await harness.plugin(SandboxPolicy, { mode: 'danger-full-access' })
+  await harness.plugin(NodeRuntime, {})
+}
+
+/** Keyless real-process harness for direct typed-binding acceptance tests. */
 async function typedPtcModeHarness(): Promise<Context> {
   const harness = new Context()
   await harness.plugin(SystemPrompt)
   await harness.plugin(ToolRuntime, { mode: 'ptc' })
-  await harness.plugin(WorkerThreadCodeRuntime, {})
+  await mountRuntime(harness)
   return harness
 }
 
-/** Keyless real-worker harness with the task-owned bash lifecycle. */
+/** Keyless real-process harness with the task-owned bash lifecycle. */
 async function backgroundPtcModeHarness(cwd: string): Promise<Context> {
   const harness = await typedPtcModeHarness()
   await harness.plugin(LocalJobRegistry)
   await harness.plugin(ToolTasks, {})
-  await harness.plugin(LocalSubprocessRuntime)
+  if (harness.get('subprocess') === undefined) await harness.plugin(LocalSubprocessRuntime)
   await harness.plugin(BashEnvPlugin)
   await harness.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 })
   await harness.plugin(ToolBash)
   return harness
 }
 
-describe('PTC mode typed values: keyless real-worker contracts', () => {
+describe('PTC mode typed values: keyless real-process contracts', () => {
   it('crosses a large intermediate value intact and exposes only typed tool failure fields', async () => {
     ctx = await typedPtcModeHarness()
     ctx.tools.register(defineTool({
@@ -270,7 +283,7 @@ describe('PTC mode typed values: keyless real-worker contracts', () => {
     await ctx.plugin(ToolCordis)
     const agent = {
       id: SessionId('ptc-cordis'),
-      session: { append: vi.fn() },
+      session: ctx.sessions.create(SessionId('ptc-cordis'), { meta: { cwd: process.cwd() } }),
     } as unknown as Agent
 
     const value = completion(await runCode(ctx, `

+ 8 - 8
apps/web/tests/lifecycle-chrome.e2e.ts

@@ -456,24 +456,24 @@ describe('web e2e: lifecycle & chrome (workspace flow / reload / dark mode)', ()
       expect(await connectionIndicatorTextAlignment(indicator)).toBe('left')
       const snapshot = await captureStableAria(recoveryPage, '[class*="footArea"]', scaffold.workspaceCwd)
       await compareOrRefreshGolden(CONNECTION_ERROR_EXPECTED, snapshot, MODE)
-      const style = await indicator.evaluate((element) => {
+      const expectedColors = await recoveryPage.evaluate(() => {
         const probe = document.createElement('span')
         probe.style.color = 'var(--dsw-alias-state-warn-label)'
         probe.style.backgroundColor = 'var(--dsw-alias-state-warn-tertiary)'
         document.body.append(probe)
-        const actual = getComputedStyle(element)
         const reference = getComputedStyle(probe)
         const result = {
-          background: actual.backgroundColor,
-          color: actual.color,
-          referenceBackground: reference.backgroundColor,
-          referenceColor: reference.color,
+          background: reference.backgroundColor,
+          color: reference.color,
         }
         probe.remove()
         return result
       })
-      expect(style.background).toBe(style.referenceBackground)
-      expect(style.color).toBe(style.referenceColor)
+      // CSS transitions use the browser's animation clock independently of the mocked retry timers.
+      await expect.poll(() => indicator.evaluate((element) => {
+        const actual = getComputedStyle(element)
+        return { background: actual.backgroundColor, color: actual.color }
+      })).toEqual(expectedColors)
       expect(await indicator.locator('svg').count()).toBe(1)
       expect(await indicator.getAttribute('title')).toBeNull()
       rejectConnections = false

+ 95 - 0
apps/web/tests/ptc-escalation.e2e.ts

@@ -0,0 +1,95 @@
+// Real PTC sandbox denial followed by one approved program execution.
+import { readFile } from 'node:fs/promises'
+import { fileURLToPath } from 'node:url'
+import { join } from 'node:path'
+import type { Browser, Page } from 'playwright'
+import { chromium } from 'playwright'
+import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
+import type { SessionEvent } from '@deepseek-ai/dsh-session'
+import type {} from '@deepseek-ai/dsh-user-approval'
+import {
+  assertFinalWorkspaceSnapshot, assertFixtureInventory, captureStableAria, compareOrRefreshGolden, fixtureUserPrompts,
+  launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold,
+} from './scaffold.ts'
+import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
+
+const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/ptc-escalation-approved', import.meta.url))
+const FIXTURE = join(SNAPSHOT_DIR, 'session.v3.jsonl')
+const UI_EXPECTED = join(SNAPSHOT_DIR, 'approval.expected.md')
+const MODE = webSnapshotMode()
+const PROMPT = 'Use run_code with timeoutMs 120000 and direct Node filesystem access to create approved.txt in the working directory containing exactly "approved\\n". '
+  + 'Use await import("node:fs/promises") and writeFile; do not call nested tools. First attempt the write under the current read-only sandbox without escalation. '
+  + 'In that first program, catch only filesystem errors with code EPERM, EACCES or EROFS and return exactly "EXPECTED_SANDBOX_DENIAL"; rethrow any other error. '
+  + 'If the sandbox denies it, explicitly retry the program with sandbox_permissions "workspace-write" and justification "Create the file requested by the user". '
+  + 'I will answer the approval prompt. After the file is written, reply DONE and stop.'
+
+describe('web e2e: PTC program sandbox escalation', () => {
+  let scaffold: WebScaffold
+  let browser: Browser
+  let page: Page
+  let tripwire: ReturnType<typeof watchConsole>
+  const events: SessionEvent[] = []
+
+  beforeAll(async () => {
+    scaffold = await launchWebScaffold({
+      agentPresets: { roots: [], default: 'ptc' },
+      compareReplaySession: true,
+      ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, paceMs: 15 }),
+    })
+    scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { events.push(event) })
+    browser = await chromium.launch()
+    page = await newEnglishPage(browser)
+    tripwire = watchConsole(page)
+    await page.goto(scaffold.authenticatedUrl, { waitUntil: 'load' })
+    await page.waitForSelector('[class*="frame"]', { timeout: 30_000 })
+    await connectFreshWorkspace(page, scaffold.workspaceCwd)
+  }, 120_000)
+
+  afterAll(async () => {
+    await browser?.close()
+    await scaffold?.close()
+  })
+
+  it('keeps the file absent until approval and records the granted program', async () => {
+    onTestFailed(() => saveFailureShot(page, 'web-e2e-ptc-escalation'))
+    if (MODE !== 'record') expect(fixtureUserPrompts(await readFile(FIXTURE, 'utf8'))).toEqual([PROMPT])
+    const input = page.locator('[data-composer-input]').first()
+    await input.waitFor({ timeout: 10_000 })
+    await page.locator('[aria-label^="Access mode"]').click()
+    await page.getByRole('menuitem', { name: 'Read Only' }).click()
+    await expect.poll(() => page.locator('[aria-label="Access mode, current: Read Only"]').count()).toBe(1)
+    const settled = scaffold.whenTurnSettled(MODE === 'record' ? 240_000 : 60_000)
+    await input.fill(PROMPT)
+    await input.press('Enter')
+    const panel = page.locator('[data-approval-key]')
+    await panel.waitFor({ timeout: MODE === 'record' ? 180_000 : 60_000 })
+    const calls = events.filter(event => event.type === 'tool/call')
+    expect(calls.length).toBeGreaterThanOrEqual(2)
+    expect(calls.every(event => event.data.name === 'run_code')).toBe(true)
+    expect(JSON.stringify(calls[0]?.data)).toContain('node:fs/promises')
+    expect(JSON.stringify(calls.at(-1)?.data)).toContain('workspace-write')
+    const results = events.filter(event => event.type === 'tool/result')
+    expect(JSON.stringify(results[0]?.data)).toContain('EXPECTED_SANDBOX_DENIAL')
+    const file = join(scaffold.workspaceCwd, 'workspace', 'approved.txt')
+    await expect(readFile(file, 'utf8')).rejects.toMatchObject({ code: 'ENOENT' })
+    if (MODE !== 'record') {
+      await compareOrRefreshGolden(UI_EXPECTED, await captureStableAria(page, '[data-approval-key]', scaffold.workspaceCwd), MODE)
+    }
+    await panel.getByRole('button', { name: 'Allow once' }).click()
+    const sessionId = await settled
+    expect(await readFile(file, 'utf8')).toBe('approved\n')
+    expect(events.filter(event => event.type === 'tool/ptc-dispatch')).toHaveLength(0)
+    expect(events.filter(event => event.type === 'approval/decided').map(event => event.data)).toMatchObject([{ outcome: 'allowed-once' }])
+    expect(await page.locator('[aria-label="Access mode, current: Read Only"]').count()).toBe(1)
+    await expect.poll(() => page.getByText('DONE', { exact: true }).count()).toBeGreaterThanOrEqual(1)
+    expect(await panel.count()).toBe(0)
+    expect(tripwire.pageErrors).toEqual([])
+    expect(tripwire.warnings).toEqual([])
+    if (MODE === 'record') await recordFixture(scaffold, sessionId, FIXTURE)
+    await assertFinalWorkspaceSnapshot(SNAPSHOT_DIR, join(scaffold.workspaceCwd, 'workspace'))
+  }, 300_000)
+
+  it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => {
+    await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'approval.expected.md', 'workspace.expected'])
+  })
+})

+ 1 - 1
apps/web/tests/scaffold.ts

@@ -348,7 +348,7 @@ export interface LaunchOptions {
   /**
    * Tool presentation mode patched onto the shipped `tools` row (`code`
    * collapses the wire to run_code + the SDK prompt section). Omit for the
-   * yml default. The code runtime row is always in the tree, so no extra
+   * yml default. The PTC runtime row is always in the tree, so no extra
    * insertion is needed.
    */
   toolsMode?: 'native' | 'ptc' | 'both'

+ 1 - 0
apps/web/tsconfig.json

@@ -33,6 +33,7 @@
     "tests/live-interactions.e2e.ts",
     "tests/question-composer.e2e.ts",
     "tests/approval-composer.e2e.ts",
+    "tests/ptc-escalation.e2e.ts",
     "tests/plan-control-row.e2e.ts",
     "tests/plan-review.e2e.ts",
     "tests/steering.e2e.ts",

+ 2 - 2
docs/capability-seams.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/capability-seams.md
-capability-seams.md: 547414830411d1d80781c614e75d839f8a9547fb
-capability-seams.zh.md: 8b89c0240a9881625f3d6a65bb0f1a31e887e1ef
+capability-seams.md: a6c9d505ce97c1d3b2d170ca4e904d5b9eeba893
+capability-seams.zh.md: 7328b6743c334d956de68d1865ffc980f8cc6231

+ 35 - 12
docs/capability-seams.md

@@ -7,6 +7,10 @@ A service can be a core spine service, a swappable capability seam, or a bundle/
 
 ```mermaid
 flowchart LR
+  pkg_computer_use["computer-use"]
+  svc_computerUse["ctx.computerUse<br/>Computer-use provider registration"]
+  pkg_experimental_computer_use_cua_driver_mcp["experimental-computer-use-cua-driver-mcp"]
+  pkg_experimental_computer_use_cua_driver_native["experimental-computer-use-cua-driver-native"]
   pkg_attachment["attachment"]
   svc_attachments["ctx.attachments<br/>Durable binary attachment storage"]
   pkg_attachment_local["attachment-local"]
@@ -133,6 +137,11 @@ flowchart LR
   pkg_sdk_minimal["sdk-minimal"]
   pkg_goal["goal"]
   svc_goals["ctx.goals<br/>Same-session goal domain"]
+  pkg_ssh["ssh"]
+  svc_ssh["ctx.ssh<br/>POSIX SSH connection owner"]
+  pkg_fs_ssh["fs-ssh"]
+  pkg_subprocess_ssh["subprocess-ssh"]
+  pkg_sandbox_ssh["sandbox-ssh"]
   pkg_subprocess["subprocess"]
   svc_subprocess["ctx.subprocess<br/>Subprocess seam"]
   pkg_subprocess_local["subprocess-local"]
@@ -161,10 +170,10 @@ flowchart LR
   svc_approval["ctx.approval<br/>Approval seam"]
   pkg_permission_presets["permission-presets"]
   svc_permissionPresets["ctx.permissionPresets<br/>Permission presets"]
-  pkg_code_runtime["code-runtime"]
-  svc_codeRuntime["ctx.codeRuntime<br/>Code-execution seam"]
-  pkg_code_runtime_worker_thread["code-runtime-worker-thread"]
-  pkg_experimental_code_runtime_python["experimental-code-runtime-python"]
+  pkg_ptc_runtime["ptc-runtime"]
+  svc_ptcRuntime["ctx.ptcRuntime<br/>PTC execution seam"]
+  pkg_ptc_runtime_node["ptc-runtime-node"]
+  pkg_experimental_ptc_runtime_python["experimental-ptc-runtime-python"]
   pkg_fs["fs"]
   svc_fs["ctx.fs<br/>Filesystem provider seam"]
   pkg_fs_local["fs-local"]
@@ -240,25 +249,27 @@ flowchart LR
   pkg_bash_sandbox --> svc_shell
   pkg_client_file_upload --> svc_fileUploads
   pkg_client_modules --> svc_clientModules
-  pkg_code_runtime --> svc_codeRuntime
-  pkg_code_runtime_worker_thread --> svc_codeRuntime
   pkg_command_feedback --> svc_sessionFeedback
   pkg_commands --> svc_commands
   pkg_compaction --> svc_compaction
   pkg_compaction_basic --> svc_compaction
   pkg_compaction_tool_result_pruner --> svc_toolResultPruner
+  pkg_computer_use --> svc_computerUse
   pkg_cordis_host_runner --> svc_cordisInspect
   pkg_cordis_host_runner --> svc_dynamicCordisRunner
   pkg_credentials --> svc_credentials
   pkg_credentials_local --> svc_credentials
   pkg_deepseek_llm_api_extensions --> svc_deepseekLlmApiExtensions
   pkg_experimental_agent_team --> svc_agentTeams
-  pkg_experimental_code_runtime_python --> svc_codeRuntime
+  pkg_experimental_computer_use_cua_driver_mcp --> svc_computerUse
+  pkg_experimental_computer_use_cua_driver_native --> svc_computerUse
+  pkg_experimental_ptc_runtime_python --> svc_ptcRuntime
   pkg_file_reference --> svc_fileReferences
   pkg_file_reference_local --> svc_fileReferences
   pkg_fs --> svc_fs
   pkg_fs_local --> svc_fs
   pkg_fs_sandbox --> svc_fs
+  pkg_fs_ssh --> svc_fs
   pkg_goal --> svc_goals
   pkg_host_directory_picker --> svc_directoryPicker
   pkg_host_directory_picker_browse --> svc_directoryPicker
@@ -278,10 +289,13 @@ flowchart LR
   pkg_permission_presets --> svc_permissionPresets
   pkg_plan_mode --> svc_planMode
   pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions
+  pkg_ptc_runtime --> svc_ptcRuntime
+  pkg_ptc_runtime_node --> svc_ptcRuntime
   pkg_pwsh_local --> svc_shell
   pkg_sandbox --> svc_sandbox
   pkg_sandbox_local --> svc_sandbox
   pkg_sandbox_policy --> svc_sandboxPolicy
+  pkg_sandbox_ssh --> svc_sandbox
   pkg_session --> svc_sessions
   pkg_session_log_deepseek --> svc_deepseekLlmApiExtensions
   pkg_session_persistence --> svc_sessionPersistence
@@ -305,6 +319,7 @@ flowchart LR
   pkg_skill_filesystem --> svc_skills
   pkg_spill --> svc_spillStore
   pkg_spill_local --> svc_spillStore
+  pkg_ssh --> svc_ssh
   pkg_storage --> svc_storage
   pkg_storage_domain --> svc_storageDomain
   pkg_storage_json --> svc_storage
@@ -318,6 +333,7 @@ flowchart LR
   pkg_subagent_spawn_in_process --> svc_subagents
   pkg_subprocess --> svc_subprocess
   pkg_subprocess_local --> svc_subprocess
+  pkg_subprocess_ssh --> svc_subprocess
   pkg_system_prompt --> svc_systemPrompt
   pkg_terminal --> svc_terminals
   pkg_terminal_bash --> svc_terminals
@@ -354,8 +370,9 @@ flowchart LR
   svc_attachments --> pkg_tool_fs
   svc_authorization --> pkg_llm_pi_ai
   svc_clientModules --> pkg_client_hmr
-  svc_codeRuntime --> pkg_tools
   svc_compaction --> pkg_compaction_basic
+  svc_computerUse --> pkg_experimental_computer_use_cua_driver_mcp
+  svc_computerUse --> pkg_experimental_computer_use_cua_driver_native
   svc_cordisInspect --> pkg_tool_cordis
   svc_credentials --> pkg_api_settings_controller
   svc_credentials --> pkg_llm_deepseek
@@ -377,6 +394,7 @@ flowchart LR
   svc_llm --> pkg_agent_loop
   svc_llm --> pkg_compaction_basic
   svc_lsp --> pkg_tool_lsp
+  svc_ptcRuntime --> pkg_tools
   svc_sandbox --> pkg_bash_sandbox
   svc_sandbox --> pkg_terminal_bash
   svc_sandboxPolicy --> pkg_bash_sandbox
@@ -417,6 +435,9 @@ flowchart LR
   svc_shellEnv --> pkg_tool_pwsh
   svc_skills --> pkg_tool_skill
   svc_spillStore --> pkg_spill_policy
+  svc_ssh --> pkg_fs_ssh
+  svc_ssh --> pkg_sandbox_ssh
+  svc_ssh --> pkg_subprocess_ssh
   svc_storage --> pkg_storage_domain
   svc_storageDomain --> pkg_workspace
   svc_subagentModelSelection --> pkg_tool_subagent
@@ -465,6 +486,7 @@ flowchart LR
 
 | ctx key | Role | Owner | Implementations | Direct consumers | Companion plugins | Note |
 | --- | --- | --- | --- | --- | --- | --- |
+| `ctx.computerUse` | `seam` | [`computer-use`](../packages/computer-use/computer-use) | [`experimental-computer-use-cua-driver-mcp`](../packages/experimental/computer-use-cua-driver-mcp), [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | [`experimental-computer-use-cua-driver-mcp`](../packages/experimental/computer-use-cua-driver-mcp), [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | - | One provider-owned name per service instance. Each provider also owns its model tools; the service has no common action API, runtime selection, or Session workflow lock. |
 | `ctx.attachments` | `seam` | [`attachment`](../packages/attachment/attachment) | [`attachment-local`](../packages/attachment/attachment-local) | [`api-session-controller`](../packages/api/session-controller), [`tool-fs`](../packages/fs/tool-fs), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-deepseek`](../packages/llm/llm-deepseek) | - | The host commits accepted images before session events; provider adapters resolve authorized durable references into provider-native content. |
 | `ctx.fileUploads` | `core` | [`client-file-upload`](../packages/client/file-upload) | - | [`api-session-controller`](../packages/api/session-controller) | - | Owns streaming intake, durable storage, and staged receipt lifetime; the Session controller binds receipts to accepted submissions. |
 | `ctx.llm` | `seam` | [`llm`](../packages/llm/llm) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-replay`](../packages/test-support/llm-replay) | [`agent-loop`](../packages/core/agent-loop), [`compaction-basic`](../packages/compaction/compaction-basic) | - | Adapters register provider implementations; the loop and compaction call the provider-neutral stream service. |
@@ -511,16 +533,17 @@ flowchart LR
 | `ctx.agentDefaultModel` | `core` | [`agent-default-model`](../packages/core/agent-default-model) | - | [`api-session-controller`](../packages/api/session-controller), [`headless`](../packages/bundle/headless) | - | Layers the default ModelSelection through settings so direct and Host-backed Agent entry points share one state owner. |
 | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`base`](../packages/bundle/base), [`sdk-minimal`](../packages/bundle/sdk-minimal) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
 | `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | Folds revisioned objective state from the session log and keeps live continuation activation process-local. |
-| `ctx.subprocess` | `seam` | [`subprocess`](../packages/subprocess/subprocess) | [`subprocess-local`](../packages/subprocess/subprocess-local) | [`bash-local`](../packages/shell/bash-local), [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash), [`lsp-stdio`](../packages/lsp/lsp-stdio), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | - | The bash executors, the PTY shell backend, the LSP host, and the out-of-process ACP, Codex, and Claude Code subagent backends spawn through ctx.subprocess; the service owns process coordinates, tree/session lifetime, stdio dispositions, terminal mechanics, and kill escalation. |
+| `ctx.ssh` | `core` | [`ssh`](../packages/ssh/ssh) | - | [`fs-ssh`](../packages/ssh/fs-ssh), [`subprocess-ssh`](../packages/ssh/subprocess-ssh), [`sandbox-ssh`](../packages/ssh/sandbox-ssh) | - | Owns one authenticated OpenSSH connection, installed helper identity, independent program streams and disconnect cleanup for the paired remote providers. |
+| `ctx.subprocess` | `seam` | [`subprocess`](../packages/subprocess/subprocess) | [`subprocess-local`](../packages/subprocess/subprocess-local), [`subprocess-ssh`](../packages/ssh/subprocess-ssh) | [`bash-local`](../packages/shell/bash-local), [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash), [`lsp-stdio`](../packages/lsp/lsp-stdio), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | - | The bash executors, the PTY shell backend, the LSP host, and the out-of-process ACP, Codex, and Claude Code subagent backends spawn through ctx.subprocess; the service owns process coordinates, tree/session lifetime, stdio dispositions, terminal mechanics, and kill escalation. |
 | `ctx.shell` | `seam` | [`shell`](../packages/shell/shell) | [`bash-local`](../packages/shell/bash-local), [`bash-sandbox`](../packages/shell/bash-sandbox), [`pwsh-local`](../packages/shell/pwsh-local) | [`tool-bash`](../packages/shell/tool-bash), [`tool-pwsh`](../packages/shell/tool-pwsh), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing shell tools and hook bridges consume this seam; sandboxed, remote, or PowerShell executors replace bash-local without touching them. |
 | `ctx.shellEnv` | `core` | [`shell-env`](../packages/shell/shell-env) | - | [`tool-bash`](../packages/shell/tool-bash), [`tool-pwsh`](../packages/shell/tool-pwsh) | - | Plugins declare effect-scoped DSH_* facts; each shell tool collects one trusted snapshot per execution and its executor rebuilds the namespace. |
 | `ctx.terminals` | `seam` | [`terminal`](../packages/terminal/terminal) | [`terminal-bash`](../packages/terminal/terminal-bash) | [`tool-terminal`](../packages/terminal/tool-terminal) | - | The registry owns exact-Agent session identity and cleanup; backends own terminal mechanics, while tool-terminal exposes the owner-scoped model tools. |
-| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. |
+| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local), [`sandbox-ssh`](../packages/ssh/sandbox-ssh) | [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. |
 | `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/shell/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | The one home for the deployment default mode + workspace root; only the sandboxed executor and provider read the service (the tool layers use the pure `sandbox/mode` fold it also exports). Both enforcing families read it so bash and fs cannot confine to different roots. |
 | `ctx.approval` | `seam` | [`user-approval`](../packages/interaction/user-approval) | - | [`tools`](../packages/core/tools), [`tool-bash`](../packages/shell/tool-bash), [`acp`](../packages/acp/acp) | - | One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`. |
 | `ctx.permissionPresets` | `core` | [`permission-presets`](../packages/interaction/permission-presets) | - | - | - | User-facing preset table (`workspace-write`/`danger-full-access`) bundling the sandbox-mode and approval-policy knobs; a switch writes one `permission/preset` event through to both knob events. |
-| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker-thread`](../packages/code-runtime/code-runtime-worker-thread), [`experimental-code-runtime-python`](../packages/experimental/code-runtime-python) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for PTC mode). |
-| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-observation-policy`](../packages/fs/fs-observation-policy) | tool-fs executes read/write/edit through ctx.fs; fs-sandbox fences mutations by the shared sandbox mode; fs-observation-policy contributes observed-state checks through the fs/* event gate. |
+| `ctx.ptcRuntime` | `seam` | [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime) | [`ptc-runtime-node`](../packages/ptc-runtime/ptc-runtime-node), [`experimental-ptc-runtime-python`](../packages/experimental/ptc-runtime-python) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for PTC mode). |
+| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox), [`fs-ssh`](../packages/ssh/fs-ssh) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-observation-policy`](../packages/fs/fs-observation-policy) | tool-fs executes read/write/edit through ctx.fs; fs-sandbox fences mutations by the shared sandbox mode; fs-observation-policy contributes observed-state checks through the fs/* event gate. |
 | `ctx.compaction` | `seam` | [`compaction`](../packages/compaction/compaction) | [`compaction-basic`](../packages/compaction/compaction-basic) | [`compaction-basic`](../packages/compaction/compaction-basic) | - | The basic backend consumes post-step pressure and request-error recovery events; there is no model-facing compact tool. |
 | `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process), [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code), [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control), [`tool-ralph`](../packages/workflow/tool-ralph) | - | Providers implement transports; the service also owns optional Activation-based continuation orchestration, tool-subagent selects one-shot or continuable delegation, tool-subagent-control delivers follow-ups, and tool-ralph requires one fresh structured-output route. |
 | `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team), [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | - | Owns the implicit-root roster, durable peer mailbox, shared task DAG, continuable-child lifecycle, and generated Team Remote methods; tool-agent-team contributes model controls and client-ui-agent-team mounts the browser contribution. |

+ 35 - 12
docs/capability-seams.zh.md

@@ -9,6 +9,10 @@
 
 ```mermaid
 flowchart LR
+  pkg_computer_use["computer-use"]
+  svc_computerUse["ctx.computerUse<br/>Computer-use provider registration"]
+  pkg_experimental_computer_use_cua_driver_mcp["experimental-computer-use-cua-driver-mcp"]
+  pkg_experimental_computer_use_cua_driver_native["experimental-computer-use-cua-driver-native"]
   pkg_attachment["attachment"]
   svc_attachments["ctx.attachments<br/>Durable binary attachment storage"]
   pkg_attachment_local["attachment-local"]
@@ -135,6 +139,11 @@ flowchart LR
   pkg_sdk_minimal["sdk-minimal"]
   pkg_goal["goal"]
   svc_goals["ctx.goals<br/>Same-session goal domain"]
+  pkg_ssh["ssh"]
+  svc_ssh["ctx.ssh<br/>POSIX SSH connection owner"]
+  pkg_fs_ssh["fs-ssh"]
+  pkg_subprocess_ssh["subprocess-ssh"]
+  pkg_sandbox_ssh["sandbox-ssh"]
   pkg_subprocess["subprocess"]
   svc_subprocess["ctx.subprocess<br/>Subprocess seam"]
   pkg_subprocess_local["subprocess-local"]
@@ -163,10 +172,10 @@ flowchart LR
   svc_approval["ctx.approval<br/>Approval seam"]
   pkg_permission_presets["permission-presets"]
   svc_permissionPresets["ctx.permissionPresets<br/>Permission presets"]
-  pkg_code_runtime["code-runtime"]
-  svc_codeRuntime["ctx.codeRuntime<br/>Code-execution seam"]
-  pkg_code_runtime_worker_thread["code-runtime-worker-thread"]
-  pkg_experimental_code_runtime_python["experimental-code-runtime-python"]
+  pkg_ptc_runtime["ptc-runtime"]
+  svc_ptcRuntime["ctx.ptcRuntime<br/>PTC execution seam"]
+  pkg_ptc_runtime_node["ptc-runtime-node"]
+  pkg_experimental_ptc_runtime_python["experimental-ptc-runtime-python"]
   pkg_fs["fs"]
   svc_fs["ctx.fs<br/>Filesystem provider seam"]
   pkg_fs_local["fs-local"]
@@ -242,25 +251,27 @@ flowchart LR
   pkg_bash_sandbox --> svc_shell
   pkg_client_file_upload --> svc_fileUploads
   pkg_client_modules --> svc_clientModules
-  pkg_code_runtime --> svc_codeRuntime
-  pkg_code_runtime_worker_thread --> svc_codeRuntime
   pkg_command_feedback --> svc_sessionFeedback
   pkg_commands --> svc_commands
   pkg_compaction --> svc_compaction
   pkg_compaction_basic --> svc_compaction
   pkg_compaction_tool_result_pruner --> svc_toolResultPruner
+  pkg_computer_use --> svc_computerUse
   pkg_cordis_host_runner --> svc_cordisInspect
   pkg_cordis_host_runner --> svc_dynamicCordisRunner
   pkg_credentials --> svc_credentials
   pkg_credentials_local --> svc_credentials
   pkg_deepseek_llm_api_extensions --> svc_deepseekLlmApiExtensions
   pkg_experimental_agent_team --> svc_agentTeams
-  pkg_experimental_code_runtime_python --> svc_codeRuntime
+  pkg_experimental_computer_use_cua_driver_mcp --> svc_computerUse
+  pkg_experimental_computer_use_cua_driver_native --> svc_computerUse
+  pkg_experimental_ptc_runtime_python --> svc_ptcRuntime
   pkg_file_reference --> svc_fileReferences
   pkg_file_reference_local --> svc_fileReferences
   pkg_fs --> svc_fs
   pkg_fs_local --> svc_fs
   pkg_fs_sandbox --> svc_fs
+  pkg_fs_ssh --> svc_fs
   pkg_goal --> svc_goals
   pkg_host_directory_picker --> svc_directoryPicker
   pkg_host_directory_picker_browse --> svc_directoryPicker
@@ -280,10 +291,13 @@ flowchart LR
   pkg_permission_presets --> svc_permissionPresets
   pkg_plan_mode --> svc_planMode
   pkg_plugin_package_inventory_deepseek --> svc_deepseekLlmApiExtensions
+  pkg_ptc_runtime --> svc_ptcRuntime
+  pkg_ptc_runtime_node --> svc_ptcRuntime
   pkg_pwsh_local --> svc_shell
   pkg_sandbox --> svc_sandbox
   pkg_sandbox_local --> svc_sandbox
   pkg_sandbox_policy --> svc_sandboxPolicy
+  pkg_sandbox_ssh --> svc_sandbox
   pkg_session --> svc_sessions
   pkg_session_log_deepseek --> svc_deepseekLlmApiExtensions
   pkg_session_persistence --> svc_sessionPersistence
@@ -307,6 +321,7 @@ flowchart LR
   pkg_skill_filesystem --> svc_skills
   pkg_spill --> svc_spillStore
   pkg_spill_local --> svc_spillStore
+  pkg_ssh --> svc_ssh
   pkg_storage --> svc_storage
   pkg_storage_domain --> svc_storageDomain
   pkg_storage_json --> svc_storage
@@ -320,6 +335,7 @@ flowchart LR
   pkg_subagent_spawn_in_process --> svc_subagents
   pkg_subprocess --> svc_subprocess
   pkg_subprocess_local --> svc_subprocess
+  pkg_subprocess_ssh --> svc_subprocess
   pkg_system_prompt --> svc_systemPrompt
   pkg_terminal --> svc_terminals
   pkg_terminal_bash --> svc_terminals
@@ -356,8 +372,9 @@ flowchart LR
   svc_attachments --> pkg_tool_fs
   svc_authorization --> pkg_llm_pi_ai
   svc_clientModules --> pkg_client_hmr
-  svc_codeRuntime --> pkg_tools
   svc_compaction --> pkg_compaction_basic
+  svc_computerUse --> pkg_experimental_computer_use_cua_driver_mcp
+  svc_computerUse --> pkg_experimental_computer_use_cua_driver_native
   svc_cordisInspect --> pkg_tool_cordis
   svc_credentials --> pkg_api_settings_controller
   svc_credentials --> pkg_llm_deepseek
@@ -379,6 +396,7 @@ flowchart LR
   svc_llm --> pkg_agent_loop
   svc_llm --> pkg_compaction_basic
   svc_lsp --> pkg_tool_lsp
+  svc_ptcRuntime --> pkg_tools
   svc_sandbox --> pkg_bash_sandbox
   svc_sandbox --> pkg_terminal_bash
   svc_sandboxPolicy --> pkg_bash_sandbox
@@ -419,6 +437,9 @@ flowchart LR
   svc_shellEnv --> pkg_tool_pwsh
   svc_skills --> pkg_tool_skill
   svc_spillStore --> pkg_spill_policy
+  svc_ssh --> pkg_fs_ssh
+  svc_ssh --> pkg_sandbox_ssh
+  svc_ssh --> pkg_subprocess_ssh
   svc_storage --> pkg_storage_domain
   svc_storageDomain --> pkg_workspace
   svc_subagentModelSelection --> pkg_tool_subagent
@@ -467,6 +488,7 @@ flowchart LR
 
 | ctx 键 | 角色 | 所属包 | 实现 | 直接消费方 | 配套插件 | 说明 |
 | --- | --- | --- | --- | --- | --- | --- |
+| `ctx.computerUse` | `seam` | [`computer-use`](../packages/computer-use/computer-use) | [`experimental-computer-use-cua-driver-mcp`](../packages/experimental/computer-use-cua-driver-mcp), [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | [`experimental-computer-use-cua-driver-mcp`](../packages/experimental/computer-use-cua-driver-mcp), [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | - | 每个服务实例只注册一个提供方自定的名称。各提供方也拥有自己的模型工具;服务不提供通用操作 API、运行时选择或 Session 流程锁。 |
 | `ctx.attachments` | `seam` | [`attachment`](../packages/attachment/attachment) | [`attachment-local`](../packages/attachment/attachment-local) | [`api-session-controller`](../packages/api/session-controller), [`tool-fs`](../packages/fs/tool-fs), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-deepseek`](../packages/llm/llm-deepseek) | - | 宿主会在会话事件之前提交已接受的图片;提供方适配器将已授权的持久引用解析为提供方原生内容。 |
 | `ctx.fileUploads` | `core` | [`client-file-upload`](../packages/client/file-upload) | - | [`api-session-controller`](../packages/api/session-controller) | - | 负责流式接收、持久存储和暂存回执生命周期;Session Controller 将回执绑定到已接受的提交。 |
 | `ctx.llm` | `seam` | [`llm`](../packages/llm/llm) | [`llm-deepseek`](../packages/llm/llm-deepseek), [`llm-pi-ai`](../packages/llm/llm-pi-ai), [`llm-replay`](../packages/test-support/llm-replay) | [`agent-loop`](../packages/core/agent-loop), [`compaction-basic`](../packages/compaction/compaction-basic) | - | 适配器注册提供方实现;agent loop(智能体循环)与压缩功能调用提供方无关的流服务。 |
@@ -513,16 +535,17 @@ flowchart LR
 | `ctx.agentDefaultModel` | `core` | [`agent-default-model`](../packages/core/agent-default-model) | - | [`api-session-controller`](../packages/api/session-controller), [`headless`](../packages/bundle/headless) | - | 通过 settings 分层默认 `ModelSelection`,让直接入口与 Host 支撑的 Agent 入口共享同一个状态所有者。 |
 | `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`base`](../packages/bundle/base), [`sdk-minimal`](../packages/bundle/sdk-minimal) | - | 唯一的具体循环插件;扩展包依赖 dsh-agent 的事件和服务,而不依赖此包。 |
 | `ctx.goals` | `core` | [`goal`](../packages/goal/goal) | - | - | - | 从会话日志折叠带修订版本的目标状态,并将实时延续激活保留在进程本地。 |
-| `ctx.subprocess` | `seam` | [`subprocess`](../packages/subprocess/subprocess) | [`subprocess-local`](../packages/subprocess/subprocess-local) | [`bash-local`](../packages/shell/bash-local), [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash), [`lsp-stdio`](../packages/lsp/lsp-stdio), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | - | Bash 执行器、PTY shell 后端、LSP Host,以及进程外 ACP、Codex 和 Claude Code subagent 后端都通过 ctx.subprocess 执行 spawn;该服务负责进程坐标、进程树/会话生命周期、stdio 处置、终端机制和 kill 升级。 |
+| `ctx.ssh` | `core` | [`ssh`](../packages/ssh/ssh) | - | [`fs-ssh`](../packages/ssh/fs-ssh), [`subprocess-ssh`](../packages/ssh/subprocess-ssh), [`sandbox-ssh`](../packages/ssh/sandbox-ssh) | - | 负责一条经过认证的 OpenSSH 连接、已安装辅助程序身份、独立程序流,以及配套远端提供方的断连清理。 |
+| `ctx.subprocess` | `seam` | [`subprocess`](../packages/subprocess/subprocess) | [`subprocess-local`](../packages/subprocess/subprocess-local), [`subprocess-ssh`](../packages/ssh/subprocess-ssh) | [`bash-local`](../packages/shell/bash-local), [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash), [`lsp-stdio`](../packages/lsp/lsp-stdio), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | - | Bash 执行器、PTY shell 后端、LSP Host,以及进程外 ACP、Codex 和 Claude Code subagent 后端都通过 ctx.subprocess 执行 spawn;该服务负责进程坐标、进程树/会话生命周期、stdio 处置、终端机制和 kill 升级。 |
 | `ctx.shell` | `seam` | [`shell`](../packages/shell/shell) | [`bash-local`](../packages/shell/bash-local), [`bash-sandbox`](../packages/shell/bash-sandbox), [`pwsh-local`](../packages/shell/pwsh-local) | [`tool-bash`](../packages/shell/tool-bash), [`tool-pwsh`](../packages/shell/tool-pwsh), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) | - | 面向模型的 shell 工具和钩子桥接消费此 seam;沙箱、远程或 PowerShell 执行器可以替换 bash-local,而无需改动这些消费方。 |
 | `ctx.shellEnv` | `core` | [`shell-env`](../packages/shell/shell-env) | - | [`tool-bash`](../packages/shell/tool-bash), [`tool-pwsh`](../packages/shell/tool-pwsh) | - | 插件声明限定于 effect 作用域的 DSH_* 事实;每个 shell 工具在每次执行时收集一份可信快照,其执行器据此重建命名空间。 |
 | `ctx.terminals` | `seam` | [`terminal`](../packages/terminal/terminal) | [`terminal-bash`](../packages/terminal/terminal-bash) | [`tool-terminal`](../packages/terminal/tool-terminal) | - | 注册表负责精确到 Agent 的会话身份和清理;后端负责终端机制,tool-terminal 则提供限定于所有者作用域的模型接口。 |
-| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | 消费方交出即将执行 spawn 的确切 argv;与宿主共享文件系统和内核的后端按每次调用的策略包装该 argv,并报告强制执行情况。 |
+| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local), [`sandbox-ssh`](../packages/ssh/sandbox-ssh) | [`bash-sandbox`](../packages/shell/bash-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | 消费方交出即将执行 spawn 的确切 argv;与配套子进程提供方共享执行环境的后端按每次调用的策略包装该 argv,并报告强制执行情况。 |
 | `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/shell/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox), [`terminal-bash`](../packages/terminal/terminal-bash) | - | 统一保存部署默认模式和工作区根目录;只有沙箱执行器和提供方读取该服务(工具层使用它同时导出的纯 `sandbox/mode` 折叠区)。两类强制执行组件都读取该服务,因此 bash 与 fs 不会限制到不同的根目录。 |
 | `ctx.approval` | `seam` | [`user-approval`](../packages/interaction/user-approval) | - | [`tools`](../packages/core/tools), [`tool-bash`](../packages/shell/tool-bash), [`acp`](../packages/acp/acp) | - | 一次性权限决策通过 `approval/request` waterfall(瀑布式事件)分派;回答方是监听器(即 ACP 为自身 agent 提供的桥接),没有回答方时以 `unavailable` 关闭失败。 |
 | `ctx.permissionPresets` | `core` | [`permission-presets`](../packages/interaction/permission-presets) | - | - | - | 面向用户的预设表(`workspace-write`/`danger-full-access`),将沙箱模式与审批策略选项组合在一起;一次切换会写入一个 `permission/preset` 事件,并贯通到两个选项事件。 |
-| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker-thread`](../packages/code-runtime/code-runtime-worker-thread), [`experimental-code-runtime-python`](../packages/experimental/code-runtime-python) | [`tools`](../packages/core/tools) | - | 使用 Host 提供的异步绑定运行一段由模型编写的程序;各后端采用不同的基础环境和语言(工具注册表在 PTC mode 下消费该服务)。 |
-| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-observation-policy`](../packages/fs/fs-observation-policy) | tool-fs 通过 ctx.fs 执行读取/写入/编辑;fs-sandbox 按共享沙箱模式限制变更;fs-observation-policy 通过 fs/* 事件门禁贡献基于观测状态的检查。 |
+| `ctx.ptcRuntime` | `seam` | [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime) | [`ptc-runtime-node`](../packages/ptc-runtime/ptc-runtime-node), [`experimental-ptc-runtime-python`](../packages/experimental/ptc-runtime-python) | [`tools`](../packages/core/tools) | - | 使用 Host 提供的异步绑定运行一段由模型编写的程序;各后端采用不同的基础环境和语言(工具注册表在 PTC mode 下消费该服务)。 |
+| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox), [`fs-ssh`](../packages/ssh/fs-ssh) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-observation-policy`](../packages/fs/fs-observation-policy) | tool-fs 通过 ctx.fs 执行读取/写入/编辑;fs-sandbox 按共享沙箱模式限制变更;fs-observation-policy 通过 fs/* 事件门禁贡献基于观测状态的检查。 |
 | `ctx.compaction` | `seam` | [`compaction`](../packages/compaction/compaction) | [`compaction-basic`](../packages/compaction/compaction-basic) | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 基础后端消费步骤后的压力事件和请求错误恢复事件;不存在面向模型的压缩工具。 |
 | `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process), [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code), [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control), [`tool-ralph`](../packages/workflow/tool-ralph) | - | 提供方实现传输;该服务还负责可选的、基于 Activation 的延续编排,tool-subagent 选择一次性或可延续委派,tool-subagent-control 传递后续消息,而 tool-ralph 要求一条全新的结构化输出路由。 |
 | `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team), [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | - | 负责隐式 Root roster、持久 peer mailbox、共享任务 DAG、continuable child 生命周期与生成式 Team Remote method;tool-agent-team 提供模型控制工具,client-ui-agent-team 挂载浏览器 contribution。 |

+ 2 - 2
docs/config-catalog.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/config-catalog.md
-config-catalog.md: 1cae88929488b55cae52fc5acbbcf1fd1158b5b6
-config-catalog.zh.md: 73c2dad801ab04fe194c4d702e3b9d51b10c3d41
+config-catalog.md: 3f4079e02a7e147c80dc6a29566498fd4ac8fdf7
+config-catalog.zh.md: 6c1e2cb3205fc00c4ee0312a31d9d664d35b1267

+ 166 - 105
docs/config-catalog.md

@@ -401,43 +401,6 @@ export interface Config {
 
 Source: [`packages/client/hmr/src/index.ts:31`](../packages/client/hmr/src/index.ts)
 
-<a id="deepseek-aidsh-code-runtime-worker-thread"></a>
-
-## `@deepseek-ai/dsh-code-runtime-worker-thread`
-
-```ts config-catalog
-/** Plugin config: every execution cap, changeable from `cordis.yml` (no hardcoded tunables). */
-export interface Config {
-  /**
-   * Busy-time budget in milliseconds: the run fails with kind `'timeout'`
-   * once the worker's MEASURED event-loop active time
-   * (`worker.performance.eventLoopUtilization()`) exceeds this. Metering
-   * measured busy time — not wall time, not host-side pending-call
-   * bookkeeping — is what makes the budget both fair (a program awaiting a
-   * slow tool accrues nothing) and ungameable (a hot loop accrues whether
-   * or not a decoy dispatch is in flight).
-   */
-  computeMs?: number
-  /**
-   * Wall-clock ceiling in milliseconds; never pauses for anything. The
-   * backstop for what busy-time cannot see (a program awaiting a promise
-   * nobody will resolve). At most `2_147_483_647` (Node's maximum
-   * `setTimeout` delay, about 24.9 days): a longer value is rejected at load
-   * because `setTimeout` would clamp it to 1 ms.
-   */
-  maxWallMs?: number
-  /**
-   * Hard cap for serialized log-array, completion-value, and failure-message payloads;
-   * fixed result-envelope syntax is excluded.
-   */
-  maxOutputBytes?: number
-  /** The worker's max old-generation heap in MiB (`resourceLimits`); overflow kills the worker, surfacing as kind `'worker-exit'`. */
-  maxOldGenerationSizeMb?: number
-}
-```
-
-Source: [`packages/code-runtime/code-runtime-worker-thread/src/index.ts:25`](../packages/code-runtime/code-runtime-worker-thread/src/index.ts)
-
 <a id="deepseek-aidsh-compaction-basic"></a>
 
 ## `@deepseek-ai/dsh-compaction-basic`
@@ -564,72 +527,29 @@ export interface Config {
 
 Source: [`packages/experimental/agent-team/src/types.ts:130`](../packages/experimental/agent-team/src/types.ts)
 
-<a id="deepseek-aidsh-experimental-code-runtime-python"></a>
+<a id="deepseek-aidsh-experimental-computer-use-cua-driver-mcp"></a>
+
+## `@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp`
 
-## `@deepseek-ai/dsh-experimental-code-runtime-python`
+Requires: `computerUse` · `tools`
 
 ```ts config-catalog
-/** Plugin config: every cap, changeable from `cordis.yml` (no hardcoded tunables). */
+/** Installed executable and MCP connection overrides. */
 export interface Config {
-  /**
-   * RLIMIT_CPU in whole seconds (a positive integer — `setrlimit` in the child
-   * rejects a float). The child sets the soft limit to `cpuSeconds` and the
-   * hard limit to `cpuSeconds + 1`: the kernel delivers SIGXCPU at the soft
-   * limit, which the host classifies as a `timeout`; the +1s hard limit is a
-   * SIGKILL backstop for a program that traps SIGXCPU. Granularity is seconds —
-   * a coarser counterpart to the worker backend's millisecond `computeMs`.
-   */
-  cpuSeconds?: number
-  /** Wall-clock ceiling in milliseconds; backstops CPU time for programs awaiting a promise nobody resolves. */
-  maxWallMs?: number
-  /**
-   * RLIMIT_AS in mebibytes; caps address space so a runaway allocation fails
-   * cleanly. Not applied on Darwin, where the dyld shared cache mapped into
-   * every process at exec exceeds any practical cap and the kernel rejects
-   * the call; `cpuSeconds` and `maxWallMs` still bound the run there. Bounds
-   * `maxLogBytes`/`maxValueBytes` at load on EVERY platform (this static check
-   * runs on Darwin too, where only the runtime `setrlimit` is skipped): each
-   * budget times a worst-case Unicode expansion must fit this byte count minus a
-   * fixed interpreter baseline, so a near-budget output cannot breach the address
-   * space during the child's build-and-encode.
-   */
-  addressSpaceMb?: number
-  /**
-   * Shared byte budget for captured log text (host-side ledger). Bounded at load
-   * against `addressSpaceMb`: the child builds and encodes a near-budget entry
-   * under RLIMIT_AS with several copies live at once, so this cap times the
-   * worst-case Unicode expansion must fit the address space left after the
-   * interpreter baseline (see `addressSpaceMb`) — a load-time rejection, not a
-   * runtime clamp. Also bounded at load by the host's configured heap like
-   * `maxValueBytes` (see its JSDoc): the effective frame cap minus the frame
-   * envelope.
-   */
-  maxLogBytes?: number
-  /**
-   * Byte cap for the completion value. Bounded at load against `addressSpaceMb`
-   * the same way `maxLogBytes` is: the child builds and encodes a near-budget
-   * value under RLIMIT_AS with several copies live at once, so this cap times the
-   * worst-case Unicode expansion must fit the address space left after the
-   * interpreter baseline. Both budgets are ALSO bounded at load by the host's
-   * configured heap: the effective frame cap (the protocol cap, or a lower
-   * heap-derived ceiling when the host heap cannot safely parse a near-cap
-   * frame — see `hostFrameParseCeiling`) minus the frame envelope, so a budget
-   * whose honest frame could OOM the host's own JSON.parse is rejected up
-   * front.
-   */
-  maxValueBytes?: number
-  /** SIGTERM→SIGKILL grace period on kill, matching bash-local's default. */
-  graceMs?: number
-  /**
-   * Absolute path, relative path, or basename of a CPython 3.10+ interpreter.
-   * Resolved and validated once at plugin load under a five-second force-kill
-   * deadline; a basename searches `PATH`.
-   */
-  pythonBin?: string
+  /** Executable path or PATH command; defaults to `cua-driver`. */
+  command: string
+  /** Arguments passed without a shell; defaults to `['mcp']`. */
+  args: string[]
+  /** Per-call timeout in milliseconds; omission uses the MCP client's default. */
+  toolCallTimeoutMs?: number
+  /** Reconnection overrides; defaults to the MCP client's policy. */
+  reconnect: McpClient.ReconnectConfig
 }
 ```
 
-Source: [`packages/experimental/code-runtime-python/src/index.ts:42`](../packages/experimental/code-runtime-python/src/index.ts)
+Depends on: [`McpClient`](../packages/mcp/mcp-client/src/index.ts)
+
+Source: [`packages/experimental/computer-use-cua-driver-mcp/src/index.ts:20`](../packages/experimental/computer-use-cua-driver-mcp/src/index.ts)
 
 <a id="deepseek-aidsh-experimental-inspector"></a>
 
@@ -699,6 +619,72 @@ export interface InspectorOptions {
 
 Source: [`packages/experimental/inspector/src/index.ts:66`](../packages/experimental/inspector/src/index.ts)
 
+<a id="deepseek-aidsh-experimental-ptc-runtime-python"></a>
+
+## `@deepseek-ai/dsh-experimental-ptc-runtime-python`
+
+```ts config-catalog
+/** Plugin config: every cap, changeable from `cordis.yml` (no hardcoded tunables). */
+export interface Config {
+  /**
+   * RLIMIT_CPU in whole seconds (a positive integer — `setrlimit` in the child
+   * rejects a float). The child sets the soft limit to `cpuSeconds` and the
+   * hard limit to `cpuSeconds + 1`: the kernel delivers SIGXCPU at the soft
+   * limit, which the host classifies as a `timeout`; the +1s hard limit is a
+   * SIGKILL backstop for a program that traps SIGXCPU. Granularity is whole seconds.
+   */
+  cpuSeconds?: number
+  /** Wall-clock ceiling in milliseconds; backstops CPU time for programs awaiting a promise nobody resolves. */
+  maxWallMs?: number
+  /**
+   * RLIMIT_AS in mebibytes; caps address space so a runaway allocation fails
+   * cleanly. Not applied on Darwin, where the dyld shared cache mapped into
+   * every process at exec exceeds any practical cap and the kernel rejects
+   * the call; `cpuSeconds` and `maxWallMs` still bound the run there. Bounds
+   * `maxLogBytes`/`maxValueBytes` at load on EVERY platform (this static check
+   * runs on Darwin too, where only the runtime `setrlimit` is skipped): each
+   * budget times a worst-case Unicode expansion must fit this byte count minus a
+   * fixed interpreter baseline, so a near-budget output cannot breach the address
+   * space during the child's build-and-encode.
+   */
+  addressSpaceMb?: number
+  /**
+   * Shared byte budget for captured log text (host-side ledger). Bounded at load
+   * against `addressSpaceMb`: the child builds and encodes a near-budget entry
+   * under RLIMIT_AS with several copies live at once, so this cap times the
+   * worst-case Unicode expansion must fit the address space left after the
+   * interpreter baseline (see `addressSpaceMb`) — a load-time rejection, not a
+   * runtime clamp. Also bounded at load by the host's configured heap like
+   * `maxValueBytes` (see its JSDoc): the effective frame cap minus the frame
+   * envelope.
+   */
+  maxLogBytes?: number
+  /**
+   * Byte cap for the completion value. Bounded at load against `addressSpaceMb`
+   * the same way `maxLogBytes` is: the child builds and encodes a near-budget
+   * value under RLIMIT_AS with several copies live at once, so this cap times the
+   * worst-case Unicode expansion must fit the address space left after the
+   * interpreter baseline. Both budgets are ALSO bounded at load by the host's
+   * configured heap: the effective frame cap (the protocol cap, or a lower
+   * heap-derived ceiling when the host heap cannot safely parse a near-cap
+   * frame — see `hostFrameParseCeiling`) minus the frame envelope, so a budget
+   * whose honest frame could OOM the host's own JSON.parse is rejected up
+   * front.
+   */
+  maxValueBytes?: number
+  /** SIGTERM→SIGKILL grace period on kill, matching bash-local's default. */
+  graceMs?: number
+  /**
+   * Absolute path, relative path, or basename of a CPython 3.10+ interpreter.
+   * Resolved and validated once at plugin load under a five-second force-kill
+   * deadline; a basename searches `PATH`.
+   */
+  pythonBin?: string
+}
+```
+
+Source: [`packages/experimental/ptc-runtime-python/src/index.ts:42`](../packages/experimental/ptc-runtime-python/src/index.ts)
+
 <a id="deepseek-aidsh-experimental-tool-agent-team"></a>
 
 ## `@deepseek-ai/dsh-experimental-tool-agent-team`
@@ -754,7 +740,7 @@ export interface Config {
 }
 ```
 
-Source: [`packages/fs/fs-local/src/index.ts:42`](../packages/fs/fs-local/src/index.ts)
+Source: [`packages/fs/fs-local/src/index.ts:43`](../packages/fs/fs-local/src/index.ts)
 
 <a id="deepseek-aidsh-fs-sandbox"></a>
 
@@ -810,7 +796,7 @@ export interface Config {
 }
 ```
 
-Source: [`packages/bundle/headless/src/index.ts:41`](../packages/bundle/headless/src/index.ts)
+Source: [`packages/bundle/headless/src/index.ts:42`](../packages/bundle/headless/src/index.ts)
 
 <a id="deepseek-aidsh-hooks-claude-code"></a>
 
@@ -1571,7 +1557,7 @@ export interface ReconnectConfig {
 }
 ```
 
-Source: [`packages/mcp/mcp-client/src/index.ts:98`](../packages/mcp/mcp-client/src/index.ts)
+Source: [`packages/mcp/mcp-client/src/index.ts:99`](../packages/mcp/mcp-client/src/index.ts)
 
 <a id="deepseek-aidsh-message-feedback"></a>
 
@@ -1690,6 +1676,42 @@ export interface Config {
 
 Source: [`packages/llm/plugin-package-inventory-deepseek/src/index.ts:31`](../packages/llm/plugin-package-inventory-deepseek/src/index.ts)
 
+<a id="deepseek-aidsh-ptc-runtime-node"></a>
+
+## `@deepseek-ai/dsh-ptc-runtime-node`
+
+Requires: `fs` · `subprocess` · `sandbox` · `sandboxPolicy`
+
+```ts config-catalog
+/** Deployment-varying runtime bounds and launch choices. */
+export interface Config extends LaunchConfig {
+  /** Default elapsed deadline, including nested tool and approval waits. */
+  timeoutMs?: number
+  /** Maximum elapsed deadline accepted by resolve. */
+  maxTimeoutMs?: number
+  /** Combined serialized logs, completion and diagnostic byte cap. */
+  maxOutputBytes?: number
+  /** V8 old-generation heap limit in MiB; native allocations are excluded. */
+  maxOldGenerationSizeMb?: number
+  /** Maximum control frame, outstanding argument and queued control-output bytes. */
+  maxMessageBytes?: number
+  /** Maximum simultaneous host binding calls accepted from a program. */
+  maxPendingCalls?: number
+  /** Managed process termination and output-drain grace in milliseconds. */
+  graceMs?: number
+}
+
+/** Deployment-owned Node executable and optional preinstalled built bootstrap. */
+export interface LaunchConfig {
+  /** Executable in the subprocess world; defaults to the current Node executable. */
+  nodeExecutable?: string
+  /** Absolute preinstalled built bootstrap in the execution world. */
+  bootstrapPath?: string
+}
+```
+
+Source: [`packages/ptc-runtime/ptc-runtime-node/src/index.ts:26`](../packages/ptc-runtime/ptc-runtime-node/src/index.ts)
+
 <a id="deepseek-aidsh-pwsh-local"></a>
 
 ## `@deepseek-ai/dsh-pwsh-local`
@@ -1829,7 +1851,7 @@ export interface Config {
   /** File-sandbox mode a session starts from (default: `read-only`). */
   mode?: SandboxMode
   /**
-   * Fallback root for agentless calls and sessions without a cwd (default:
+   * Absolute fallback root for agentless calls and sessions without a cwd (default:
    * `process.cwd()`). Normal agent calls use their session cwd instead.
    */
   workspaceRoot?: string
@@ -1838,7 +1860,7 @@ export interface Config {
 
 Depends on: [`SandboxMode`](subsystems/sandbox.md)
 
-Source: [`packages/sandbox/sandbox-policy/src/index.ts:70`](../packages/sandbox/sandbox-policy/src/index.ts)
+Source: [`packages/sandbox/sandbox-policy/src/index.ts:71`](../packages/sandbox/sandbox-policy/src/index.ts)
 
 <a id="deepseek-aidsh-sdk-app"></a>
 
@@ -2264,6 +2286,40 @@ export interface Config {
 
 Source: [`packages/spill/spill-policy/src/index.ts:61`](../packages/spill/spill-policy/src/index.ts)
 
+<a id="deepseek-aidsh-ssh"></a>
+
+## `@deepseek-ai/dsh-ssh`
+
+```ts config-catalog
+/** Deployment-owned SSH identity and installed helper; no model argument selects these values. */
+export interface Config {
+  /** OpenSSH host alias, including its existing user, key and known-host configuration. */
+  host: string
+  /** Absolute remote Node executable. */
+  node: string
+  /** Absolute path to the installed, bundled helper entry. */
+  helper: string
+  /** SHA-256 of that bundled helper; mismatches refuse the connection. */
+  helperHash: string
+  /** Absolute remote default workspace. */
+  workspace: string
+  /** Optional preinstalled built PTC entry, paired with its expected digest. */
+  bootstrapPath?: string
+  /** SHA-256 of bootstrapPath; both fields must be supplied together. */
+  bootstrapHash?: string
+  /** Connection and administrative-request deadline, at most 2,147,483,647 milliseconds. */
+  requestTimeoutMs?: number
+  /** Maximum JSON payload bytes per helper request or response. */
+  maxFrameBytes?: number
+  /** Maximum ordinary requests; heartbeat and bounded resource cleanup have reserved capacity. */
+  maxPending?: number
+  /** Remote helper lease; loss of heartbeats starts remote managed cleanup. */
+  leaseMs?: number
+}
+```
+
+Source: [`packages/ssh/ssh/src/index.ts:17`](../packages/ssh/ssh/src/index.ts)
+
 <a id="deepseek-aidsh-storage-domain"></a>
 
 ## `@deepseek-ai/dsh-storage-domain`
@@ -2587,7 +2643,7 @@ export interface Config {
 }
 ```
 
-Source: [`packages/core/system-prompt/src/index.ts:246`](../packages/core/system-prompt/src/index.ts)
+Source: [`packages/core/system-prompt/src/index.ts:247`](../packages/core/system-prompt/src/index.ts)
 
 <a id="deepseek-aidsh-terminal-bash"></a>
 
@@ -3149,7 +3205,7 @@ export interface Config {
    * sends only `run_code` plus a generated SDK prompt and collapses the
    * executor to the same surface (a model-direct call may only name
    * `run_code`; `run_code` SDK sub-dispatches keep every visible tool); `both`
-   * sends both forms. PTC mode requires a `ctx.codeRuntime` whose `language`
+   * sends both forms. PTC mode requires a `ctx.ptcRuntime` whose `language`
    * has a registered SDK renderer (TypeScript or Python) and fail prompt
    * assembly when it is absent or has no renderer. Under `ptc`, native names
    * in `toolOrder` are invalid.
@@ -3169,7 +3225,7 @@ export interface Config {
 export type ToolPresentationMode = 'native' | 'ptc' | 'both'
 ```
 
-Source: [`packages/core/tools/src/index.ts:655`](../packages/core/tools/src/index.ts)
+Source: [`packages/core/tools/src/index.ts:656`](../packages/core/tools/src/index.ts)
 
 <a id="deepseek-aidsh-typert-loader"></a>
 
@@ -3476,17 +3532,21 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
 - `@deepseek-ai/dsh-command-feedback` — requires `commands` ([`packages/feedback/command-feedback/src/index.ts`](../packages/feedback/command-feedback/src/index.ts))
 - `@deepseek-ai/dsh-command-goal` — requires `commands` · `goals` ([`packages/goal/command-goal/src/index.ts`](../packages/goal/command-goal/src/index.ts))
 - `@deepseek-ai/dsh-commands` ([`packages/interaction/commands/src/index.ts`](../packages/interaction/commands/src/index.ts))
+- `@deepseek-ai/dsh-computer-use` ([`packages/computer-use/computer-use/src/index.ts`](../packages/computer-use/computer-use/src/index.ts))
 - `@deepseek-ai/dsh-cordis-client-runner` ([`packages/extensions/cordis-client-runner/src/index.ts`](../packages/extensions/cordis-client-runner/src/index.ts))
 - `@deepseek-ai/dsh-deepseek-llm-api-extensions` ([`packages/llm/deepseek-llm-api-extensions/src/index.ts`](../packages/llm/deepseek-llm-api-extensions/src/index.ts))
 - `@deepseek-ai/dsh-experimental-auto-review` — requires `llm` · `permissionPresets` · `sessions` · `tools` ([`packages/experimental/auto-review/src/index.ts`](../packages/experimental/auto-review/src/index.ts))
 - `@deepseek-ai/dsh-experimental-client-ui-agent-team` ([`packages/experimental/client-ui-agent-team/src/index.ts`](../packages/experimental/client-ui-agent-team/src/index.ts))
+- `@deepseek-ai/dsh-experimental-computer-use-cua-driver-native` — requires `computerUse` · `tools` · `systemPrompt` ([`packages/experimental/computer-use-cua-driver-native/src/index.ts`](../packages/experimental/computer-use-cua-driver-native/src/index.ts))
 - `@deepseek-ai/dsh-fs-observation-policy` ([`packages/fs/fs-observation-policy/src/index.ts`](../packages/fs/fs-observation-policy/src/index.ts))
+- `@deepseek-ai/dsh-fs-ssh` — requires `ssh` · `sandboxPolicy` ([`packages/ssh/fs-ssh/src/index.ts`](../packages/ssh/fs-ssh/src/index.ts))
 - `@deepseek-ai/dsh-goal-round-driver` — requires `agents` · `goals` · `sessions` ([`packages/goal/goal-round-driver/src/index.ts`](../packages/goal/goal-round-driver/src/index.ts))
 - `@deepseek-ai/dsh-host-directory-picker-auto` — requires `webServer` · `loader` ([`packages/host/directory-picker-auto/src/index.ts`](../packages/host/directory-picker-auto/src/index.ts))
 - `@deepseek-ai/dsh-host-directory-picker-native` ([`packages/host/directory-picker-native/src/index.ts`](../packages/host/directory-picker-native/src/index.ts))
 - `@deepseek-ai/dsh-host-plugin-inventory` — requires `loader` ([`packages/host/plugin-inventory/src/index.ts`](../packages/host/plugin-inventory/src/index.ts))
 - `@deepseek-ai/dsh-llm` ([`packages/llm/llm/src/index.ts`](../packages/llm/llm/src/index.ts))
 - `@deepseek-ai/dsh-lsp` ([`packages/lsp/lsp/src/index.ts`](../packages/lsp/lsp/src/index.ts))
+- `@deepseek-ai/dsh-sandbox-ssh` — requires `ssh` ([`packages/ssh/sandbox-ssh/src/index.ts`](../packages/ssh/sandbox-ssh/src/index.ts))
 - `@deepseek-ai/dsh-schedule` — requires `agents` · `sessions` · `tools` · `sessionPersistence` ([`packages/schedule/schedule/src/index.ts`](../packages/schedule/schedule/src/index.ts))
 - `@deepseek-ai/dsh-session` ([`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts))
 - `@deepseek-ai/dsh-session-checkpoint-policy` — requires `llm` · `sessionPersistence` · `sessions` · `tools` ([`packages/session/session-checkpoint-policy/src/index.ts`](../packages/session/session-checkpoint-policy/src/index.ts))
@@ -3497,6 +3557,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
 - `@deepseek-ai/dsh-storage` ([`packages/storage/storage/src/index.ts`](../packages/storage/storage/src/index.ts))
 - `@deepseek-ai/dsh-subagent` ([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts))
 - `@deepseek-ai/dsh-subprocess-local` ([`packages/subprocess/subprocess-local/src/index.ts`](../packages/subprocess/subprocess-local/src/index.ts))
+- `@deepseek-ai/dsh-subprocess-ssh` — requires `ssh` ([`packages/ssh/subprocess-ssh/src/index.ts`](../packages/ssh/subprocess-ssh/src/index.ts))
 - `@deepseek-ai/dsh-terminal` ([`packages/terminal/terminal/src/index.ts`](../packages/terminal/terminal/src/index.ts))
 - `@deepseek-ai/dsh-tool-ask-user` — requires `tools` · `userQuestions` ([`packages/interaction/tool-ask-user/src/index.ts`](../packages/interaction/tool-ask-user/src/index.ts))
 - `@deepseek-ai/dsh-tool-call-timeout-policy` — requires `tools` ([`packages/guard/timeout-policy/src/index.ts`](../packages/guard/timeout-policy/src/index.ts))
@@ -3511,13 +3572,13 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
 Abstract service classes — a deployment loads a concrete implementation package instead ([capability seams](../.agents/notes/implemented/architecture/2026-06-13-capability-seams.md)).
 
 - `@deepseek-ai/dsh-attachment` — abstract `AttachmentStore` ([`packages/attachment/attachment/src/index.ts`](../packages/attachment/attachment/src/index.ts))
-- `@deepseek-ai/dsh-code-runtime` — abstract `CodeRuntime` ([`packages/code-runtime/code-runtime/src/index.ts`](../packages/code-runtime/code-runtime/src/index.ts))
 - `@deepseek-ai/dsh-compaction` — abstract `CompactionEngine` ([`packages/compaction/compaction/src/index.ts`](../packages/compaction/compaction/src/index.ts))
 - `@deepseek-ai/dsh-credentials` — abstract `CredentialProvider` ([`packages/credentials/credentials/src/index.ts`](../packages/credentials/credentials/src/index.ts))
 - `@deepseek-ai/dsh-file-reference` — abstract `FileReferenceService` ([`packages/context/file-reference/src/index.ts`](../packages/context/file-reference/src/index.ts))
 - `@deepseek-ai/dsh-fs` — abstract `FileSystem` ([`packages/fs/fs/src/index.ts`](../packages/fs/fs/src/index.ts))
 - `@deepseek-ai/dsh-host-directory-picker` — abstract `DirectoryPicker` ([`packages/host/directory-picker/src/index.ts`](../packages/host/directory-picker/src/index.ts))
 - `@deepseek-ai/dsh-jobs` — abstract `JobRegistry` ([`packages/jobs/jobs/src/index.ts`](../packages/jobs/jobs/src/index.ts))
+- `@deepseek-ai/dsh-ptc-runtime` — abstract `PtcRuntime` ([`packages/ptc-runtime/ptc-runtime/src/index.ts`](../packages/ptc-runtime/ptc-runtime/src/index.ts))
 - `@deepseek-ai/dsh-sandbox` — abstract `SandboxProvider` ([`packages/sandbox/sandbox/src/index.ts`](../packages/sandbox/sandbox/src/index.ts))
 - `@deepseek-ai/dsh-session-persistence` — abstract `SessionPersistence` ([`packages/session/session-persistence/src/index.ts`](../packages/session/session-persistence/src/index.ts))
 - `@deepseek-ai/dsh-session-query` — abstract `SessionQueryEngine` ([`packages/session-query/session-query/src/index.ts`](../packages/session-query/session-query/src/index.ts))

+ 169 - 108
docs/config-catalog.zh.md

@@ -385,7 +385,7 @@ export interface ConnectionRecoveryConfig {
 }
 ```
 
-来源: [`packages/client/connection/src/index.ts:72`](../packages/client/connection/src/index.ts)
+来源:[`packages/client/connection/src/index.ts:72`](../packages/client/connection/src/index.ts)
 
 <a id="deepseek-aidsh-client-hmr"></a>
 
@@ -403,43 +403,6 @@ export interface Config {
 
 来源:[`packages/client/hmr/src/index.ts:31`](../packages/client/hmr/src/index.ts)
 
-<a id="deepseek-aidsh-code-runtime-worker-thread"></a>
-
-## `@deepseek-ai/dsh-code-runtime-worker-thread`
-
-```ts config-catalog
-/** Plugin config: every execution cap, changeable from `cordis.yml` (no hardcoded tunables). */
-export interface Config {
-  /**
-   * Busy-time budget in milliseconds: the run fails with kind `'timeout'`
-   * once the worker's MEASURED event-loop active time
-   * (`worker.performance.eventLoopUtilization()`) exceeds this. Metering
-   * measured busy time — not wall time, not host-side pending-call
-   * bookkeeping — is what makes the budget both fair (a program awaiting a
-   * slow tool accrues nothing) and ungameable (a hot loop accrues whether
-   * or not a decoy dispatch is in flight).
-   */
-  computeMs?: number
-  /**
-   * Wall-clock ceiling in milliseconds; never pauses for anything. The
-   * backstop for what busy-time cannot see (a program awaiting a promise
-   * nobody will resolve). At most `2_147_483_647` (Node's maximum
-   * `setTimeout` delay, about 24.9 days): a longer value is rejected at load
-   * because `setTimeout` would clamp it to 1 ms.
-   */
-  maxWallMs?: number
-  /**
-   * Hard cap for serialized log-array, completion-value, and failure-message payloads;
-   * fixed result-envelope syntax is excluded.
-   */
-  maxOutputBytes?: number
-  /** The worker's max old-generation heap in MiB (`resourceLimits`); overflow kills the worker, surfacing as kind `'worker-exit'`. */
-  maxOldGenerationSizeMb?: number
-}
-```
-
-来源:[`packages/code-runtime/code-runtime-worker-thread/src/index.ts:25`](../packages/code-runtime/code-runtime-worker-thread/src/index.ts)
-
 <a id="deepseek-aidsh-compaction-basic"></a>
 
 ## `@deepseek-ai/dsh-compaction-basic`
@@ -566,72 +529,29 @@ export interface Config {
 
 来源:[`packages/experimental/agent-team/src/types.ts:130`](../packages/experimental/agent-team/src/types.ts)
 
-<a id="deepseek-aidsh-experimental-code-runtime-python"></a>
+<a id="deepseek-aidsh-experimental-computer-use-cua-driver-mcp"></a>
+
+## `@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp`
 
-## `@deepseek-ai/dsh-experimental-code-runtime-python`
+需要:`computerUse` · `tools`
 
 ```ts config-catalog
-/** Plugin config: every cap, changeable from `cordis.yml` (no hardcoded tunables). */
+/** Installed executable and MCP connection overrides. */
 export interface Config {
-  /**
-   * RLIMIT_CPU in whole seconds (a positive integer — `setrlimit` in the child
-   * rejects a float). The child sets the soft limit to `cpuSeconds` and the
-   * hard limit to `cpuSeconds + 1`: the kernel delivers SIGXCPU at the soft
-   * limit, which the host classifies as a `timeout`; the +1s hard limit is a
-   * SIGKILL backstop for a program that traps SIGXCPU. Granularity is seconds —
-   * a coarser counterpart to the worker backend's millisecond `computeMs`.
-   */
-  cpuSeconds?: number
-  /** Wall-clock ceiling in milliseconds; backstops CPU time for programs awaiting a promise nobody resolves. */
-  maxWallMs?: number
-  /**
-   * RLIMIT_AS in mebibytes; caps address space so a runaway allocation fails
-   * cleanly. Not applied on Darwin, where the dyld shared cache mapped into
-   * every process at exec exceeds any practical cap and the kernel rejects
-   * the call; `cpuSeconds` and `maxWallMs` still bound the run there. Bounds
-   * `maxLogBytes`/`maxValueBytes` at load on EVERY platform (this static check
-   * runs on Darwin too, where only the runtime `setrlimit` is skipped): each
-   * budget times a worst-case Unicode expansion must fit this byte count minus a
-   * fixed interpreter baseline, so a near-budget output cannot breach the address
-   * space during the child's build-and-encode.
-   */
-  addressSpaceMb?: number
-  /**
-   * Shared byte budget for captured log text (host-side ledger). Bounded at load
-   * against `addressSpaceMb`: the child builds and encodes a near-budget entry
-   * under RLIMIT_AS with several copies live at once, so this cap times the
-   * worst-case Unicode expansion must fit the address space left after the
-   * interpreter baseline (see `addressSpaceMb`) — a load-time rejection, not a
-   * runtime clamp. Also bounded at load by the host's configured heap like
-   * `maxValueBytes` (see its JSDoc): the effective frame cap minus the frame
-   * envelope.
-   */
-  maxLogBytes?: number
-  /**
-   * Byte cap for the completion value. Bounded at load against `addressSpaceMb`
-   * the same way `maxLogBytes` is: the child builds and encodes a near-budget
-   * value under RLIMIT_AS with several copies live at once, so this cap times the
-   * worst-case Unicode expansion must fit the address space left after the
-   * interpreter baseline. Both budgets are ALSO bounded at load by the host's
-   * configured heap: the effective frame cap (the protocol cap, or a lower
-   * heap-derived ceiling when the host heap cannot safely parse a near-cap
-   * frame — see `hostFrameParseCeiling`) minus the frame envelope, so a budget
-   * whose honest frame could OOM the host's own JSON.parse is rejected up
-   * front.
-   */
-  maxValueBytes?: number
-  /** SIGTERM→SIGKILL grace period on kill, matching bash-local's default. */
-  graceMs?: number
-  /**
-   * Absolute path, relative path, or basename of a CPython 3.10+ interpreter.
-   * Resolved and validated once at plugin load under a five-second force-kill
-   * deadline; a basename searches `PATH`.
-   */
-  pythonBin?: string
+  /** Executable path or PATH command; defaults to `cua-driver`. */
+  command: string
+  /** Arguments passed without a shell; defaults to `['mcp']`. */
+  args: string[]
+  /** Per-call timeout in milliseconds; omission uses the MCP client's default. */
+  toolCallTimeoutMs?: number
+  /** Reconnection overrides; defaults to the MCP client's policy. */
+  reconnect: McpClient.ReconnectConfig
 }
 ```
 
-来源:[`packages/experimental/code-runtime-python/src/index.ts:42`](../packages/experimental/code-runtime-python/src/index.ts)
+依赖:[`McpClient`](../packages/mcp/mcp-client/src/index.ts)
+
+来源:[`packages/experimental/computer-use-cua-driver-mcp/src/index.ts:20`](../packages/experimental/computer-use-cua-driver-mcp/src/index.ts)
 
 <a id="deepseek-aidsh-experimental-inspector"></a>
 
@@ -701,6 +621,72 @@ export interface InspectorOptions {
 
 来源:[`packages/experimental/inspector/src/index.ts:66`](../packages/experimental/inspector/src/index.ts)
 
+<a id="deepseek-aidsh-experimental-ptc-runtime-python"></a>
+
+## `@deepseek-ai/dsh-experimental-ptc-runtime-python`
+
+```ts config-catalog
+/** Plugin config: every cap, changeable from `cordis.yml` (no hardcoded tunables). */
+export interface Config {
+  /**
+   * RLIMIT_CPU in whole seconds (a positive integer — `setrlimit` in the child
+   * rejects a float). The child sets the soft limit to `cpuSeconds` and the
+   * hard limit to `cpuSeconds + 1`: the kernel delivers SIGXCPU at the soft
+   * limit, which the host classifies as a `timeout`; the +1s hard limit is a
+   * SIGKILL backstop for a program that traps SIGXCPU. Granularity is whole seconds.
+   */
+  cpuSeconds?: number
+  /** Wall-clock ceiling in milliseconds; backstops CPU time for programs awaiting a promise nobody resolves. */
+  maxWallMs?: number
+  /**
+   * RLIMIT_AS in mebibytes; caps address space so a runaway allocation fails
+   * cleanly. Not applied on Darwin, where the dyld shared cache mapped into
+   * every process at exec exceeds any practical cap and the kernel rejects
+   * the call; `cpuSeconds` and `maxWallMs` still bound the run there. Bounds
+   * `maxLogBytes`/`maxValueBytes` at load on EVERY platform (this static check
+   * runs on Darwin too, where only the runtime `setrlimit` is skipped): each
+   * budget times a worst-case Unicode expansion must fit this byte count minus a
+   * fixed interpreter baseline, so a near-budget output cannot breach the address
+   * space during the child's build-and-encode.
+   */
+  addressSpaceMb?: number
+  /**
+   * Shared byte budget for captured log text (host-side ledger). Bounded at load
+   * against `addressSpaceMb`: the child builds and encodes a near-budget entry
+   * under RLIMIT_AS with several copies live at once, so this cap times the
+   * worst-case Unicode expansion must fit the address space left after the
+   * interpreter baseline (see `addressSpaceMb`) — a load-time rejection, not a
+   * runtime clamp. Also bounded at load by the host's configured heap like
+   * `maxValueBytes` (see its JSDoc): the effective frame cap minus the frame
+   * envelope.
+   */
+  maxLogBytes?: number
+  /**
+   * Byte cap for the completion value. Bounded at load against `addressSpaceMb`
+   * the same way `maxLogBytes` is: the child builds and encodes a near-budget
+   * value under RLIMIT_AS with several copies live at once, so this cap times the
+   * worst-case Unicode expansion must fit the address space left after the
+   * interpreter baseline. Both budgets are ALSO bounded at load by the host's
+   * configured heap: the effective frame cap (the protocol cap, or a lower
+   * heap-derived ceiling when the host heap cannot safely parse a near-cap
+   * frame — see `hostFrameParseCeiling`) minus the frame envelope, so a budget
+   * whose honest frame could OOM the host's own JSON.parse is rejected up
+   * front.
+   */
+  maxValueBytes?: number
+  /** SIGTERM→SIGKILL grace period on kill, matching bash-local's default. */
+  graceMs?: number
+  /**
+   * Absolute path, relative path, or basename of a CPython 3.10+ interpreter.
+   * Resolved and validated once at plugin load under a five-second force-kill
+   * deadline; a basename searches `PATH`.
+   */
+  pythonBin?: string
+}
+```
+
+来源:[`packages/experimental/ptc-runtime-python/src/index.ts:42`](../packages/experimental/ptc-runtime-python/src/index.ts)
+
 <a id="deepseek-aidsh-experimental-tool-agent-team"></a>
 
 ## `@deepseek-ai/dsh-experimental-tool-agent-team`
@@ -756,7 +742,7 @@ export interface Config {
 }
 ```
 
-来源:[`packages/fs/fs-local/src/index.ts:42`](../packages/fs/fs-local/src/index.ts)
+来源:[`packages/fs/fs-local/src/index.ts:43`](../packages/fs/fs-local/src/index.ts)
 
 <a id="deepseek-aidsh-fs-sandbox"></a>
 
@@ -812,7 +798,7 @@ export interface Config {
 }
 ```
 
-来源:[`packages/bundle/headless/src/index.ts:41`](../packages/bundle/headless/src/index.ts)
+来源:[`packages/bundle/headless/src/index.ts:42`](../packages/bundle/headless/src/index.ts)
 
 <a id="deepseek-aidsh-hooks-claude-code"></a>
 
@@ -1091,7 +1077,7 @@ export interface DeepSeekCatalogModel {
 
 依赖: [`ModelModality`](../packages/llm/llm/src/index.ts) · [`RetryPolicyConfig`](../packages/llm/llm/src/index.ts) · [`SystemPromptUpdate`](../packages/llm/llm/src/index.ts)
 
-来源: [`packages/llm/llm-deepseek/src/config.ts:25`](../packages/llm/llm-deepseek/src/config.ts)
+来源:[`packages/llm/llm-deepseek/src/config.ts:25`](../packages/llm/llm-deepseek/src/config.ts)
 
 <a id="deepseek-aidsh-llm-pi-ai"></a>
 
@@ -1573,7 +1559,7 @@ export interface ReconnectConfig {
 }
 ```
 
-来源:[`packages/mcp/mcp-client/src/index.ts:98`](../packages/mcp/mcp-client/src/index.ts)
+来源:[`packages/mcp/mcp-client/src/index.ts:99`](../packages/mcp/mcp-client/src/index.ts)
 
 <a id="deepseek-aidsh-message-feedback"></a>
 
@@ -1692,6 +1678,42 @@ export interface Config {
 
 来源:[`packages/llm/plugin-package-inventory-deepseek/src/index.ts:31`](../packages/llm/plugin-package-inventory-deepseek/src/index.ts)
 
+<a id="deepseek-aidsh-ptc-runtime-node"></a>
+
+## `@deepseek-ai/dsh-ptc-runtime-node`
+
+需要: `fs` · `subprocess` · `sandbox` · `sandboxPolicy`
+
+```ts config-catalog
+/** Deployment-varying runtime bounds and launch choices. */
+export interface Config extends LaunchConfig {
+  /** Default elapsed deadline, including nested tool and approval waits. */
+  timeoutMs?: number
+  /** Maximum elapsed deadline accepted by resolve. */
+  maxTimeoutMs?: number
+  /** Combined serialized logs, completion and diagnostic byte cap. */
+  maxOutputBytes?: number
+  /** V8 old-generation heap limit in MiB; native allocations are excluded. */
+  maxOldGenerationSizeMb?: number
+  /** Maximum control frame, outstanding argument and queued control-output bytes. */
+  maxMessageBytes?: number
+  /** Maximum simultaneous host binding calls accepted from a program. */
+  maxPendingCalls?: number
+  /** Managed process termination and output-drain grace in milliseconds. */
+  graceMs?: number
+}
+
+/** Deployment-owned Node executable and optional preinstalled built bootstrap. */
+export interface LaunchConfig {
+  /** Executable in the subprocess world; defaults to the current Node executable. */
+  nodeExecutable?: string
+  /** Absolute preinstalled built bootstrap in the execution world. */
+  bootstrapPath?: string
+}
+```
+
+来源:[`packages/ptc-runtime/ptc-runtime-node/src/index.ts:26`](../packages/ptc-runtime/ptc-runtime-node/src/index.ts)
+
 <a id="deepseek-aidsh-pwsh-local"></a>
 
 ## `@deepseek-ai/dsh-pwsh-local`
@@ -1831,7 +1853,7 @@ export interface Config {
   /** File-sandbox mode a session starts from (default: `read-only`). */
   mode?: SandboxMode
   /**
-   * Fallback root for agentless calls and sessions without a cwd (default:
+   * Absolute fallback root for agentless calls and sessions without a cwd (default:
    * `process.cwd()`). Normal agent calls use their session cwd instead.
    */
   workspaceRoot?: string
@@ -1840,7 +1862,7 @@ export interface Config {
 
 依赖:[`SandboxMode`](subsystems/sandbox.zh.md)
 
-来源:[`packages/sandbox/sandbox-policy/src/index.ts:70`](../packages/sandbox/sandbox-policy/src/index.ts)
+来源:[`packages/sandbox/sandbox-policy/src/index.ts:71`](../packages/sandbox/sandbox-policy/src/index.ts)
 
 <a id="deepseek-aidsh-sdk-app"></a>
 
@@ -2266,6 +2288,40 @@ export interface Config {
 
 来源:[`packages/spill/spill-policy/src/index.ts:61`](../packages/spill/spill-policy/src/index.ts)
 
+<a id="deepseek-aidsh-ssh"></a>
+
+## `@deepseek-ai/dsh-ssh`
+
+```ts config-catalog
+/** Deployment-owned SSH identity and installed helper; no model argument selects these values. */
+export interface Config {
+  /** OpenSSH host alias, including its existing user, key and known-host configuration. */
+  host: string
+  /** Absolute remote Node executable. */
+  node: string
+  /** Absolute path to the installed, bundled helper entry. */
+  helper: string
+  /** SHA-256 of that bundled helper; mismatches refuse the connection. */
+  helperHash: string
+  /** Absolute remote default workspace. */
+  workspace: string
+  /** Optional preinstalled built PTC entry, paired with its expected digest. */
+  bootstrapPath?: string
+  /** SHA-256 of bootstrapPath; both fields must be supplied together. */
+  bootstrapHash?: string
+  /** Connection and administrative-request deadline, at most 2,147,483,647 milliseconds. */
+  requestTimeoutMs?: number
+  /** Maximum JSON payload bytes per helper request or response. */
+  maxFrameBytes?: number
+  /** Maximum ordinary requests; heartbeat and bounded resource cleanup have reserved capacity. */
+  maxPending?: number
+  /** Remote helper lease; loss of heartbeats starts remote managed cleanup. */
+  leaseMs?: number
+}
+```
+
+来源:[`packages/ssh/ssh/src/index.ts:17`](../packages/ssh/ssh/src/index.ts)
+
 <a id="deepseek-aidsh-storage-domain"></a>
 
 ## `@deepseek-ai/dsh-storage-domain`
@@ -2589,7 +2645,7 @@ export interface Config {
 }
 ```
 
-来源:[`packages/core/system-prompt/src/index.ts:246`](../packages/core/system-prompt/src/index.ts)
+来源:[`packages/core/system-prompt/src/index.ts:247`](../packages/core/system-prompt/src/index.ts)
 
 <a id="deepseek-aidsh-terminal-bash"></a>
 
@@ -2867,7 +2923,7 @@ export interface Config {
 }
 ```
 
-来源: [`packages/fs/tool-present/src/index.ts:15`](../packages/fs/tool-present/src/index.ts)
+来源:[`packages/fs/tool-present/src/index.ts:15`](../packages/fs/tool-present/src/index.ts)
 
 <a id="deepseek-aidsh-tool-pwsh"></a>
 
@@ -3151,7 +3207,7 @@ export interface Config {
    * sends only `run_code` plus a generated SDK prompt and collapses the
    * executor to the same surface (a model-direct call may only name
    * `run_code`; `run_code` SDK sub-dispatches keep every visible tool); `both`
-   * sends both forms. PTC mode requires a `ctx.codeRuntime` whose `language`
+   * sends both forms. PTC mode requires a `ctx.ptcRuntime` whose `language`
    * has a registered SDK renderer (TypeScript or Python) and fail prompt
    * assembly when it is absent or has no renderer. Under `ptc`, native names
    * in `toolOrder` are invalid.
@@ -3171,7 +3227,7 @@ export interface Config {
 export type ToolPresentationMode = 'native' | 'ptc' | 'both'
 ```
 
-来源:[`packages/core/tools/src/index.ts:655`](../packages/core/tools/src/index.ts)
+来源:[`packages/core/tools/src/index.ts:656`](../packages/core/tools/src/index.ts)
 
 <a id="deepseek-aidsh-typert-loader"></a>
 
@@ -3478,17 +3534,21 @@ export interface Config {
 - `@deepseek-ai/dsh-command-feedback` — 需要 `commands`([`packages/feedback/command-feedback/src/index.ts`](../packages/feedback/command-feedback/src/index.ts))
 - `@deepseek-ai/dsh-command-goal` — 需要 `commands` · `goals`([`packages/goal/command-goal/src/index.ts`](../packages/goal/command-goal/src/index.ts))
 - `@deepseek-ai/dsh-commands`([`packages/interaction/commands/src/index.ts`](../packages/interaction/commands/src/index.ts))
+- `@deepseek-ai/dsh-computer-use` ([`packages/computer-use/computer-use/src/index.ts`](../packages/computer-use/computer-use/src/index.ts))
 - `@deepseek-ai/dsh-cordis-client-runner`([`packages/extensions/cordis-client-runner/src/index.ts`](../packages/extensions/cordis-client-runner/src/index.ts))
 - `@deepseek-ai/dsh-deepseek-llm-api-extensions`([`packages/llm/deepseek-llm-api-extensions/src/index.ts`](../packages/llm/deepseek-llm-api-extensions/src/index.ts))
 - `@deepseek-ai/dsh-experimental-auto-review` — 需要 `llm` · `permissionPresets` · `sessions` · `tools`([`packages/experimental/auto-review/src/index.ts`](../packages/experimental/auto-review/src/index.ts))
 - `@deepseek-ai/dsh-experimental-client-ui-agent-team`([`packages/experimental/client-ui-agent-team/src/index.ts`](../packages/experimental/client-ui-agent-team/src/index.ts))
+- `@deepseek-ai/dsh-experimental-computer-use-cua-driver-native` — requires `computerUse` · `tools` · `systemPrompt` ([`packages/experimental/computer-use-cua-driver-native/src/index.ts`](../packages/experimental/computer-use-cua-driver-native/src/index.ts))
 - `@deepseek-ai/dsh-fs-observation-policy`([`packages/fs/fs-observation-policy/src/index.ts`](../packages/fs/fs-observation-policy/src/index.ts))
+- `@deepseek-ai/dsh-fs-ssh` — 需要 `ssh` · `sandboxPolicy`([`packages/ssh/fs-ssh/src/index.ts`](../packages/ssh/fs-ssh/src/index.ts))
 - `@deepseek-ai/dsh-goal-round-driver` — 需要 `agents` · `goals` · `sessions`([`packages/goal/goal-round-driver/src/index.ts`](../packages/goal/goal-round-driver/src/index.ts))
 - `@deepseek-ai/dsh-host-directory-picker-auto` — 需要 `webServer` · `loader`([`packages/host/directory-picker-auto/src/index.ts`](../packages/host/directory-picker-auto/src/index.ts))
 - `@deepseek-ai/dsh-host-directory-picker-native`([`packages/host/directory-picker-native/src/index.ts`](../packages/host/directory-picker-native/src/index.ts))
 - `@deepseek-ai/dsh-host-plugin-inventory` — 需要 `loader`([`packages/host/plugin-inventory/src/index.ts`](../packages/host/plugin-inventory/src/index.ts))
 - `@deepseek-ai/dsh-llm`([`packages/llm/llm/src/index.ts`](../packages/llm/llm/src/index.ts))
 - `@deepseek-ai/dsh-lsp`([`packages/lsp/lsp/src/index.ts`](../packages/lsp/lsp/src/index.ts))
+- `@deepseek-ai/dsh-sandbox-ssh` — 需要 `ssh`([`packages/ssh/sandbox-ssh/src/index.ts`](../packages/ssh/sandbox-ssh/src/index.ts))
 - `@deepseek-ai/dsh-schedule` — 需要 `agents` · `sessions` · `tools` · `sessionPersistence`([`packages/schedule/schedule/src/index.ts`](../packages/schedule/schedule/src/index.ts))
 - `@deepseek-ai/dsh-session`([`packages/core/session/src/index.ts`](../packages/core/session/src/index.ts))
 - `@deepseek-ai/dsh-session-checkpoint-policy` — 需要 `llm` · `sessionPersistence` · `sessions` · `tools`([`packages/session/session-checkpoint-policy/src/index.ts`](../packages/session/session-checkpoint-policy/src/index.ts))
@@ -3499,6 +3559,7 @@ export interface Config {
 - `@deepseek-ai/dsh-storage`([`packages/storage/storage/src/index.ts`](../packages/storage/storage/src/index.ts))
 - `@deepseek-ai/dsh-subagent`([`packages/subagent/subagent/src/index.ts`](../packages/subagent/subagent/src/index.ts))
 - `@deepseek-ai/dsh-subprocess-local`([`packages/subprocess/subprocess-local/src/index.ts`](../packages/subprocess/subprocess-local/src/index.ts))
+- `@deepseek-ai/dsh-subprocess-ssh` — 需要 `ssh`([`packages/ssh/subprocess-ssh/src/index.ts`](../packages/ssh/subprocess-ssh/src/index.ts))
 - `@deepseek-ai/dsh-terminal`([`packages/terminal/terminal/src/index.ts`](../packages/terminal/terminal/src/index.ts))
 - `@deepseek-ai/dsh-tool-ask-user` — 需要 `tools` · `userInteraction`([`packages/interaction/tool-ask-user/src/index.ts`](../packages/interaction/tool-ask-user/src/index.ts))
 - `@deepseek-ai/dsh-tool-call-timeout-policy` — 需要 `tools`([`packages/guard/timeout-policy/src/index.ts`](../packages/guard/timeout-policy/src/index.ts))
@@ -3513,13 +3574,13 @@ export interface Config {
 抽象服务类——部署时应改为加载具体的实现包(参见[能力 seam](../.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md))。
 
 - `@deepseek-ai/dsh-attachment` — 抽象 `AttachmentStore`([`packages/attachment/attachment/src/index.ts`](../packages/attachment/attachment/src/index.ts))
-- `@deepseek-ai/dsh-code-runtime` — 抽象 `CodeRuntime`([`packages/code-runtime/code-runtime/src/index.ts`](../packages/code-runtime/code-runtime/src/index.ts))
 - `@deepseek-ai/dsh-compaction` — 抽象 `CompactionEngine`([`packages/compaction/compaction/src/index.ts`](../packages/compaction/compaction/src/index.ts))
 - `@deepseek-ai/dsh-credentials` — 抽象 `Credentials`([`packages/credentials/credentials/src/index.ts`](../packages/credentials/credentials/src/index.ts))
 - `@deepseek-ai/dsh-file-reference` — 抽象 `FileReferenceService`([`packages/context/file-reference/src/index.ts`](../packages/context/file-reference/src/index.ts))
 - `@deepseek-ai/dsh-fs` — 抽象 `FileSystem`([`packages/fs/fs/src/index.ts`](../packages/fs/fs/src/index.ts))
 - `@deepseek-ai/dsh-host-directory-picker` — 抽象 `DirectoryPicker`([`packages/host/directory-picker/src/index.ts`](../packages/host/directory-picker/src/index.ts))
 - `@deepseek-ai/dsh-jobs` — 抽象 `JobRegistry`([`packages/jobs/jobs/src/index.ts`](../packages/jobs/jobs/src/index.ts))
+- `@deepseek-ai/dsh-ptc-runtime` — 抽象 `PtcRuntime`([`packages/ptc-runtime/ptc-runtime/src/index.ts`](../packages/ptc-runtime/ptc-runtime/src/index.ts))
 - `@deepseek-ai/dsh-sandbox` — 抽象 `SandboxProvider`([`packages/sandbox/sandbox/src/index.ts`](../packages/sandbox/sandbox/src/index.ts))
 - `@deepseek-ai/dsh-session-persistence` — 抽象 `SessionPersistence`([`packages/session/session-persistence/src/index.ts`](../packages/session/session-persistence/src/index.ts))
 - `@deepseek-ai/dsh-session-query` — 抽象 `SessionQueryEngine`([`packages/session-query/session-query/src/index.ts`](../packages/session-query/session-query/src/index.ts))

+ 2 - 2
docs/cookbook/adding-a-tool.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/cookbook/adding-a-tool.md
-adding-a-tool.md: d1957fc5419a4f59fe6c2f54fd2a5f5f9c9dbb22
-adding-a-tool.zh.md: 8513ca09fef4548ab8806d489d9f91f5eb22159a
+adding-a-tool.md: 659f87bf88ac7fc4b8bc7dd65672c86e6b4326f6
+adding-a-tool.zh.md: 4804c22b22e5761e27c59392d76a322a7407af99

+ 1 - 1
docs/cookbook/adding-a-tool.md

@@ -92,7 +92,7 @@ The neutral vocabulary lives in `dsh-tools`; tools never import a UI or transpor
 
 ## Web Client presentation
 
-The built-in Web Client does not consume `presentCall` or `presentResult`. Session `page` and `follow` transport raw `tool/call` and `tool/result` events, including persisted `result.meta`. A Client plugin registers its wire tool name in the `tool.call.toolview` keyed slot and derives component props from the `ToolCallBlock` arguments, content, error, metadata, existing Code Dispatch `parentCallId`, and Session path facts. It validates these wire values locally and returns the generic row for malformed or unsupported input.
+The built-in Web Client does not consume `presentCall` or `presentResult`. Session `page` and `follow` transport raw `tool/call` and `tool/result` events, including persisted `result.meta`. A Client plugin registers its wire tool name in the `tool.call.toolview` keyed slot and derives component props from the `ToolCallBlock` arguments, content, error, metadata, existing PTC dispatch `parentCallId`, and Session path facts. It validates these wire values locally and returns the generic row for malformed or unsupported input.
 
 Use `output.presentationMeta(args, value)` when an existing Web card needs bounded structured result facts that model-facing content cannot preserve losslessly. Do not store React props or a selected card in metadata, import a Host tool implementation into a browser bundle, or create another Client presenter registry. Defining Host presentation methods alone does not add a specialized Web card. The [Client-derived presentation Agent Note](../../.agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.md) defines ownership, fallback, and equivalence requirements.
 

+ 1 - 1
docs/cookbook/adding-a-tool.zh.md

@@ -94,7 +94,7 @@ producer 提供同步的 `cancel`、在资源清理后 settle 且不 reject 的
 
 ## Web Client 展示
 
-内置 Web Client 不消费 `presentCall` 或 `presentResult`。Session `page` 与 `follow` 运输原始 `tool/call` 和 `tool/result` 事件,包括持久化的 `result.meta`。Client 插件在 keyed slot `tool.call.toolview` 中注册自己的 wire 工具名称,并从 `ToolCallBlock` 的参数、内容、错误、metadata、现有 Code Dispatch `parentCallId` 与 Session 路径事实派生组件 props。插件在本地校验这些 wire 值,并让格式错误或不受支持的输入回退到 generic 行。
+内置 Web Client 不消费 `presentCall` 或 `presentResult`。Session `page` 与 `follow` 运输原始 `tool/call` 和 `tool/result` 事件,包括持久化的 `result.meta`。Client 插件在 keyed slot `tool.call.toolview` 中注册自己的 wire 工具名称,并从 `ToolCallBlock` 的参数、内容、错误、metadata、现有 PTC dispatch `parentCallId` 与 Session 路径事实派生组件 props。插件在本地校验这些 wire 值,并让格式错误或不受支持的输入回退到 generic 行。
 
 现有 Web 卡片需要模型可见内容无法无损保存的有界结构化结果事实时,使用 `output.presentationMeta(args, value)`。不要在 metadata 中保存 React props 或预选卡片,不要把 Host 工具实现导入浏览器 bundle,也不要建立另一套 Client presenter registry。只定义 Host 展示方法不会增加专用 Web 卡片。[Client 派生展示 Agent Note](../../.agents/notes/implemented/architecture/2026-08-23-client-derived-tool-presentation.zh.md)规定 owner、fallback 与对等要求。
 

+ 2 - 2
docs/event-producer-consumer.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/event-producer-consumer.md
-event-producer-consumer.md: 813c473da7a281fc073572f8db0fa063c3d82f55
-event-producer-consumer.zh.md: 97e6a423a24320b00aa9af563debb8027a2bf2c2
+event-producer-consumer.md: b9467e4e4fac4f18f8e9e624b7d46290a8c505fd
+event-producer-consumer.zh.md: b45c9c4f92fc7d1eaf072c0083b67f4de752097c

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.