dsh-ptc-plus 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +166 -76
- package/README.md +167 -77
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +0 -0
- package/assets/ptc-plus-icon.svg +5 -0
- package/client.js +593 -306
- package/compiler-core.cjs +162 -160
- package/compiler-core.cjs.map +3 -3
- package/docs/README.md +3 -1
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +8 -7
- package/docs/adr/0008-define-agent-operating-contract.md +2 -2
- package/docs/adr/0010-session-log-derived-recovery-tips.md +7 -0
- package/docs/adr/0016-archive-resolved-review-findings.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +3 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -0
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +14 -3
- package/docs/adr/0023-global-user-bindings.md +1 -1
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +23 -7
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +4 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +26 -6
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +29 -0
- package/docs/adr/README.md +4 -4
- package/docs/architecture.md +6 -5
- package/docs/binding-review-placement-design.md +1 -1
- package/docs/client-ui.md +8 -4
- package/docs/durability-design.md +7 -7
- package/docs/installation.md +26 -8
- package/docs/runtime-reference.md +18 -11
- package/docs/semantic-validation.md +7 -3
- package/docs/user-bindings.en.md +7 -6
- package/docs/user-bindings.md +6 -5
- package/docs/value-wire.md +1 -1
- package/docs/verification.md +38 -1
- package/index.js +39 -34
- package/internal/async-local-storage-intrinsics.js +12 -7
- package/internal/callable-source-facts.js +37 -1
- package/internal/cell-analysis.js +64 -9
- package/internal/config-spec.js +1 -1
- package/internal/cordis-tools-owner.js +20 -10
- package/internal/edit-transport-owner.js +80 -20
- package/internal/execution-seam-compat.js +192 -30
- package/internal/kernel-worker.js +162 -72
- package/internal/message-sources.js +14 -0
- package/internal/module-compilation.js +2 -1
- package/internal/repl-memory-projection.js +10 -0
- package/internal/repl-scope-normalizer.js +72 -35
- package/internal/repl-value-observer.js +141 -45
- package/internal/runtime-config.js +62 -2
- package/internal/runtime-intrinsics.js +3 -0
- package/internal/runtime-messages.js +6 -4
- package/internal/session-cell-executor.js +24 -1
- package/internal/session-journal-recovery.js +267 -67
- package/internal/session-journal.js +466 -40
- package/internal/session-log-view.js +5 -4
- package/internal/session-runtime.js +45 -21
- package/internal/session-state.js +7 -29
- package/internal/settings-compat.js +66 -1
- package/internal/source-position-map.js +19 -0
- package/internal/user-binding-console-worker.js +23 -9
- package/internal/user-binding-draft-projection.js +3 -2
- package/internal/user-bindings-owner.js +2 -1
- package/internal/user-bindings.js +6 -1
- package/internal/validated-parse-repair.js +6 -2
- package/internal/value-wire.js +95 -30
- package/internal/worker-client.js +99 -8
- package/internal/worker-cwd-virtualization.js +10 -9
- package/internal/worker-output-capture.js +187 -0
- package/internal/worker-output-fence.js +26 -0
- package/internal/worker-realm-surfaces.js +197 -0
- package/internal/worker-repl-realm.js +10 -4
- package/package.json +36 -4
- package/src/client-activity.js +4 -1
- package/src/client-authoring-view.js +121 -62
- package/src/client-console.js +4 -2
- package/src/client-copy.js +4 -2
- package/src/client-feature-gates.js +7 -9
- package/src/client-host-compat.js +104 -27
- package/src/client-repl-view.js +3 -1
- package/src/client-styles.js +12 -1
- package/src/client-tool-view.js +3 -1
- package/src/client-transport.js +2 -2
- package/src/client-workbench.js +4 -3
- package/src/client.js +56 -17
|
@@ -95,7 +95,7 @@ SessionRuntime 创建 kernel 时完全忽略历史 nodes、head、checkpoints
|
|
|
95
95
|
|
|
96
96
|
journal、diagnostic、source、cause、call、operation、completion 和 completion error 都使用封闭字段集合;未知、symbol 或非枚举自有字段会使 journal 无效。capability-call `args`/`value` 与 return completion `value` 都是封闭、规范化的 `ptc-value-graph/v1` envelope。诊断结构、source frame 依赖和稳定代码见[架构说明](architecture.md#journal-与恢复)。
|
|
97
97
|
|
|
98
|
-
当前实现只写入 `version: 10` schema。v10 必须携带 `languageSemantics` 与匹配的 `moduleTransform`。v1–v8 固定迁移为 `legacy-v1` 和 legacy transform;v9 保留其语言字段,并按该版本发布时的映射补入 transform:`stateful-v1` 使用 `stateful-module-v1`,`protected-v1` 使用 `protected-module-v1`,`legacy-v1` 使用 legacy transform
|
|
98
|
+
当前实现只写入 `version: 10` schema。v10 必须携带 `languageSemantics` 与匹配的 `moduleTransform`。v1–v8 固定迁移为 `legacy-v1` 和 legacy transform;v9 保留其语言字段,并按该版本发布时的映射补入 transform:`stateful-v1` 使用 `stateful-module-v1`,`protected-v1` 使用 `protected-module-v1`,`legacy-v1` 使用 legacy transform。当前代际新提交的源码可把 `import.meta` lowering 为记录 session module base 的 cell-local metadata;`legacy-v1` 仍使用冻结 parser 并保留历史拒绝,且此前不可能成功记录的 MetaProperty 不能被恢复时重新解释。恢复不能因当前配置或新 lowering 改变隐藏状态([ADR 0025](adr/0025-use-versioned-logical-binding-identities.md))。v1-v9 作为封闭 predecessor 输入规范化为 v10:v1-v3 的旧 `bindingMode` 精确映射到 `bindingPolicy.variableRedeclarations`,而 `functionClassRedeclarations` 固定为 `false`,因为旧 pipeline 不具备该语义;`moduleSemantics.defaultExportBinding` 固定迁移为 `legacy-variable`,使 loose 历史保留旧的可写生成 binding、strict 历史保留旧的 const 行为;v1-v6 的 `moduleSemantics.importExpressionBoundary` 固定迁移为 `legacy`,使历史写入沿用其记录时的语句 lowering,v7 保留自身记录的边界代际。v1 仍使用三个 rewrite 开关全关的历史默认值,并只在 session log 唯一证明 call identity 时迁移字符串 confirmation;v2/v3 保留自身 `rewritePolicy`。v4/v5 保留已有 binding/rewrite/module 语义;v6 与 v7 保留其记录的 `userBindingsReusePolicy`。v1-v4 的 `userBindingsFingerprint` 固定为 `null`,v5 及之后的代际保留自身记录的快照指纹;v1-v5 的 `userBindingsReusePolicy` 固定为 `fingerprint-v1`。v1-v7 的 `userBindingsShadowPolicy` 固定迁移为 `whole-entry`,`userBindingNames` 固定为 `null`,因此历史 node 继续按整条条目退出,只有 v8 起的 cell 使用 `per-name` 名称证据。无法表示为非负 event sequence 的 confirmation 必须形成 unknown boundary 并触发状态收缩。包括 `languageSemantics`、`moduleTransform`、`bindingPolicy`、`rewritePolicy`、`moduleSemantics`、`userBindingsFingerprint`、`userBindingsReusePolicy`、`userBindingsShadowPolicy`、`userBindingNames`、`diagnostics` 在内的当前必需字段缺失或策略未知时 journal 必须失效,否则会削弱最终持久值与 tentative journal 的严格一致性确认;失效 journal 不证明旧 binding,但也不单独成为后续当前调用的 availability gate。profile 后续切换任一 binding 或 AST rewrite 开关只影响新 cell,历史 node 始终按自身记录的 policy、module semantics 与 transform 重放。新旧全局绑定复用策略与覆盖粒度可共存于同一历史,重复规范化不能把旧策略或旧粒度升级为当前值;没有返回值的 completion 不能证明模块内部状态相等。
|
|
99
99
|
|
|
100
100
|
## Capability Call Transcript
|
|
101
101
|
|
|
@@ -161,7 +161,7 @@ pre-execute -> tools/execute -> post-execute
|
|
|
161
161
|
|
|
162
162
|
恢复以当前请求需要的 head 为目标,沿可证明 parent 关系向更早 frontier 收缩;实现可以前向折叠 event 建图,但不得把 unknown boundary 两侧的日志位置当作状态依赖证据。恢复入口先把当前 call id 解析为持久化 `tool/call.seq`,再把该序号作为 live boundary 传入折叠器;对应的在途 call event 不属于历史。每个 `edit_run_code` 在其 call event 处,根据此前已经结算的调用确定并固定可编辑目标;之后出现的 result 只影响随后发起的 edit:
|
|
163
163
|
|
|
164
|
-
1. 用 `sourceEventSeqs[0]` 关联 `tool/result` 与 `tool/call
|
|
164
|
+
1. 用 `sourceEventSeqs[0]` 关联 `tool/result` 与 `tool/call`;若 result 同时带有 `message.source.callId`,两种身份必须指向同一个 call,否则该 call 建立 untrusted boundary。每个 ordinary result 在 journal 解析前闭合一次 call settlement;同一 call 的第二个 ordinary result 被拒绝。经过结构校验的 prune replacement 是唯一不占用第二次 ordinary settlement 的替代表示;
|
|
165
165
|
2. 预收集 valid journal 中的 `confirms`;
|
|
166
166
|
3. 排除与 live boundary 序号相同的在途 call,并让已确认 no-op 的无 journal call 不改变状态;
|
|
167
167
|
4. 缺失源码、缺失/损坏 journal 或无法验证的 call/result/prune 关联建立 untrusted boundary,不作为旧 binding 的证据;
|
|
@@ -180,11 +180,11 @@ pre-execute -> tools/execute -> post-execute
|
|
|
180
180
|
|
|
181
181
|
## 模型可知状态
|
|
182
182
|
|
|
183
|
-
DSH session 同时提供两个不同投影:append-only event log 保存审计、journal 与 replay evidence;ordered surface/derived request history 是模型请求实际可见的消息历史。PTC Plus 的默认状态 frontier 是“结构可重建”和“模型可知”的交集,不能用 raw log 的存在替代 surface evidence
|
|
183
|
+
DSH session 同时提供两个不同投影:append-only event log 保存审计、journal 与 replay evidence;ordered surface/derived request history 是模型请求实际可见的消息历史。PTC Plus 的默认状态 frontier 是“结构可重建”和“模型可知”的交集,不能用 raw log 的存在替代 surface evidence。模型可知只采用外部可验证事实,不猜模型内部记忆:可见 assistant tool-call block 必须以相同 call id、tool name 和原始 JSON arguments 精确对应 raw dispatch,或插件/宿主通过有界结构把准确 binding identity 与 provenance 放入模型上下文。只匹配 call id、可见 `tool/result`、result source relation 或 raw log 不能证明声明源码可见。若 ordered surface 缺失、格式错误或不可读取,就没有历史 executable provenance 可证明,恢复必须收缩到空 frontier;这只否定历史 binding 证据,当前有效 cell 仍从空 REPL 正常执行。
|
|
184
184
|
|
|
185
185
|
surface replacement 是否删除状态取决于模型失去了什么,而不取决于是否出现 `compaction/prune` 这个名字。只替换过长 tool result、仍保留 assistant `run_code`/`edit_run_code` 调用及源码时,模型仍知道该 cell,可继续使用其 journal。若 replacement 或 summary 遮蔽了声明 provenance,raw journal 即使完整也不能让 binding 默认继续存在;自然语言 summary 不解析为依赖或 binding 证明。当前没有模型可见的完整 binding inventory,因此缺少精确 provenance 时按 cell 与依赖后缀收缩。
|
|
186
186
|
|
|
187
|
-
该规则也约束 live worker。runtime
|
|
187
|
+
该规则也约束 live worker。structured host session 的 runtime 在每个后续 cell 前都重新读取 DSH public ordered surface,并验证所有现存 live call 的精确 assistant provenance;`replaceGeneration` 没有变化不构成继续复用的证明。generation 改变、缺失或不可读取也进入同一验证。若现存 binding 超出模型可知 frontier,先重置 worker、重建交集 frontier,再执行当前 cell。只有不带 host session 的内部 primitive session id 明确使用 live-only 连续性。Client header card 的 `dshPtcPlusBindings` 只帮助用户检查,不进入模型上下文,也不延长 binding 生命周期;相同 contraction 应让 UI inventory 进入不可确认状态。未来若新增模型可见状态投影,它必须由用户、模型或明确配置选择,有固定 schema、硬预算、append-only runtime-context 证据和明确清除条件,不能仅为延长隐藏状态而默认注入,也不能污染稳定 prompt prefix。
|
|
188
188
|
|
|
189
189
|
## Completion 校验
|
|
190
190
|
|
|
@@ -226,7 +226,7 @@ type StateOperation =
|
|
|
226
226
|
|
|
227
227
|
静态分类器理解 top-level、block、function、catch 和 loop binding。它只把实际未绑定的 ambient reference 作为降级候选;属性键和局部同名变量无影响。
|
|
228
228
|
|
|
229
|
-
运行时对以下访问标记 volatile
|
|
229
|
+
运行时对以下访问标记 volatile。durability 归因依据是 worker 当前 active execution,不使用异步回调继承的 `AsyncLocalStorage` store。active execution 从开始求值持续到 capability calls 结算、返回值 PTC value graph 编码或异常归一化全部完成;这些阶段的 getter、Proxy 或字符串转换仍可能执行用户代码。最终 durability 必须在转换后采样,纯 wire message 构造完成后才在最外层 `finally` 清除 active execution。只有此后发生的访问才暂存 reason 并使下一 cell volatile:
|
|
230
230
|
|
|
231
231
|
- Date、performance、fetch、WebSocket、crypto、Intl;
|
|
232
232
|
- setTimeout、setInterval、setImmediate;
|
|
@@ -234,12 +234,12 @@ type StateOperation =
|
|
|
234
234
|
- `Math.random()`;
|
|
235
235
|
- durable allowlist 之外的 dynamic import,包括 `node:path`。
|
|
236
236
|
|
|
237
|
-
普通 `Math` intrinsic 保持完整。`process.stdout/stderr.write`
|
|
237
|
+
普通 `Math` intrinsic 保持完整。`console` 与 `process.stdout/stderr.write` 使用私有 `AsyncLocalStorage` 保留其调度 cell 的日志归属;该 cell 关闭后的结构化输出产生诊断并重置 worker,不静默丢弃。直接 fd 1/2 与继承的 child stdio 没有调度身份,Host 以每轮 stdout/stderr start/end fence 的物理到达窗口捕获;窗口内字节计入该轮预算。窗口外第一笔字节立即关闭 capture 并触发重置;若当时没有 active cell,Host 保留一次失败并让下一次执行显式失败,而不是继续无界 drain。stdout、stderr 和结构化日志各自保持顺序,合并结果不声称跨通道全序。该日志规则与上段 durability 归因分别拥有输出交付和恢复分类,不能互相替代。
|
|
238
238
|
|
|
239
239
|
worker 不继承 Electron 的工作目录语义。插件通过现有 `tools/execute` context 读取不可变的 `agent.session.header.cwd` 并注入 session worker;`process.cwd()` 返回该值且保持 durable。header 未记录 cwd 时才回退宿主值并在运行时标记 volatile。
|
|
240
240
|
同一 session 中,`child_process` 的 `exec`、`execFile`、`fork`、`spawn` 及同步变体在未提供 `options.cwd` 时以该 cwd 启动;显式 cwd 保持调用方选择。worker 保留 Node、package manager 和 shell 所需的宿主环境变量,仅将 `TEMP`、`TMP` 和 `TMPDIR` 覆盖为该 session 的 scratch 目录。`node:fs`、`node:fs/promises` 和 glob 的相对入口统一以 session cwd 解析,绝对路径与显式路径选项保持原生语义。
|
|
241
241
|
|
|
242
|
-
直接访问 `worker_threads` 或 `cluster`
|
|
242
|
+
直接访问 `worker_threads` 或 `cluster` 的 static import、dynamic import 和未遮蔽 `require` 会在 cell 任何语句执行前的 preflight 阶段被拒绝,因此外围 `try`/`catch` 不能捕获该拒绝。直接 `process`、`require`、dynamic import 与 static import 取得的 `process.exit/abort/kill/chdir` 共享同一组拒绝函数,因为这些操作暴露或破坏 worker lifecycle control。该 gate 只维护 REPL 生命周期,不是恶意
|
|
243
243
|
代码安全沙箱;native tool 的安全依赖 DSH policy,ambient Node 的安全依赖进程隔离和操作系统权限。
|
|
244
244
|
|
|
245
245
|
## 恢复通知
|
package/docs/installation.md
CHANGED
|
@@ -24,14 +24,14 @@ The plugin attaches to whichever program-execution service the installed DSH reg
|
|
|
24
24
|
|
|
25
25
|
Every installation path uses an unmodified official DSH distribution. Local plugin development packages this checkout; it does not build, patch, or substitute the Host. Missing public layout interfaces remain a feature limitation, not an instruction to customize DSH. See [ADR 0017](adr/0017-track-the-latest-dsh-public-surface.md) for distribution and acceptance requirements.
|
|
26
26
|
|
|
27
|
-
The Client root requires `
|
|
27
|
+
The Client root requires `slots`, `locale`, `connection`, and `remote`; it resolves the installed generation's settings transport (`configForms` on the current alpha, `settingsScope` on the preceding RC) through an optional injection. Contributions wait for their public slots and consume the supplied session hooks; the composer action additionally requires `remote.commands`. Neither an unused `ui-session` module nor a renamed conversation registry gates activation. Preset selection prefers `agentPreset` projection evidence, falling back only to the preceding public session summary field when that projection is absent. Binding values and draft capabilities always require their own projections. No private store or raw-log fallback is used. Slot and provider disposal withdraw the contributions they own.
|
|
28
28
|
|
|
29
29
|
## npm Release
|
|
30
30
|
|
|
31
31
|
Use this form after the selected version is available from the npm registry:
|
|
32
32
|
|
|
33
33
|
```sh
|
|
34
|
-
dsh plugin --profile <profile> add dsh-ptc-plus@0.4.
|
|
34
|
+
dsh plugin --profile <profile> add dsh-ptc-plus@0.4.3
|
|
35
35
|
dsh --profile <profile> --dump-config
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -68,7 +68,7 @@ pnpm dsh --profile <profile> --dump-config
|
|
|
68
68
|
|
|
69
69
|
```sh
|
|
70
70
|
npm pack
|
|
71
|
-
dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.4.
|
|
71
|
+
dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.4.3.tgz
|
|
72
72
|
dsh --profile <profile> --dump-config
|
|
73
73
|
```
|
|
74
74
|
|
|
@@ -106,20 +106,38 @@ dsh plugin add github:muyuanjin/dsh-ptc-plus#main
|
|
|
106
106
|
dsh --dump-config
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
After an npm release, the package spec may instead be `dsh-ptc-plus@0.4.
|
|
109
|
+
After an npm release, the package spec may instead be `dsh-ptc-plus@0.4.3`. For a local package, use its absolute tarball path. Restart DSH Desktop after installation. Linux Desktop is not a current DSH Desktop release target; use DSH CLI/Web on Linux.
|
|
110
|
+
|
|
111
|
+
## Upgrades
|
|
112
|
+
|
|
113
|
+
PTC Plus upgrades independently of the host. Replace the installed package with the new release the same way it was installed — `npm install dsh-ptc-plus@latest`, a new tarball, or an updated pinned Git revision in the profile patch — and restart the host so every profile reloads the plugin. The plugin never patches DSH and never requires a custom host build, so a host upgrade keeps the plugin working as long as the public surfaces it needs are still published.
|
|
114
|
+
|
|
115
|
+
Your settings document and session logs are not rewritten by an upgrade. A release that adds a configuration field applies its documented default until you choose a value. A release that raises the Session format generation requires the migration below before an older log is opened by the new Host; the plugin reports that condition instead of rewriting history on its own.
|
|
116
|
+
|
|
117
|
+
## Troubleshooting
|
|
118
|
+
|
|
119
|
+
Start from the exact diagnostic: every message below names the surface that is missing, and the plugin keeps the rest of its behavior intact while the condition holds.
|
|
120
|
+
|
|
121
|
+
- `ptc-plus: no host execution seam (ptcRuntime or codeRuntime) is registered` — the profile publishes no DSH code runtime, or that entry failed to activate. Repair or enable the runtime row and reload; the plugin attaches as soon as the service appears.
|
|
122
|
+
- PTC Plus says the settings service is unavailable, and its indicator and sparkle button are missing — a retained plugin dependency may lack the schema capability required by the current Host. Reinstall the current plugin package and restart DSH; the dependency range requires a builder that supports live settings. The development launcher performs that installation on its next launch. This message alone does not prove that runtime execution is disabled. A missing required `volatile()` builder now produces an explicit dependency diagnostic during plugin loading.
|
|
123
|
+
- `ptc-plus: Cordis companion tooling withdrew from an agent scope` — an exposed companion service, preset, Skill or child fiber failed its required contract. Repair the specific cause attached to the warning, or disable `cordisToolsEnabled` if that integration is unavailable. A scope missing optional companion surfaces instead waits silently and retries when its tool surface changes or its next prompt is assembled.
|
|
124
|
+
- A commit is refused with a verification or proof error — follow [deterministic verification](verification.md): resolve any incomplete lanes shown by `npm run review:status`, run `npm run check` on the frozen tree, then `npm run review:finalize`. Stage that verified candidate and retry. Use `npm run hooks:install` if the hook is missing; never bypass it.
|
|
125
|
+
- A historical PTC session cannot be resumed — follow the Session format upgrade below, keep the original log, and report the exact refusal message.
|
|
110
126
|
|
|
111
127
|
## Session format upgrades
|
|
112
128
|
|
|
113
129
|
The public DSH Session format catalog can renumber events when converting historical assistant streams. It treats plugin result metadata as opaque, so its automatic conversion alone cannot preserve PTC sequence references. Before opening historical PTC sessions in the new Host, stop the processes using that profile and retain a backup of each original log.
|
|
114
130
|
|
|
115
|
-
From this source checkout, convert a source artifact with the destination Host's public catalog:
|
|
131
|
+
From this source checkout, convert a standalone source artifact with the destination Host's public catalog. An empty child set must be explicit:
|
|
116
132
|
|
|
117
133
|
```sh
|
|
118
|
-
npm run session:migrate -- --dsh-entry /path/to/dsh/lib/bin.js --input /backup/session.jsonl --output /staging/session.v2.jsonl
|
|
134
|
+
npm run session:migrate -- --dsh-entry /path/to/dsh/lib/bin.js --no-children --input /backup/session.jsonl --output /staging/session.v2.jsonl
|
|
119
135
|
```
|
|
120
136
|
|
|
137
|
+
For a parent session, pass `--child-facts /backup/child-facts.json` instead. The JSON file must be the complete array of direct-child evidence produced through the selected Host's public historical catalog and child-fact APIs. Do not use `--no-children` merely because child artifacts are unavailable: newer format migrations use those facts to preserve or reconstruct the parent's child catalog and deliberately refuse to guess. Current-format inputs need neither option because no historical body migration runs.
|
|
138
|
+
|
|
121
139
|
The output header and command report identify the actual target **Session format**, independent of the DSH package version. Use the corresponding canonical filename `session.v<N>.jsonl` in that session's existing directory. Match the persistence provider's encoding: `.zstd` input/output uses the `zstd` executable, with separate header and event frames. The example names format 2; use the format reported by the selected catalog. Only place the validated output in the stopped profile after inspecting it. Keep the predecessor artifact for rollback; do not continue editing the same history through old and new Hosts in parallel.
|
|
122
140
|
|
|
123
|
-
The converter verifies that ordered tool records retain their
|
|
141
|
+
The converter verifies that ordered tool records retain their normalized call arguments, result content, error status, PTC metadata, and recorded effects while allowing the public Host migration to update generation-specific message envelopes. It maps journal confirmations, edit targets and recovery boundaries, then revalidates the output. Input files are never overwritten, including through hard links; existing destinations require explicit `--force`. A current-format source needs no output. A Host without the public catalog cannot perform Host format conversion.
|
|
124
142
|
|
|
125
|
-
Already converted logs with stale PTC references cannot be repaired from their numeric values alone: use the original predecessor or backup. Preserve any newer activity separately. Logs containing retired `ptc-plus/recovery-boundary` events are refused by Host format conversion; the existing command without `--dsh-entry` remains the separate retired-event converter
|
|
143
|
+
Already converted logs with stale PTC references cannot be repaired from their numeric values alone: use the original predecessor or backup. Preserve any newer activity separately. Logs containing retired `ptc-plus/recovery-boundary` events are refused by Host format conversion; the existing command without `--dsh-entry` remains the separate retired-event converter. That converter accepts only Session format 0 with a contiguous zero-based source log and, before writing, validates PTC recovery plus every sequence relation in that frozen Host vocabulary, including surface replacements, command sources, title sources and compaction shadows. It also subtracts removed boundary events from a seeded session's exact inherited-prefix cut. A different source format, missing, removed, forward, duplicate, wrong-kind or semantically inconsistent relation leaves the source unchanged and produces no output. Run the retired-event conversion first, then use its validated output as the input to any Host format upgrade. The migration tool does not modify a running persistence queue or silently repair unproved history.
|
|
@@ -6,7 +6,7 @@ Each top-level `run_code` executes a JavaScript/TypeScript cell with top-level `
|
|
|
6
6
|
|
|
7
7
|
With the default `bindingUpdates: 'stateful'`, declarations and assignments update one logical identity in the same scope and activation, including local const bindings. An existing closure observes later updates, while a saved function value keeps its own identity. Separate block, function and iteration scopes stay separate. A bare `let x`, `var x` or `const x` preserves an existing value and creates undefined when new. Each declarator publishes its complete candidate only after its initializer and binding pattern succeed. Earlier declarators and ordinary assignment effects remain real if a later operation fails. Unreached lexical declarations do not reserve a name for future cells.
|
|
8
8
|
|
|
9
|
-
An import alias follows its module until an actual successful assignment or declaration supplies a local value.
|
|
9
|
+
An import alias follows its module until an actual successful assignment or declaration supplies a local value. A static import in a later cell switches that same identity back to the module, so existing new-generation closures observe the change. Within one cell, all static imports install their sources before the body; an ordinary declaration or assignment in the body can override them regardless of where the import appears in the text. Named default declarations associate `__default` with the local identity until independently overwritten. Static dependencies link before the body and keep their effects even if a later dependency fails.
|
|
10
10
|
|
|
11
11
|
`bindingUpdates: 'protected'` retains native local restrictions, rejects cross-cell redeclarations, and protects newly created const/import bindings. Switching policy neither resets the worker nor freezes earlier writable identities. Historical cells replay under their recorded language generation; earlier closures retain that generation's behavior. See [ADR 0025](adr/0025-use-versioned-logical-binding-identities.md) for the ownership and compatibility contract.
|
|
12
12
|
|
|
@@ -20,9 +20,9 @@ For PTC mode requests using the code-only direct-tool projection, PTC Plus prese
|
|
|
20
20
|
|
|
21
21
|
`edit_run_code` is a real registered tool call. Session history and later model requests retain its original name and delta arguments. The plugin executes the materialized source as a host-derived `run_code`, returns only the edit status, value, and logs to the model, and keeps the complete source plus journal in private replay metadata. It does not rewrite assistant history or emit edit-specific runtime context.
|
|
22
22
|
|
|
23
|
-
When a live parse error occurs exactly at the cell EOF, PTC Plus checks
|
|
23
|
+
When a live parse error occurs exactly at the cell EOF, PTC Plus checks all 12 suffixes of one or two closing tokens drawn from `}`, `)`, and `]` with the same preparation context. If exactly one correction succeeds, the existing editor can express it from a bounded unique suffix, and the rejected call has a persistent event identity, `PTC-C001` includes a directly callable `edit_run_code({ edits: [...], expected_target_call_seq })` invocation. The guard must match the target captured when the edit call is persisted, so a later editable cell suppresses execution even when the same literal replacement would match it. The plugin does not apply or execute the correction automatically; ambiguous, broader, and unbound repairs keep the ordinary length-adaptive guidance.
|
|
24
24
|
|
|
25
|
-
Use `return` for an explicit result and `console` for logs. Native expression completion can also supply a result value:
|
|
25
|
+
Use `return` for an explicit result and the native Node `console` surface for logs. Its formatting methods, including `dir` options, tables, counters, groups, timers, assertions, and traces, retain Node semantics. `process.stdout/stderr.write`, direct fd 1/2 writes and inherited child stdio are also captured. Console entries retain call order and each descriptor pipe retains byte order; the merged log does not claim a total chronology across stdout, stderr and the private control channel. All forms share the cell's aggregate output budget. Native expression completion can also supply a result value:
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
28
|
const records = [{ id: 1 }, { id: 2 }]
|
|
@@ -31,6 +31,8 @@ return records.map(record => record.id)
|
|
|
31
31
|
|
|
32
32
|
Parse, filter, and aggregate large results before returning them. Aggregate log/return overflow terminates the worker and discards the cell; its bindings are not available for a later reduction. An encoding-budget rejection (`PTC-O001`, `invalid-output`) instead keeps the worker live, so an already initialized binding can be reduced in a later cell. Neither path undoes external effects.
|
|
33
33
|
|
|
34
|
+
Each physical cell has acknowledged start and end fences on stdout and stderr. Descriptor bytes arriving inside that window belong to the current physical run. The first output observed outside a round, or structured output whose async-local scheduling cell has already settled, produces an output-attribution diagnostic, closes capture and resets the worker rather than disappearing or being silently attached to another cell. If no cell is active, the Host retains that failure and rejects the next execution once instead of continuing to drain an unbounded background producer. Bare descriptor writes do not carry callback provenance; when they arrive inside a later acknowledged window, only that physical-arrival attribution is knowable.
|
|
35
|
+
|
|
34
36
|
Prefer executing work directly in the current cell; reserve `code.run` for source already held as data. When nested source must be written inline, escape quotes, backslashes, and newlines exactly — its parse errors point only at the generated text, not at a position in the outer cell.
|
|
35
37
|
|
|
36
38
|
## Capability Discovery
|
|
@@ -71,7 +73,7 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
|
|
|
71
73
|
durableReplay: true
|
|
72
74
|
tipsEnabled: true
|
|
73
75
|
cordisToolsEnabled: false
|
|
74
|
-
userBindingsEnabled:
|
|
76
|
+
userBindingsEnabled: true
|
|
75
77
|
computeMs: 60000
|
|
76
78
|
maxWallMs: 600000
|
|
77
79
|
maxOldGenerationSizeMb: 512
|
|
@@ -87,7 +89,7 @@ The configuration uses `ptc-plus` as the DSH plugin ID and `dsh-ptc-plus` as the
|
|
|
87
89
|
|
|
88
90
|
The five legacy binding and module switches remain migration inputs. Complete enabled/protected choices map to the corresponding new policy; mixed old choices retain their effective controls until the user explicitly selects the new switch. The Host schema preserves an omitted `bindingUpdates` value until migration, and the settings card resolves the same policy. Selecting the switch also clears the compatibility marker.
|
|
89
91
|
|
|
90
|
-
`enabled: false` is the settings-based kill switch: the Host keeps the settings namespace, client card, and passive cleanup that can withdraw old PTC declarations at the next permitted accepted step. It removes execution hooks, prompt sections, and tool surfaces without reading binding storage. Every setting is applied live when its owner can reconcile it. A submitted cell retains one configuration snapshot through preflight, execution, binding calls, completion validation, and diagnostics; an update accepted while it runs applies to cells submitted afterward. Node fixes a worker's V8 old-generation limit at creation, so changing `maxOldGenerationSizeMb` while a session worker is active is rejected and rolled back rather than reported as applied. Other non-`enabled` updates reconfigure existing owners without replacing session-bound bindings. The settings card renders in whichever settings seat the installed DSH declares — the
|
|
92
|
+
`enabled: false` is the settings-based kill switch: the Host keeps the settings namespace, client card, and passive cleanup that can withdraw old PTC declarations at the next permitted accepted step. It removes execution hooks, prompt sections, and tool surfaces without reading binding storage. Every setting is applied live when its owner can reconcile it. A submitted cell retains one configuration snapshot through preflight, execution, binding calls, completion validation, and diagnostics; an update accepted while it runs applies to cells submitted afterward. Node fixes a worker's V8 old-generation limit at creation, so changing `maxOldGenerationSizeMb` while a session worker is active is rejected and rolled back rather than reported as applied. Other non-`enabled` updates reconfigure existing owners without replacing session-bound bindings. The settings card renders in whichever settings seat the installed DSH declares — the sidebar **Plugins → Installed** page per-bundle configuration (`plugins.row.config`), or the preceding Host's **Settings → Plugin configuration** card (`settings.plugin.item`) ([ADR 0019](adr/0019-plugin-settings-and-kill-switch.md)) — shows “已启用” or “已停用”, and disables every control except `enabled` while the plugin is off. The card does not register in General settings; the sparkle menu opens the same complete form in an application dialog. If a live enable or reconfiguration cannot install a complete runtime, PTC Plus unwinds or restores every owner and persists the last applied settings; a failed compensating settings write is surfaced as an activation diagnostic. The TypeScript language check is deferred until runtime activation, so a non-TypeScript host can load the plugin in disabled mode but cannot enable it.
|
|
91
93
|
|
|
92
94
|
`cordisToolsEnabled: true` atomically mounts the official `@deepseek-ai/dsh-tool-cordis` plugin and exactly the shipped `cordis-plugin-development` companion Skill in agent scopes that expose the `run_code` and `skill` tools plus the public preset and Skill services. PTC Plus resolves the official `cordis` preset through DSH's public preset service, lets the maintained filesystem provider own its Skill root, and filters that provider at the public registration boundary so sibling Skills are never published to the PTC scope. It does not copy the Skill or switch the agent's preset. The first request waits until the Cordis tool fiber, owner guidance, exact Skill provider, and scoped Skill load are all ready. An agent scope that omits one of those surfaces is left for later: the owner publishes nothing there, reports nothing, and retries when the tool surface changes or the next prompt is assembled. A scope whose exposed preset, Skill/tool service, declared Cordis service, inspect manifest, or activation contradicts the host contract fails the setting change at installation and removes every provisional contribution; reached through the host's own `agent/created` or assemble call it withdraws from that scope with one warning instead, so this plugin's unavailability never fails an agent another plugin created ([ADR 0029](adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md)). Disable and agent/runtime disposal remove both fibers. Native agents inherit neither the resulting `tools.*` members nor the Skill, and the code-only direct-tool projection remains `[run_code, edit_run_code]`. Cordis can evaluate model-written plugins against the live runtime, so enabling it grants shell-equivalent trust.
|
|
93
95
|
|
|
@@ -97,7 +99,7 @@ Cordis Plugins and Runs are process-local. On a resumed agent or after Cordis is
|
|
|
97
99
|
|
|
98
100
|
## Global User Bindings
|
|
99
101
|
|
|
100
|
-
`userBindingsEnabled` defaults to `false
|
|
102
|
+
`userBindingsEnabled` defaults to `true`; an explicit `false` is preserved. Omitted settings use the current default, including after an upgrade. Individual entry states and user-controlled draft saving are unchanged. While disabled, PTC Plus does not read binding storage, add new declarations, inject values, register the management RPC or `/binding` command, or render Global User Binding controls. The next permitted accepted step can withdraw previously delivered declarations. Enabling it loads the single document at `$DSH_HOME/ptc-plus/bindings.json`; writes use a file lock, atomic replacement, owner-only file permissions, and an expected process-local revision. A stale revision or an external file change rejects the mutation and requires reload rather than overwriting newer content. A damaged document produces an explicit catalog error and remains unwritable until the user repairs it and reloads.
|
|
101
103
|
|
|
102
104
|
Each entry has a stable `id`, display `name`, `namespace` or `top-level` scope, selected symbols, one-line purpose, enabled state, and TypeScript source. A namespace name must be a JavaScript identifier; a top-level entry uses its exported symbols as call identifiers and keeps `name` for display. Source must provide named value exports; default exports and re-exports are rejected. An omitted symbol list derives every named value export, while an explicit list limits the exposed API; clearing the Settings symbol field requests fresh derivation from the edited source. The parser derives declaration kinds, parameter and return annotations, bounded inferred types, and model-visible declarations from the selected source exports. A `namespace` entry contributes one object binding under its name, while a `top-level` entry contributes each selected export. Reserved REPL names and duplicate active identifiers fail validation. The document accepts at most 64 entries, 65,536 UTF-16 code units per source, 262,144 across all sources, and 16,384 across active declarations. Mutations validate the complete enabled declaration set before atomic replacement; an externally written document that exceeds the limit is reported as damaged storage and contributes no active entries.
|
|
103
105
|
|
|
@@ -123,18 +125,20 @@ Every completed cell records the exact source-derived active snapshot in private
|
|
|
123
125
|
|
|
124
126
|
`durableReplay: false` starts new kernels without recorded REPL state while preserving live bindings in the current process. It does not delete session logs.
|
|
125
127
|
|
|
126
|
-
With `durableReplay: true`, recovery claims only bindings backed by both verified journal ancestry and exact provenance available to the model that produced the current call. DSH's append-only event log proves reconstructability; its ordered model-visible surface proves awareness. Raw events shadowed from
|
|
128
|
+
With `durableReplay: true`, recovery claims only bindings backed by both verified journal ancestry and exact provenance available to the model that produced the current call. DSH's append-only event log proves reconstructability; its ordered model-visible surface proves awareness. Before every subsequent cell in a structured host session, the runtime revalidates each existing live call against that exact surface even when `replaceGeneration` is unchanged. Raw events shadowed from the surface and UI-only `dshPtcPlusBindings` metadata do not make a binding model-knowable. A result-only replacement may retain a cell when the assistant call containing its source remains visible. If compaction shadows that call, the live worker and cold recovery contract the affected state unless an explicitly selected bounded structured projection exposes the exact bindings in model context. Such a projection is not injected by default merely to retain hidden state. Natural-language summaries are not parsed as state evidence.
|
|
127
129
|
|
|
128
130
|
An invalid historical association, prune replacement, or recovery boundary is also rejected as evidence for its bindings; it is not by itself a reason to reject every later valid `run_code`. Recovery takes the intersection of the model-knowable and structurally verified frontiers, then discards cells and bindings that may depend on an unknown boundary. Because the current journal does not contain a complete per-binding dependency graph, the conservative unit is an affected cell and suffix unless stronger owned evidence exists. If no non-empty frontier is provable, the worker starts from an empty REPL and executes the current cell as a new root.
|
|
129
131
|
|
|
130
132
|
The first continued cell reports `PTC-R002` once so the model can redeclare any missing bindings, and its result persists the contraction for later cold starts. Recovery never redispatches, reverses, or certifies effects from discarded history. This availability fallback applies only to historical PTC Plus recovery data; validation, policy, authority, approval, cancellation, and other failures of the current request retain their normal DSH behavior.
|
|
131
133
|
|
|
132
|
-
PTC dynamic messages carry `source: { kind: 'plugin
|
|
134
|
+
PTC dynamic messages carry `source: { kind: 'plugin:ptc-plus', form: 'snapshot' | 'catalog' | 'notice' }`. Current-state snapshots replace only earlier PTC state, including historical PTC aggregate sections; they do not replace the binding API catalog, tasks, Skill instructions, tool results, or other producers. Only a later binding catalog replaces an earlier catalog, and an explicit empty catalog withdraws its API documentation. Host runtime-context clearance does not invalidate the catalog; suppression gates new delivery. Empty snapshots withdraw earlier state claims. Delivery uses the public accepted pre-step decision and honors `includeRuntimeContext`, scoped suppression, and cancellation through an empty assembly witness. Committed messages and the public ordered surface drive deduplication and reconciliation after compaction or resume. Proposals do not count as delivery; raw history does not establish model knowledge.
|
|
133
135
|
|
|
134
136
|
Recovery tips are disabled with `tipsEnabled: false`. When enabled, a bounded independent PTC notice identified by `tools:ptc-plus-tip/<trigger>/<ordinal>` may appear after a repeated binding failure or a diagnostic that identifies an executable, shell, or path problem in the current execution world. The trigger and per-trigger ordinal are reconstructed from committed PTC notices and canonical historical DSH snapshot sections; each identity counts once across both sources, and visible wording does not advance history. A tip is subject to `tipCooldownMessages` and becomes detailed only after `tipEscalationFailures` unresolved matches; it never changes the code-only direct-tool list or schema. `edit_run_code` does not emit a runtime context because its real call and result already carry the relevant fact.
|
|
135
137
|
|
|
136
138
|
Static and dynamic imports retain Node's resolver from the session cwd, falling back to the worker cwd when none was recorded. Relative files, bare packages, conditional exports, URLs and attributes retain their Node meaning. A missing requested export fails at static linking before that target module evaluates. Namespace property writes still follow the namespace object's own contract; updating an alias does not mutate the namespace. A managed namespace's members are readonly: a member write is refused and leaves the member's live value unchanged, while reflection continues to report that live value. `Reflect.set` returns `false`, and `Object.defineProperty` with a different value throws `TypeError`; a direct assignment from a non-strict cell body is silently discarded, exactly as for any other readonly property.
|
|
137
139
|
|
|
140
|
+
Current `stateful-v1` and `protected-v1` cells expose one mutable, null-prototype `import.meta` object per cell. Its `url` is the file URL of the synthetic `<session cwd>/repl` parent, `filename` is that synthetic path, and `dirname` is the recorded session cwd; when no cwd was recorded, all three use the worker cwd. Relative `new URL(..., import.meta.url)`, static import, dynamic import and provided `require` therefore start from the same parent. Frozen `legacy-v1` cells retain their historical `import.meta` parse rejection. Cell source otherwise has a deliberate CommonJS-like subset: `require`, `require.resolve` and `require.cache` exist; `require.main` is `undefined`; ambient `__dirname`, `__filename` and `module` do not exist. Use `import.meta.dirname`, `import.meta.filename` and `import.meta.url` for cell path metadata.
|
|
141
|
+
|
|
138
142
|
Cell export modifiers retain local declarations; remote re-exports retain dependency loading, type-only exports are erased, and default values use the ordinary `__default` identity. Real modules expose their actual export names through a stable PTC-managed namespace. Member reads follow live sources until actual local override and return original function/object values. A namespace retained by an external function continues to provide live reads. Its readonly properties do not make it a native module namespace object. Opaque code independently using native import on the raw compiled URL, or its own native createRequire, can see the transport representation and does not receive PTC's writable-export interface. PTC-provided imports and require, binding modules, workbench commands and `code.run` children share the managed interface with independent state and lifecycle. The complete contract and acceptance conditions belong to [ADR 0025](adr/0025-use-versioned-logical-binding-identities.md#managed-module-interface).
|
|
139
143
|
|
|
140
144
|
User-selected dynamic compilers retain their native syntax goals and results. Their environments remain part of the source contract: direct eval resolves the caller's logical scope, while indirect eval and Function constructors use their owning realm's root. Environment adaptation must preserve those relationships, ordinary shadowed functions and actual effects. A valid call failing because it sees generated storage names is an implementation defect.
|
|
@@ -145,13 +149,13 @@ Intrinsic interfaces belong to their source generation. Source selects the ownin
|
|
|
145
149
|
|
|
146
150
|
Rewrites are recorded as `meta.dshPtcPlusRewrites` on the tool result (parallel to the journal, a closed schema). Settled rewrites do not emit redundant runtime context: failures already carry execution and retry guidance in their result. Only when the rewritten cell has no valid journal does `tools:ptc-plus-rewrite-info` describe unknown completion in the next model request.
|
|
147
151
|
|
|
148
|
-
`require(...)` is classified exactly like a dynamic import: allowlisted builtins (`node:assert`, `node:buffer`, `node:querystring`, `node:string_decoder`, `node:stream`, `node:util`, `node:url`, `node:zlib`) stay durable, other modules are volatile, `node:worker_threads`/`node:cluster` are rejected
|
|
152
|
+
`require(...)` is classified exactly like a dynamic import: allowlisted builtins (`node:assert`, `node:buffer`, `node:querystring`, `node:string_decoder`, `node:stream`, `node:util`, `node:url`, `node:zlib`) stay durable, other modules are volatile, `node:worker_threads`/`node:cluster` are rejected during preflight (`PTC-C002`), and non-literal arguments are dynamic module resolution. This rejection occurs before any cell statement executes, so a surrounding `try`/`catch` cannot catch it.
|
|
149
153
|
|
|
150
154
|
## Diagnostics
|
|
151
155
|
|
|
152
156
|
| Code | Meaning | State effect |
|
|
153
157
|
| --- | --- | --- |
|
|
154
|
-
| `PTC-C001` | The cell cannot be parsed | Not executed; REPL unchanged; a uniquely validated and target-bound
|
|
158
|
+
| `PTC-C001` | The cell cannot be parsed | Not executed; REPL unchanged; a uniquely validated and target-bound one- or two-token EOF closure includes a directly callable declared `edit_run_code` repair; otherwise retry a short corrected cell with `run_code`, use edit for a localized long-cell correction, and resend with `run_code` for broad, ambiguous, or unbound repairs |
|
|
155
159
|
| `PTC-C002` | Preflight rejected a kernel-control import | Not executed; REPL unchanged |
|
|
156
160
|
| `PTC-N001` | Top-level binding conflict | Not executed; REPL unchanged |
|
|
157
161
|
| `PTC-O001` | Unsupported or over-budget output | Cell executed; earlier mutations may exist |
|
|
@@ -181,7 +185,10 @@ Inside a cell, omitting the JavaScript argument of a native `tools` member is ac
|
|
|
181
185
|
- The runtime is identified as `typescript`. Stateful and protected cells erase type-only syntax and lower enums, namespaces, parameter properties, and decorators through maintained TypeScript and decorator transforms. JSX is outside the cell syntax. Historical cells retain their recorded language generation.
|
|
182
186
|
- `tools.read` is a bounded inspection window in the current DSH tool contract. Whole-file computation in `danger-full-access` should use `node:fs/promises.readFile` or streams and becomes `volatile`.
|
|
183
187
|
- Relative Node filesystem entry points, including `node:fs/promises` helpers and globbing, resolve from the recorded session cwd. Absolute paths and explicit filesystem options remain unchanged; sessions without a recorded cwd use the worker's native cwd and are classified as volatile when the access is ambient.
|
|
188
|
+
- A recorded `process.cwd` and the `process.exit`/`abort`/`kill`/`chdir` lifecycle guards are immutable, non-configurable realm properties. Lifecycle calls throw `Error` with code `ERR_PTC_PROCESS_CONTROL`; assignment cannot replace these surfaces. Plugin-private `AsyncLocalStorage` receivers have immutable captured `getStore`, `enterWith`, and `run` own methods so user prototype changes cannot alter control state. Separately, during bootstrap only, PTC Plus synchronously intercepts the private Node REPL `AsyncLocalStorage.run` call, protects that one platform receiver, and restores the prototype in `finally` before user source or microtasks can execute.
|
|
189
|
+
|
|
190
|
+
`internal/worker-realm-surfaces.js` is the tracked exception registry and write boundary for worker globals, process properties and native prototypes. Every write is classified as a stable native divergence, temporary compiler name, per-cell program binding, user declaration or restoration. A stable write must cite a registry row with the same classification; it cannot borrow a temporary or language-only row. The cwd-projected `node:fs`, `node:fs/promises`, `node:path`, and `node:child_process` callable family is a separate stable surface from `process.cwd`; its verifier compares dynamically enumerated callable descriptors and retained callable properties with a plain Node worker. Private `AsyncLocalStorage` receiver methods are likewise a separate stable surface from the temporary prototype bootstrap. Stable rows name their semantic owner, native or transport reference and executable verifier; a structured source check rejects supported direct `defineProperty`, assignment and deletion forms that bypass the boundary. Module-language behavior remains owned by the compiler/executor contract even when its `require` installation uses the same boundary.
|
|
184
191
|
- The durable import allowlist is `node:assert`, `node:buffer`, `node:querystring`, `node:string_decoder`, `node:stream`, `node:url`, `node:util`, and `node:zlib`. Other Node imports remain usable but make the cell volatile.
|
|
185
|
-
- Direct `node:worker_threads` and `node:cluster`
|
|
192
|
+
- Direct `node:worker_threads` and `node:cluster` references through static import, dynamic import, or unshadowed `require` are rejected during preflight, before any cell statement executes. Calls to `process.exit`, `process.abort`, `process.kill`, and `process.chdir` are rejected through direct `process`, `require`, and dynamic or static `node:process` imports.
|
|
186
193
|
- Cold recovery replays journals from the session log; there are no compressed checkpoints or worker-LRU eviction.
|
|
187
194
|
- DSH services or plugin APIs not exposed as a native tool or owner-provided program binding are not made callable through name-based reflection.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 语义义务与编译验证
|
|
2
2
|
|
|
3
|
-
`npm run
|
|
3
|
+
[`semantic-obligations.json`](../semantic-obligations.json) 是插件自有语义边界的 tracked owner graph,[ADR 0031](adr/0031-close-semantic-obligations-by-owner.md) 定义其闭合范围。`npm run semantic:check` 要求 `internal/`、`src/`、`index.js`、`client.js` 与 journal migration entry 中的每个受管实现文件恰好归属一个义务,并核对 contract、可执行 evidence、依赖图、journal 连续代际以及 language/module 导出代际。报告分别统计 `preserved`、`intentional-difference`、`bounded-external` 与 `unknown`;只有 `unknown: 0` 才表示声明范围内闭合,不表示对全部 Node 或 DSH 行为的等价证明。
|
|
4
|
+
|
|
5
|
+
每项义务必须记录 owner、实现边界、源 contract、可观察量、入口、生命周期、代际、平台边界、消费者、oracle 与 evidence。新增文件会直接使 source inventory 失败;现有 owner 内新增转换时,维护者还必须判断它是否改变当前义务、依赖或 oracle。能够从中央注册点机械闭合的边界继续使用更强检查,例如 realm mutation 的分类写入入口、journal schema 和 language/module generation set。该图不能替代具体反例、覆盖率或独立 review lane。
|
|
6
|
+
|
|
7
|
+
`npm run test:semantics` 运行三个互补的确定性编译测试层。它们也由 `npm run verify` / `npm run check` 的常规测试发现机制执行,不需要网络或模型调用。定向命令用于快速诊断,不能替代完整检查、资源验收和独立审查。
|
|
4
8
|
|
|
5
9
|
| 层 | Oracle 与执行方式 | 代码 owner |
|
|
6
10
|
| --- | --- | --- |
|
|
@@ -24,6 +28,6 @@ Test262 子集位于 `test/fixtures/test262/`。`manifest.json` 记录上游仓
|
|
|
24
28
|
|
|
25
29
|
## 发布验证边界
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
完整确定性检查与有效的独立审查 lane 聚合是提交门槛,不代表发布稳定性证明。发布前仍需补齐 Node 22/24 下连续会话、反复创建与释放 worker、覆盖插桩和资源压力组合的稳定性验证。进程级失败必须保留原始退出码、signal 与 stderr;仅有测试文件级 `test failed` 无法区分运行时缺陷、引擎退出或外部终止。
|
|
28
32
|
|
|
29
33
|
Windows Node 24/26 的 worker teardown native 崩溃已由插件自有 helper 进程边界限制在可回收子进程内(见 [ADR 0027](adr/0027-run-the-session-kernel-in-a-killable-helper-process.md));`npm run check` 现在执行一次确定性运行,不再根据 TAP 文件级 abort 自动重试。发布前仍须在受支持 Node 版本上保留原始退出码、signal 与 stderr,并覆盖连续创建/释放 helper、覆盖插桩和资源压力组合。文件级失败仍按实际 owner 修复,不得用重试掩盖。
|
package/docs/user-bindings.en.md
CHANGED
|
@@ -6,10 +6,11 @@ Global bindings save reusable TypeScript helpers for use across sessions. For ex
|
|
|
6
6
|
|
|
7
7
|
## Create and use a binding
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
Global bindings and the sparkle shortcut default to on; a new installation needs no setup switch. If an existing configuration explicitly disables global bindings, enable them under **Plugins → Installed → dsh-ptc-plus → Configure**. If only the shortcut is hidden, enable **Show the PTC Plus shortcut**. The feature switch does not change saved entries' enabled states.
|
|
10
|
+
|
|
11
|
+
1. In a PTC session, open the sparkle menu by the composer and choose **Write a new binding**, or enter `/binding new <requirement>`.
|
|
12
|
+
2. Inspect the draft's source, interface, and model prompt in the panel above the composer.
|
|
13
|
+
3. Choose **Save and enable**. Subsequent `run_code` calls load the binding; initialization failures appear in execution results.
|
|
13
14
|
|
|
14
15
|
During authoring, the Agent starts with a small in-memory check of the core behavior, then adds normal, edge, and failure assertions with `node:assert/strict`. Bulk tests report totals and a few representative failures instead of printing every successful case into the context. Tests must not modify external files or services. File and network helpers use in-memory substitutes to verify argument forwarding and error propagation; the answer identifies integration behavior that remains untested. Submission delivers a draft for your review. You decide whether to save and enable it.
|
|
15
16
|
|
|
@@ -24,9 +25,9 @@ A new draft opens above the composer. Click the title bar or use Enter/Space to
|
|
|
24
25
|
- **Discard draft**: abandon this unsaved candidate.
|
|
25
26
|
- **Close panel**: hide it without saving or discarding.
|
|
26
27
|
|
|
27
|
-
The sparkle button shows a PTC Plus tooltip
|
|
28
|
+
The sparkle button shows a delayed PTC Plus tooltip identifying it as the global bindings and settings entry; a pending draft changes it to the draft state. The tooltip stays hidden while the menu, management dialog, or settings dialog is open. The button shows a badge for pending drafts. Open its menu by hovering, clicking, or using the keyboard, then select the draft to show it again. Each session retains one current candidate. A confirmed save or discard closes the panel; failures leave the draft available for retry.
|
|
28
29
|
|
|
29
|
-
The sparkle menu is available before the first message and opens once the pointer dwells on the entry; incidental pointer movement across the composer leaves it closed. A hovered menu closes as soon as the pointer leaves it; clicking keeps it open until you click again, click elsewhere, or press Escape. Global bindings show their names, purposes, and enabled states using text and
|
|
30
|
+
The sparkle menu is available before the first message and opens once the pointer dwells on the entry; incidental pointer movement across the composer leaves it closed. A hovered menu closes as soon as the pointer leaves it; clicking keeps it open until you click again, click elsewhere, or press Escape. Global bindings show their names, purposes, and enabled states using status text and color. Click an entry to toggle it for all sessions. The catalog refreshes in the background, and the entries stay usable while it does; writes are serialized, so an entry stays usable during an in-flight write and a duplicate toggle is dropped rather than queued, and you reload after a conflict or failure before trying again. **Manage global bindings** opens the full workbench. **Write a new binding** and **Revise a binding** appear when the authoring command is available; the revision step lists existing entries and prefills `/binding edit <id> `. The reusable REPL binding list shows how many later cells reused each binding, with the total in the section title; the count comes from static references in each later cell's compiled source, and a redeclaration counts as neither a reuse nor a reset of the accumulated count.
|
|
30
31
|
|
|
31
32
|
The original binding request retains its requirement, status, and accepted source in history. Historical inspection does not restore permission to save that draft. When another edit changes the catalog, reload its current state before deciding whether to save.
|
|
32
33
|
|
package/docs/user-bindings.md
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
## 创建和使用
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
全局绑定和星光入口默认开启;新安装无需先改设置。若已有配置显式关闭了全局绑定,可从 **插件 → 已安装 → dsh-ptc-plus → 配置** 重新开启;若只隐藏了快捷入口,可打开“显示 PTC Plus 快捷入口”。功能开关不改变已保存条目的启停状态。
|
|
10
|
+
|
|
11
|
+
1. 在 PTC 会话中打开输入框星光按钮的菜单,选择“编写新绑定”,或输入 `/binding new <需求>`。
|
|
12
|
+
2. 等待 Agent 提交草稿,在输入框上方检查源码、接口和给模型的提示词。
|
|
13
|
+
3. 选择“保存并启用”。后续 `run_code` 会加载该绑定;如果初始化失败,执行结果会说明原因。
|
|
13
14
|
|
|
14
15
|
编写过程中,Agent 会先用小型内存样例验证核心行为,再用 `node:assert/strict` 补充正常、边界和失败断言。批量测试只报告总数和少量代表性失败,避免把每个成功样例写入上下文。测试不得修改外部文件或服务;文件和网络 helper 可以用内存替代对象验证参数转发与错误传播,未实际验证的集成部分应在回答中说明。提交草稿只交给你审阅,保存与启用由你决定。
|
|
15
16
|
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
|
|
27
28
|
输入框星光按钮显示待处理草稿的角标。悬停、点击或键盘打开菜单后,选择草稿即可重新显示。每个会话保留一份当前候选。保存或丢弃确认成功后面板关闭;失败时保留草稿供重试。
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
星光按钮的 PTC Plus 提示延迟显示,说明它是全局绑定与设置入口;有待处理草稿时改为提示草稿状态。菜单、管理窗口或设置窗口打开时隐藏提示,避免遮挡。星光菜单在首条消息发出前即可使用,鼠标停留一下才展开,扫过输入区不会弹出。悬停打开后鼠标移开菜单即自动关闭;点击可让菜单保持常开,再点一次、点击别处或按 Escape 关闭。全局绑定按名称和用途列出,状态文字与颜色样式表示启用状态;点击条目切换启停,配置对所有会话生效。目录在后台刷新,刷新期间沿用上一次的列表并可以继续操作;切换写入逐条串行化,写入进行中条目保持可用,重复请求被直接丢弃;发生冲突或失败时可重新加载后再操作。“管理全局绑定”打开完整工作台;编写命令可用时显示“编写新绑定”和“修改绑定”,“修改绑定”第二步从已有条目中选择后预填 `/binding edit <id> `。REPL 可复用绑定列表会显示每项被后续 cell 复用的次数,并在区块标题显示总复用次数;次数按后续 cell 编译后源码中的静态引用统计,重定义或重声明本身不算一次复用,也不会把已累计的次数清零。
|
|
30
31
|
|
|
31
32
|
历史中的“绑定编写”请求保留需求、状态和接受时的源码。它供查看当时内容,不重新获得保存资格。目录发生并发修改时,重新加载最新状态后再决定是否保存。
|
|
32
33
|
|
package/docs/value-wire.md
CHANGED
|
@@ -38,7 +38,7 @@ wire 从不使用 `eval` 或 `Function` 解码,模型文本也从不作为可
|
|
|
38
38
|
|
|
39
39
|
`Atom` 是 JSON primitive,或封闭 tag:`undefined`、special number、BigInt、node reference。object node 保存有序 `[key, Atom]` entries;array node 保存 `length` 与有序 `[index, Atom]` entries,因此 hole 与显式 `undefined` 不同。decoder 拒绝未知/额外字段、重复 key/index、越界或 dangling reference、不可达 node 和非 canonical 编码;`__proto__` 通过 data descriptor hydrate,不触发 setter。
|
|
40
40
|
|
|
41
|
-
graph 发现顺序与 ECMAScript own-key 顺序共同确定唯一编码。plain JSON tree 保持线性;shared object 只编码一次。结构预算为 `maxValueNodes`、`maxValueEdges`、`maxValueArrayLength` 和 `maxValueBigIntDigits
|
|
41
|
+
graph 发现顺序与 ECMAScript own-key 顺序共同确定唯一编码。plain JSON tree 保持线性;shared object 只编码一次。结构预算为 `maxValueNodes`、`maxValueEdges`、`maxValueArrayLength` 和 `maxValueBigIntDigits`;`maxValueArrayLength` 同时限制单个数组长度与一份 graph 中全部数组声明的累计槽位,decoder 在分配目标数组前完成整个 wire 的结构、预算、可达性与 canonical 校验。总 IPC/journal/render byte ceiling 继续由 `maxOutputBytes` 约束。
|
|
42
42
|
|
|
43
43
|
## Completion 与外层投影
|
|
44
44
|
|
package/docs/verification.md
CHANGED
|
@@ -2,7 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
`npm run check` runs `npm run verify`, checks that the source tree and review ledger did not change, and records the verified tree for the commit hook. Run `check` alone when the ledger is absent or resolved; running `verify` immediately before it repeats the full suite. While the ledger has unresolved findings, use `npm run verify` to collect verification evidence.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Incremental independent review
|
|
6
|
+
|
|
7
|
+
Independent review is partitioned before reviewers start. Create the ignored working plan, replace every placeholder, then record it against the commit that defines the change scope:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm run review:plan:new
|
|
11
|
+
npm run review:plan -- --base <base-commit>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The plan uses `dsh-review-plan/v1`, and the ignored root `REVIEW_PLAN.json` is its only active source. All five standard obligations from the template must remain present. Each standard or change-specific obligation is assigned to a lane or excluded with a concrete reason. A lane declares semantic scope, exact file paths or directory prefixes, upstream lanes, owners, consumers, and counterexamples. `.` selects the complete checkout. Paths only determine content inputs and changed-path coverage; they do not replace the semantic declarations. A lane fingerprint covers that declaration, its assigned obligations, selected raw Git entry identities, persistent invalidation generation, and upstream fingerprints. Dependencies must form a DAG. The plan command refuses missing standard obligations, placeholders, unknown obligations, dependency cycles, a non-ancestor base, and changed paths outside every lane.
|
|
15
|
+
|
|
16
|
+
The plan command prints one `review-lane <id> <head> <fingerprint>` line per lane. Lanes may be reviewed concurrently. Record each clean external report with the identity captured before that review:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm run review:lane -- \
|
|
20
|
+
--lane <id> \
|
|
21
|
+
--evidence <report-path> \
|
|
22
|
+
--expect-head <head> \
|
|
23
|
+
--expect-fingerprint <lane-fingerprint>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The report's last non-empty line must be `VERDICT: NO FINDINGS`. A readable findings or incomplete report retires the named lane and every declared dependent from the recorded plan before checking whether the current candidate drifted; restoring an earlier tree therefore cannot revive evidence that the report rejected. An unreadable or missing report path is a command-input error and does not retire valid evidence. Unrelated lanes remain effective. A clean report retains the captured `HEAD` as provenance; an unrelated concurrent edit or `HEAD` advance does not prevent recording. Direct input, upstream fingerprint, scope, obligation, owner, consumer, counterexample, or dependency changes do. Put findings in `REVIEW_FINDINGS.md`, fix them, and rerun only the incomplete lanes shown by `npm run review:status`.
|
|
27
|
+
|
|
28
|
+
Review-state mutations and authorization decisions use one atomic lock under checkout-local Git metadata. Plan recording, clean or negative lane recording, finalization, clearing, and pre-commit therefore have one observable order. A negative report queued behind an older clean recorder advances the reported lane's persistent invalidation `{epoch, revision}`, including while that lane is temporarily absent from the current plan, before retiring its current dependent closure. Advancement increments the revision normally and rotates to a new random epoch with revision zero at `Number.MAX_SAFE_INTEGER`, so a valid tuple never becomes unretirable. A temporarily absent lane must already have a retained tuple; unknown or mistyped lane IDs are rejected rather than creating state. Every active lane must have an explicit tuple. The tuple participates in the lane fingerprint and propagates through dependency fingerprints, so an older clean report cannot be submitted after re-addition. It survives normal `review:clear` and temporary lane removal; only a review captured after retirement can authorize the repaired fingerprint. Missing, partial, or malformed generation state blocks status and authorization. Explicit clear preserves valid generations but removes damaged generation state; plan recording then creates a new random epoch, so old reports still cannot match. The lock records a PID and unique release token and bounds acquisition, but never reclaims another owner automatically. If a command crashed, confirm that no review-state command is active, obtain the exact checkout-local path with `git rev-parse --git-path review-findings/state.lock`, remove only that directory, and retry. `review:status` remains a diagnostic snapshot and does not grant authorization. Finalization and pre-commit also re-read the active plan, candidate, proof, lane records, and evidence before they persist or return a decision, so edits made outside the review commands fail closed.
|
|
29
|
+
|
|
30
|
+
Do not mutate a recorded boundary implicitly. Every status, clean lane verdict, finalization, and commit decision compares the normalized active file with the recorded plan. Missing, malformed, or semantically changed active content blocks clean authorization; it does not block a findings or incomplete report from retiring the recorded lane closure. Whitespace and object-key formatting alone do not change plan identity. When evidence reveals a missing path or semantic dependency, edit `REVIEW_PLAN.json` and run `npm run review:plan` without another `--base`; the command reuses the recorded base. The new plan hash invalidates its aggregate verdict, but clean lane records survive when their base, complete declaration, selected Git entries, upstream fingerprints, and evidence hashes are unchanged.
|
|
31
|
+
|
|
32
|
+
After `review:status` reports `clean`, run the deterministic gate once and bind the composite verdict to it:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm run check
|
|
36
|
+
npm run review:finalize
|
|
37
|
+
git diff --check HEAD --
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The final verdict uses `dsh-review-verdict/v2`. Pre-commit verifies its plan hash, lane fingerprints, evidence hashes, whole source fingerprint, `HEAD`, and prospective index. A later `HEAD` change retires the proof and aggregate verdict. Source or index drift blocks the gate while it differs; restoring the exact finalized candidate preserves those records, while keeping a changed candidate requires another `check` and `review:finalize`. Only lanes whose effective inputs changed need another independent review. `npm run review:clear` abandons the recorded plan, lane records, aggregate verdict, and verification proof while leaving the working `REVIEW_PLAN.json` available for revision; persistent invalidation generations remain so pre-retirement reports cannot revive in a rebuilt plan. Clearing is not a normal retry step. The prior single-report `verdict` command and `dsh-review-verdict/v1` records are not accepted.
|
|
41
|
+
|
|
42
|
+
`npm run verify` and `npm run check` validate the ledger, compare generated bundles, check syntax, run Client tests, and run all backend tests with coverage. Backend test files run in separate Node processes. The default process count is half the available CPUs, rounded down and bounded between one and four; each process can also own runtime Workers. Tests within each file retain their existing ordering. Coverage still requires 100% lines and functions and at least 95% branches, including compiler source remapping. The graded merge additionally asserts that the plugin-source closure it claims — `index.js` and every `internal/*.js` owner — appears in the merged report, so a module no instrumented process loads fails the gate instead of silently leaving it. Generated artifacts stay outside that closure: the root `compiler-*.cjs` bundles and the `internal/*.cjs` shims are remapped onto their original `internal/` sources or verified by the bundle checks, and `scripts/migrate-session-log.mjs` is verified by its own migration suite.
|
|
6
43
|
|
|
7
44
|
Use focused coverage while developing or after a full gate identifies a narrow coverage failure. Select every source whose threshold matters and the smallest test set that can distinguish the correction:
|
|
8
45
|
|