dsh-ptc-plus 0.3.2 → 0.4.2
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 +63 -80
- package/README.md +63 -80
- 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 +30827 -742
- 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 +556 -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 +7 -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 +5 -3
- package/docs/adr/0007-separate-worker-transport-from-session-semantics.md +1 -1
- 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 +3 -1
- package/docs/adr/0014-persist-cell-rewrite-policy.md +6 -2
- 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 +5 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0021-separate-host-bootstrap-from-session-runtime.md +12 -0
- 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 +77 -21
- package/docs/binding-review-placement-design.md +200 -0
- package/docs/capability-projection.md +1 -1
- package/docs/client-ui.md +95 -19
- package/docs/durability-design.md +28 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +76 -25
- package/docs/semantic-validation.md +29 -0
- package/docs/stateful-binding-updates-proposal.md +759 -0
- package/docs/user-bindings.en.md +66 -0
- package/docs/user-bindings.md +66 -0
- package/docs/value-wire.md +3 -3
- package/docs/verification-optimization-work.md +41 -0
- package/docs/verification.md +71 -0
- package/index.js +369 -92
- package/internal/ast-traversal.js +5 -2
- package/internal/async-local-storage-intrinsics.js +38 -0
- package/internal/binding-declaration-contract.js +8 -0
- 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 +708 -0
- package/internal/binding-update-policy.js +72 -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 +253 -290
- 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 -677
- 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 +50 -0
- package/internal/compiler-module-links.js +37 -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 +83 -27
- 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 +91 -0
- package/internal/dynamic-environment-runtime.js +383 -0
- package/internal/dynamic-native-calls.js +153 -0
- package/internal/dynamic-native-runtime.js +262 -0
- package/internal/dynamic-scope-analysis.js +90 -0
- package/internal/edit-transport-owner.js +27 -2
- package/internal/execution-seam-compat.js +193 -0
- package/internal/failure-reporting.js +136 -28
- package/internal/host-rpc.js +66 -0
- package/internal/isolated-worker.js +555 -0
- package/internal/kernel-child.js +139 -0
- package/internal/kernel-worker.js +1179 -233
- 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 +322 -0
- package/internal/module-runtime-platform.js +19 -0
- package/internal/module-transform-contract.js +42 -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/record-utils.js +24 -15
- package/internal/recovery-tips.js +27 -15
- package/internal/rejected-cell-editor.js +13 -8
- package/internal/repl-convenience.js +2 -289
- package/internal/repl-memory-projection.js +81 -12
- package/internal/repl-observation-interest.js +70 -0
- package/internal/repl-rewrite-contract.js +6 -0
- package/internal/repl-scope-normalizer.js +2091 -0
- package/internal/repl-value-observer.js +117 -0
- package/internal/rpc-contract.js +52 -0
- package/internal/runtime-bridge-owner.js +172 -28
- package/internal/runtime-config.js +48 -52
- package/internal/runtime-contexts.js +82 -10
- package/internal/runtime-intrinsics.js +101 -0
- package/internal/runtime-messages.js +111 -0
- package/internal/session-cell-executor.js +402 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +123 -0
- package/internal/session-journal.js +199 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +257 -95
- package/internal/session-state.js +247 -71
- package/internal/source-position-map.js +230 -71
- package/internal/stateful-module-compiler.js +154 -0
- package/internal/stateful-module-runtime.js +303 -0
- package/internal/stateful-root-compiler.js +600 -0
- package/internal/stateful-root-runtime.js +340 -0
- package/internal/typert-codec-compat.js +28 -0
- package/internal/typescript-transform.js +31 -0
- package/internal/user-binding-authoring.js +70 -0
- package/internal/user-binding-console-worker.js +244 -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 +361 -0
- package/internal/value-wire-schema.js +28 -0
- package/internal/value-wire.js +130 -91
- package/internal/worker-client.js +57 -67
- package/internal/worker-cwd-virtualization.js +80 -53
- package/internal/worker-environment.js +65 -0
- package/internal/worker-output-relay.js +29 -0
- package/internal/worker-repl-realm.js +134 -0
- package/internal/worker-shutdown.js +14 -0
- package/package.json +76 -7
- package/src/client-activity.js +95 -53
- 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,42 +2,76 @@ 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
|
+
import { LEGACY_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
|
|
6
|
+
import { LEGACY_USER_BINDING_TRANSFORM, historicalModuleTransformForLanguage,
|
|
7
|
+
moduleTransformForLanguage, normalizeModuleTransform } from './module-transform-contract.js'
|
|
5
8
|
import {
|
|
6
9
|
LEGACY_DEFAULT_EXPORT_BINDING,
|
|
7
|
-
|
|
10
|
+
LEGACY_IMPORT_EXPRESSION_BOUNDARY,
|
|
11
|
+
LIVE_MODULE_SEMANTICS,
|
|
8
12
|
} from './repl-rewrite-contract.js'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
13
|
+
import { userBindingsSnapshotFromMeta } from './user-bindings.js'
|
|
14
|
+
import {
|
|
15
|
+
JOURNAL_KEY,
|
|
16
|
+
EDIT_TARGET_KEY,
|
|
17
|
+
DERIVED_RUN_KEY,
|
|
18
|
+
REWRITES_KEY,
|
|
19
|
+
RECOVERY_BOUNDARY_KEY,
|
|
20
|
+
JOURNAL_VERSION,
|
|
21
|
+
LANGUAGE_SEMANTICS_JOURNAL_VERSION,
|
|
22
|
+
LANGUAGE_SEMANTICS_JOURNAL_FIELDS,
|
|
23
|
+
IMPORT_BOUNDARY_JOURNAL_VERSION,
|
|
24
|
+
PER_NAME_USER_BINDINGS_JOURNAL_VERSION,
|
|
25
|
+
PER_NAME_JOURNAL_FIELDS,
|
|
26
|
+
WHOLE_ENTRY_JOURNAL_FIELDS,
|
|
27
|
+
LIVE_USER_BINDINGS_SHADOW_POLICY,
|
|
28
|
+
LEGACY_USER_BINDINGS_SHADOW_POLICY,
|
|
29
|
+
normalizeJournalUserBindingNames,
|
|
30
|
+
LIVE_USER_BINDINGS_REUSE_POLICY,
|
|
31
|
+
LEGACY_USER_BINDINGS_REUSE_POLICY,
|
|
32
|
+
LEGACY_JOURNAL_VERSION,
|
|
33
|
+
USER_BINDING_RELATIONLESS_JOURNAL_VERSION,
|
|
34
|
+
FINGERPRINT_REUSE_JOURNAL_VERSION,
|
|
35
|
+
VERSIONED_BINDING_REUSE_JOURNAL_VERSION,
|
|
36
|
+
RECOVERY_BOUNDARY_EVENT,
|
|
37
|
+
STATUSES,
|
|
38
|
+
BINDING_MODES,
|
|
39
|
+
JOURNAL_FIELDS,
|
|
40
|
+
FINGERPRINT_REUSE_JOURNAL_FIELDS,
|
|
41
|
+
RELATIONLESS_JOURNAL_FIELDS,
|
|
42
|
+
PREDECESSOR_JOURNAL_FIELDS,
|
|
43
|
+
LEGACY_JOURNAL_FIELDS,
|
|
44
|
+
BINDING_POLICY_FIELDS,
|
|
45
|
+
REWRITE_POLICY_FIELDS,
|
|
46
|
+
MODULE_SEMANTICS_FIELDS,
|
|
47
|
+
LEGACY_MODULE_SEMANTICS_FIELDS,
|
|
48
|
+
IMPORT_EXPRESSION_BOUNDARIES,
|
|
49
|
+
DEFAULT_EXPORT_BINDINGS,
|
|
50
|
+
CALL_SUCCESS_FIELDS,
|
|
51
|
+
CALL_ERROR_FIELDS,
|
|
52
|
+
OPERATION_FIELDS,
|
|
53
|
+
RETURN_FIELDS,
|
|
54
|
+
THROW_FIELDS,
|
|
55
|
+
ERROR_FIELDS,
|
|
56
|
+
EDIT_TARGET_FIELDS,
|
|
57
|
+
DERIVED_RUN_FIELDS,
|
|
58
|
+
JOURNAL_VERSIONS,
|
|
59
|
+
USER_BINDINGS_REUSE_POLICIES,
|
|
60
|
+
RECOVERY_BOUNDARY_FIELDS,
|
|
61
|
+
REWRITE_FIELDS,
|
|
62
|
+
REWRITE_KINDS,
|
|
63
|
+
} from './session-journal-schema.js'
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
JOURNAL_KEY,
|
|
67
|
+
EDIT_TARGET_KEY,
|
|
68
|
+
DERIVED_RUN_KEY,
|
|
69
|
+
REWRITES_KEY,
|
|
70
|
+
RECOVERY_BOUNDARY_KEY,
|
|
71
|
+
JOURNAL_VERSION,
|
|
72
|
+
LIVE_USER_BINDINGS_REUSE_POLICY,
|
|
73
|
+
RECOVERY_BOUNDARY_EVENT,
|
|
74
|
+
} from './session-journal-schema.js'
|
|
41
75
|
|
|
42
76
|
function cloneJson(value) {
|
|
43
77
|
if (value === undefined) return undefined
|
|
@@ -197,11 +231,71 @@ function normalizeModuleSemantics(value) {
|
|
|
197
231
|
if (!DEFAULT_EXPORT_BINDINGS.has(value.defaultExportBinding)) {
|
|
198
232
|
throw new Error('invalid dsh-ptc-plus journal default export binding semantics')
|
|
199
233
|
}
|
|
200
|
-
|
|
234
|
+
if (!IMPORT_EXPRESSION_BOUNDARIES.has(value.importExpressionBoundary)) {
|
|
235
|
+
throw new Error('invalid dsh-ptc-plus journal import expression boundary semantics')
|
|
236
|
+
}
|
|
237
|
+
return Object.freeze({
|
|
238
|
+
defaultExportBinding: value.defaultExportBinding,
|
|
239
|
+
importExpressionBoundary: value.importExpressionBoundary,
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function migrateLegacyModuleSemantics(value) {
|
|
244
|
+
if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal module semantics')
|
|
245
|
+
assertOwnFields(value, LEGACY_MODULE_SEMANTICS_FIELDS, 'journal module semantics')
|
|
246
|
+
return { ...value, importExpressionBoundary: LEGACY_IMPORT_EXPRESSION_BOUNDARY }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function normalizeUserBindingsFingerprint(value) {
|
|
250
|
+
if (value !== null && (typeof value !== 'string' || !/^[a-f0-9]{64}$/.test(value))) {
|
|
251
|
+
throw new Error('invalid dsh-ptc-plus journal user binding fingerprint')
|
|
252
|
+
}
|
|
253
|
+
return value
|
|
201
254
|
}
|
|
202
255
|
|
|
203
256
|
function migrateJournal(value, resolveLegacyConfirm) {
|
|
204
257
|
if (value.version === JOURNAL_VERSION) return value
|
|
258
|
+
if (value.version === LANGUAGE_SEMANTICS_JOURNAL_VERSION) {
|
|
259
|
+
assertOwnFields(value, LANGUAGE_SEMANTICS_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
260
|
+
const languageSemantics = normalizeLanguageSemantics(value.languageSemantics)
|
|
261
|
+
return { ...value, version: JOURNAL_VERSION, moduleTransform: historicalModuleTransformForLanguage(languageSemantics) }
|
|
262
|
+
}
|
|
263
|
+
if (value.version === PER_NAME_USER_BINDINGS_JOURNAL_VERSION) {
|
|
264
|
+
assertOwnFields(value, PER_NAME_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
265
|
+
return { ...value, version: JOURNAL_VERSION, languageSemantics: LEGACY_LANGUAGE_SEMANTICS,
|
|
266
|
+
moduleTransform: LEGACY_USER_BINDING_TRANSFORM }
|
|
267
|
+
}
|
|
268
|
+
const shadow = { userBindingsShadowPolicy: LEGACY_USER_BINDINGS_SHADOW_POLICY, userBindingNames: null,
|
|
269
|
+
languageSemantics: LEGACY_LANGUAGE_SEMANTICS, moduleTransform: LEGACY_USER_BINDING_TRANSFORM }
|
|
270
|
+
if (value.version === IMPORT_BOUNDARY_JOURNAL_VERSION) {
|
|
271
|
+
assertOwnFields(value, WHOLE_ENTRY_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
272
|
+
return { ...value, version: JOURNAL_VERSION, ...shadow }
|
|
273
|
+
}
|
|
274
|
+
if (value.version === VERSIONED_BINDING_REUSE_JOURNAL_VERSION) {
|
|
275
|
+
assertOwnFields(value, WHOLE_ENTRY_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
276
|
+
return { ...value, version: JOURNAL_VERSION, ...shadow, moduleSemantics: migrateLegacyModuleSemantics(value.moduleSemantics) }
|
|
277
|
+
}
|
|
278
|
+
if (value.version === FINGERPRINT_REUSE_JOURNAL_VERSION) {
|
|
279
|
+
assertOwnFields(value, FINGERPRINT_REUSE_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
280
|
+
return {
|
|
281
|
+
...value,
|
|
282
|
+
version: JOURNAL_VERSION,
|
|
283
|
+
moduleSemantics: migrateLegacyModuleSemantics(value.moduleSemantics),
|
|
284
|
+
userBindingsReusePolicy: LEGACY_USER_BINDINGS_REUSE_POLICY,
|
|
285
|
+
...shadow,
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (value.version === USER_BINDING_RELATIONLESS_JOURNAL_VERSION) {
|
|
289
|
+
assertOwnFields(value, RELATIONLESS_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
290
|
+
return {
|
|
291
|
+
...value,
|
|
292
|
+
version: JOURNAL_VERSION,
|
|
293
|
+
moduleSemantics: migrateLegacyModuleSemantics(value.moduleSemantics),
|
|
294
|
+
userBindingsFingerprint: null,
|
|
295
|
+
userBindingsReusePolicy: LEGACY_USER_BINDINGS_REUSE_POLICY,
|
|
296
|
+
...shadow,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
205
299
|
const legacy = value.version === LEGACY_JOURNAL_VERSION
|
|
206
300
|
assertOwnFields(
|
|
207
301
|
value,
|
|
@@ -218,7 +312,13 @@ function migrateJournal(value, resolveLegacyConfirm) {
|
|
|
218
312
|
functionClassRedeclarations: false,
|
|
219
313
|
},
|
|
220
314
|
rewritePolicy: legacy ? LEGACY_REWRITE_POLICY : value.rewritePolicy,
|
|
221
|
-
moduleSemantics: {
|
|
315
|
+
moduleSemantics: {
|
|
316
|
+
defaultExportBinding: LEGACY_DEFAULT_EXPORT_BINDING,
|
|
317
|
+
importExpressionBoundary: LEGACY_IMPORT_EXPRESSION_BOUNDARY,
|
|
318
|
+
},
|
|
319
|
+
userBindingsFingerprint: null,
|
|
320
|
+
userBindingsReusePolicy: LEGACY_USER_BINDINGS_REUSE_POLICY,
|
|
321
|
+
...shadow,
|
|
222
322
|
confirms: legacy
|
|
223
323
|
? normalizeLegacyConfirms(value.confirms, resolveLegacyConfirm)
|
|
224
324
|
: value.confirms,
|
|
@@ -228,15 +328,23 @@ function migrateJournal(value, resolveLegacyConfirm) {
|
|
|
228
328
|
/** Validate and detach one journal emitted by the runtime. */
|
|
229
329
|
export function normalizeJournal(value, options = {}) {
|
|
230
330
|
if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal')
|
|
231
|
-
if (!
|
|
331
|
+
if (!JOURNAL_VERSIONS.has(value.version)
|
|
232
332
|
|| !STATUSES.has(value.status)) {
|
|
233
333
|
throw new Error('invalid dsh-ptc-plus journal')
|
|
234
334
|
}
|
|
235
335
|
const migrated = migrateJournal(value, options.resolveLegacyConfirm)
|
|
236
336
|
assertOwnFields(migrated, JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
337
|
+
const languageSemantics = normalizeLanguageSemantics(migrated.languageSemantics)
|
|
338
|
+
const moduleTransform = normalizeModuleTransform(migrated.moduleTransform, languageSemantics)
|
|
237
339
|
const bindingPolicy = normalizeBindingPolicy(migrated.bindingPolicy)
|
|
238
340
|
const rewritePolicy = normalizeRewritePolicy(migrated.rewritePolicy)
|
|
239
341
|
const moduleSemantics = normalizeModuleSemantics(migrated.moduleSemantics)
|
|
342
|
+
const userBindingsFingerprint = normalizeUserBindingsFingerprint(migrated.userBindingsFingerprint)
|
|
343
|
+
const userBindingsReusePolicy = migrated.userBindingsReusePolicy
|
|
344
|
+
const userBindingNames = normalizeJournalUserBindingNames(migrated)
|
|
345
|
+
if (!USER_BINDINGS_REUSE_POLICIES.has(userBindingsReusePolicy)) {
|
|
346
|
+
throw new Error('invalid dsh-ptc-plus journal user binding reuse policy')
|
|
347
|
+
}
|
|
240
348
|
const calls = normalizeCalls(migrated.calls)
|
|
241
349
|
const operations = normalizeOperations(migrated.operations)
|
|
242
350
|
const confirms = normalizeConfirms(migrated.confirms)
|
|
@@ -257,9 +365,15 @@ export function normalizeJournal(value, options = {}) {
|
|
|
257
365
|
}
|
|
258
366
|
return Object.freeze({
|
|
259
367
|
version: JOURNAL_VERSION,
|
|
368
|
+
languageSemantics,
|
|
369
|
+
moduleTransform,
|
|
260
370
|
bindingPolicy,
|
|
261
371
|
rewritePolicy,
|
|
262
372
|
moduleSemantics,
|
|
373
|
+
userBindingsFingerprint,
|
|
374
|
+
userBindingsReusePolicy,
|
|
375
|
+
userBindingsShadowPolicy: migrated.userBindingsShadowPolicy,
|
|
376
|
+
userBindingNames,
|
|
263
377
|
status: migrated.status,
|
|
264
378
|
calls: Object.freeze(calls),
|
|
265
379
|
operations: Object.freeze(operations),
|
|
@@ -292,15 +406,53 @@ export function normalizeDerivedEditResult(meta, expectedTargetCallSeq) {
|
|
|
292
406
|
const recoveryBoundaries = meta[RECOVERY_BOUNDARY_KEY] === undefined
|
|
293
407
|
? undefined
|
|
294
408
|
: normalizeRecoveryBoundaries(meta[RECOVERY_BOUNDARY_KEY])
|
|
409
|
+
const userBindings = userBindingsForJournal(meta, journal)
|
|
295
410
|
return Object.freeze({
|
|
296
411
|
targetCallSeq: target.targetCallSeq,
|
|
297
412
|
code: derived.code,
|
|
298
413
|
description: derived.description,
|
|
299
414
|
journal,
|
|
300
415
|
...(recoveryBoundaries === undefined ? {} : { recoveryBoundaries }),
|
|
416
|
+
...(userBindings === undefined ? {} : { userBindings }),
|
|
301
417
|
})
|
|
302
418
|
}
|
|
303
419
|
|
|
420
|
+
export function userBindingsForJournal(meta, journal) {
|
|
421
|
+
const userBindings = userBindingsSnapshotFromMeta(meta)
|
|
422
|
+
if (journal.userBindingsFingerprint === null) {
|
|
423
|
+
if (userBindings !== undefined) {
|
|
424
|
+
throw new Error('dsh-ptc-plus journal does not declare user binding metadata')
|
|
425
|
+
}
|
|
426
|
+
validateUserBindingNameProviders(journal, undefined)
|
|
427
|
+
return undefined
|
|
428
|
+
}
|
|
429
|
+
if (userBindings === undefined) {
|
|
430
|
+
throw new Error('dsh-ptc-plus journal requires user binding metadata')
|
|
431
|
+
}
|
|
432
|
+
if (userBindings.fingerprint !== journal.userBindingsFingerprint) {
|
|
433
|
+
throw new Error('dsh-ptc-plus journal user binding fingerprint does not match metadata')
|
|
434
|
+
}
|
|
435
|
+
validateUserBindingNameProviders(journal, userBindings)
|
|
436
|
+
return userBindings
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function validateUserBindingNameProviders(journal, snapshot) {
|
|
440
|
+
const providers = new Map((snapshot?.entries ?? []).flatMap(entry => (
|
|
441
|
+
(entry.scope === 'namespace' ? [entry.name] : entry.symbols).map(name => [name, entry.id])
|
|
442
|
+
)))
|
|
443
|
+
for (const fact of journal.userBindingNames ?? []) {
|
|
444
|
+
if (fact.state === 'provider' && providers.get(fact.name) !== fact.entryId) {
|
|
445
|
+
throw new Error('journal user binding name evidence has no matching provider')
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (journal.userBindingNames !== null) {
|
|
449
|
+
const names = new Set(journal.userBindingNames.map(fact => fact.name))
|
|
450
|
+
if ([...providers.keys()].some(name => !names.has(name))) {
|
|
451
|
+
throw new Error('journal user binding name evidence is incomplete')
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
304
456
|
/** Compare only the required persisted relation for one derived edit. */
|
|
305
457
|
export function derivedEditResultsEqual(leftMeta, rightMeta, expectedTargetCallSeq) {
|
|
306
458
|
try {
|
|
@@ -341,7 +493,7 @@ function normalizeRecoveryBoundaryValue(value, eventSeq = undefined) {
|
|
|
341
493
|
&& (!Number.isSafeInteger(value.frontierCallSeq) || value.frontierCallSeq < 0))) {
|
|
342
494
|
throw new Error('invalid dsh-ptc-plus recovery boundary')
|
|
343
495
|
}
|
|
344
|
-
assertOwnFields(value,
|
|
496
|
+
assertOwnFields(value, RECOVERY_BOUNDARY_FIELDS, 'recovery boundary')
|
|
345
497
|
if (eventSeq !== undefined && (!Number.isSafeInteger(eventSeq) || eventSeq < 0)) {
|
|
346
498
|
throw new Error('invalid dsh-ptc-plus recovery boundary event sequence')
|
|
347
499
|
}
|
|
@@ -443,7 +595,8 @@ export function liveToolCallSeq(session, callId, toolName) {
|
|
|
443
595
|
}
|
|
444
596
|
|
|
445
597
|
/** Start a mutable journal for one live cell. */
|
|
446
|
-
export function createJournal(confirms = [], bindingPolicy, rewritePolicy
|
|
598
|
+
export function createJournal(confirms = [], bindingPolicy, rewritePolicy, languageSemantics = LEGACY_LANGUAGE_SEMANTICS,
|
|
599
|
+
moduleTransform = moduleTransformForLanguage(languageSemantics)) {
|
|
447
600
|
if (typeof bindingPolicy === 'string') {
|
|
448
601
|
if (!BINDING_MODES.has(bindingPolicy)) throw new TypeError('invalid dsh-ptc-plus journal binding mode')
|
|
449
602
|
bindingPolicy = {
|
|
@@ -451,13 +604,18 @@ export function createJournal(confirms = [], bindingPolicy, rewritePolicy) {
|
|
|
451
604
|
functionClassRedeclarations: false,
|
|
452
605
|
}
|
|
453
606
|
}
|
|
607
|
+
languageSemantics = normalizeLanguageSemantics(languageSemantics)
|
|
454
608
|
return {
|
|
455
609
|
version: JOURNAL_VERSION,
|
|
610
|
+
languageSemantics,
|
|
611
|
+
moduleTransform: normalizeModuleTransform(moduleTransform, languageSemantics),
|
|
456
612
|
bindingPolicy: normalizeBindingPolicy(bindingPolicy),
|
|
457
613
|
rewritePolicy: normalizeRewritePolicy(rewritePolicy),
|
|
458
|
-
moduleSemantics: normalizeModuleSemantics(
|
|
459
|
-
|
|
460
|
-
|
|
614
|
+
moduleSemantics: normalizeModuleSemantics(LIVE_MODULE_SEMANTICS),
|
|
615
|
+
userBindingsFingerprint: null,
|
|
616
|
+
userBindingsReusePolicy: LIVE_USER_BINDINGS_REUSE_POLICY,
|
|
617
|
+
userBindingsShadowPolicy: LIVE_USER_BINDINGS_SHADOW_POLICY,
|
|
618
|
+
userBindingNames: null,
|
|
461
619
|
calls: [],
|
|
462
620
|
operations: [],
|
|
463
621
|
confirms: [...confirms],
|
|
@@ -465,15 +623,6 @@ export function createJournal(confirms = [], bindingPolicy, rewritePolicy) {
|
|
|
465
623
|
}
|
|
466
624
|
}
|
|
467
625
|
|
|
468
|
-
function sourceForRunCall(call) {
|
|
469
|
-
try {
|
|
470
|
-
const args = JSON.parse(call.data.arguments)
|
|
471
|
-
return isRecord(args) && typeof args.code === 'string' ? args.code : undefined
|
|
472
|
-
} catch {
|
|
473
|
-
return undefined
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
626
|
/** Return an owned state transition; reject volatile saves and unknown named restores. */
|
|
478
627
|
export function reduceStateOperations({ nodes, head, checkpoints }, operations, nodeIndex) {
|
|
479
628
|
const nextCheckpoints = new Map(checkpoints)
|
|
@@ -501,607 +650,6 @@ export function reduceStateOperations({ nodes, head, checkpoints }, operations,
|
|
|
501
650
|
return { head: nextHead, checkpoints: nextCheckpoints, restored }
|
|
502
651
|
}
|
|
503
652
|
|
|
504
|
-
function applyOperations(state, operations, nodeIndex) {
|
|
505
|
-
const transition = reduceStateOperations(state, operations, nodeIndex)
|
|
506
|
-
state.head = transition.head
|
|
507
|
-
state.checkpoints = transition.checkpoints
|
|
508
|
-
if (transition.restored) {
|
|
509
|
-
state.trusted = true
|
|
510
|
-
state.volatileSuffix.length = 0
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function applyRecord(state, record, invalidCallSeqs) {
|
|
515
|
-
const { call, code, result } = record
|
|
516
|
-
if (invalidCallSeqs.has(call.seq)) return
|
|
517
|
-
const journal = result.journal
|
|
518
|
-
if (journal.status === 'noop') return
|
|
519
|
-
if (journal.status === 'discarded') {
|
|
520
|
-
if (journal.volatileReason !== undefined) {
|
|
521
|
-
state.trusted = false
|
|
522
|
-
state.volatileSuffix.push({ seq: call.seq, code, reason: journal.volatileReason })
|
|
523
|
-
}
|
|
524
|
-
return
|
|
525
|
-
}
|
|
526
|
-
if (journal.status === 'volatile') {
|
|
527
|
-
state.trusted = false
|
|
528
|
-
state.volatileSuffix.push({ seq: call.seq, code, reason: journal.volatileReason ?? 'volatile cell' })
|
|
529
|
-
applyOperations(state, journal.operations, undefined)
|
|
530
|
-
return
|
|
531
|
-
}
|
|
532
|
-
if (!state.trusted && !state.surfaceContracted) {
|
|
533
|
-
state.trusted = true
|
|
534
|
-
state.volatileSuffix.length = 0
|
|
535
|
-
}
|
|
536
|
-
const node = Object.freeze({
|
|
537
|
-
code,
|
|
538
|
-
journal,
|
|
539
|
-
callSeq: call.seq,
|
|
540
|
-
parent: state.head,
|
|
541
|
-
})
|
|
542
|
-
const index = state.nodes.push(node) - 1
|
|
543
|
-
state.head = index
|
|
544
|
-
try {
|
|
545
|
-
applyOperations(state, journal.operations, index)
|
|
546
|
-
} catch (error) {
|
|
547
|
-
state.nodes.pop()
|
|
548
|
-
state.head = node.parent
|
|
549
|
-
throw error
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
function recordEventSeq(record) {
|
|
554
|
-
return record.result?.eventSeq ?? record.call.seq
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function foldRecords(records, invalidCallSeqs, options = {}) {
|
|
558
|
-
const state = {
|
|
559
|
-
nodes: [],
|
|
560
|
-
head: undefined,
|
|
561
|
-
checkpoints: new Map(),
|
|
562
|
-
volatileSuffix: [],
|
|
563
|
-
trusted: true,
|
|
564
|
-
surfaceContracted: options.surfaceContracted === true,
|
|
565
|
-
}
|
|
566
|
-
for (const record of records) {
|
|
567
|
-
applyRecord(state, record, invalidCallSeqs)
|
|
568
|
-
}
|
|
569
|
-
return state
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function dependsOn(nodes, index, ancestor) {
|
|
573
|
-
for (let cursor = index; cursor !== undefined; cursor = nodes[cursor]?.parent) {
|
|
574
|
-
if (cursor === ancestor) return true
|
|
575
|
-
}
|
|
576
|
-
return false
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/** Return executable call sequences whose provenance remains model-visible. */
|
|
580
|
-
export function visibleExecutableCallSeqs(session) {
|
|
581
|
-
const events = sessionEvents(session)
|
|
582
|
-
let nodes
|
|
583
|
-
try {
|
|
584
|
-
nodes = session?.surface?.nodes
|
|
585
|
-
} catch {
|
|
586
|
-
return undefined
|
|
587
|
-
}
|
|
588
|
-
if (!Array.isArray(events) || !Array.isArray(nodes)) return undefined
|
|
589
|
-
const eventBySeq = new Map()
|
|
590
|
-
for (const event of events) eventBySeq.set(event?.seq, event)
|
|
591
|
-
for (const seq of nodes) {
|
|
592
|
-
if (!Number.isSafeInteger(seq) || seq < 0 || eventBySeq.get(seq) === undefined) return undefined
|
|
593
|
-
}
|
|
594
|
-
const visible = new Set(nodes)
|
|
595
|
-
const resultSources = new Set()
|
|
596
|
-
const visibleAssistantCallSeqs = new Set()
|
|
597
|
-
const pendingAssistantCalls = new Map()
|
|
598
|
-
let assistantCallEvidence = false
|
|
599
|
-
for (const event of events) {
|
|
600
|
-
if (event?.type === 'turn/start' || event?.type === 'turn/end') {
|
|
601
|
-
pendingAssistantCalls.clear()
|
|
602
|
-
continue
|
|
603
|
-
}
|
|
604
|
-
if (event?.type === 'assistant/message') {
|
|
605
|
-
pendingAssistantCalls.clear()
|
|
606
|
-
const content = event.data?.message?.content
|
|
607
|
-
if (Array.isArray(content)) {
|
|
608
|
-
for (const part of content) {
|
|
609
|
-
if (part?.type === 'tool-call' && typeof part.id === 'string') {
|
|
610
|
-
const isVisible = visible.has(event.seq)
|
|
611
|
-
if (isVisible) assistantCallEvidence = true
|
|
612
|
-
let occurrences = pendingAssistantCalls.get(part.id)
|
|
613
|
-
if (occurrences === undefined) {
|
|
614
|
-
occurrences = []
|
|
615
|
-
pendingAssistantCalls.set(part.id, occurrences)
|
|
616
|
-
}
|
|
617
|
-
occurrences.push(isVisible)
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
continue
|
|
622
|
-
}
|
|
623
|
-
if (event?.type === 'tool/call' && typeof event.data?.callId === 'string') {
|
|
624
|
-
const occurrences = pendingAssistantCalls.get(event.data.callId)
|
|
625
|
-
if (occurrences !== undefined && occurrences.length > 0) {
|
|
626
|
-
const isVisible = occurrences.shift()
|
|
627
|
-
if (occurrences.length === 0) pendingAssistantCalls.delete(event.data.callId)
|
|
628
|
-
if (isVisible && ['run_code', 'edit_run_code'].includes(event.data.name)) {
|
|
629
|
-
visibleAssistantCallSeqs.add(event.seq)
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
if (visible.has(event?.seq)
|
|
634
|
-
&& event?.type === 'tool/result' && Array.isArray(event.sourceEventSeqs)) {
|
|
635
|
-
for (const sourceSeq of event.sourceEventSeqs) {
|
|
636
|
-
if (Number.isSafeInteger(sourceSeq) && sourceSeq >= 0) resultSources.add(sourceSeq)
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
const calls = new Set()
|
|
641
|
-
for (const event of events) {
|
|
642
|
-
if (event?.type !== 'tool/call' || !['run_code', 'edit_run_code'].includes(event.data?.name)) continue
|
|
643
|
-
if (assistantCallEvidence
|
|
644
|
-
? visibleAssistantCallSeqs.has(event.seq)
|
|
645
|
-
: (visible.has(event.seq) || resultSources.has(event.seq))) calls.add(event.seq)
|
|
646
|
-
}
|
|
647
|
-
return calls
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
function timelineRun(call, result, eventIndex, journal) {
|
|
651
|
-
let args
|
|
652
|
-
try {
|
|
653
|
-
const parsed = JSON.parse(call.data.arguments)
|
|
654
|
-
if (isRecord(parsed)) args = Object.freeze(parsed)
|
|
655
|
-
} catch {
|
|
656
|
-
// Invalid persisted arguments cannot support derived source facts.
|
|
657
|
-
}
|
|
658
|
-
return Object.freeze({
|
|
659
|
-
index: eventIndex,
|
|
660
|
-
callSeq: Number.isSafeInteger(call.seq) ? call.seq : undefined,
|
|
661
|
-
args,
|
|
662
|
-
source: typeof args?.code === 'string' ? args.code : undefined,
|
|
663
|
-
journal,
|
|
664
|
-
rewrites: validatedRewrites(result.data?.meta),
|
|
665
|
-
})
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function timelineDerivedRun(call, result, eventIndex, derived) {
|
|
669
|
-
return Object.freeze({
|
|
670
|
-
index: eventIndex,
|
|
671
|
-
callSeq: Number.isSafeInteger(call.seq) ? call.seq : undefined,
|
|
672
|
-
args: Object.freeze({ description: derived.description }),
|
|
673
|
-
source: derived.code,
|
|
674
|
-
journal: derived.journal,
|
|
675
|
-
rewrites: validatedRewrites(result.data?.meta),
|
|
676
|
-
})
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
function successfulTimelineRun(run) {
|
|
680
|
-
return run.journal?.completion?.kind === 'return' && run.journal.status !== 'noop'
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* Fold persisted tool events once into the call/result and edit-target timeline
|
|
685
|
-
* shared by prompt projection and cold journal recovery.
|
|
686
|
-
*/
|
|
687
|
-
export function foldSessionTimeline(events) {
|
|
688
|
-
const empty = {
|
|
689
|
-
openTurn: false,
|
|
690
|
-
executableCalls: new Map(),
|
|
691
|
-
calls: [],
|
|
692
|
-
results: new Map(),
|
|
693
|
-
boundaries: [],
|
|
694
|
-
found: false,
|
|
695
|
-
unavailableResultSeq: undefined,
|
|
696
|
-
lastSuccessfulRunIndex: undefined,
|
|
697
|
-
latestRun: undefined,
|
|
698
|
-
editableRun: undefined,
|
|
699
|
-
editTargets: new Map(),
|
|
700
|
-
}
|
|
701
|
-
if (!Array.isArray(events)) return empty
|
|
702
|
-
|
|
703
|
-
const state = { ...empty }
|
|
704
|
-
const pendingByCallId = new Map()
|
|
705
|
-
const seenCallIds = new Set()
|
|
706
|
-
const claimedEditTargets = new Set()
|
|
707
|
-
const editClaims = new Map()
|
|
708
|
-
const journalResultSeqs = new Set()
|
|
709
|
-
let pruneReplacementWindow
|
|
710
|
-
let scope = 0
|
|
711
|
-
|
|
712
|
-
const resetTurn = (openTurn) => {
|
|
713
|
-
state.openTurn = openTurn
|
|
714
|
-
scope += 1
|
|
715
|
-
pendingByCallId.clear()
|
|
716
|
-
seenCallIds.clear()
|
|
717
|
-
claimedEditTargets.clear()
|
|
718
|
-
state.latestRun = undefined
|
|
719
|
-
state.editableRun = undefined
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
for (let eventIndex = 0; eventIndex < events.length; eventIndex += 1) {
|
|
723
|
-
const event = events[eventIndex]
|
|
724
|
-
if (pruneReplacementWindow !== undefined
|
|
725
|
-
&& event?.type !== 'compaction/prune'
|
|
726
|
-
&& eventIndex !== pruneReplacementWindow.lastEventIndex + 1) {
|
|
727
|
-
pruneReplacementWindow = undefined
|
|
728
|
-
}
|
|
729
|
-
if (event?.type === 'turn/start') {
|
|
730
|
-
resetTurn(true)
|
|
731
|
-
continue
|
|
732
|
-
}
|
|
733
|
-
if (event?.type === 'turn/end') {
|
|
734
|
-
resetTurn(false)
|
|
735
|
-
continue
|
|
736
|
-
}
|
|
737
|
-
if (event?.type === RECOVERY_BOUNDARY_EVENT) {
|
|
738
|
-
state.unavailableResultSeq ??= Number.isSafeInteger(event.seq) ? event.seq : eventIndex
|
|
739
|
-
continue
|
|
740
|
-
}
|
|
741
|
-
if (event?.type === 'compaction/prune' && Array.isArray(event.data?.shadowedSeqs)) {
|
|
742
|
-
const shadowedSeqs = event.data.shadowedSeqs
|
|
743
|
-
const seqs = new Set(shadowedSeqs)
|
|
744
|
-
const shadowedRange = event.data.shadowedRange
|
|
745
|
-
const rangeValid = shadowedRange === undefined
|
|
746
|
-
|| (isRecord(shadowedRange)
|
|
747
|
-
&& shadowedRange.start === shadowedSeqs[0]
|
|
748
|
-
&& shadowedRange.end === shadowedSeqs[shadowedSeqs.length - 1])
|
|
749
|
-
const valid = shadowedSeqs.length > 0
|
|
750
|
-
&& seqs.size === shadowedSeqs.length
|
|
751
|
-
&& shadowedSeqs.every(seq => Number.isSafeInteger(seq) && seq >= 0)
|
|
752
|
-
&& shadowedSeqs.every((seq, index) => index === 0 || seq > shadowedSeqs[index - 1])
|
|
753
|
-
&& rangeValid
|
|
754
|
-
pruneReplacementWindow = { seqs: valid ? seqs : new Set(), lastEventIndex: eventIndex }
|
|
755
|
-
continue
|
|
756
|
-
}
|
|
757
|
-
if (event?.type === 'tool/call' && typeof event.data?.callId === 'string') {
|
|
758
|
-
const executable = event.data.name === 'run_code' || event.data.name === 'edit_run_code'
|
|
759
|
-
let entry = { event, eventIndex, scope }
|
|
760
|
-
if (executable) {
|
|
761
|
-
if (Number.isSafeInteger(event.seq) && event.seq >= 0 && state.executableCalls.has(event.seq)) {
|
|
762
|
-
throw new Error('session log contains a duplicate run_code call sequence')
|
|
763
|
-
}
|
|
764
|
-
if (event.data.name === 'edit_run_code') {
|
|
765
|
-
const targetCallSeq = state.editableRun?.callSeq
|
|
766
|
-
const target = targetCallSeq !== undefined && state.editableRun.source !== undefined
|
|
767
|
-
&& !claimedEditTargets.has(targetCallSeq)
|
|
768
|
-
? Object.freeze({ source: state.editableRun.source, callSeq: targetCallSeq })
|
|
769
|
-
: undefined
|
|
770
|
-
entry = { ...entry, editTarget: target }
|
|
771
|
-
state.editTargets.set(event.seq, target)
|
|
772
|
-
if (target !== undefined) {
|
|
773
|
-
claimedEditTargets.add(target.callSeq)
|
|
774
|
-
editClaims.set(event.seq, target.callSeq)
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
state.calls.push(event)
|
|
778
|
-
if (Number.isSafeInteger(event.seq) && event.seq >= 0) {
|
|
779
|
-
state.executableCalls.set(event.seq, entry)
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
if (seenCallIds.has(event.data.callId)) pendingByCallId.set(event.data.callId, null)
|
|
783
|
-
else {
|
|
784
|
-
seenCallIds.add(event.data.callId)
|
|
785
|
-
pendingByCallId.set(event.data.callId, entry)
|
|
786
|
-
}
|
|
787
|
-
continue
|
|
788
|
-
}
|
|
789
|
-
if (event?.type !== 'tool/result') continue
|
|
790
|
-
|
|
791
|
-
const sourceSeq = event.sourceEventSeqs?.[0]
|
|
792
|
-
const callId = event.data?.message?.source?.callId
|
|
793
|
-
let entry = Number.isSafeInteger(sourceSeq) ? state.executableCalls.get(sourceSeq) : undefined
|
|
794
|
-
let prunedReplacement = false
|
|
795
|
-
if (entry === undefined && Number.isSafeInteger(sourceSeq)
|
|
796
|
-
&& pruneReplacementWindow?.seqs.has(sourceSeq)
|
|
797
|
-
&& eventIndex === pruneReplacementWindow.lastEventIndex + 1
|
|
798
|
-
&& Array.isArray(event.sourceEventSeqs) && event.sourceEventSeqs.length === 1
|
|
799
|
-
&& typeof callId === 'string') {
|
|
800
|
-
const candidate = pendingByCallId.get(callId)
|
|
801
|
-
const candidateSeq = candidate?.event?.seq
|
|
802
|
-
const pruneSeq = events[pruneReplacementWindow.lastEventIndex]?.seq
|
|
803
|
-
const resultEventSeq = event.seq
|
|
804
|
-
const surfaceOp = event.surfaceOp
|
|
805
|
-
if (candidate !== undefined && candidate !== null
|
|
806
|
-
&& (candidate.event.data.name === 'run_code' || candidate.event.data.name === 'edit_run_code')
|
|
807
|
-
&& Number.isSafeInteger(candidateSeq) && candidateSeq >= 0
|
|
808
|
-
&& sourceSeq > candidateSeq
|
|
809
|
-
&& Number.isSafeInteger(pruneSeq) && pruneSeq >= 0
|
|
810
|
-
&& Number.isSafeInteger(resultEventSeq) && resultEventSeq - pruneSeq === 1
|
|
811
|
-
&& sourceSeq < pruneSeq
|
|
812
|
-
&& (surfaceOp === undefined
|
|
813
|
-
|| (surfaceOp?.op === 'replace' && surfaceOp.start === sourceSeq && surfaceOp.end === sourceSeq))) {
|
|
814
|
-
entry = candidate
|
|
815
|
-
prunedReplacement = true
|
|
816
|
-
pruneReplacementWindow.lastEventIndex = eventIndex
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
if (entry === undefined && !Number.isSafeInteger(sourceSeq) && typeof callId === 'string') {
|
|
820
|
-
entry = pendingByCallId.get(callId)
|
|
821
|
-
}
|
|
822
|
-
if (typeof callId === 'string') pendingByCallId.delete(callId)
|
|
823
|
-
if (entry === null) {
|
|
824
|
-
state.latestRun = undefined
|
|
825
|
-
state.editableRun = undefined
|
|
826
|
-
continue
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
const call = entry?.event
|
|
830
|
-
const callSeq = call?.seq
|
|
831
|
-
const meta = event.data?.meta
|
|
832
|
-
let normalized
|
|
833
|
-
if (isRecord(meta) && Object.hasOwn(meta, JOURNAL_KEY)) {
|
|
834
|
-
if (Number.isSafeInteger(sourceSeq) && journalResultSeqs.has(sourceSeq)) {
|
|
835
|
-
state.unavailableResultSeq ??= sourceSeq
|
|
836
|
-
state.results.set(sourceSeq, {
|
|
837
|
-
eventSeq: Number.isSafeInteger(event.seq) && event.seq >= 0 ? event.seq : sourceSeq,
|
|
838
|
-
eventIndex,
|
|
839
|
-
error: `session log contains duplicate PTC journal results for call seq ${sourceSeq}`,
|
|
840
|
-
})
|
|
841
|
-
state.found = true
|
|
842
|
-
continue
|
|
843
|
-
}
|
|
844
|
-
if (Number.isSafeInteger(sourceSeq)) journalResultSeqs.add(sourceSeq)
|
|
845
|
-
if (entry === undefined) {
|
|
846
|
-
if (Number.isSafeInteger(sourceSeq)) {
|
|
847
|
-
state.unavailableResultSeq ??= sourceSeq
|
|
848
|
-
}
|
|
849
|
-
continue
|
|
850
|
-
}
|
|
851
|
-
const resultSeq = prunedReplacement
|
|
852
|
-
? callSeq
|
|
853
|
-
: Number.isSafeInteger(sourceSeq) ? sourceSeq : callSeq
|
|
854
|
-
const raw = {
|
|
855
|
-
meta,
|
|
856
|
-
eventSeq: Number.isSafeInteger(event.seq) && event.seq >= 0 ? event.seq : resultSeq,
|
|
857
|
-
eventIndex,
|
|
858
|
-
}
|
|
859
|
-
if (Object.hasOwn(meta, RECOVERY_BOUNDARY_KEY)) {
|
|
860
|
-
try {
|
|
861
|
-
state.boundaries.push(...normalizeRecoveryBoundaries(
|
|
862
|
-
meta[RECOVERY_BOUNDARY_KEY], raw.eventSeq,
|
|
863
|
-
))
|
|
864
|
-
} catch {
|
|
865
|
-
state.unavailableResultSeq ??= callSeq
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
try {
|
|
869
|
-
if (call.data.name === 'edit_run_code') {
|
|
870
|
-
const derived = normalizeDerivedEditResult(meta, entry.editTarget?.callSeq)
|
|
871
|
-
normalized = { ...raw, journal: derived.journal, derived }
|
|
872
|
-
} else if (call.data.name === 'run_code') {
|
|
873
|
-
const rawJournal = meta[JOURNAL_KEY]
|
|
874
|
-
const resolveLegacyConfirm = rawJournal?.version === LEGACY_JOURNAL_VERSION
|
|
875
|
-
? legacyCallId => {
|
|
876
|
-
const candidates = [...state.executableCalls.values()]
|
|
877
|
-
.filter(candidate => candidate.eventIndex < eventIndex
|
|
878
|
-
&& candidate.event.data?.name === 'run_code'
|
|
879
|
-
&& candidate.event.data.callId === legacyCallId
|
|
880
|
-
&& !state.results.has(candidate.event.seq))
|
|
881
|
-
return candidates.length === 1 ? candidates[0].event.seq : undefined
|
|
882
|
-
}
|
|
883
|
-
: undefined
|
|
884
|
-
normalized = { ...raw, journal: normalizeJournal(rawJournal, { resolveLegacyConfirm }) }
|
|
885
|
-
}
|
|
886
|
-
} catch (error) {
|
|
887
|
-
normalized = { ...raw, error: error.message }
|
|
888
|
-
}
|
|
889
|
-
if (Number.isSafeInteger(resultSeq)) state.results.set(resultSeq, normalized)
|
|
890
|
-
state.found = true
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
const claimedTarget = editClaims.get(callSeq)
|
|
894
|
-
if (claimedTarget !== undefined) {
|
|
895
|
-
editClaims.delete(callSeq)
|
|
896
|
-
if (normalized?.derived === undefined) claimedEditTargets.delete(claimedTarget)
|
|
897
|
-
}
|
|
898
|
-
if (entry === undefined || entry.scope !== scope) continue
|
|
899
|
-
if (call.data.name === 'edit_run_code') {
|
|
900
|
-
if (normalized?.derived !== undefined) {
|
|
901
|
-
const run = timelineDerivedRun(call, event, eventIndex, normalized.derived)
|
|
902
|
-
state.latestRun = run
|
|
903
|
-
state.editableRun = run
|
|
904
|
-
if (successfulTimelineRun(run)) state.lastSuccessfulRunIndex = eventIndex
|
|
905
|
-
}
|
|
906
|
-
continue
|
|
907
|
-
}
|
|
908
|
-
if (call.data.name !== 'run_code') {
|
|
909
|
-
// Unrelated native settlements do not change the current editable cell.
|
|
910
|
-
// The edit target is captured at dispatch time and remains valid until a
|
|
911
|
-
// new executable cell or turn boundary supersedes it.
|
|
912
|
-
continue
|
|
913
|
-
}
|
|
914
|
-
const run = timelineRun(call, event, eventIndex, normalized?.journal)
|
|
915
|
-
state.latestRun = run
|
|
916
|
-
state.editableRun = run
|
|
917
|
-
if (successfulTimelineRun(run)) state.lastSuccessfulRunIndex = eventIndex
|
|
918
|
-
}
|
|
919
|
-
return state
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
/** Fold the session log into the last exactly replayable frontier. */
|
|
923
|
-
export function recoverJournal(session, currentCallSeq, options = {}) {
|
|
924
|
-
const events = sessionEvents(session)
|
|
925
|
-
if (!Array.isArray(events)) {
|
|
926
|
-
return { nodes: [], head: undefined, checkpoints: new Map(), volatileSuffix: [], available: true }
|
|
927
|
-
}
|
|
928
|
-
const timeline = foldSessionTimeline(events)
|
|
929
|
-
const calls = timeline.calls.filter(call => call.seq !== currentCallSeq)
|
|
930
|
-
const { executableCalls, results, found } = timeline
|
|
931
|
-
const visibleCalls = options?.visibleCallSeqs
|
|
932
|
-
let unavailableSeq = timeline.unavailableResultSeq
|
|
933
|
-
let unavailableReason = 'unavailable or malformed dsh-ptc-plus journal result'
|
|
934
|
-
for (const [resultSeq, result] of results.entries()) {
|
|
935
|
-
if (result?.error !== undefined && Number.isSafeInteger(resultSeq)) {
|
|
936
|
-
unavailableSeq ??= resultSeq
|
|
937
|
-
unavailableReason = result.error
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
const extraBoundaries = options?.extraBoundaries ?? []
|
|
941
|
-
let normalizedExtraBoundaries = []
|
|
942
|
-
try {
|
|
943
|
-
normalizedExtraBoundaries = normalizeRecoveryBoundaries(extraBoundaries, undefined)
|
|
944
|
-
} catch {
|
|
945
|
-
normalizedExtraBoundaries = []
|
|
946
|
-
}
|
|
947
|
-
const boundaries = [
|
|
948
|
-
...timeline.boundaries,
|
|
949
|
-
...normalizedExtraBoundaries.map(boundary => ({
|
|
950
|
-
...boundary,
|
|
951
|
-
eventSeq: Number.POSITIVE_INFINITY,
|
|
952
|
-
})),
|
|
953
|
-
].sort((left, right) => left.eventSeq - right.eventSeq)
|
|
954
|
-
// An unavailable historical result contracts the replay frontier. It must
|
|
955
|
-
// not prevent the current request from executing on the verified prefix.
|
|
956
|
-
|
|
957
|
-
const confirmedNoops = new Set()
|
|
958
|
-
const invalidResultReasons = new Map()
|
|
959
|
-
for (const [resultSeq, { journal, eventIndex }] of results.entries()) {
|
|
960
|
-
const candidateConfirms = []
|
|
961
|
-
for (const callSeq of journal?.confirms ?? []) {
|
|
962
|
-
const confirmed = executableCalls.get(callSeq)
|
|
963
|
-
if (confirmed === undefined || confirmed.eventIndex >= eventIndex || results.has(callSeq)) {
|
|
964
|
-
const reason = `confirmed no-op does not identify an earlier unjournaled run_code call seq ${callSeq}`
|
|
965
|
-
invalidResultReasons.set(resultSeq, reason)
|
|
966
|
-
unavailableSeq ??= resultSeq
|
|
967
|
-
unavailableReason = reason
|
|
968
|
-
break
|
|
969
|
-
}
|
|
970
|
-
candidateConfirms.push(callSeq)
|
|
971
|
-
}
|
|
972
|
-
if (!invalidResultReasons.has(resultSeq)) {
|
|
973
|
-
for (const callSeq of candidateConfirms) confirmedNoops.add(callSeq)
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
const records = []
|
|
977
|
-
const orderedRecords = []
|
|
978
|
-
const invalidCallSeqs = new Set()
|
|
979
|
-
let state = foldRecords(records, invalidCallSeqs)
|
|
980
|
-
let blockedUnavailableSeq = unavailableSeq
|
|
981
|
-
let boundaryIndex = 0
|
|
982
|
-
const applyBoundariesBefore = (seq) => {
|
|
983
|
-
while (boundaryIndex < boundaries.length && boundaries[boundaryIndex].eventSeq <= seq) {
|
|
984
|
-
const boundary = boundaries[boundaryIndex++]
|
|
985
|
-
const failedCall = executableCalls.get(boundary.failedCallSeq)
|
|
986
|
-
if (failedCall === undefined || failedCall.event.seq >= boundary.eventSeq) continue
|
|
987
|
-
const failedIndex = state.nodes.findIndex(node => node.callSeq === boundary.failedCallSeq)
|
|
988
|
-
const expectedFrontier = failedIndex < 0 ? state.head : state.nodes[failedIndex].parent
|
|
989
|
-
const frontierIndex = boundary.frontierCallSeq === null
|
|
990
|
-
? undefined
|
|
991
|
-
: state.nodes.findIndex(node => node.callSeq === boundary.frontierCallSeq)
|
|
992
|
-
if (frontierIndex !== expectedFrontier) continue
|
|
993
|
-
const resolvesUnavailable = blockedUnavailableSeq === boundary.failedCallSeq
|
|
994
|
-
if (failedIndex < 0 && !resolvesUnavailable) continue
|
|
995
|
-
for (let index = 0; index < state.nodes.length; index += 1) {
|
|
996
|
-
if (dependsOn(state.nodes, index, failedIndex)) invalidCallSeqs.add(state.nodes[index].callSeq)
|
|
997
|
-
}
|
|
998
|
-
if (failedIndex < 0) {
|
|
999
|
-
for (const record of records) {
|
|
1000
|
-
if (record.call.seq >= boundary.failedCallSeq) invalidCallSeqs.add(record.call.seq)
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
state = foldRecords(records, invalidCallSeqs, {
|
|
1004
|
-
surfaceContracted: resolvesUnavailable ? false : state.surfaceContracted,
|
|
1005
|
-
})
|
|
1006
|
-
if (resolvesUnavailable) {
|
|
1007
|
-
blockedUnavailableSeq = undefined
|
|
1008
|
-
if (unavailableSeq === boundary.failedCallSeq) unavailableSeq = undefined
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
for (const call of calls) {
|
|
1013
|
-
const result = results.get(call.seq)
|
|
1014
|
-
const code = call.data?.name === 'edit_run_code' ? result?.derived?.code : sourceForRunCall(call)
|
|
1015
|
-
const record = {
|
|
1016
|
-
call,
|
|
1017
|
-
code,
|
|
1018
|
-
result: invalidResultReasons.has(call.seq)
|
|
1019
|
-
? { ...result, journal: undefined, error: invalidResultReasons.get(call.seq) }
|
|
1020
|
-
: result,
|
|
1021
|
-
hidden: visibleCalls instanceof Set && !visibleCalls.has(call.seq),
|
|
1022
|
-
}
|
|
1023
|
-
if (call.data?.name === 'edit_run_code' && record.code === undefined && result === undefined) continue
|
|
1024
|
-
if (result?.journal === undefined && confirmedNoops.has(call.seq)) continue
|
|
1025
|
-
orderedRecords.push(record)
|
|
1026
|
-
}
|
|
1027
|
-
orderedRecords.sort((left, right) => (
|
|
1028
|
-
recordEventSeq(left) - recordEventSeq(right) || left.call.seq - right.call.seq
|
|
1029
|
-
))
|
|
1030
|
-
for (const record of orderedRecords) {
|
|
1031
|
-
applyBoundariesBefore(recordEventSeq(record))
|
|
1032
|
-
records.push(record)
|
|
1033
|
-
if (record.hidden) {
|
|
1034
|
-
blockedUnavailableSeq ??= record.call.seq
|
|
1035
|
-
unavailableSeq ??= record.call.seq
|
|
1036
|
-
unavailableReason = 'model-visible provenance was shadowed'
|
|
1037
|
-
invalidCallSeqs.add(record.call.seq)
|
|
1038
|
-
state.surfaceContracted = true
|
|
1039
|
-
state.trusted = false
|
|
1040
|
-
state.volatileSuffix.push({ seq: record.call.seq, code: undefined, reason: unavailableReason })
|
|
1041
|
-
continue
|
|
1042
|
-
}
|
|
1043
|
-
if (blockedUnavailableSeq !== undefined && record.call.seq >= blockedUnavailableSeq) {
|
|
1044
|
-
invalidCallSeqs.add(record.call.seq)
|
|
1045
|
-
if (!state.volatileSuffix.some(item => item.seq === record.call.seq)) {
|
|
1046
|
-
state.trusted = false
|
|
1047
|
-
state.volatileSuffix.push({
|
|
1048
|
-
seq: record.call.seq,
|
|
1049
|
-
code: record.code,
|
|
1050
|
-
reason: unavailableReason,
|
|
1051
|
-
})
|
|
1052
|
-
}
|
|
1053
|
-
continue
|
|
1054
|
-
}
|
|
1055
|
-
if (record.result?.journal === undefined) {
|
|
1056
|
-
blockedUnavailableSeq ??= record.call.seq
|
|
1057
|
-
unavailableSeq ??= record.call.seq
|
|
1058
|
-
unavailableReason = record.result?.error ?? 'missing dsh-ptc-plus journal result'
|
|
1059
|
-
invalidCallSeqs.add(record.call.seq)
|
|
1060
|
-
state.trusted = false
|
|
1061
|
-
state.volatileSuffix.push({ seq: record.call.seq, code: record.code, reason: unavailableReason })
|
|
1062
|
-
continue
|
|
1063
|
-
}
|
|
1064
|
-
try {
|
|
1065
|
-
applyRecord(state, record, invalidCallSeqs)
|
|
1066
|
-
} catch (error) {
|
|
1067
|
-
unavailableSeq ??= record.call.seq
|
|
1068
|
-
blockedUnavailableSeq ??= record.call.seq
|
|
1069
|
-
unavailableReason = error.message
|
|
1070
|
-
invalidCallSeqs.add(record.call.seq)
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
applyBoundariesBefore(Number.POSITIVE_INFINITY)
|
|
1074
|
-
if (unavailableSeq !== undefined
|
|
1075
|
-
&& !state.volatileSuffix.some(item => item.seq === unavailableSeq)) {
|
|
1076
|
-
state.trusted = false
|
|
1077
|
-
state.volatileSuffix.push({
|
|
1078
|
-
seq: unavailableSeq,
|
|
1079
|
-
code: undefined,
|
|
1080
|
-
reason: unavailableReason,
|
|
1081
|
-
})
|
|
1082
|
-
}
|
|
1083
|
-
return {
|
|
1084
|
-
nodes: state.nodes,
|
|
1085
|
-
head: state.head,
|
|
1086
|
-
checkpoints: state.checkpoints,
|
|
1087
|
-
volatileSuffix: state.volatileSuffix,
|
|
1088
|
-
available: found && !state.surfaceContracted && blockedUnavailableSeq === undefined,
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
/** Return source nodes from the empty state to a selected durable head. */
|
|
1093
|
-
export function pathToHead(state) {
|
|
1094
|
-
const path = []
|
|
1095
|
-
for (let cursor = state.head; cursor !== undefined;) {
|
|
1096
|
-
const node = state.nodes[cursor]
|
|
1097
|
-
if (node === undefined) throw new Error('invalid dsh-ptc-plus journal head')
|
|
1098
|
-
path.push(node)
|
|
1099
|
-
cursor = node.parent
|
|
1100
|
-
}
|
|
1101
|
-
path.reverse()
|
|
1102
|
-
return path
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
653
|
/** Merge the journal into the tool result's private metadata. */
|
|
1106
654
|
export function withJournal(meta, journal) {
|
|
1107
655
|
const base = isRecord(meta) ? { ...meta } : meta === undefined ? {} : { value: cloneJson(meta) }
|
|
@@ -1109,9 +657,6 @@ export function withJournal(meta, journal) {
|
|
|
1109
657
|
return base
|
|
1110
658
|
}
|
|
1111
659
|
|
|
1112
|
-
const REWRITE_FIELDS = new Set(['kind', 'description', 'source'])
|
|
1113
|
-
const REWRITE_KINDS = new Set(['import', 'redeclaration', 'export'])
|
|
1114
|
-
|
|
1115
660
|
/** Validate and detach one rewrite record emitted by the runtime. */
|
|
1116
661
|
export function normalizeRewrites(value) {
|
|
1117
662
|
if (!Array.isArray(value)) throw new Error('invalid dsh-ptc-plus rewrites')
|