dsh-ptc-plus 0.3.1 → 0.4.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 (200) hide show
  1. package/README.en.md +66 -73
  2. package/README.md +66 -73
  3. package/assets/ptc-plus-bindings-en.png +0 -0
  4. package/assets/ptc-plus-bindings-zh.png +0 -0
  5. package/assets/ptc-plus-repair-en.png +0 -0
  6. package/assets/ptc-plus-repair-zh.png +0 -0
  7. package/assets/ptc-plus-repl-workspace-en.png +0 -0
  8. package/assets/ptc-plus-repl-workspace-zh.png +0 -0
  9. package/assets/ptc-plus-settings-en.png +0 -0
  10. package/assets/ptc-plus-settings-zh.png +0 -0
  11. package/client.js +30610 -722
  12. package/compiler-amaro.cjs +5 -0
  13. package/compiler-amaro.cjs.map +6 -0
  14. package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
  15. package/compiler-assets.json +3 -0
  16. package/compiler-core.cjs +554 -0
  17. package/compiler-core.cjs.map +6 -0
  18. package/compiler-platform.cjs +2 -0
  19. package/compiler-platform.cjs.map +6 -0
  20. package/compiler-typescript.cjs +388 -0
  21. package/compiler-typescript.cjs.map +6 -0
  22. package/docs/README.md +6 -0
  23. package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
  24. package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +6 -4
  25. package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
  26. package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
  27. package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
  28. package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
  29. package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
  30. package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +9 -3
  31. package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
  32. package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
  33. package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
  34. package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
  35. package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
  36. package/docs/adr/0019-plugin-settings-and-kill-switch.md +8 -6
  37. package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
  38. package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
  39. package/docs/adr/0023-global-user-bindings.md +104 -0
  40. package/docs/adr/0024-repl-console-observation.md +33 -0
  41. package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
  42. package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
  43. package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
  44. package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
  45. package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
  46. package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
  47. package/docs/architecture.md +82 -22
  48. package/docs/binding-review-placement-design.md +200 -0
  49. package/docs/capability-projection.md +1 -1
  50. package/docs/client-ui.md +96 -20
  51. package/docs/durability-design.md +61 -24
  52. package/docs/evaluation.md +78 -6
  53. package/docs/installation.md +41 -6
  54. package/docs/program-data-plane.md +6 -0
  55. package/docs/publishing.md +5 -3
  56. package/docs/runtime-reference.md +79 -25
  57. package/docs/semantic-validation.md +29 -0
  58. package/docs/stateful-binding-updates-proposal.md +755 -0
  59. package/docs/user-bindings.en.md +64 -0
  60. package/docs/user-bindings.md +64 -0
  61. package/docs/value-wire.md +3 -3
  62. package/docs/verification.md +57 -0
  63. package/index.js +374 -92
  64. package/internal/ast-traversal.js +5 -2
  65. package/internal/binding-descriptors.js +1 -0
  66. package/internal/binding-pattern-traversal.js +25 -0
  67. package/internal/binding-pattern.js +26 -29
  68. package/internal/binding-source-analysis.js +391 -0
  69. package/internal/binding-update-policy.js +66 -0
  70. package/internal/bounded-worker-round.js +56 -0
  71. package/internal/callable-definition-environment.js +42 -0
  72. package/internal/callable-source-catalog.js +129 -0
  73. package/internal/callable-source-encoding.js +47 -0
  74. package/internal/callable-source-facts.js +459 -0
  75. package/internal/callable-source-format.js +2 -0
  76. package/internal/callable-source-lowering.js +123 -0
  77. package/internal/cell-analysis-contract.js +25 -0
  78. package/internal/cell-analysis.js +252 -242
  79. package/internal/cell-completion.js +15 -0
  80. package/internal/cell-error.js +7 -0
  81. package/internal/cell-parser.js +66 -0
  82. package/internal/cell-rewriter.js +3 -553
  83. package/internal/commonjs-export-evidence.js +63 -0
  84. package/internal/commonjs-source-evidence.js +72 -0
  85. package/internal/compiler-amaro.cjs +2 -0
  86. package/internal/compiler-compression.js +12 -0
  87. package/internal/compiler-data.js +58 -0
  88. package/internal/compiler-descriptors.js +54 -0
  89. package/internal/compiler-dynamic-cache.js +48 -0
  90. package/internal/compiler-entry.js +35 -0
  91. package/internal/compiler-generated-names.js +17 -0
  92. package/internal/compiler-intrinsics.js +84 -0
  93. package/internal/compiler-module-links.js +34 -0
  94. package/internal/compiler-operations.js +61 -0
  95. package/internal/compiler-platform-factory.js +84 -0
  96. package/internal/compiler-platform.js +136 -0
  97. package/internal/compiler-record-roles.js +53 -0
  98. package/internal/compiler-region-output.js +259 -0
  99. package/internal/compiler-resource-lowering.js +103 -0
  100. package/internal/compiler-scope-facts.js +115 -0
  101. package/internal/compiler-service.js +109 -0
  102. package/internal/compiler-source-cache.js +32 -0
  103. package/internal/compiler-source-pieces.js +93 -0
  104. package/internal/compiler-source-regions.js +132 -0
  105. package/internal/compiler-storage-source.js +54 -0
  106. package/internal/compiler-text.js +15 -0
  107. package/internal/compiler-typescript.cjs +2 -0
  108. package/internal/config-spec.js +91 -25
  109. package/internal/cordis-tools-owner.js +130 -21
  110. package/internal/diagnostic.js +27 -1
  111. package/internal/direct-surface-owner.js +76 -23
  112. package/internal/dynamic-binding-evidence.js +16 -0
  113. package/internal/dynamic-environment-compiler.js +779 -0
  114. package/internal/dynamic-environment-integration.js +87 -0
  115. package/internal/dynamic-environment-runtime.js +362 -0
  116. package/internal/dynamic-native-calls.js +150 -0
  117. package/internal/dynamic-native-runtime.js +248 -0
  118. package/internal/dynamic-scope-analysis.js +90 -0
  119. package/internal/edit-transport-owner.js +28 -3
  120. package/internal/execution-seam-compat.js +193 -0
  121. package/internal/failure-reporting.js +118 -18
  122. package/internal/host-rpc.js +66 -0
  123. package/internal/isolated-worker.js +548 -0
  124. package/internal/kernel-child.js +139 -0
  125. package/internal/kernel-worker.js +911 -116
  126. package/internal/language-semantics.js +14 -0
  127. package/internal/legacy-binding-catalog.js +48 -0
  128. package/internal/legacy-cell-analysis.js +600 -0
  129. package/internal/legacy-cell-parser.js +50 -0
  130. package/internal/legacy-cell-rewriter.js +631 -0
  131. package/internal/legacy-repl-convenience.js +291 -0
  132. package/internal/managed-module-operations.js +170 -0
  133. package/internal/module-compilation.js +321 -0
  134. package/internal/module-runtime-platform.js +19 -0
  135. package/internal/module-transform-contract.js +12 -0
  136. package/internal/native-await-declarations.js +68 -0
  137. package/internal/native-root-compilation.js +54 -0
  138. package/internal/native-root-dynamic.js +67 -0
  139. package/internal/recovery-tips.js +27 -15
  140. package/internal/rejected-cell-editor.js +13 -8
  141. package/internal/repl-convenience.js +2 -201
  142. package/internal/repl-memory-projection.js +81 -12
  143. package/internal/repl-observation-interest.js +70 -0
  144. package/internal/repl-rewrite-contract.js +21 -0
  145. package/internal/repl-scope-normalizer.js +2091 -0
  146. package/internal/repl-value-observer.js +114 -0
  147. package/internal/rpc-contract.js +52 -0
  148. package/internal/runtime-bridge-owner.js +172 -28
  149. package/internal/runtime-config.js +48 -51
  150. package/internal/runtime-contexts.js +82 -10
  151. package/internal/runtime-messages.js +111 -0
  152. package/internal/session-cell-executor.js +455 -91
  153. package/internal/session-journal-recovery.js +684 -0
  154. package/internal/session-journal-schema.js +106 -0
  155. package/internal/session-journal.js +237 -440
  156. package/internal/session-log-view.js +42 -9
  157. package/internal/session-runtime.js +325 -80
  158. package/internal/session-state.js +249 -40
  159. package/internal/source-position-map.js +211 -71
  160. package/internal/stateful-module-compiler.js +154 -0
  161. package/internal/stateful-module-runtime.js +303 -0
  162. package/internal/stateful-root-compiler.js +598 -0
  163. package/internal/stateful-root-runtime.js +340 -0
  164. package/internal/typert-codec-compat.js +28 -0
  165. package/internal/typescript-transform.js +29 -0
  166. package/internal/user-binding-authoring.js +70 -0
  167. package/internal/user-binding-console-worker.js +184 -0
  168. package/internal/user-binding-console.js +148 -0
  169. package/internal/user-binding-draft-projection.js +335 -0
  170. package/internal/user-binding-model-context.js +31 -0
  171. package/internal/user-binding-runner.js +56 -0
  172. package/internal/user-bindings-owner.js +1065 -0
  173. package/internal/user-bindings-store.js +293 -0
  174. package/internal/user-bindings.js +353 -0
  175. package/internal/value-wire-schema.js +28 -0
  176. package/internal/value-wire.js +29 -20
  177. package/internal/worker-client.js +57 -67
  178. package/internal/worker-environment.js +45 -0
  179. package/internal/worker-output-relay.js +29 -0
  180. package/internal/worker-shutdown.js +14 -0
  181. package/package.json +81 -11
  182. package/src/client-activity.js +109 -45
  183. package/src/client-authoring-view.js +580 -0
  184. package/src/client-binding-review.js +230 -0
  185. package/src/client-bindings-data.js +29 -0
  186. package/src/client-catalog.js +127 -0
  187. package/src/client-code-editor.js +84 -0
  188. package/src/client-console.js +112 -0
  189. package/src/client-copy.js +370 -0
  190. package/src/client-feature-gates.js +62 -0
  191. package/src/client-host-compat.js +150 -0
  192. package/src/client-indicator-view.js +267 -0
  193. package/src/client-repl-view.js +292 -0
  194. package/src/client-rpc.js +20 -0
  195. package/src/client-settings-view.js +221 -0
  196. package/src/client-styles.js +106 -0
  197. package/src/client-tool-view.js +108 -0
  198. package/src/client-transport.js +159 -0
  199. package/src/client-workbench.js +521 -0
  200. package/src/client.js +233 -615
