dsh-ptc-plus 0.2.1

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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/README.zh.md +137 -0
  4. package/assets/dsh-ptc-plus-banner.webp +0 -0
  5. package/assets/ptc-plus-repair.png +0 -0
  6. package/assets/ptc-plus-settings.png +0 -0
  7. package/client.js +366 -0
  8. package/cordis.patch.yml +5 -0
  9. package/docs/README.md +21 -0
  10. package/docs/adr/0001-delegate-governance-to-dsh.md +3 -0
  11. package/docs/adr/0002-limit-work-map-scope.md +3 -0
  12. package/docs/adr/0003-prefer-native-program-surfaces.md +3 -0
  13. package/docs/adr/0004-declare-program-bindings-at-the-host.md +5 -0
  14. package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +129 -0
  15. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +45 -0
  16. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +39 -0
  17. package/docs/adr/0008-define-agent-operating-contract.md +25 -0
  18. package/docs/adr/0009-bound-expensive-acceptance-default.md +39 -0
  19. package/docs/adr/0010-session-log-derived-recovery-tips.md +26 -0
  20. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +20 -0
  21. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +23 -0
  22. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +21 -0
  23. package/docs/adr/0014-persist-cell-rewrite-policy.md +25 -0
  24. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +25 -0
  25. package/docs/adr/0016-archive-resolved-review-findings.md +43 -0
  26. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +23 -0
  27. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +25 -0
  28. package/docs/adr/0019-plugin-settings-and-kill-switch.md +32 -0
  29. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +36 -0
  30. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +68 -0
  31. package/docs/adr/README.md +18 -0
  32. package/docs/architecture.md +134 -0
  33. package/docs/capability-projection.md +48 -0
  34. package/docs/client-ui.md +47 -0
  35. package/docs/durability-design.md +265 -0
  36. package/docs/evaluation.md +62 -0
  37. package/docs/installation.md +82 -0
  38. package/docs/program-data-plane.md +61 -0
  39. package/docs/publishing.md +37 -0
  40. package/docs/runtime-reference.md +119 -0
  41. package/docs/value-wire.md +61 -0
  42. package/index.js +532 -0
  43. package/internal/ast-traversal.js +20 -0
  44. package/internal/binding-descriptors.js +95 -0
  45. package/internal/binding-pattern.js +54 -0
  46. package/internal/builtin-esm-sync.js +9 -0
  47. package/internal/cell-analysis.js +467 -0
  48. package/internal/cell-rewriter.js +520 -0
  49. package/internal/config-spec.js +179 -0
  50. package/internal/cordis-tools-owner.js +363 -0
  51. package/internal/diagnostic.js +133 -0
  52. package/internal/direct-surface-owner.js +326 -0
  53. package/internal/edit-transport-owner.js +287 -0
  54. package/internal/failure-reporting.js +139 -0
  55. package/internal/kernel-worker.js +484 -0
  56. package/internal/module-policy.js +46 -0
  57. package/internal/program-bindings.js +187 -0
  58. package/internal/record-utils.js +53 -0
  59. package/internal/recovery-tips.js +81 -0
  60. package/internal/rejected-cell-editor.js +328 -0
  61. package/internal/repl-convenience.js +201 -0
  62. package/internal/runtime-bridge-owner.js +417 -0
  63. package/internal/runtime-config.js +77 -0
  64. package/internal/runtime-contexts.js +31 -0
  65. package/internal/sdk-projection.js +88 -0
  66. package/internal/session-cell-executor.js +612 -0
  67. package/internal/session-journal.js +889 -0
  68. package/internal/session-log-view.js +80 -0
  69. package/internal/session-runtime.js +536 -0
  70. package/internal/session-state.js +45 -0
  71. package/internal/source-position-map.js +224 -0
  72. package/internal/tool-call-canonicalizer.js +339 -0
  73. package/internal/value-wire.js +421 -0
  74. package/internal/worker-client.js +218 -0
  75. package/internal/worker-cwd-virtualization.js +155 -0
  76. package/package.json +100 -0
  77. package/src/client.js +165 -0
