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.
- package/README.en.md +66 -73
- package/README.md +66 -73
- package/assets/ptc-plus-bindings-en.png +0 -0
- package/assets/ptc-plus-bindings-zh.png +0 -0
- package/assets/ptc-plus-repair-en.png +0 -0
- package/assets/ptc-plus-repair-zh.png +0 -0
- package/assets/ptc-plus-repl-workspace-en.png +0 -0
- package/assets/ptc-plus-repl-workspace-zh.png +0 -0
- package/assets/ptc-plus-settings-en.png +0 -0
- package/assets/ptc-plus-settings-zh.png +0 -0
- package/client.js +30610 -722
- package/compiler-amaro.cjs +5 -0
- package/compiler-amaro.cjs.map +6 -0
- package/compiler-asset-124f80c1bba92ff5fe79cfec6e8ad518f476de4a42514cae3919538887f510e8.txt +1 -0
- package/compiler-assets.json +3 -0
- package/compiler-core.cjs +554 -0
- package/compiler-core.cjs.map +6 -0
- package/compiler-platform.cjs +2 -0
- package/compiler-platform.cjs.map +6 -0
- package/compiler-typescript.cjs +388 -0
- package/compiler-typescript.cjs.map +6 -0
- package/docs/README.md +6 -0
- package/docs/adr/0004-declare-program-bindings-at-the-host.md +1 -1
- package/docs/adr/0006-rewrite-module-syntax-with-an-ast.md +6 -4
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +15 -3
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -0
- package/docs/adr/0010-session-log-derived-recovery-tips.md +59 -5
- package/docs/adr/0011-isolate-the-local-recovery-tip-provider.md +1 -1
- package/docs/adr/0012-preserve-mixed-destructuring-initialization.md +3 -1
- package/docs/adr/0013-isolate-repl-redeclaration-convenience.md +9 -3
- package/docs/adr/0014-persist-cell-rewrite-policy.md +11 -5
- package/docs/adr/0015-preserve-source-positions-through-rewrites.md +1 -1
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +49 -0
- package/docs/adr/0018-use-versioned-fixture-for-ab-benchmark.md +3 -3
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +8 -6
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +17 -3
- package/docs/adr/0023-global-user-bindings.md +104 -0
- package/docs/adr/0024-repl-console-observation.md +33 -0
- package/docs/adr/0025-use-versioned-logical-binding-identities.md +119 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +21 -0
- package/docs/adr/0027-run-the-session-kernel-in-a-killable-helper-process.md +37 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +103 -0
- package/docs/adr/0029-keep-plugin-unavailability-out-of-the-host-agent-lifecycle.md +27 -0
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +27 -0
- package/docs/architecture.md +82 -22
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +96 -20
- package/docs/durability-design.md +61 -24
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/program-data-plane.md +6 -0
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +79 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +755 -0
- package/docs/user-bindings.en.md +64 -0
- package/docs/user-bindings.md +64 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification.md +57 -0
- package/index.js +374 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/binding-descriptors.js +1 -0
- package/internal/binding-pattern-traversal.js +25 -0
- package/internal/binding-pattern.js +26 -29
- package/internal/binding-source-analysis.js +391 -0
- package/internal/binding-update-policy.js +66 -0
- package/internal/bounded-worker-round.js +56 -0
- package/internal/callable-definition-environment.js +42 -0
- package/internal/callable-source-catalog.js +129 -0
- package/internal/callable-source-encoding.js +47 -0
- package/internal/callable-source-facts.js +459 -0
- package/internal/callable-source-format.js +2 -0
- package/internal/callable-source-lowering.js +123 -0
- package/internal/cell-analysis-contract.js +25 -0
- package/internal/cell-analysis.js +252 -242
- package/internal/cell-completion.js +15 -0
- package/internal/cell-error.js +7 -0
- package/internal/cell-parser.js +66 -0
- package/internal/cell-rewriter.js +3 -553
- package/internal/commonjs-export-evidence.js +63 -0
- package/internal/commonjs-source-evidence.js +72 -0
- package/internal/compiler-amaro.cjs +2 -0
- package/internal/compiler-compression.js +12 -0
- package/internal/compiler-data.js +58 -0
- package/internal/compiler-descriptors.js +54 -0
- package/internal/compiler-dynamic-cache.js +48 -0
- package/internal/compiler-entry.js +35 -0
- package/internal/compiler-generated-names.js +17 -0
- package/internal/compiler-intrinsics.js +84 -0
- package/internal/compiler-module-links.js +34 -0
- package/internal/compiler-operations.js +61 -0
- package/internal/compiler-platform-factory.js +84 -0
- package/internal/compiler-platform.js +136 -0
- package/internal/compiler-record-roles.js +53 -0
- package/internal/compiler-region-output.js +259 -0
- package/internal/compiler-resource-lowering.js +103 -0
- package/internal/compiler-scope-facts.js +115 -0
- package/internal/compiler-service.js +109 -0
- package/internal/compiler-source-cache.js +32 -0
- package/internal/compiler-source-pieces.js +93 -0
- package/internal/compiler-source-regions.js +132 -0
- package/internal/compiler-storage-source.js +54 -0
- package/internal/compiler-text.js +15 -0
- package/internal/compiler-typescript.cjs +2 -0
- package/internal/config-spec.js +91 -25
- package/internal/cordis-tools-owner.js +130 -21
- package/internal/diagnostic.js +27 -1
- package/internal/direct-surface-owner.js +76 -23
- package/internal/dynamic-binding-evidence.js +16 -0
- package/internal/dynamic-environment-compiler.js +779 -0
- package/internal/dynamic-environment-integration.js +87 -0
- package/internal/dynamic-environment-runtime.js +362 -0
- package/internal/dynamic-native-calls.js +150 -0
- package/internal/dynamic-native-runtime.js +248 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +28 -3
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +118 -18
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +548 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +911 -116
- package/internal/language-semantics.js +14 -0
- package/internal/legacy-binding-catalog.js +48 -0
- package/internal/legacy-cell-analysis.js +600 -0
- package/internal/legacy-cell-parser.js +50 -0
- package/internal/legacy-cell-rewriter.js +631 -0
- package/internal/legacy-repl-convenience.js +291 -0
- package/internal/managed-module-operations.js +170 -0
- package/internal/module-compilation.js +321 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +12 -0
- package/internal/native-await-declarations.js +68 -0
- package/internal/native-root-compilation.js +54 -0
- package/internal/native-root-dynamic.js +67 -0
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -201
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +21 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +114 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -51
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +455 -91
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +237 -440
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +325 -80
- package/internal/session-state.js +249 -40
- package/internal/source-position-map.js +211 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +598 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +29 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +184 -0
- package/internal/user-binding-console.js +148 -0
- package/internal/user-binding-draft-projection.js +335 -0
- package/internal/user-binding-model-context.js +31 -0
- package/internal/user-binding-runner.js +56 -0
- package/internal/user-bindings-owner.js +1065 -0
- package/internal/user-bindings-store.js +293 -0
- package/internal/user-bindings.js +353 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +29 -20
- package/internal/worker-client.js +57 -67
- package/internal/worker-environment.js +45 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +81 -11
- package/src/client-activity.js +109 -45
- package/src/client-authoring-view.js +580 -0
- package/src/client-binding-review.js +230 -0
- package/src/client-bindings-data.js +29 -0
- package/src/client-catalog.js +127 -0
- package/src/client-code-editor.js +84 -0
- package/src/client-console.js +112 -0
- package/src/client-copy.js +370 -0
- package/src/client-feature-gates.js +62 -0
- package/src/client-host-compat.js +150 -0
- package/src/client-indicator-view.js +267 -0
- package/src/client-repl-view.js +292 -0
- package/src/client-rpc.js +20 -0
- package/src/client-settings-view.js +221 -0
- package/src/client-styles.js +106 -0
- package/src/client-tool-view.js +108 -0
- package/src/client-transport.js +159 -0
- package/src/client-workbench.js +521 -0
- 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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
172
|
-
|
|
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
|
-
...
|
|
298
|
+
...rest,
|
|
175
299
|
version: JOURNAL_VERSION,
|
|
176
|
-
|
|
177
|
-
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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 = [],
|
|
398
|
-
if (
|
|
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
|
-
|
|
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')
|