@@ -2,30 +2,72 @@ import { decodeValue, encodeValue, normalizeValueWire } from './value-wire.js'
2
2
  import { normalizeDiagnostic } from './diagnostic.js'
3
3
  import { assertOwnFields, isRecord } from './record-utils.js'
4
4
  import { sessionEvents } from './session-events.js'
5
-
6
- export const JOURNAL_KEY = 'dshPtcPlus'
7
- export const EDIT_TARGET_KEY = 'dshPtcPlusEdit'
8
- export const DERIVED_RUN_KEY = 'dshPtcPlusDerivedRun'
9
- export const REWRITES_KEY = 'dshPtcPlusRewrites'
10
- export const RECOVERY_BOUNDARY_KEY = 'dshPtcPlusRecoveryBoundaries'
11
- export const JOURNAL_VERSION = 3
12
- const LEGACY_JOURNAL_VERSION = 1
13
- const INTERMEDIATE_JOURNAL_VERSION = 2
14
- export const RECOVERY_BOUNDARY_EVENT = 'ptc-plus/recovery-boundary'
15
-
16
- const STATUSES = new Set(['durable', 'volatile', 'discarded', 'noop'])
17
- const BINDING_MODES = new Set(['loose', 'strict'])
18
- const JOURNAL_FIELDS = new Set(['version', 'bindingMode', 'rewritePolicy', 'status', 'calls', 'operations', 'confirms', 'diagnostics', 'completion', 'volatileReason'])
19
- const LEGACY_JOURNAL_FIELDS = new Set([...JOURNAL_FIELDS].filter(field => field !== 'rewritePolicy'))
20
- const REWRITE_POLICY_FIELDS = new Set(['autoRewriteImports', 'autoStripExports', 'autoSplitRedeclarations'])
21
- const CALL_SUCCESS_FIELDS = new Set(['global', 'member', 'args', 'ok', 'value', 'settle'])
22
- const CALL_ERROR_FIELDS = new Set(['global', 'member', 'args', 'ok', 'error', 'settle'])
23
- const OPERATION_FIELDS = new Set(['action', 'name'])
24
- const RETURN_FIELDS = new Set(['kind', 'hasValue', 'value'])
25
- const THROW_FIELDS = new Set(['kind', 'error'])
26
- const ERROR_FIELDS = new Set(['kind', 'message'])
27
- const EDIT_TARGET_FIELDS = new Set(['targetCallSeq'])
28
- const DERIVED_RUN_FIELDS = new Set(['code', 'description'])
5
+ import { LEGACY_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
6
+ import {
7
+ LEGACY_DEFAULT_EXPORT_BINDING,
8
+ LEGACY_IMPORT_EXPRESSION_BOUNDARY,
9
+ LIVE_MODULE_SEMANTICS,
10
+ } from './repl-rewrite-contract.js'
11
+ import { userBindingsSnapshotFromMeta } from './user-bindings.js'
12
+ import {
13
+ JOURNAL_KEY,
14
+ EDIT_TARGET_KEY,
15
+ DERIVED_RUN_KEY,
16
+ REWRITES_KEY,
17
+ RECOVERY_BOUNDARY_KEY,
18
+ JOURNAL_VERSION,
19
+ IMPORT_BOUNDARY_JOURNAL_VERSION,
20
+ PER_NAME_USER_BINDINGS_JOURNAL_VERSION,
21
+ PER_NAME_JOURNAL_FIELDS,
22
+ WHOLE_ENTRY_JOURNAL_FIELDS,
23
+ LIVE_USER_BINDINGS_SHADOW_POLICY,
24
+ LEGACY_USER_BINDINGS_SHADOW_POLICY,
25
+ normalizeJournalUserBindingNames,
26
+ LIVE_USER_BINDINGS_REUSE_POLICY,
27
+ LEGACY_USER_BINDINGS_REUSE_POLICY,
28
+ LEGACY_JOURNAL_VERSION,
29
+ USER_BINDING_RELATIONLESS_JOURNAL_VERSION,
30
+ FINGERPRINT_REUSE_JOURNAL_VERSION,
31
+ VERSIONED_BINDING_REUSE_JOURNAL_VERSION,
32
+ RECOVERY_BOUNDARY_EVENT,
33
+ STATUSES,
34
+ BINDING_MODES,
35
+ JOURNAL_FIELDS,
36
+ FINGERPRINT_REUSE_JOURNAL_FIELDS,
37
+ RELATIONLESS_JOURNAL_FIELDS,
38
+ PREDECESSOR_JOURNAL_FIELDS,
39
+ LEGACY_JOURNAL_FIELDS,
40
+ BINDING_POLICY_FIELDS,
41
+ REWRITE_POLICY_FIELDS,
42
+ MODULE_SEMANTICS_FIELDS,
43
+ LEGACY_MODULE_SEMANTICS_FIELDS,
44
+ IMPORT_EXPRESSION_BOUNDARIES,
45
+ DEFAULT_EXPORT_BINDINGS,
46
+ CALL_SUCCESS_FIELDS,
47
+ CALL_ERROR_FIELDS,
48
+ OPERATION_FIELDS,
49
+ RETURN_FIELDS,
50
+ THROW_FIELDS,
51
+ ERROR_FIELDS,
52
+ EDIT_TARGET_FIELDS,
53
+ DERIVED_RUN_FIELDS,
54
+ JOURNAL_VERSIONS,
55
+ USER_BINDINGS_REUSE_POLICIES,
56
+ RECOVERY_BOUNDARY_FIELDS,
57
+ REWRITE_FIELDS,
58
+ REWRITE_KINDS,
59
+ } from './session-journal-schema.js'
60
+
61
+ export {
62
+ JOURNAL_KEY,
63
+ EDIT_TARGET_KEY,
64
+ DERIVED_RUN_KEY,
65
+ REWRITES_KEY,
66
+ RECOVERY_BOUNDARY_KEY,
67
+ JOURNAL_VERSION,
68
+ LIVE_USER_BINDINGS_REUSE_POLICY,
69
+ RECOVERY_BOUNDARY_EVENT,
70
+ } from './session-journal-schema.js'
29
71
 
30
72
  function cloneJson(value) {
31
73
  if (value === undefined) return undefined
@@ -167,28 +209,131 @@ function normalizeRewritePolicy(value) {
167
209
  })
168
210
  }