@@ -0,0 +1,265 @@
1
+ # Durable / Volatile 恢复协议
2
+
3
+ ## 状态
4
+
5
+ 本文描述 `dsh-ptc-plus` 的 journal、两阶段确认和冷恢复行为。代码事实源是 `internal/session-journal.js` 与 `internal/session-runtime.js`。
6
+
7
+ ## 恢复承诺
8
+
9
+ PTC Plus 的正确承诺是:
10
+
11
+ > **精确恢复 durable 部分,完整运行 volatile 部分,不自动重放 volatile 副作用。**
12
+
13
+ - durable cell 的源码、tool transcript、结算顺序和 completion 足以精确重放;
14
+ - volatile cell 可使用 policy 允许但无法 journal 的 Node 能力,只保证当前 worker 生命周期内延续;
15
+ - 一旦进入 volatile,整个 live 后缀保持 volatile;
16
+ - restore 命名的 durable 状态可以显式丢弃 volatile 后缀;
17
+ - abort、timeout、worker exit、OOM 和进程恢复都回到最后一个 durable frontier;
18
+ - `durableReplay: false` 时,新 kernel 从空状态开始且所有已执行 live cell 都是 volatile,作为
19
+ 用户怀疑恢复正确性时的显式逃生模式;
20
+ - 被跳过的源码仍保存在原始 `tool/call` 中。
21
+
22
+ 这避免了两个错误极端:既不为追求恢复而删除正常 REPL 能力,也不把无法迁移的 heap 伪装成可确定重建。
23
+
24
+ ## Journal
25
+
26
+ 每个进入 CodeRuntime 的 cell 创建 mutable tentative journal:
27
+
28
+ ```ts
29
+ {
30
+ version: 3,
31
+ bindingMode: "loose" | "strict",
32
+ rewritePolicy: {
33
+ autoRewriteImports: boolean,
34
+ autoStripExports: boolean,
35
+ autoSplitRedeclarations: boolean
36
+ },
37
+ status: "durable" | "volatile" | "discarded" | "noop",
38
+ calls: CapabilityCall[],
39
+ operations: StateOperation[],
40
+ confirms: number[],
41
+ diagnostics: Diagnostic[],
42
+ completion?:
43
+ | { kind: "return", hasValue: false }
44
+ | { kind: "return", hasValue: true, value: PtcValueGraphV1 }
45
+ | { kind: "throw", error: { kind: string, message: string } },
46
+ volatileReason?: string
47
+ }
48
+ ```
49
+
50
+ 字段含义:
51
+
52
+ - `durable`:创建可重放 node,推进 `durableHead`;
53
+ - `bindingMode`:记录该 cell 实际采用的顶层 binding 语义;冷重放读取每个 node 的记录值,不读取恢复时的 profile 配置;
54
+ - `rewritePolicy`:记录该 cell 解析和 lowering 时使用的三个 AST rewrite 开关;冷重放读取每个 node 的记录值,不读取恢复时的 profile 配置;
55
+ - `volatile`:只推进 live heap,不推进 `durableHead`;
56
+ - `discarded`:基础设施失败,calls 和 operations 必须为空;若中止时仍有未结算的 program binding call,则以首个 `global.member` 保留 `volatileReason`,恢复时不能把这个 possible-effect boundary 折叠为 no-op;
57
+ - `noop`:程序未执行,calls 和 operations 必须为空;
58
+ - `confirms`:以 `tool/call.seq` 确认此前无 journal 的 call 没有进入 runtime;
59
+ - `diagnostics`:本 cell 产生的封闭结构化诊断;
60
+ - `completion`:区分普通 return 与可重放的语义 throw;
61
+ - `volatileReason`:记录第一次运行时降级原因,或 discarded cell 中最先观察到的未结算 program binding。
62
+
63
+ `durableReplay` 不写入 journal,也不引入 schema 分支。它默认是 `true`。设为 `false` 时,
64
+ SessionRuntime 创建 kernel 时完全忽略历史 nodes、head、checkpoints 与 volatile suffix;之后实际
65
+ 求值的 cell 无论静态分类如何都记录为 `volatile`。这是已知配置态,不是运行时降级,因此不
66
+ 发送模型可见警告;system prompt 直接告知模型 binding 仅在当前进程可复用,`repl.state(list)`
67
+ 返回同一状态。未进入 evaluator 的调用仍是 `noop`,取消、超时和 worker failure 仍是
68
+ `discarded`。关闭期间不能保存 durable named state;restore 后的下一 cell 仍被强制为 volatile。
69
+
70
+ 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-与恢复)。
71
+
72
+ 当前实现写入 `version: 3` schema。`version: 2` 且 `confirms` 为空的 journal 可无歧义规范化为 v3;v2 中非空的 call-id confirmation 无法可靠映射到可能重复的 session event,必须形成 unknown suffix,不能猜测迁移。包括 `bindingMode`、`rewritePolicy`、`diagnostics` 在内的必需字段缺失时 journal 必须失效,否则会削弱最终持久值与 tentative journal 的严格一致性确认。profile 后续切换宽松/严格模式或 AST rewrite 开关只影响新 cell,历史 node 始终按自身记录的模式重放。
73
+
74
+ ## Capability Call Transcript
75
+
76
+ 每个 native `tools.*` 或 cell program binding call 保存:
77
+
78
+ ```ts
79
+ {
80
+ global: string,
81
+ member: string,
82
+ args: PtcValueGraphV1,
83
+ ok: boolean,
84
+ value?: PtcValueGraphV1,
85
+ error?: string,
86
+ settle: number
87
+ }
88
+ ```
89
+
90
+ `settle` 必须是从 0 开始的连续序列。重放按源码产生调用,但不重新 dispatch;它校验 global、member、args 和提交数量,再按 recorded settlement order 释放 recorded value/error。这个 `recorded-value` 承诺重建的是 REPL 计算状态,不表示外部 effect 被重做、撤销或验证。completeness、effect、authority 和 source evidence 属于 capability metadata,不伪装成 journal 字段。
91
+
92
+ journal 的 capability-call `args`/`value` 与 completion value 保存 `ptc-value-graph/v1` canonical envelope,不保存 decoded rich JS value,也不依赖递归 `JSON.stringify` 或 `structuredClone`。因此深层数组/对象、own `__proto__`、`undefined`、special number、BigInt、hole、shared identity 和 cycle 不会被外层 session JSON 改写。完整支持域和预算见 [PTC Value Graph V1](value-wire.md)。
93
+
94
+ ## 两阶段确认
95
+
96
+ 当前 DSH 公共扩展流水线是:
97
+
98
+ ```text
99
+ pre-execute -> tools/execute -> post-execute
100
+ -> content finalization -> tools/result
101
+ -> persistent tool/result event
102
+ ```
103
+
104
+ 插件采用:
105
+
106
+ 1. `tools/execute` 保存 execution 对象与 session context;
107
+ 2. `CodeRuntime.run` 真正进入 kernel 时创建 tentative journal;
108
+ 3. 成功结果由 `presentationMeta` 投影 journal,失败结果由 around hook 附加;
109
+ 4. `tools/result` 规范化最终 `meta.dshPtcPlus` 与 runtime tentative journal;
110
+ 5. 两者语义完全相同时确认 tentative 状态;
111
+ 6. cell 已执行但 metadata 缺失、损坏或被替换时,live kernel 单调降为 volatile;
112
+ 7. 从未进入 runtime 时,把 call id 加入 pending no-op;
113
+ 8. 下一个成功 journal 通过 `confirms` 持久确认 pending no-op。
114
+
115
+ 真值表:
116
+
117
+ | 观察 | Live 行为 | 冷恢复行为 |
118
+ | --- | --- | --- |
119
+ | valid journal | 按 status 提交 | 按 journal 折叠 |
120
+ | 已执行,最终 journal 被删除 | cell 与后缀 volatile | unknown boundary |
121
+ | 未进入 runtime | pending no-op | 后续 `confirms` 存在时忽略 |
122
+ | 无法判断且未被确认 | 保守处理 | 回到此前 durable frontier |
123
+
124
+ 比较先严格规范化 journal,再使用 PTC value graph 的扁平 wire 表示,不对深层参数做递归遍历。额外无关 metadata 不影响确认,但 `dshPtcPlus` 自身任何可观察差异都拒绝确认。
125
+
126
+ `tools/result` 不修改结果。若 pending no-op 尚未被后续 journal 确认进程就退出,冷恢复保守形成 unknown boundary。
127
+
128
+ ## Nested run_code
129
+
130
+ 模型直接发起的 top-level `run_code` 与真实注册的 `edit_run_code` 派生执行都创建本 schema 的 cell journal。前者随 `run_code` result 持久化;后者随外层 edit result 的 derived metadata 持久化,并与模型原始 edit call 关联。隔离 child binding 不创建 child PTC journal,也不产生可合并到父 heap 的 binding。
131
+
132
+ 父 cell 把隔离 child 当成普通 program binding call,记录 graph-encoded arguments、canonical result/error 和 settlement order。正常结算后,cold replay 返回 recorded child result,不重新执行 child 源码。若取消、超时或 worker failure 发生在调用结算前,`discarded` journal 以 `code.run` 保留 possible-effect boundary。这个结果来自所有 program binding 共用的 pending/settled 生命周期,不是 `code.run` 名称特例。它不注册第二个模型工具,也不伪造 UI、policy hook、调用树或事件。
133
+
134
+ ## 日志折叠
135
+
136
+ 恢复按 session event 顺序处理外层 `run_code` 和带明确 derived-run metadata 的 `edit_run_code`。恢复入口先把当前 call id 解析为持久化 `tool/call.seq`,再把该序号作为 live boundary 传入折叠器;对应的在途 call event 不属于历史。每个 `edit_run_code` 在其 call event 处,根据此前已经结算的调用确定并固定可编辑目标;之后出现的 result 只影响随后发起的 edit:
137
+
138
+ 1. 用 `sourceEventSeqs[0]` 关联 `tool/result` 与 `tool/call`;
139
+ 2. 预收集 valid journal 中的 `confirms`;
140
+ 3. 排除与 live boundary 序号相同的在途 call,并让已确认 no-op 的无 journal call 不改变状态;
141
+ 4. 缺失源码、缺失/损坏 journal 进入 untrusted suffix;
142
+ 5. `noop` 与不含 `volatileReason` 的 `discarded` 不改变语言状态;带 `volatileReason` 的 `discarded` 表示 heap 已回滚但外部 effect 未知,进入 untrusted suffix;
143
+ 6. `volatile` 进入 untrusted suffix,只应用可独立持久的 delete/restore 操作;
144
+ 7. `restore` 命名状态重新建立 trusted durable head;
145
+ 8. untrusted suffix 后的首个 `durable` journal从当前 durable head 建立新分支,并清除旧 suffix;
146
+ 9. durable node 保存 parent link,命名状态保存 node index;
147
+ 10. `meta.dshPtcPlusRecoveryBoundaries` 在 `tool/result` event sequence 参与排序前完成规范化;任一损坏 boundary 使恢复失败,合法 boundary 再通过失败 call seq 选择 node,并要求记录的 frontier 恰好是该 node 的 parent;折叠器剪除该 node 及依赖后代,按剩余记录重算 checkpoints,再把 head 设为记录的 frontier。
148
+
149
+ 第 8 步是必要不变量:冷恢复已经实际丢弃 volatile/unknown heap,因此此后执行成功的 durable cell 不依赖该 heap。如果不把它作为可信重基点,旧 unknown 调用会在每次重启时永久吞掉所有后续状态。
150
+
151
+ ## Completion 校验
152
+
153
+ 普通异常可能已经建立或修改 binding,所以 durable `throw` 属于语言历史。重放接受的语义结果只有:
154
+
155
+ - recorded `return` 再次 return;
156
+ - recorded `throw` 再次产生相同 error kind 和 message;
157
+
158
+ 以下结果永远是恢复基础设施失败,不能因为 recorded completion 也是 error 而接受:
159
+
160
+ - abort;
161
+ - compute/wall timeout;
162
+ - output limit;
163
+ - worker exit/OOM;
164
+ - recovery divergence;
165
+ - durable replay 触发 volatile capability。
166
+
167
+ 基础设施失败会终止当前 worker,并把 log-only recovery boundary 放入当前已结算 `tool/result` 的私有 metadata。kernel 从失败 node 的 parent 重新重放;若该 frontier 仍失败则继续向 parent 收缩,直到某个 frontier 验证成功或到达空 REPL。触发恢复的当前 `run_code` 在验证成功前不执行,因此可以在同一次请求中安全继续;如果 boundary 无效或历史本身无法折叠,则返回 recovery error。结构损坏的日志没有可证明的 frontier,后续调用会重新读取日志而不会接受一个无法持久表达 ancestry 的新分支。已经写入旧版自定义 boundary event 的日志必须先通过显式迁移移除该事件并把 boundary 合并到后续 `tool/result` metadata,迁移失败时不得覆盖原始日志。
168
+
169
+ ## State Operations
170
+
171
+ ```ts
172
+ type StateOperation =
173
+ | { action: "save", name: string }
174
+ | { action: "restore", name?: string }
175
+ | { action: "delete", name: string }
176
+ ```
177
+
178
+ - `save` 只可提交到 durable node;
179
+ - cell 静态判断 durable、但在 `save` 后运行时降级时,tentative save 会被删除;
180
+ - `delete` 可独立应用于命名索引;
181
+ - 无名称 `restore` 选择当前 cell 之前的最后 durable head,清除 volatile suffix;
182
+ - `restore` 把 head 切回已存在的 durable node,清除 volatile suffix;
183
+ - list 不写 operation,返回当前 checkpoint 名称、`mode` 和首次 `volatileReason`。
184
+
185
+ 状态名称由 agent 选择,内部 node index、hash、revision 和日志位置不进入模型接口。
186
+
187
+ ## Capability 规则
188
+
189
+ 静态分类器理解 top-level、block、function、catch 和 loop binding。它只把实际未绑定的 ambient reference 作为降级候选;属性键和局部同名变量无影响。
190
+
191
+ 运行时对以下访问标记 volatile。归因依据是 worker 当前 active execution,不使用异步回调继承的 `AsyncLocalStorage` store。active execution 从开始求值持续到 capability calls 结算、返回值 PTC value graph 编码或异常归一化全部完成;这些阶段的 getter、Proxy 或字符串转换仍可能执行用户代码。最终 durability 必须在转换后采样,纯 wire message 构造完成后才在最外层 `finally` 清除 active execution。只有此后发生的访问才暂存 reason 并使下一 cell volatile:
192
+
193
+ - Date、performance、fetch、WebSocket、crypto、Intl;
194
+ - setTimeout、setInterval、setImmediate;
195
+ - eval、Function、除捕获的 stdout/stderr 与 session-backed `cwd()` 外的 process 能力、require;
196
+ - `Math.random()`;
197
+ - durable allowlist 之外的 dynamic import,包括 `node:path`。
198
+
199
+ 普通 `Math` intrinsic 保持完整。`process.stdout/stderr.write` 被捕获为 cell log,不因输出本身降级。
200
+
201
+ worker 不继承 Electron 的工作目录语义。插件通过现有 `tools/execute` context 读取不可变的 `agent.session.header.cwd` 并注入 session worker;`process.cwd()` 返回该值且保持 durable。header 未记录 cwd 时才回退宿主值并在运行时标记 volatile。
202
+ 同一 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 解析,绝对路径与显式路径选项保持原生语义。
203
+
204
+ 直接访问 `worker_threads` 或 `cluster` 的常见 import/require 形式会被拒绝。直接 `process`、`require`、dynamic import 与 static import 取得的 `process.exit/abort/kill/chdir` 共享同一组拒绝函数,因为这些操作暴露或破坏 worker lifecycle control。该 gate 只维护 REPL 生命周期,不是恶意
205
+ 代码安全沙箱;native tool 的安全依赖 DSH policy,ambient Node 的安全依赖进程隔离和操作系统权限。
206
+
207
+ ## 恢复通知
208
+
209
+ 构造 kernel 时若折叠结果含 volatile/unknown suffix,或本次重放产生 recovery boundary,第一次实际执行的 `run_code` 记录并投影 `PTC-R002`。它只统计当前 call 之前实际跳过的历史 cell:
210
+
211
+ ```text
212
+ warning[PTC-R002]: restored the durable head and skipped N unreconstructable historical cells
213
+ phase: recover
214
+ state: ...
215
+ help: ...
216
+ ```
217
+
218
+ 通知进入正常 CodeRuntime logs,结构化值进入当前 journal,因此成功结果和错误结果都能呈现并从 session log 重建。每个 kernel 只发送一次,避免污染后续上下文。
219
+
220
+ live kernel 首次进入 volatile 只更新 journal 的 `status` / `volatileReason` 和 `repl.state(list)`,不向模型投影 warning/note。该转换不要求当前任务采取行动;只有 cold recovery 已实际跳过 volatile、unknown 或 replay-abandoned 历史时才发送 `PTC-R002`。worker 在首次观察到直接 Node/OS 边界时立即通知主线程,因此后续 hard abort、timeout 或 worker exit 仍能把原因写入 discarded journal。
221
+
222
+ ## 失败状态语义
223
+
224
+ 诊断的 `stateEffect` 描述当前 cell 的 live/冷恢复状态事实,不由 severity 推断,也不表示当前 binding 是否仍可用:
225
+
226
+ - parse、无法安全放宽的跨 cell collision 在执行前失败,使用 `unchanged`,journal 为 `noop`;
227
+ - 求值开始后的普通 throw 使用 `partially-applied`,因为此前 binding mutation 可能已生效;
228
+ - PTC Value V1 输出超出支持域或预算时使用 `PTC-O001` 与 `partially-applied`,因为返回前的 binding/mutation 已经执行;`undefined`、special number、BigInt、hole、shared identity 和 cycle 属于受支持值,不要求模型为了 transport 改写;
229
+ - 冷恢复丢弃不可信后缀使用 `rolled-back`;
230
+ - 只有已知外部 dispatch 发生但 completion 无法确定时才使用 `unknown` 并附 `dispatchState: "unknown"`,插件不得在宿主未提供该事实时猜测。
231
+
232
+ 模型可见文本与 `diagnostics` 必须由同一个结构确定性生成。恢复不通过解析既有 message 重建诊断;session export/import 和 replay 均保留结构化 code、cause、dispatch state 与 state effect。源码 frame 使用 `@babel/code-frame` 的无色投影。
233
+
234
+ ## 插件边界
235
+
236
+ 协议只依赖:
237
+
238
+ - `tools/execute`;
239
+ - `tools/result`;
240
+ - `CodeRuntime.run`;
241
+ - 当前 `CodeRuntime.run` request 的 public bindings 与 signal;
242
+ - `run_code.output.presentationMeta`;
243
+ - 标准 `tool/call` 与 `tool/result.meta`。
244
+
245
+ 以上是当前已实现的 PTC journal 协议。native `tools.*` 保持 owner contract;cell program binding 不得变成新的模型 transport。
246
+
247
+ 任何公共扩展面无法持久确认的状态都必须收缩为 volatile/unknown 边界。修改或 fork DSH、patch 私有
248
+ scheduler、复制 policy/event protocol 或伪造事件不属于本协议。若上游以后提供 owner-declared
249
+ typed binding,它可以使用 DSH nested dispatch,但不得伪装成 journal confirmation,也不得绕过
250
+ scope、policy 或 settlement。
251
+
252
+ ## 已验证场景
253
+
254
+ - durable continuation 和仅凭持久 session-log JSON 的 cold recovery;
255
+ - tool call record/replay 与并发 settlement order;
256
+ - volatile live continuation、冷恢复跳过和 durable 重基;
257
+ - post-execute metadata 删除与 pre-dispatch no-op 确认;
258
+ - hard abort、冷 worker 启动取消和未结算 program binding 的 possible-effect boundary;
259
+ - replay timeout fail closed;
260
+ - Math intrinsic、局部 ambient 名称、CWD 相关 `node:path`;
261
+ - 命名状态 save/restore/delete 与 volatile restore;
262
+ - 深层 graph value、own `__proto__` key、`undefined`、special number、BigInt、hole、alias 与 cycle;
263
+ - 宽松变量重声明与严格/混合名称 collision preflight;
264
+ - runtime throw、invalid output 和真实 recovery 的结构化诊断与模型可见投影;普通 volatile transition 保持安静;
265
+ - 未修改 DSH 的真实公共扩展面,并在全新 session runtime 中从日志恢复。
@@ -0,0 +1,62 @@
1
+ # Evaluation
2
+
3
+ ## Host prerequisites
4
+
5
+ The model-backed runners support native Windows paths and WSL repositories mounted as `/mnt/<drive>/...`. They invoke the Windows DSH installation through `pwsh.exe`; the Windows process environment must resolve `dsh --version` and a drive-qualified `DSH_HOME` or user-profile `.dsh` directory. Native Linux and macOS DSH execution are not supported by these runners.
6
+
7
+ Both entrypoints validate path conversion, PowerShell startup, the installed DSH command, and the Windows DSH home before creating artifacts, scratch directories, or installing the development plugin. A failure uses the `PTC-EVAL-PREREQ` diagnostic and leaves the evaluation workspace unchanged.
8
+
9
+ ## Stable ordinary-task A/B fixture
10
+
11
+ `npm run test:ab` uses a versioned, zero-dependency Node.js fixture as the primary ordinary-task workload. The fixture lives in `fixtures/ab-node-project-v1` and is copied independently into each arm workspace. Both arms receive byte-identical working trees, the same deterministic Git history, and the same deterministic uncommitted dirty state prepared by the runner.
12
+
13
+ The fixture manifest, `fixtures/ab-node-project-v1/benchmark-manifest.json`, owns:
14
+
15
+ - `fixtureName` and `fixtureVersion`;
16
+ - a content SHA-256 over the fixture tree (excluding the manifest itself);
17
+ - the Git identity, commit message, and date used when materializing each arm;
18
+ - the deterministic dirty-state entries applied after the initial commit.
19
+
20
+ The current series is `ab-node-project-v1` / `1.0.0` with content SHA-256 `807580baf64e367bb2dc047c389edf471e13077d54d50ee4b3a143c38f0cebd1`. Reports written by the A/B runner record the fixture path, version, and hash.
21
+
22
+ Self-hosting acceptance against the active PTC Plus checkout is a separate track: `npm run test:expensive` covers plugin-specific model workflows and integration edges. Comparable ordinary-task reports are grouped only by the fixture version and content hash.
23
+
24
+ ## Protocol
25
+
26
+ The paired run uses the latest installed DSH release, a configured model, PTC mode's code-only direct-tool projection, and `danger-full-access`. The A/B task set contains ordinary project tasks plus a cheap machine-checkable canary that directly invokes `run_code` once and exercises the transport first. After the canary pair passes, the remaining pairs run with the configured concurrency.
27
+
28
+ Resolved configurations are identical except for `ptc-plus.disabled`. Agent instructions, skills, auxiliary title generation, and local identity extensions are disabled. The model route, permission mode, neutral persona, injected initial context, fixture bytes, and resolved tool surface stay paired within each run.
29
+
30
+ The generated overlay explicitly owns and the preflight validates `tools.mode`, `sandbox-policy.mode`, and `approval.policy`. The runners do not rely on child-process environment variables to project these policies across the WSL-to-Windows execution boundary.
31
+
32
+ Each arm executes in an independently named, opaque scratch tree outside the evaluator artifact tree. Treatment names are confined to evaluator-owned records. Blind packets replace the session workspace in descriptions, source, results, and final answers, including Windows, forward-slash, and JSON-escaped spellings, before the arm map is written.
33
+
34
+ Machine budgets are per session and are enforced as failures. Machine evidence comes from exact tool-result JSON, workspace postconditions, or runner-owned subprocess results. Free-form final answers are never interpreted with prose or negation matching; their correctness remains `blind-pending` until the blind packet is reviewed. The transport canary separately requires the observed `run_code` result to equal the fixture package name, so workspace state alone cannot admit the paid matrix.
35
+
36
+ For `test-gate`, a successfully completed runner-owned subprocess is valid evidence whether the tested project exits zero or nonzero. The observed exit code is recorded, while blind semantic review decides whether the answer reports it correctly. Failure to start or complete the oracle remains a runner failure.
37
+
38
+ Acceptance metrics follow durable session-log semantics. `modelRequests` counts `step/start` events, each representing one logical model loop step. `headerEpochs` counts `request/header` events, while `headerChanges` counts the subset whose reason is `change`; initial and equal resume epochs remain distinct epochs without being changes. `historyReplacements` counts events whose `surfaceOp.op` is `replace`. Provider retries can issue multiple adapter attempts within one logical step, and the current durable log does not expose a complete physical-attempt count, so no report field infers one from headers, assistant messages, or usage.
39
+
40
+ Stable-header acceptance canonicalizes empty fields by DSH rules and compares the exact system value and ordered complete tool-schema JSON across every header epoch. Any unapproved change fails with the first differing field. `headerPolicy.allowedTransitions` names the exact epoch and route, configuration, or capability condition; `headerPolicy.historyReplacements` declares an exact replacement count and never permits header drift. Hashes remain diagnostic report data rather than the correctness test.
41
+
42
+ ## Reporting
43
+
44
+ Both runners require an explicit model route and credential-variable name before any host probing or artifact creation. The referenced credential variable must also contain a value:
45
+
46
+ ```sh
47
+ DSH_PTC_ACCEPTANCE_PROVIDER=<provider> \
48
+ DSH_PTC_ACCEPTANCE_MODEL=<model> \
49
+ DSH_PTC_ACCEPTANCE_API_KEY_ENV=PROVIDER_API_KEY \
50
+ PROVIDER_API_KEY=<credential> \
51
+ npm run test:expensive
52
+
53
+ DSH_PTC_AB_PROVIDER=<provider> \
54
+ DSH_PTC_AB_MODEL=<model> \
55
+ DSH_PTC_AB_API_KEY_ENV=PROVIDER_API_KEY \
56
+ PROVIDER_API_KEY=<credential> \
57
+ npm run test:ab
58
+ ```
59
+
60
+ `npm run test:ab` writes `report.json`, `report.md`, per-session trajectory artifacts, and blind-review packets under `artifacts/ab-trajectories/`. Both commands invoke the configured model and consume quota.
61
+
62
+ Token traffic is the sum of input, cache-read, cache-write, and output tokens. Model behavior is stochastic, so comparable new results establish a reproducible observation rather than a universal performance claim.
@@ -0,0 +1,82 @@
1
+ # Installation
2
+
3
+ PTC Plus requires Node.js `^22.19.0 || >=24.0.0` and the latest available DSH release with TypeScript PTC mode. Compatibility follows DSH's public extension surfaces rather than a version allowlist.
4
+
5
+ Install the plugin into the profile that actually runs the target DSH surface. Do not assume a profile named `default` is active.
6
+
7
+ ## Compatibility and Authority
8
+
9
+ | Component | Current contract |
10
+ | --- | --- |
11
+ | DeepSeek Harness | Latest available release; validate the live public extension surfaces after every upstream release |
12
+ | Runtime | DSH TypeScript PTC mode; cells currently accept modern JavaScript syntax |
13
+ | Node.js | `^22.19.0 || >=24.0.0` |
14
+ | Platforms | Windows CLI/Desktop and Linux CLI/Web verified locally; macOS package tests and import smoke run in CI, while live DSH integration and Desktop require release-time smoke testing |
15
+ | Recommended permission | `danger-full-access` |
16
+
17
+ `danger-full-access` is the primary supported experience. The worker isolates the REPL lifecycle; it is not a malicious-code sandbox. DSH continues to own native-tool scope, policy, approval, cancellation, sandboxing, and scheduling. Narrower profiles expose only their available capabilities; PTC Plus does not simulate missing authority or add another permission system.
18
+
19
+ The optional `cordisToolsEnabled` integration requires the current DSH installation to provide its shipped `cordis` preset plus the public preset, Skill, Cordis, settings, and tool-runtime packages. PTC Plus declares those host-owned DSH packages as required peers instead of installing private runtime copies; DSH's profile module fallback must resolve them from the active installation. Do not copy `SKILL.md` or add the Cordis preset's Skill directory to global roots. If the host surface is incomplete, plugin activation or enabling the setting fails instead of loading a second DSH core.
20
+
21
+ ## npm Release
22
+
23
+ Use this form after the selected version is available from the npm registry:
24
+
25
+ ```sh
26
+ dsh plugin --profile <profile> add dsh-ptc-plus@0.2.1
27
+ dsh --profile <profile> --dump-config
28
+ ```
29
+
30
+ Until then, use a pinned Git revision, source checkout, or tarball.
31
+
32
+ ## Pinned Git Revision
33
+
34
+ The repository ships runnable JavaScript and does not require a build step:
35
+
36
+ ```sh
37
+ dsh plugin --profile <profile> add github:muyuanjin/dsh-ptc-plus#COMMIT_SHA
38
+ dsh --profile <profile> --dump-config
39
+ ```
40
+
41
+ Replace `COMMIT_SHA` with a reviewed commit.
42
+
43
+ ## Source Checkout
44
+
45
+ ```sh
46
+ git clone https://github.com/muyuanjin/dsh-ptc-plus.git
47
+ cd dsh-ptc-plus
48
+ dsh plugin --profile <profile> add .
49
+ dsh --profile <profile> --dump-config
50
+ ```
51
+
52
+ When DSH itself runs from a source checkout, use its launcher:
53
+
54
+ ```sh
55
+ pnpm dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus
56
+ pnpm dsh --profile <profile> --dump-config
57
+ ```
58
+
59
+ ## Tarball
60
+
61
+ ```sh
62
+ npm pack
63
+ dsh plugin --profile <profile> add /absolute/path/to/dsh-ptc-plus-0.2.1.tgz
64
+ dsh --profile <profile> --dump-config
65
+ ```
66
+
67
+ Windows development checkouts can create and install an immutable content-addressed snapshot. The profile defaults to `web` when omitted:
68
+
69
+ ```bat
70
+ scripts\install-dev.cmd <profile>
71
+ ```
72
+
73
+ ## DSH Desktop
74
+
75
+ On Windows or macOS, choose **Open DSH Terminal** from the Desktop tray. Bare commands in that terminal target the active profile:
76
+
77
+ ```sh
78
+ dsh plugin add github:muyuanjin/dsh-ptc-plus#main
79
+ dsh --dump-config
80
+ ```
81
+
82
+ After an npm release, the package spec may instead be `dsh-ptc-plus@0.2.1`. 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.
@@ -0,0 +1,61 @@
1
+ # Program Data Plane
2
+
3
+ 程序取得数据、模型看到结果和 cold replay 重建值是三个不同问题。PTC Plus 保留这个区别,不用一个“安全工具”名单代替契约。
4
+
5
+ ## Typed tools
6
+
7
+ 浏览、搜索和 DSH 服务调用使用当前 scope 的 native `tools.*`。canonical result 直接进入 cell,不经过 PTC 参数翻译或结果裁剪。对完全省略参数的 native call,runtime 只在 DSH 的 live object schema 验证 `{}` 合法时把 omission 规范为 `{}`;该值随后统一进入 encoding、dispatch、journal 和 replay comparison。`tools.cordis_inspect_list()` 因而等价于显式传入 `{}`,但显式 `undefined`、需要输入的工具、`capabilities.*` 和 owner-provided namespace 不受影响。具体结果可能 complete、bounded、incremental、open-world 或 unknown;模型/UI rendering 的裁剪不能反推 program value 的完整性。
8
+
9
+ PTC 模式的 code-only direct-tool projection 只声明模型直接调用真实注册的 `run_code` 与 `edit_run_code`。模型误发的顶层 native call 不属于这个合法 direct protocol;当 live schema 能唯一证明该工具时,transport normalization 将它包装为 `run_code`,cell 再调用同一 `tools.*` member。canonicalizer 先解析并校验一次原始 JSON:普通参数原样嵌入 JavaScript,含 own `__proto__` 的值改用安全字面量;因此派生 cell 不需要 `JSON.parse`,同时保持参数执行语义。规范化不向模型暴露 provenance、纠错提示或额外标记。参数仍由 DSH owner contract 正式验证,未知、畸形或不一致调用原样交给宿主诊断。该历史规范化只适用于无效的 out-of-surface call,不适用于已经合法声明的 `edit_run_code`。
10
+
11
+ `edit_run_code` 是宿主 composite tool:外层 result 向模型返回精简的编辑状态、值与日志,完整物化源码和 journal 只进入标记为 derived 的私有 metadata。cold replay 消费该记录,不把完整源码重新投影给模型,也不把派生 `run_code` 归因给 assistant。
12
+
13
+ 每次调用进入 journal:
14
+
15
+ ```text
16
+ DSH authority/policy -> native dispatch -> canonical result
17
+ |-> current cell
18
+ `-> args/result + settlement transcript
19
+ ```
20
+
21
+ cold replay 校验相同的调用序列并返回 recorded value,不重新执行工具。这保证 REPL 计算状态可重建,但不声称外部 effect 可逆或仍与历史相同。
22
+
23
+ 同一 settlement 规则适用于当前 request 中的全部 program binding,包括 owner-provided namespace 与 `code.run`。已结算的 value/error 可进入 durable transcript;若取消、超时或 worker failure 发生时调用仍未结算,cell heap 回滚,discarded journal 以实际 `global.member` 保留 possible-effect boundary。分类来自可观察生命周期,不来自 capability 名称表。
24
+
25
+ 直接 Node/OS 输入没有 binding transcript。worker 首次观察到这类 volatile 边界时立即通知主线程;即使 cell 随后 hard abort、timeout 或 worker exit,discarded journal 仍保留已观察原因。该恢复事实不作为普通成功任务的模型可见警告。
26
+
27
+ ## Native Node and processes
28
+
29
+ 在 `danger-full-access` 下,模型可以使用熟悉的 Node、filesystem、process、network、child-process 和
30
+ 生态 SDK。直接 ambient access 受 worker 进程与操作系统的实际约束,不经过 DSH tool policy;
31
+ PTC Plus 不另造 filesystem Consumer、跨平台权限系统或命令 DSL。更窄 profile 中缺少某个 native
32
+ tool,也不能被解释为相应 Node API 已被隔离。
33
+
34
+ 记录了 session cwd 时,child process 在未提供 `options.cwd` 时继承该目录;显式 `cwd` 保持调用方选择。
35
+ worker 继承宿主的 Node、package manager 和 shell 所需环境变量,只把 `TEMP`、`TMP` 和 `TMPDIR`
36
+ 覆盖为本 session 的 scratch 目录。`node:fs`、`node:fs/promises` 及 glob 的相对入口同样以 session cwd
37
+ 为基准,绝对路径仍按原生语义处理。
38
+
39
+ 完整文件读取示例:
40
+
41
+ ```ts
42
+ import { readFile } from "node:fs/promises"
43
+
44
+ const source = await readFile("README.md", "utf8")
45
+ return source.length
46
+ ```
47
+
48
+ 这类直接能力不经过 tool transcript,因此当前 cell 与后续 live suffix 进入 sticky volatile;当前进程继续使用既有 binding,cold recovery 回到最近 durable frontier。其他 profile 不具备入口时应明确失败,不能拼接 bounded `tools.read` 窗口伪造无损文本。
49
+
50
+ 普通已知程序优先 argv spawn;只有命令本身需要 shell 语义时才使用 shell。shell 不是权限系统,也不是 REPL 前置条件;PTY/ConPTY 只用于交互进程。Windows、WSL 与 POSIX 环境的 executable、路径、resolver、signal 和 TTY 必须分别探查,不能由一个 execution world 推断另一个。
51
+
52
+ ## Metadata boundaries
53
+
54
+ 能力 explorer 分别报告:
55
+
56
+ - `completeness`:结果能证明多少;
57
+ - `effect` 与 `authority`:owner 声明的外部状态与治理边界;
58
+ - `replay`:recorded value、owner replay、volatile 或 unknown;
59
+ - schema/source/revision:存在证据时用于解释定义。
60
+
61
+ 当前 live tool schema 只足以机械取得 name、description、parameters 和 output。PTC Plus 可证明自己的 `recorded-value` 行为与 DSH tool dispatch authority;其余字段保持 unknown,不通过自然语言摘要升级为事实。
@@ -0,0 +1,37 @@
1
+ # Publishing
2
+
3
+ 本仓库是普通 DSH bundle:`package.json` 声明 `dsh.bundle.patch`、入口、依赖、Node 范围和发布白名单,`cordis.patch.yml` 只插入 `dsh-ptc-plus`。插件自带的运行时依赖由 npm 包声明;必须与宿主共享实例的 DSH service 包声明为 peer dependencies,并由宿主按 `inject` 装配。
4
+
5
+ ## 命名约定
6
+
7
+ 项目在不同集成层使用不同名称:
8
+
9
+ | 名称 | 用途 |
10
+ | --- | --- |
11
+ | `dsh-ptc-plus` | 仓库和 npm 包名;浏览器 bundle 的 module name 也从 `package.json` 取得它。 |
12
+ | `ptc-plus` | DSH 插件 ID、运行时 loader name 和 settings namespace。 |
13
+ | `PTC Plus` | 面向用户的产品名。 |
14
+
15
+ 在 `cordis.patch.yml` 和解析后的 DSH 配置中,`id: ptc-plus` 标识插件配置项,`name: dsh-ptc-plus` 标识待加载的包。内部错误前缀、runtime context 名称和 worker 临时路径会按所属子系统使用 `ptc-plus` 或 `dsh-ptc-plus`;它们不是另外的公开产品名。
16
+
17
+ ## Release checklist
18
+
19
+ 1. 在 Node `22.19.0` 与 Node 24.x 上运行 `npm ci`、`npm run build:check` 和 `npm run check`;Node 24.x 还应覆盖 Windows、Linux 与 macOS。仓库 CI 定义同一矩阵。
20
+ 2. 运行 `npm audit`、`npx publint`、`git diff --check`、Markdown local-link check 和 `npm pack --silent --dry-run --json`。确认 tarball 只包含发布白名单内的运行时代码、文档和展示资产,不含凭据、本地路径、开发脚本或测试夹具,并从 tarball 安装到空目录执行 ESM import smoke。机器读取 `npm pack --json` 时保持 `--silent`,避免 lifecycle 输出混入 JSON stream。
21
+ 3. 更新到最新可用 DSH release 并记录 `dsh --version`。分别从 npm 包、固定 Git commit、源码 checkout 和 tarball 安装到临时 profile,再执行 `dsh --profile <profile> --dump-config`,确认只新增 `ptc-plus` row。每个上游 release 都重新验证公共扩展面、CLI/Web 集成和 profile 装配,不设置版本白名单。
22
+ 4. 在 Windows 与 Linux CLI/Web 实机启动临时 profile。macOS 由原生 runner 验证 CLI/Web;Windows 与 macOS Desktop 从托盘打开当前 profile 的 DSH Terminal 安装,重启 Desktop 后做一次 PTC 模式 smoke。Desktop 当前发布平台不包括 Linux。
23
+ 5. 只有在明确授权模型消耗后才运行 `npm run test:expensive` 与 `npm run test:ab`。发布结论以结构化测试结果为准,不把一次模型样本推广为普遍保证。
24
+ 6. 确认 npm 包名可用、待发布版本高于 registry 版本、README 截图来自真实 DSH 会话,并核对 repository、homepage、bugs、license 与 `dsh-plugin` topic。
25
+ 7. 最后检查 `npm pack --silent --json` 的名称、版本、大小与文件列表,再创建并推送 release commit/tag,随后发布同一 commit 产生的 tarball。
26
+
27
+ 远程仓库、registry、签名凭据、目标平台运行状态和模型额度都属于发布时事实,不能由本地 manifest 推断。
28
+
29
+ ## Permission disclosure
30
+
31
+ PTC Plus 自身不配置外部 endpoint,也不读取 API key。它让模型代码使用当前 request 的 DSH native tools,并在 worker 进程和操作系统实际允许时直接使用 Node.js filesystem、process、network 与 child process API。DSH 负责 native-tool policy;ambient Node/OS access 不经过该 policy。worker thread 是生命周期隔离,不是恶意代码安全沙箱。
32
+
33
+ DSH 当前公共 bundle surface 未定义供插件使用的机器可读 permission-disclosure contract,因此这些权限边界记录在人类可读文档中。
34
+
35
+ ## Client UI
36
+
37
+ 发布包包含设置卡片和会话头部启用标识的 Client UI。发布截图必须来自 DSH Web 或 Desktop 中真实的 PTC 模式 surface;实现与构建边界见 [Client UI](client-ui.md)。