dsh-ptc-plus 0.3.2 → 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 +65 -78
- package/README.md +65 -78
- 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 +30596 -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 +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 +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 +73 -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 +25 -10
- package/docs/evaluation.md +78 -6
- package/docs/installation.md +41 -6
- package/docs/publishing.md +5 -3
- package/docs/runtime-reference.md +72 -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 +369 -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 +240 -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 +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 +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 +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 +27 -2
- 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 +904 -120
- 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 -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 +114 -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-messages.js +111 -0
- package/internal/session-cell-executor.js +401 -87
- package/internal/session-journal-recovery.js +684 -0
- package/internal/session-journal-schema.js +106 -0
- package/internal/session-journal.js +184 -654
- package/internal/session-log-view.js +42 -9
- package/internal/session-runtime.js +256 -95
- package/internal/session-state.js +247 -71
- 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 +75 -7
- package/src/client-activity.js +88 -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,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
|
+
import { LEGACY_LANGUAGE_SEMANTICS, normalizeLanguageSemantics } from './language-semantics.js'
|
|
5
6
|
import {
|
|
6
7
|
LEGACY_DEFAULT_EXPORT_BINDING,
|
|
7
|
-
|
|
8
|
+
LEGACY_IMPORT_EXPRESSION_BOUNDARY,
|
|
9
|
+
LIVE_MODULE_SEMANTICS,
|
|
8
10
|
} 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
|
-
|
|
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'
|
|
41
71
|
|
|
42
72
|
function cloneJson(value) {
|
|
43
73
|
if (value === undefined) return undefined
|
|
@@ -197,11 +227,65 @@ function normalizeModuleSemantics(value) {
|
|
|
197
227
|
if (!DEFAULT_EXPORT_BINDINGS.has(value.defaultExportBinding)) {
|
|
198
228
|
throw new Error('invalid dsh-ptc-plus journal default export binding semantics')
|
|
199
229
|
}
|
|
200
|
-
|
|
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
|
|
201
250
|
}
|
|
202
251
|
|
|
203
252
|
function migrateJournal(value, resolveLegacyConfirm) {
|
|
204
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
|
+
}
|
|
205
289
|
const legacy = value.version === LEGACY_JOURNAL_VERSION
|
|
206
290
|
assertOwnFields(
|
|
207
291
|
value,
|
|
@@ -218,7 +302,13 @@ function migrateJournal(value, resolveLegacyConfirm) {
|
|
|
218
302
|
functionClassRedeclarations: false,
|
|
219
303
|
},
|
|
220
304
|
rewritePolicy: legacy ? LEGACY_REWRITE_POLICY : value.rewritePolicy,
|
|
221
|
-
moduleSemantics: {
|
|
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,
|
|
222
312
|
confirms: legacy
|
|
223
313
|
? normalizeLegacyConfirms(value.confirms, resolveLegacyConfirm)
|
|
224
314
|
: value.confirms,
|
|
@@ -228,15 +318,22 @@ function migrateJournal(value, resolveLegacyConfirm) {
|
|
|
228
318
|
/** Validate and detach one journal emitted by the runtime. */
|
|
229
319
|
export function normalizeJournal(value, options = {}) {
|
|
230
320
|
if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal')
|
|
231
|
-
if (!
|
|
321
|
+
if (!JOURNAL_VERSIONS.has(value.version)
|
|
232
322
|
|| !STATUSES.has(value.status)) {
|
|
233
323
|
throw new Error('invalid dsh-ptc-plus journal')
|
|
234
324
|
}
|
|
235
325
|
const migrated = migrateJournal(value, options.resolveLegacyConfirm)
|
|
236
326
|
assertOwnFields(migrated, JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
327
|
+
const languageSemantics = normalizeLanguageSemantics(migrated.languageSemantics)
|
|
237
328
|
const bindingPolicy = normalizeBindingPolicy(migrated.bindingPolicy)
|
|
238
329
|
const rewritePolicy = normalizeRewritePolicy(migrated.rewritePolicy)
|
|
239
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
|
+
}
|
|
240
337
|
const calls = normalizeCalls(migrated.calls)
|
|
241
338
|
const operations = normalizeOperations(migrated.operations)
|
|
242
339
|
const confirms = normalizeConfirms(migrated.confirms)
|
|
@@ -257,9 +354,14 @@ export function normalizeJournal(value, options = {}) {
|
|
|
257
354
|
}
|
|
258
355
|
return Object.freeze({
|
|
259
356
|
version: JOURNAL_VERSION,
|
|
357
|
+
languageSemantics,
|
|
260
358
|
bindingPolicy,
|
|
261
359
|
rewritePolicy,
|
|
262
360
|
moduleSemantics,
|
|
361
|
+
userBindingsFingerprint,
|
|
362
|
+
userBindingsReusePolicy,
|
|
363
|
+
userBindingsShadowPolicy: migrated.userBindingsShadowPolicy,
|
|
364
|
+
userBindingNames,
|
|
263
365
|
status: migrated.status,
|
|
264
366
|
calls: Object.freeze(calls),
|
|
265
367
|
operations: Object.freeze(operations),
|
|
@@ -292,15 +394,53 @@ export function normalizeDerivedEditResult(meta, expectedTargetCallSeq) {
|
|
|
292
394
|
const recoveryBoundaries = meta[RECOVERY_BOUNDARY_KEY] === undefined
|
|
293
395
|
? undefined
|
|
294
396
|
: normalizeRecoveryBoundaries(meta[RECOVERY_BOUNDARY_KEY])
|
|
397
|
+
const userBindings = userBindingsForJournal(meta, journal)
|
|
295
398
|
return Object.freeze({
|
|
296
399
|
targetCallSeq: target.targetCallSeq,
|
|
297
400
|
code: derived.code,
|
|
298
401
|
description: derived.description,
|
|
299
402
|
journal,
|
|
300
403
|
...(recoveryBoundaries === undefined ? {} : { recoveryBoundaries }),
|
|
404
|
+
...(userBindings === undefined ? {} : { userBindings }),
|
|
301
405
|
})
|
|
302
406
|
}
|
|
303
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
|
+
|
|
304
444
|
/** Compare only the required persisted relation for one derived edit. */
|
|
305
445
|
export function derivedEditResultsEqual(leftMeta, rightMeta, expectedTargetCallSeq) {
|
|
306
446
|
try {
|
|
@@ -341,7 +481,7 @@ function normalizeRecoveryBoundaryValue(value, eventSeq = undefined) {
|
|
|
341
481
|
&& (!Number.isSafeInteger(value.frontierCallSeq) || value.frontierCallSeq < 0))) {
|
|
342
482
|
throw new Error('invalid dsh-ptc-plus recovery boundary')
|
|
343
483
|
}
|
|
344
|
-
assertOwnFields(value,
|
|
484
|
+
assertOwnFields(value, RECOVERY_BOUNDARY_FIELDS, 'recovery boundary')
|
|
345
485
|
if (eventSeq !== undefined && (!Number.isSafeInteger(eventSeq) || eventSeq < 0)) {
|
|
346
486
|
throw new Error('invalid dsh-ptc-plus recovery boundary event sequence')
|
|
347
487
|
}
|
|
@@ -443,7 +583,7 @@ export function liveToolCallSeq(session, callId, toolName) {
|
|
|
443
583
|
}
|
|
444
584
|
|
|
445
585
|
/** Start a mutable journal for one live cell. */
|
|
446
|
-
export function createJournal(confirms = [], bindingPolicy, rewritePolicy) {
|
|
586
|
+
export function createJournal(confirms = [], bindingPolicy, rewritePolicy, languageSemantics = LEGACY_LANGUAGE_SEMANTICS) {
|
|
447
587
|
if (typeof bindingPolicy === 'string') {
|
|
448
588
|
if (!BINDING_MODES.has(bindingPolicy)) throw new TypeError('invalid dsh-ptc-plus journal binding mode')
|
|
449
589
|
bindingPolicy = {
|
|
@@ -453,11 +593,14 @@ export function createJournal(confirms = [], bindingPolicy, rewritePolicy) {
|
|
|
453
593
|
}
|
|
454
594
|
return {
|
|
455
595
|
version: JOURNAL_VERSION,
|
|
596
|
+
languageSemantics: normalizeLanguageSemantics(languageSemantics),
|
|
456
597
|
bindingPolicy: normalizeBindingPolicy(bindingPolicy),
|
|
457
598
|
rewritePolicy: normalizeRewritePolicy(rewritePolicy),
|
|
458
|
-
moduleSemantics: normalizeModuleSemantics(
|
|
459
|
-
|
|
460
|
-
|
|
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,
|
|
461
604
|
calls: [],
|
|
462
605
|
operations: [],
|
|
463
606
|
confirms: [...confirms],
|
|
@@ -465,15 +608,6 @@ export function createJournal(confirms = [], bindingPolicy, rewritePolicy) {
|
|
|
465
608
|
}
|
|
466
609
|
}
|
|
467
610
|
|
|
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
611
|
/** Return an owned state transition; reject volatile saves and unknown named restores. */
|
|
478
612
|
export function reduceStateOperations({ nodes, head, checkpoints }, operations, nodeIndex) {
|
|
479
613
|
const nextCheckpoints = new Map(checkpoints)
|
|
@@ -501,607 +635,6 @@ export function reduceStateOperations({ nodes, head, checkpoints }, operations,
|
|
|
501
635
|
return { head: nextHead, checkpoints: nextCheckpoints, restored }
|
|
502
636
|
}
|
|
503
637
|
|
|
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
638
|
/** Merge the journal into the tool result's private metadata. */
|
|
1106
639
|
export function withJournal(meta, journal) {
|
|
1107
640
|
const base = isRecord(meta) ? { ...meta } : meta === undefined ? {} : { value: cloneJson(meta) }
|
|
@@ -1109,9 +642,6 @@ export function withJournal(meta, journal) {
|
|
|
1109
642
|
return base
|
|
1110
643
|
}
|
|
1111
644
|
|
|
1112
|
-
const REWRITE_FIELDS = new Set(['kind', 'description', 'source'])
|
|
1113
|
-
const REWRITE_KINDS = new Set(['import', 'redeclaration', 'export'])
|
|
1114
|
-
|
|
1115
645
|
/** Validate and detach one rewrite record emitted by the runtime. */
|
|
1116
646
|
export function normalizeRewrites(value) {
|
|
1117
647
|
if (!Array.isArray(value)) throw new Error('invalid dsh-ptc-plus rewrites')
|