169
211
 
212
+ function normalizeBindingPolicy(value) {
213
+ if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal binding policy')
214
+ assertOwnFields(value, BINDING_POLICY_FIELDS, 'journal binding policy')
215
+ for (const key of BINDING_POLICY_FIELDS) {
216
+ if (typeof value[key] !== 'boolean') throw new Error(`invalid dsh-ptc-plus journal binding policy ${key}`)
217
+ }
218
+ return Object.freeze({
219
+ variableRedeclarations: value.variableRedeclarations,
220
+ functionClassRedeclarations: value.functionClassRedeclarations,
221
+ })
222
+ }
223
+
224
+ function normalizeModuleSemantics(value) {
225
+ if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal module semantics')
226
+ assertOwnFields(value, MODULE_SEMANTICS_FIELDS, 'journal module semantics')
227
+ if (!DEFAULT_EXPORT_BINDINGS.has(value.defaultExportBinding)) {
228
+ throw new Error('invalid dsh-ptc-plus journal default export binding semantics')
229
+ }
230
+ if (!IMPORT_EXPRESSION_BOUNDARIES.has(value.importExpressionBoundary)) {
231
+ throw new Error('invalid dsh-ptc-plus journal import expression boundary semantics')
232
+ }
233
+ return Object.freeze({
234
+ defaultExportBinding: value.defaultExportBinding,
235
+ importExpressionBoundary: value.importExpressionBoundary,
236
+ })
237
+ }
238
+
239
+ function migrateLegacyModuleSemantics(value) {
240
+ if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal module semantics')
241
+ assertOwnFields(value, LEGACY_MODULE_SEMANTICS_FIELDS, 'journal module semantics')
242
+ return { ...value, importExpressionBoundary: LEGACY_IMPORT_EXPRESSION_BOUNDARY }
243
+ }
244
+
245
+ function normalizeUserBindingsFingerprint(value) {
246
+ if (value !== null && (typeof value !== 'string' || !/^[a-f0-9]{64}$/.test(value))) {
247
+ throw new Error('invalid dsh-ptc-plus journal user binding fingerprint')
248
+ }
249
+ return value
250
+ }
251
+
170
252
  function migrateJournal(value, resolveLegacyConfirm) {
171
- if (value.version !== LEGACY_JOURNAL_VERSION) return value
172
- assertOwnFields(value, LEGACY_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
253
+ if (value.version === JOURNAL_VERSION) return value
254
+ if (value.version === PER_NAME_USER_BINDINGS_JOURNAL_VERSION) {
255
+ assertOwnFields(value, PER_NAME_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
256
+ return { ...value, version: JOURNAL_VERSION, languageSemantics: LEGACY_LANGUAGE_SEMANTICS }
257
+ }
258
+ const shadow = { userBindingsShadowPolicy: LEGACY_USER_BINDINGS_SHADOW_POLICY, userBindingNames: null,
259
+ languageSemantics: LEGACY_LANGUAGE_SEMANTICS }
260
+ if (value.version === IMPORT_BOUNDARY_JOURNAL_VERSION) {
261
+ assertOwnFields(value, WHOLE_ENTRY_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
262
+ return { ...value, version: JOURNAL_VERSION, ...shadow }
263
+ }
264
+ if (value.version === VERSIONED_BINDING_REUSE_JOURNAL_VERSION) {
265
+ assertOwnFields(value, WHOLE_ENTRY_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
266
+ return { ...value, version: JOURNAL_VERSION, ...shadow, moduleSemantics: migrateLegacyModuleSemantics(value.moduleSemantics) }
267
+ }
268
+ if (value.version === FINGERPRINT_REUSE_JOURNAL_VERSION) {
269
+ assertOwnFields(value, FINGERPRINT_REUSE_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
270
+ return {
271
+ ...value,
272
+ version: JOURNAL_VERSION,
273
+ moduleSemantics: migrateLegacyModuleSemantics(value.moduleSemantics),
274
+ userBindingsReusePolicy: LEGACY_USER_BINDINGS_REUSE_POLICY,
275
+ ...shadow,
276
+ }
277
+ }
278
+ if (value.version === USER_BINDING_RELATIONLESS_JOURNAL_VERSION) {
279
+ assertOwnFields(value, RELATIONLESS_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
280
+ return {
281
+ ...value,
282
+ version: JOURNAL_VERSION,
283
+ moduleSemantics: migrateLegacyModuleSemantics(value.moduleSemantics),
284
+ userBindingsFingerprint: null,
285
+ userBindingsReusePolicy: LEGACY_USER_BINDINGS_REUSE_POLICY,
286
+ ...shadow,
287
+ }
288
+ }
289
+ const legacy = value.version === LEGACY_JOURNAL_VERSION
290
+ assertOwnFields(
291
+ value,
292
+ legacy ? LEGACY_JOURNAL_FIELDS : PREDECESSOR_JOURNAL_FIELDS,
293
+ 'dsh-ptc-plus journal',
294
+ )
295
+ if (!BINDING_MODES.has(value.bindingMode)) throw new Error('invalid dsh-ptc-plus journal binding mode')
296
+ const { bindingMode, ...rest } = value
173
297
  return {
174
- ...value,
298
+ ...rest,
175
299
  version: JOURNAL_VERSION,
176
- rewritePolicy: LEGACY_REWRITE_POLICY,
177
- confirms: normalizeLegacyConfirms(value.confirms, resolveLegacyConfirm),
300
+ bindingPolicy: {
301
+ variableRedeclarations: bindingMode === 'loose',
302
+ functionClassRedeclarations: false,
303
+ },
304
+ rewritePolicy: legacy ? LEGACY_REWRITE_POLICY : value.rewritePolicy,
305
+ moduleSemantics: {
306
+ defaultExportBinding: LEGACY_DEFAULT_EXPORT_BINDING,
307
+ importExpressionBoundary: LEGACY_IMPORT_EXPRESSION_BOUNDARY,
308
+ },
309
+ userBindingsFingerprint: null,
310
+ userBindingsReusePolicy: LEGACY_USER_BINDINGS_REUSE_POLICY,
311
+ ...shadow,
312
+ confirms: legacy
313
+ ? normalizeLegacyConfirms(value.confirms, resolveLegacyConfirm)
314
+ : value.confirms,
178
315
  }
179
316
  }
180
317
 
181
318
  /** Validate and detach one journal emitted by the runtime. */
182
319
  export function normalizeJournal(value, options = {}) {
183
320
  if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal')
184
- if (![LEGACY_JOURNAL_VERSION, INTERMEDIATE_JOURNAL_VERSION, JOURNAL_VERSION].includes(value.version)
321
+ if (!JOURNAL_VERSIONS.has(value.version)
185
322
  || !STATUSES.has(value.status)) {
186
323
  throw new Error('invalid dsh-ptc-plus journal')
187
324
  }
188
325
  const migrated = migrateJournal(value, options.resolveLegacyConfirm)
189
326
  assertOwnFields(migrated, JOURNAL_FIELDS, 'dsh-ptc-plus journal')
190
- if (!BINDING_MODES.has(migrated.bindingMode)) throw new Error('invalid dsh-ptc-plus journal binding mode')
327
+ const languageSemantics = normalizeLanguageSemantics(migrated.languageSemantics)
328
+ const bindingPolicy = normalizeBindingPolicy(migrated.bindingPolicy)
191
329
  const rewritePolicy = normalizeRewritePolicy(migrated.rewritePolicy)
330
+ const moduleSemantics = normalizeModuleSemantics(migrated.moduleSemantics)
331
+ const userBindingsFingerprint = normalizeUserBindingsFingerprint(migrated.userBindingsFingerprint)
332
+ const userBindingsReusePolicy = migrated.userBindingsReusePolicy
333
+ const userBindingNames = normalizeJournalUserBindingNames(migrated)
334
+ if (!USER_BINDINGS_REUSE_POLICIES.has(userBindingsReusePolicy)) {
335
+ throw new Error('invalid dsh-ptc-plus journal user binding reuse policy')
336
+ }
192
337
  const calls = normalizeCalls(migrated.calls)
193
338
  const operations = normalizeOperations(migrated.operations)
194
339
  const confirms = normalizeConfirms(migrated.confirms)
@@ -209,8 +354,14 @@ export function normalizeJournal(value, options = {}) {
209
354
  }
210
355
  return Object.freeze({
211
356
  version: JOURNAL_VERSION,
212
- bindingMode: migrated.bindingMode,
357
+ languageSemantics,
358
+ bindingPolicy,
213
359
  rewritePolicy,
360
+ moduleSemantics,
361
+ userBindingsFingerprint,
362
+ userBindingsReusePolicy,
363
+ userBindingsShadowPolicy: migrated.userBindingsShadowPolicy,
364
+ userBindingNames,
214
365
  status: migrated.status,
215
366
  calls: Object.freeze(calls),
216
367
  operations: Object.freeze(operations),
@@ -243,15 +394,53 @@ export function normalizeDerivedEditResult(meta, expectedTargetCallSeq) {
243
394
  const recoveryBoundaries = meta[RECOVERY_BOUNDARY_KEY] === undefined
244
395
  ? undefined
245
396
  : normalizeRecoveryBoundaries(meta[RECOVERY_BOUNDARY_KEY])
397
+ const userBindings = userBindingsForJournal(meta, journal)
246
398
  return Object.freeze({
247
399
  targetCallSeq: target.targetCallSeq,
248
400
  code: derived.code,
249
401
  description: derived.description,
250
402
  journal,
251
403
  ...(recoveryBoundaries === undefined ? {} : { recoveryBoundaries }),
404
+ ...(userBindings === undefined ? {} : { userBindings }),
252
405
  })
253
406
  }
254
407
 
408
+ export function userBindingsForJournal(meta, journal) {
409
+ const userBindings = userBindingsSnapshotFromMeta(meta)
410
+ if (journal.userBindingsFingerprint === null) {
411
+ if (userBindings !== undefined) {
412
+ throw new Error('dsh-ptc-plus journal does not declare user binding metadata')
413
+ }
414
+ validateUserBindingNameProviders(journal, undefined)
415
+ return undefined
416
+ }
417
+ if (userBindings === undefined) {
418
+ throw new Error('dsh-ptc-plus journal requires user binding metadata')
419
+ }
420
+ if (userBindings.fingerprint !== journal.userBindingsFingerprint) {
421
+ throw new Error('dsh-ptc-plus journal user binding fingerprint does not match metadata')
422
+ }
423
+ validateUserBindingNameProviders(journal, userBindings)
424
+ return userBindings
425
+ }
426
+
427
+ function validateUserBindingNameProviders(journal, snapshot) {
428
+ const providers = new Map((snapshot?.entries ?? []).flatMap(entry => (
429
+ (entry.scope === 'namespace' ? [entry.name] : entry.symbols).map(name => [name, entry.id])
430
+ )))
431
+ for (const fact of journal.userBindingNames ?? []) {
432
+ if (fact.state === 'provider' && providers.get(fact.name) !== fact.entryId) {
433
+ throw new Error('journal user binding name evidence has no matching provider')
434
+ }
435
+ }
436
+ if (journal.userBindingNames !== null) {
437
+ const names = new Set(journal.userBindingNames.map(fact => fact.name))
438
+ if ([...providers.keys()].some(name => !names.has(name))) {
439
+ throw new Error('journal user binding name evidence is incomplete')
440
+ }
441
+ }
442
+ }
443
+
255
444
  /** Compare only the required persisted relation for one derived edit. */
256
445
  export function derivedEditResultsEqual(leftMeta, rightMeta, expectedTargetCallSeq) {
257
446
  try {
@@ -292,7 +481,7 @@ function normalizeRecoveryBoundaryValue(value, eventSeq = undefined) {
292
481
  && (!Number.isSafeInteger(value.frontierCallSeq) || value.frontierCallSeq < 0))) {
293
482
  throw new Error('invalid dsh-ptc-plus recovery boundary')
294
483
  }
295
- assertOwnFields(value, new Set(['failedCallSeq', 'frontierCallSeq']), 'recovery boundary')
484
+ assertOwnFields(value, RECOVERY_BOUNDARY_FIELDS, 'recovery boundary')
296
485
  if (eventSeq !== undefined && (!Number.isSafeInteger(eventSeq) || eventSeq < 0)) {
297
486
  throw new Error('invalid dsh-ptc-plus recovery boundary event sequence')
298
487
  }
@@ -394,12 +583,24 @@ export function liveToolCallSeq(session, callId, toolName) {
394
583
  }
395
584
 
396
585
  /** Start a mutable journal for one live cell. */
397
- export function createJournal(confirms = [], bindingMode, rewritePolicy) {
398
- if (!BINDING_MODES.has(bindingMode)) throw new TypeError('invalid dsh-ptc-plus journal binding mode')
586
+ export function createJournal(confirms = [], bindingPolicy, rewritePolicy, languageSemantics = LEGACY_LANGUAGE_SEMANTICS) {
587
+ if (typeof bindingPolicy === 'string') {
588
+ if (!BINDING_MODES.has(bindingPolicy)) throw new TypeError('invalid dsh-ptc-plus journal binding mode')
589
+ bindingPolicy = {
590
+ variableRedeclarations: bindingPolicy === 'loose',
591
+ functionClassRedeclarations: false,
592
+ }
593
+ }
399
594
  return {
400
595
  version: JOURNAL_VERSION,
401
- bindingMode,
596
+ languageSemantics: normalizeLanguageSemantics(languageSemantics),
597
+ bindingPolicy: normalizeBindingPolicy(bindingPolicy),
402
598
  rewritePolicy: normalizeRewritePolicy(rewritePolicy),
599
+ moduleSemantics: normalizeModuleSemantics(LIVE_MODULE_SEMANTICS),
600
+ userBindingsFingerprint: null,
601
+ userBindingsReusePolicy: LIVE_USER_BINDINGS_REUSE_POLICY,
602
+ userBindingsShadowPolicy: LIVE_USER_BINDINGS_SHADOW_POLICY,
603
+ userBindingNames: null,
403
604
  calls: [],
404
605
  operations: [],
405
606
  confirms: [...confirms],
@@ -407,15 +608,6 @@ export function createJournal(confirms = [], bindingMode, rewritePolicy) {
407
608
  }
408
609
  }
409
610
 
410
- function sourceForRunCall(call) {
411
- try {
412
- const args = JSON.parse(call.data.arguments)
413
- return isRecord(args) && typeof args.code === 'string' ? args.code : undefined
414
- } catch {
415
- return undefined
416
- }
417
- }
418
-
419
611
  /** Return an owned state transition; reject volatile saves and unknown named restores. */
420
612
  export function reduceStateOperations({ nodes, head, checkpoints }, operations, nodeIndex) {
421
613
  const nextCheckpoints = new Map(checkpoints)
@@ -443,398 +635,6 @@ export function reduceStateOperations({ nodes, head, checkpoints }, operations,
443
635
  return { head: nextHead, checkpoints: nextCheckpoints, restored }
444
636
  }
445
637
 
446
- function applyOperations(state, operations, nodeIndex) {
447
- const transition = reduceStateOperations(state, operations, nodeIndex)
448
- state.head = transition.head
449
- state.checkpoints = transition.checkpoints
450
- if (transition.restored) {
451
- state.trusted = true
452
- state.volatileSuffix.length = 0
453
- }
454
- }
455
-
456
- function applyRecord(state, record, invalidCallSeqs) {
457
- const { call, code, result } = record
458
- if (invalidCallSeqs.has(call.seq)) return
459
- if (code === undefined || result?.journal === undefined) {
460
- state.trusted = false
461
- state.volatileSuffix.push({ seq: call.seq, code, reason: result?.error ?? 'missing dsh-ptc-plus journal result' })
462
- return
463
- }
464
- const journal = result.journal
465
- if (journal.status === 'noop') return
466
- if (journal.status === 'discarded') {
467
- if (journal.volatileReason !== undefined) {
468
- state.trusted = false
469
- state.volatileSuffix.push({ seq: call.seq, code, reason: journal.volatileReason })
470
- }
471
- return
472
- }
473
- if (journal.status === 'volatile') {
474
- state.trusted = false
475
- state.volatileSuffix.push({ seq: call.seq, code, reason: journal.volatileReason ?? 'volatile cell' })
476
- applyOperations(state, journal.operations, undefined)
477
- return
478
- }
479
- if (!state.trusted) {
480
- state.trusted = true
481
- state.volatileSuffix.length = 0
482
- }
483
- const node = Object.freeze({
484
- code,
485
- journal,
486
- callSeq: call.seq,
487
- parent: state.head,
488
- })
489
- const index = state.nodes.push(node) - 1
490
- state.head = index
491
- applyOperations(state, journal.operations, index)
492
- }
493
-
494
- function forceRecoveryHead(state, boundary) {
495
- state.head = boundary.frontierCallSeq === null
496
- ? undefined
497
- : state.nodes.findIndex(node => node.callSeq === boundary.frontierCallSeq)
498
- if (state.head === -1) throw new Error('recovery boundary frontier is not reconstructable')
499
- state.trusted = true
500
- state.volatileSuffix = []
501
- }
502
-
503
- function recordEventSeq(record) {
504
- return record.result?.eventSeq ?? record.call.seq
505
- }
506
-
507
- function foldRecords(records, invalidCallSeqs, boundaries = []) {
508
- const state = {
509
- nodes: [],
510
- head: undefined,
511
- checkpoints: new Map(),
512
- volatileSuffix: [],
513
- trusted: true,
514
- }
515
- let boundaryIndex = 0
516
- for (const record of records) {
517
- while (boundaryIndex < boundaries.length && boundaries[boundaryIndex].eventSeq < recordEventSeq(record)) {
518
- forceRecoveryHead(state, boundaries[boundaryIndex++])
519
- }
520
- applyRecord(state, record, invalidCallSeqs)
521
- }
522
- while (boundaryIndex < boundaries.length) forceRecoveryHead(state, boundaries[boundaryIndex++])
523
- return state
524
- }
525
-
526
- function dependsOn(nodes, index, ancestor) {
527
- for (let cursor = index; cursor !== undefined; cursor = nodes[cursor]?.parent) {
528
- if (cursor === ancestor) return true
529
- }
530
- return false
531
- }
532
-
533
- function timelineRun(call, result, eventIndex, journal) {
534
- let args
535
- try {
536
- const parsed = JSON.parse(call.data.arguments)
537
- if (isRecord(parsed)) args = Object.freeze(parsed)
538
- } catch {
539
- // Invalid persisted arguments cannot support derived source facts.
540
- }
541
- return Object.freeze({
542
- index: eventIndex,
543
- callSeq: Number.isSafeInteger(call.seq) ? call.seq : undefined,
544
- args,
545
- source: typeof args?.code === 'string' ? args.code : undefined,
546
- journal,
547
- rewrites: validatedRewrites(result.data?.meta),
548
- })
549
- }
550
-
551
- function timelineDerivedRun(call, result, eventIndex, derived) {
552
- return Object.freeze({
553
- index: eventIndex,
554
- callSeq: Number.isSafeInteger(call.seq) ? call.seq : undefined,
555
- args: Object.freeze({ description: derived.description }),
556
- source: derived.code,
557
- journal: derived.journal,
558
- rewrites: validatedRewrites(result.data?.meta),
559
- })
560
- }
561
-
562
- function successfulTimelineRun(run) {
563
- return run.journal?.completion?.kind === 'return' && run.journal.status !== 'noop'
564
- }
565
-
566
- /**
567
- * Fold persisted tool events once into the call/result and edit-target timeline
568
- * shared by prompt projection and cold journal recovery.
569
- */
570
- export function foldSessionTimeline(events) {
571
- const empty = {
572
- openTurn: false,
573
- executableCalls: new Map(),
574
- calls: [],
575
- results: new Map(),
576
- boundaries: [],
577
- found: false,
578
- unavailableResultSeq: undefined,
579
- lastSuccessfulRunIndex: undefined,
580
- latestRun: undefined,
581
- editableRun: undefined,
582
- editTargets: new Map(),
583
- }
584
- if (!Array.isArray(events)) return empty
585
-
586
- const state = { ...empty }
587
- const pendingByCallId = new Map()
588
- const seenCallIds = new Set()
589
- const claimedEditTargets = new Set()
590
- const editClaims = new Map()
591
- const journalResultSeqs = new Set()
592
- let scope = 0
593
-
594
- const resetTurn = (openTurn) => {
595
- state.openTurn = openTurn
596
- scope += 1
597
- pendingByCallId.clear()
598
- seenCallIds.clear()
599
- claimedEditTargets.clear()
600
- state.latestRun = undefined
601
- state.editableRun = undefined
602
- }
603
-
604
- for (let eventIndex = 0; eventIndex < events.length; eventIndex += 1) {
605
- const event = events[eventIndex]
606
- if (event?.type === 'turn/start') {
607
- resetTurn(true)
608
- continue
609
- }
610
- if (event?.type === 'turn/end') {
611
- resetTurn(false)
612
- continue
613
- }
614
- if (event?.type === RECOVERY_BOUNDARY_EVENT) {
615
- throw new Error('legacy recovery boundary requires migration before DSH session restore')
616
- continue
617
- }
618
- if (event?.type === 'tool/call' && typeof event.data?.callId === 'string') {
619
- const executable = event.data.name === 'run_code' || event.data.name === 'edit_run_code'
620
- let entry = { event, eventIndex, scope }
621
- if (executable) {
622
- if (Number.isSafeInteger(event.seq) && event.seq >= 0 && state.executableCalls.has(event.seq)) {
623
- throw new Error('session log contains a duplicate run_code call sequence')
624
- }
625
- if (event.data.name === 'edit_run_code') {
626
- const targetCallSeq = state.editableRun?.callSeq
627
- const target = targetCallSeq !== undefined && state.editableRun.source !== undefined
628
- && !claimedEditTargets.has(targetCallSeq)
629
- ? Object.freeze({ source: state.editableRun.source, callSeq: targetCallSeq })
630
- : undefined
631
- entry = { ...entry, editTarget: target }
632
- state.editTargets.set(event.seq, target)
633
- if (target !== undefined) {
634
- claimedEditTargets.add(target.callSeq)
635
- editClaims.set(event.seq, target.callSeq)
636
- }
637
- }
638
- state.calls.push(event)
639
- if (Number.isSafeInteger(event.seq) && event.seq >= 0) {
640
- state.executableCalls.set(event.seq, entry)
641
- }
642
- }
643
- if (seenCallIds.has(event.data.callId)) pendingByCallId.set(event.data.callId, null)
644
- else {
645
- seenCallIds.add(event.data.callId)
646
- pendingByCallId.set(event.data.callId, entry)
647
- }
648
- continue
649
- }
650
- if (event?.type !== 'tool/result') continue
651
-
652
- const sourceSeq = event.sourceEventSeqs?.[0]
653
- const callId = event.data?.message?.source?.callId
654
- let entry = Number.isSafeInteger(sourceSeq) ? state.executableCalls.get(sourceSeq) : undefined
655
- if (entry === undefined && !Number.isSafeInteger(sourceSeq) && typeof callId === 'string') {
656
- entry = pendingByCallId.get(callId)
657
- }
658
- if (typeof callId === 'string') pendingByCallId.delete(callId)
659
- if (entry === null) {
660
- state.latestRun = undefined
661
- state.editableRun = undefined
662
- continue
663
- }
664
-
665
- const call = entry?.event
666
- const callSeq = call?.seq
667
- const meta = event.data?.meta
668
- let normalized
669
- if (isRecord(meta) && Object.hasOwn(meta, JOURNAL_KEY)) {
670
- if (Number.isSafeInteger(sourceSeq) && journalResultSeqs.has(sourceSeq)) {
671
- throw new Error(`session log contains duplicate PTC journal results for call seq ${sourceSeq}`)
672
- }
673
- if (Number.isSafeInteger(sourceSeq)) journalResultSeqs.add(sourceSeq)
674
- if (entry === undefined) {
675
- if (Number.isSafeInteger(sourceSeq)) state.unavailableResultSeq ??= sourceSeq
676
- continue
677
- }
678
- const resultSeq = Number.isSafeInteger(sourceSeq) ? sourceSeq : callSeq
679
- const raw = {
680
- meta,
681
- eventSeq: Number.isSafeInteger(event.seq) && event.seq >= 0 ? event.seq : resultSeq,
682
- eventIndex,
683
- }
684
- if (Object.hasOwn(meta, RECOVERY_BOUNDARY_KEY)) {
685
- state.boundaries.push(...normalizeRecoveryBoundaries(
686
- meta[RECOVERY_BOUNDARY_KEY], raw.eventSeq,
687
- ))
688
- }
689
- try {
690
- if (call.data.name === 'edit_run_code') {
691
- const derived = normalizeDerivedEditResult(meta, entry.editTarget?.callSeq)
692
- normalized = { ...raw, journal: derived.journal, derived }
693
- } else if (call.data.name === 'run_code') {
694
- const rawJournal = meta[JOURNAL_KEY]
695
- const resolveLegacyConfirm = rawJournal?.version === LEGACY_JOURNAL_VERSION
696
- ? legacyCallId => {
697
- const candidates = [...state.executableCalls.values()]
698
- .filter(candidate => candidate.eventIndex < eventIndex
699
- && candidate.event.data?.name === 'run_code'
700
- && candidate.event.data.callId === legacyCallId
701
- && !state.results.has(candidate.event.seq))
702
- return candidates.length === 1 ? candidates[0].event.seq : undefined
703
- }
704
- : undefined
705
- normalized = { ...raw, journal: normalizeJournal(rawJournal, { resolveLegacyConfirm }) }
706
- }
707
- } catch (error) {
708
- normalized = { ...raw, error: error.message }
709
- }
710
- if (Number.isSafeInteger(resultSeq)) state.results.set(resultSeq, normalized)
711
- state.found = true
712
- }
713
-
714
- const claimedTarget = editClaims.get(callSeq)
715
- if (claimedTarget !== undefined) {
716
- editClaims.delete(callSeq)
717
- if (normalized?.derived === undefined) claimedEditTargets.delete(claimedTarget)
718
- }
719
- if (entry === undefined || entry.scope !== scope) continue
720
- if (call.data.name === 'edit_run_code') {
721
- if (normalized?.derived !== undefined) {
722
- const run = timelineDerivedRun(call, event, eventIndex, normalized.derived)
723
- state.latestRun = run
724
- state.editableRun = run
725
- if (successfulTimelineRun(run)) state.lastSuccessfulRunIndex = eventIndex
726
- }
727
- continue
728
- }
729
- if (call.data.name !== 'run_code') {
730
- // Unrelated native settlements do not change the current editable cell.
731
- // The edit target is captured at dispatch time and remains valid until a
732
- // new executable cell or turn boundary supersedes it.
733
- continue
734
- }
735
- const run = timelineRun(call, event, eventIndex, normalized?.journal)
736
- state.latestRun = run
737
- state.editableRun = run
738
- if (successfulTimelineRun(run)) state.lastSuccessfulRunIndex = eventIndex
739
- }
740
- return state
741
- }
742
-
743
- /** Fold the session log into the last exactly replayable frontier. */
744
- export function recoverJournal(session, currentCallSeq, options = {}) {
745
- const events = sessionEvents(session)
746
- if (!Array.isArray(events)) {
747
- return { nodes: [], head: undefined, checkpoints: new Map(), volatileSuffix: [], available: true }
748
- }
749
- const timeline = foldSessionTimeline(events)
750
- const calls = timeline.calls.filter(call => call.seq !== currentCallSeq)
751
- const { executableCalls, results, found } = timeline
752
- const extraBoundaries = options?.extraBoundaries ?? []
753
- const boundaries = [
754
- ...timeline.boundaries,
755
- ...normalizeRecoveryBoundaries(extraBoundaries, undefined).map(boundary => ({
756
- ...boundary,
757
- eventSeq: Number.POSITIVE_INFINITY,
758
- })),
759
- ].sort((left, right) => left.eventSeq - right.eventSeq)
760
- if (timeline.unavailableResultSeq !== undefined) {
761
- throw new Error(`PTC journal result references unavailable run_code call seq ${timeline.unavailableResultSeq}`)
762
- }
763
-
764
- const confirmedNoops = new Set()
765
- for (const { journal, eventIndex } of results.values()) {
766
- for (const callSeq of journal?.confirms ?? []) {
767
- const confirmed = executableCalls.get(callSeq)
768
- if (confirmed === undefined || confirmed.eventIndex >= eventIndex || results.has(callSeq)) {
769
- throw new Error(`confirmed no-op does not identify an earlier unjournaled run_code call seq ${callSeq}`)
770
- }
771
- confirmedNoops.add(callSeq)
772
- }
773
- }
774
- const records = []
775
- const orderedRecords = []
776
- const invalidCallSeqs = new Set()
777
- const appliedBoundaries = []
778
- let state = foldRecords(records, invalidCallSeqs)
779
- let boundaryIndex = 0
780
- const applyBoundariesBefore = (seq) => {
781
- while (boundaryIndex < boundaries.length && boundaries[boundaryIndex].eventSeq <= seq) {
782
- const boundary = boundaries[boundaryIndex++]
783
- const failedIndex = state.nodes.findIndex(node => node.callSeq === boundary.failedCallSeq)
784
- if (failedIndex < 0) throw new Error('recovery boundary references an unavailable failed cell')
785
- const expectedFrontier = state.nodes[failedIndex].parent
786
- const frontierIndex = boundary.frontierCallSeq === null
787
- ? undefined
788
- : state.nodes.findIndex(node => node.callSeq === boundary.frontierCallSeq)
789
- if (frontierIndex !== expectedFrontier) {
790
- throw new Error('recovery boundary does not identify the failed cell parent')
791
- }
792
- for (let index = 0; index < state.nodes.length; index += 1) {
793
- if (dependsOn(state.nodes, index, failedIndex)) invalidCallSeqs.add(state.nodes[index].callSeq)
794
- }
795
- appliedBoundaries.push(boundary)
796
- state = foldRecords(records, invalidCallSeqs, appliedBoundaries)
797
- }
798
- }
799
- for (const call of calls) {
800
- const result = results.get(call.seq)
801
- const code = call.data?.name === 'edit_run_code' ? result?.derived?.code : sourceForRunCall(call)
802
- const record = { call, code, result }
803
- if (call.data?.name === 'edit_run_code' && record.code === undefined && result === undefined) continue
804
- if (result?.journal === undefined && confirmedNoops.has(call.seq)) continue
805
- orderedRecords.push(record)
806
- }
807
- orderedRecords.sort((left, right) => (
808
- recordEventSeq(left) - recordEventSeq(right) || left.call.seq - right.call.seq
809
- ))
810
- for (const record of orderedRecords) {
811
- applyBoundariesBefore(recordEventSeq(record))
812
- records.push(record)
813
- applyRecord(state, record, invalidCallSeqs)
814
- }
815
- applyBoundariesBefore(Number.POSITIVE_INFINITY)
816
- return {
817
- nodes: state.nodes,
818
- head: state.head,
819
- checkpoints: state.checkpoints,
820
- volatileSuffix: state.volatileSuffix,
821
- available: found,
822
- }
823
- }
824
-
825
- /** Return source nodes from the empty state to a selected durable head. */
826
- export function pathToHead(state) {
827
- const path = []
828
- for (let cursor = state.head; cursor !== undefined;) {
829
- const node = state.nodes[cursor]
830
- if (node === undefined) throw new Error('invalid dsh-ptc-plus journal head')
831
- path.push(node)
832
- cursor = node.parent
833
- }
834
- path.reverse()
835
- return path
836
- }
837
-
838
638
  /** Merge the journal into the tool result's private metadata. */
839
639
  export function withJournal(meta, journal) {
840
640
  const base = isRecord(meta) ? { ...meta } : meta === undefined ? {} : { value: cloneJson(meta) }
@@ -842,9 +642,6 @@ export function withJournal(meta, journal) {
842
642
  return base
843
643
  }
844
644
 
845
- const REWRITE_FIELDS = new Set(['kind', 'description', 'source'])
846
- const REWRITE_KINDS = new Set(['import', 'redeclaration', 'export'])
847
-
848
645
  /** Validate and detach one rewrite record emitted by the runtime. */
849
646
  export function normalizeRewrites(value) {
850
647
  if (!Array.isArray(value)) throw new Error('invalid dsh-ptc-plus rewrites')