dsh-ptc-plus 0.4.10 → 0.5.0
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 +60 -130
- package/README.md +60 -130
- package/assets/dsh-ptc-plus-banner-en.webp +0 -0
- package/assets/dsh-ptc-plus-banner-zh.webp +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.cjs +42336 -0
- package/cordis.patch.yml +1 -0
- package/docs/README.md +6 -2
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -198
- package/docs/adr/0008-define-agent-operating-contract.md +5 -1
- package/docs/adr/0009-bound-expensive-acceptance-default.md +2 -2
- package/docs/adr/0016-archive-resolved-review-findings.md +1 -1
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +2 -2
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +1 -1
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +1 -1
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
- package/docs/adr/0023-global-user-bindings.md +2 -0
- package/docs/adr/0026-persist-the-delivery-verdict-against-the-verified-candidate.md +2 -0
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +25 -23
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
- package/docs/adr/0034-keep-program-values-separate-from-output-projections.md +29 -0
- package/docs/adr/0035-run-coverage-only-before-a-new-release.md +27 -0
- package/docs/adr/0036-separate-focused-development-from-delivery-verification.md +29 -0
- package/docs/adr/0037-keep-tool-hover-previews-passive.md +52 -0
- package/docs/adr/0038-freeze-session-capability-plans.md +71 -0
- package/docs/adr/0039-verify-documentation-without-runtime-suites.md +29 -0
- package/docs/adr/0040-separate-capability-configurations-from-session-observations.md +39 -0
- package/docs/adr/0041-record-cell-execution-directories.md +65 -0
- package/docs/adr/0042-persist-provider-availability-and-handback.md +41 -0
- package/docs/adr/0043-compose-capability-and-context-policies.md +37 -0
- package/docs/adr/README.md +6 -0
- package/docs/architecture.md +23 -25
- package/docs/capability-planning-components.md +93 -0
- package/docs/capability-projection.md +7 -0
- package/docs/client-ui.md +70 -23
- package/docs/client-visual-contract.md +26 -9
- package/docs/context-management.md +22 -0
- package/docs/context-optimization-plan.md +353 -0
- package/docs/context-public-interface-coverage.md +25 -0
- package/docs/durability-design.md +20 -15
- package/docs/evaluation.md +41 -6
- package/docs/evaluations/command-goat-deepseek-v4.1-flash-20261006.md +126 -0
- package/docs/evaluations/command-goat-deepseek-v4.1-flash.md +281 -0
- package/docs/installation.md +31 -10
- package/docs/program-data-plane.md +5 -3
- package/docs/publishing.md +5 -5
- package/docs/release-coverage.md +73 -0
- package/docs/release-notes.md +1 -1
- package/docs/review-procedure.md +56 -0
- package/docs/runtime-reference.md +48 -16
- package/docs/semantic-validation.md +14 -2
- package/docs/stateful-binding-updates-proposal.md +2 -2
- package/docs/user-bindings.md +1 -1
- package/docs/value-wire.md +11 -1
- package/docs/verification-optimization-work.md +26 -15
- package/docs/verification.md +30 -88
- package/index.js +123 -20
- package/internal/binding-toggle-receipt.js +24 -0
- package/internal/capability-command-receipt.js +29 -0
- package/internal/capability-environment.js +1247 -0
- package/internal/capability-plan.js +285 -0
- package/internal/capability-policy.js +96 -0
- package/internal/capability-statistics-store.js +180 -0
- package/internal/capability-statistics.js +210 -0
- package/internal/config-spec.js +79 -9
- package/internal/direct-surface-owner.js +27 -29
- package/internal/dynamic-environment-runtime.js +9 -9
- package/internal/edit-transport-owner.js +180 -139
- package/internal/execution-provider-owner.js +15 -12
- package/internal/execution-seam-compat.js +8 -8
- package/internal/kernel-worker.js +104 -32
- package/internal/native-root-dynamic.js +12 -13
- package/internal/program-bindings.js +190 -0
- package/internal/recovery-tips.js +3 -2
- package/internal/rejected-cell-editor.js +8 -8
- package/internal/repl-observation-interest.js +11 -3
- package/internal/rpc-contract.js +1 -0
- package/internal/runtime-bridge-owner.js +272 -34
- package/internal/runtime-config.js +58 -0
- package/internal/runtime-contexts.js +74 -5
- package/internal/runtime-intrinsics.js +1 -0
- package/internal/runtime-messages.js +41 -0
- package/internal/sdk-projection.js +10 -82
- package/internal/session-cell-executor.js +42 -19
- package/internal/session-events.js +34 -0
- package/internal/session-journal-recovery.js +234 -11
- package/internal/session-journal-schema.js +6 -3
- package/internal/session-journal.js +31 -2
- package/internal/session-log-view.js +1 -1
- package/internal/session-runtime.js +119 -15
- package/internal/settings-compat.js +12 -5
- package/internal/stateful-module-compiler.js +3 -2
- package/internal/stateful-module-runtime.js +7 -3
- package/internal/stateful-root-runtime.js +9 -4
- package/internal/user-binding-authoring.js +2 -7
- package/internal/user-bindings-owner.js +41 -2
- package/internal/validated-parse-repair.js +1 -1
- package/internal/value-wire.js +68 -21
- package/internal/worker-cwd-virtualization.js +20 -18
- package/internal/worker-realm-surfaces.js +3 -3
- package/package.json +47 -29
- package/src/client-authoring-view.js +67 -30
- package/src/client-binding-review.js +2 -1
- package/src/client-binding-toggle.js +18 -0
- package/src/client-capabilities-view.js +1341 -0
- package/src/client-capability-sort.js +29 -0
- package/src/client-copy.js +871 -8
- package/src/client-feature-gates.js +3 -0
- package/src/client-indicator-view.js +4 -6
- package/src/client-primitives.js +33 -2
- package/src/client-repl-view.js +20 -13
- package/src/client-settings-view.js +411 -41
- package/src/client-styles.js +108 -15
- package/src/client-tool-hover.js +185 -0
- package/src/client-tool-view.js +37 -8
- package/src/client-transport.js +44 -9
- package/src/client-workbench.js +6 -7
- package/src/client.js +76 -22
- package/client.js +0 -32479
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import { deepEqualJson, isJsonValue } from '@deepseek-ai/dsh-util-values'
|
|
3
3
|
import { deepFreeze, isRecord } from './record-utils.js'
|
|
4
|
-
import { sessionEvents } from './session-events.js'
|
|
4
|
+
import { sessionEvents, sessionSourceMessages } from './session-events.js'
|
|
5
5
|
import { USER_BINDINGS_META_KEY } from './user-bindings.js'
|
|
6
6
|
import {
|
|
7
7
|
JOURNAL_KEY,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
REWRITES_KEY,
|
|
13
13
|
EDIT_TARGET_KEY,
|
|
14
14
|
DERIVED_RUN_KEY,
|
|
15
|
+
POST_EDIT_KEY,
|
|
15
16
|
} from './session-journal-schema.js'
|
|
16
17
|
import {
|
|
17
18
|
isCanonicalSequence,
|
|
@@ -22,6 +23,8 @@ import {
|
|
|
22
23
|
userBindingsForJournal,
|
|
23
24
|
validatedRewrites,
|
|
24
25
|
} from './session-journal.js'
|
|
26
|
+
import { decodeValue } from './value-wire.js'
|
|
27
|
+
import { editRejectedCell } from './rejected-cell-editor.js'
|
|
25
28
|
|
|
26
29
|
const RECOVERY_BOUNDARY_EVIDENCE = Symbol('recoveryBoundaryEvidence')
|
|
27
30
|
const RUN_SELECTION_CLEAR_REASONS = new Set([
|
|
@@ -156,6 +159,12 @@ export function visibleExecutableCallSeqs(session) {
|
|
|
156
159
|
return new Set()
|
|
157
160
|
}
|
|
158
161
|
if (!Array.isArray(events) || !Array.isArray(nodes)) return new Set()
|
|
162
|
+
let sourceMessages
|
|
163
|
+
try {
|
|
164
|
+
sourceMessages = sessionSourceMessages(session, events, nodes)
|
|
165
|
+
} catch {
|
|
166
|
+
return new Set()
|
|
167
|
+
}
|
|
159
168
|
const eventBySeq = new Map()
|
|
160
169
|
const duplicateEventSeqs = new Set()
|
|
161
170
|
for (const event of events) {
|
|
@@ -180,7 +189,7 @@ export function visibleExecutableCallSeqs(session) {
|
|
|
180
189
|
}
|
|
181
190
|
if (event?.type === 'assistant/message') {
|
|
182
191
|
pendingAssistantCalls.clear()
|
|
183
|
-
const content = event.
|
|
192
|
+
const content = sourceMessages.get(event.seq)?.content
|
|
184
193
|
if (Array.isArray(content)) {
|
|
185
194
|
for (const part of content) {
|
|
186
195
|
if (part?.type === 'tool-call' && typeof part.id === 'string') {
|
|
@@ -214,6 +223,13 @@ export function visibleExecutableCallSeqs(session) {
|
|
|
214
223
|
if (event?.type !== 'tool/call' || !REPL_TOOL_NAMES.has(event.data?.name)) continue
|
|
215
224
|
if (visibleAssistantCallSeqs.has(event.seq)) calls.add(event.seq)
|
|
216
225
|
}
|
|
226
|
+
const timeline = foldSessionTimeline(events)
|
|
227
|
+
for (const root of timeline.calls) {
|
|
228
|
+
const post = timeline.results.get(root.seq)?.postEdit
|
|
229
|
+
if (post !== undefined && calls.has(root.seq) && calls.has(post.targetCallSeq)) {
|
|
230
|
+
calls.add(post.executionStartSeq)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
217
233
|
return calls
|
|
218
234
|
}
|
|
219
235
|
|
|
@@ -253,7 +269,11 @@ function successfulTimelineRun(run) {
|
|
|
253
269
|
function settlementRun(settlement) {
|
|
254
270
|
const publication = settlement.publication
|
|
255
271
|
if (publication === undefined) return undefined
|
|
256
|
-
const result = { data: { meta: { [REWRITES_KEY]: publication.rewrites } } }
|
|
272
|
+
const result = { data: { meta: { [REWRITES_KEY]: publication.postEditRewrites ?? publication.rewrites } } }
|
|
273
|
+
if (publication.postEdit !== undefined) {
|
|
274
|
+
return timelineJson(timelineDerivedRun({ seq: publication.postEdit.executionStartSeq }, result,
|
|
275
|
+
publication.index, publication.postEdit.derived))
|
|
276
|
+
}
|
|
257
277
|
return timelineJson(publication.derived === undefined
|
|
258
278
|
? timelineRun(settlement.call, result, publication.index, publication.journal)
|
|
259
279
|
: timelineDerivedRun(settlement.call, result, publication.index, publication.derived))
|
|
@@ -398,6 +418,18 @@ function republishedTimelineResult(result, event) {
|
|
|
398
418
|
|
|
399
419
|
function correlationEvent(event) {
|
|
400
420
|
return timelineJson({ seq: event.seq, type: event.type,
|
|
421
|
+
...(['tool/ptc-dispatch-start', 'tool/ptc-dispatch'].includes(event.type) ? { data: {
|
|
422
|
+
rootCallId: event.data?.rootCallId,
|
|
423
|
+
parentCallId: event.data?.parentCallId,
|
|
424
|
+
subCallId: event.data?.subCallId,
|
|
425
|
+
name: event.data?.name,
|
|
426
|
+
arguments: event.data?.arguments,
|
|
427
|
+
...(event.type === 'tool/ptc-dispatch' ? {
|
|
428
|
+
isError: event.data?.isError,
|
|
429
|
+
...(event.data?.name === 'run_code' && event.data.meta?.[JOURNAL_KEY] !== undefined
|
|
430
|
+
? { meta: { [JOURNAL_KEY]: event.data.meta[JOURNAL_KEY] } } : {}),
|
|
431
|
+
} : {}),
|
|
432
|
+
} } : {}),
|
|
401
433
|
...(event.type === 'tool/call' && typeof event.data?.callId === 'string'
|
|
402
434
|
? { data: timelineCallEvent(event).data } : {}),
|
|
403
435
|
...(event.type === 'tool/result' ? { data: { message: { source: {
|
|
@@ -586,11 +618,26 @@ export function validateSessionTimelineState(value) {
|
|
|
586
618
|
return true
|
|
587
619
|
}
|
|
588
620
|
const normalizedResult = result => {
|
|
589
|
-
if (!fields(result, ['eventSeq', 'eventIndex', 'positionSeq', 'error', 'journal', 'derived', 'userBindings'
|
|
621
|
+
if (!fields(result, ['eventSeq', 'eventIndex', 'positionSeq', 'error', 'journal', 'derived', 'userBindings',
|
|
622
|
+
'postEdit', 'postEditError'])
|
|
590
623
|
|| !isCanonicalSequence(result.eventSeq) || !isCanonicalSequence(result.eventIndex)
|
|
591
624
|
|| !optional(result.positionSeq, isCanonicalSequence)) return false
|
|
592
625
|
if (result.error !== undefined) return typeof result.error === 'string' && result.journal === undefined && result.derived === undefined
|
|
593
626
|
return journalFacts(result)
|
|
627
|
+
&& optional(result.postEditError, error => typeof error === 'string')
|
|
628
|
+
&& optional(result.postEdit, post => fields(post, ['startSeq', 'executionStartSeq', 'targetCallSeq', 'derived', 'rewrites'])
|
|
629
|
+
&& isCanonicalSequence(post.startSeq) && isCanonicalSequence(post.executionStartSeq)
|
|
630
|
+
&& isCanonicalSequence(post.targetCallSeq)
|
|
631
|
+
&& deepEqualJson(normalizeDerivedEditResult({
|
|
632
|
+
[JOURNAL_KEY]: post.derived?.journal,
|
|
633
|
+
[EDIT_TARGET_KEY]: { targetCallSeq: post.targetCallSeq },
|
|
634
|
+
[DERIVED_RUN_KEY]: { code: post.derived?.code, description: post.derived?.description },
|
|
635
|
+
...(post.derived?.recoveryBoundaries === undefined ? {} : {
|
|
636
|
+
[RECOVERY_BOUNDARY_KEY]: post.derived.recoveryBoundaries }),
|
|
637
|
+
...(post.derived?.userBindings === undefined ? {} : {
|
|
638
|
+
[USER_BINDINGS_META_KEY]: post.derived.userBindings }),
|
|
639
|
+
}, post.targetCallSeq), post.derived)
|
|
640
|
+
&& deepEqualJson(validatedRewrites({ [REWRITES_KEY]: post.rewrites }) ?? null, post.rewrites ?? null))
|
|
594
641
|
}
|
|
595
642
|
const results = table('results', isCanonicalSequence, normalizedResult)
|
|
596
643
|
const targets = table('editTargets', isCanonicalSequence, (field, seq) => (
|
|
@@ -629,10 +676,12 @@ export function validateSessionTimelineState(value) {
|
|
|
629
676
|
&& (!REPL_TOOL_NAMES.has(settlement.call.data.name)
|
|
630
677
|
|| deepEqualJson(executable.get(settlement.call.seq)?.event, settlement.call))
|
|
631
678
|
&& optional(settlement.publication, publication => fields(publication,
|
|
632
|
-
['index', 'journal', 'derived', 'userBindings', 'rewrites'])
|
|
679
|
+
['index', 'journal', 'derived', 'userBindings', 'rewrites', 'postEdit', 'postEditRewrites'])
|
|
633
680
|
&& deepEqualJson(publication.journal ?? null, settlement.normalized?.journal ?? null)
|
|
634
681
|
&& deepEqualJson(publication.derived ?? null, settlement.normalized?.derived ?? null)
|
|
635
682
|
&& deepEqualJson(publication.userBindings ?? null, settlement.normalized?.userBindings ?? null)
|
|
683
|
+
&& deepEqualJson(publication.postEdit ?? null, settlement.normalized?.postEdit ?? null)
|
|
684
|
+
&& deepEqualJson(publication.postEditRewrites ?? null, settlement.normalized?.postEdit?.rewrites ?? null)
|
|
636
685
|
&& !settlement.entry.ambiguous && isCanonicalSequence(publication.index)
|
|
637
686
|
&& publication.index === (settlement.ordinary ? settlement.observedIndex : settlement.entry.eventIndex)
|
|
638
687
|
&& ((settlement.call.data.name === 'run_code' && publication.derived === undefined)
|
|
@@ -651,13 +700,18 @@ export function validateSessionTimelineState(value) {
|
|
|
651
700
|
let correlationIndex = -1
|
|
652
701
|
for (const observation of value.eventFacts) {
|
|
653
702
|
requireFact(fields(observation, ['eventIndex', 'event', 'identity', 'hasJournal', 'normalized',
|
|
654
|
-
'boundaries', 'boundaryFailure', 'rewrites'])
|
|
703
|
+
'boundaries', 'boundaryFailure', 'rewrites', 'selectedTarget'])
|
|
655
704
|
&& isCanonicalSequence(observation.eventIndex) && observation.eventIndex > correlationIndex
|
|
656
705
|
&& value.lastEventIndex !== null && observation.eventIndex <= value.lastEventIndex
|
|
657
706
|
&& isRecord(observation.event) && isCanonicalSequence(observation.event.seq)
|
|
658
|
-
&& ['turn/start', 'turn/end', RECOVERY_BOUNDARY_EVENT, 'compaction/prune', 'tool/call', 'tool/result'
|
|
707
|
+
&& ['turn/start', 'turn/end', RECOVERY_BOUNDARY_EVENT, 'compaction/prune', 'tool/call', 'tool/result',
|
|
708
|
+
'tool/ptc-dispatch-start', 'tool/ptc-dispatch']
|
|
659
709
|
.includes(observation.event.type)
|
|
660
710
|
&& deepEqualJson(correlationEvent(observation.event), observation.event)
|
|
711
|
+
&& (observation.event.type === 'tool/ptc-dispatch-start'
|
|
712
|
+
&& observation.event.data?.name === 'edit_run_code'
|
|
713
|
+
? observation.selectedTarget === null || target(observation.selectedTarget)
|
|
714
|
+
: observation.selectedTarget === undefined)
|
|
661
715
|
&& (observation.event.type === 'tool/result'
|
|
662
716
|
? typeof observation.identity === 'string' && /^[a-f0-9]{64}$/u.test(observation.identity)
|
|
663
717
|
&& typeof observation.hasJournal === 'boolean'
|
|
@@ -748,8 +802,9 @@ export function validateSessionTimelineState(value) {
|
|
|
748
802
|
lastSuccessfulRunIndex = Math.max(lastSuccessfulRunIndex ?? -1, settlement.publication.index)
|
|
749
803
|
}
|
|
750
804
|
}
|
|
751
|
-
requireFact(selectedRun === undefined ||
|
|
752
|
-
&& !
|
|
805
|
+
requireFact(selectedRun === undefined || publications.some(settlement =>
|
|
806
|
+
settlement.entry.scope === value.scope && !settlement.entry.ambiguous
|
|
807
|
+
&& settlementRun(settlement).callSeq === selectedRun.callSeq))
|
|
753
808
|
requireFact(selectedRun === undefined ? value.latestRun === undefined && value.editableRun === undefined
|
|
754
809
|
: deepEqualJson(value.latestRun ?? null, selectedRun) && deepEqualJson(value.editableRun ?? null, selectedRun))
|
|
755
810
|
requireFact(value.lastSuccessfulRunIndex === lastSuccessfulRunIndex
|
|
@@ -787,8 +842,119 @@ export function replaySessionTimelineFact(previous, fact) {
|
|
|
787
842
|
return advanceTimelineEvent(previous, fact.event, fact.eventIndex, fact)
|
|
788
843
|
}
|
|
789
844
|
|
|
845
|
+
function dispatchPairWithinRoot(facts, call, resultEvent, startSeq) {
|
|
846
|
+
const selectedStarts = facts.filter(fact => fact.event.type === 'tool/ptc-dispatch-start'
|
|
847
|
+
&& fact.event.seq === startSeq)
|
|
848
|
+
if (selectedStarts.length !== 1) return undefined
|
|
849
|
+
const subCallId = selectedStarts[0].event.data?.subCallId
|
|
850
|
+
if (typeof subCallId !== 'string') return undefined
|
|
851
|
+
// A sequence locates a start; the bounded root window must also prove that
|
|
852
|
+
// its dispatch identity names exactly one start and settlement. Settled ID
|
|
853
|
+
// reuse outside this root/result window does not change this execution.
|
|
854
|
+
const identityFacts = facts.filter(fact => fact.event.seq > call.seq
|
|
855
|
+
&& fact.event.seq < resultEvent.seq && fact.event.data?.subCallId === subCallId)
|
|
856
|
+
const starts = identityFacts.filter(fact => fact.event.type === 'tool/ptc-dispatch-start')
|
|
857
|
+
const settlements = identityFacts.filter(fact => fact.event.type === 'tool/ptc-dispatch')
|
|
858
|
+
if (starts.length !== 1 || settlements.length !== 1
|
|
859
|
+
|| starts[0].event.seq !== startSeq || startSeq >= settlements[0].event.seq) return undefined
|
|
860
|
+
return { start: starts[0], settled: settlements[0] }
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function normalizePostEditFact(facts, call, resultEvent, result, replayPostEdit) {
|
|
864
|
+
const post = replayPostEdit === undefined ? resultEvent.data?.meta?.[POST_EDIT_KEY] : {
|
|
865
|
+
startSeq: replayPostEdit.startSeq,
|
|
866
|
+
executionStartSeq: replayPostEdit.executionStartSeq,
|
|
867
|
+
rootCallSeq: call.seq,
|
|
868
|
+
subCallId: facts.find(fact => fact.event.seq === replayPostEdit.startSeq)?.event.data?.subCallId,
|
|
869
|
+
meta: {
|
|
870
|
+
[JOURNAL_KEY]: replayPostEdit.derived.journal,
|
|
871
|
+
[EDIT_TARGET_KEY]: { targetCallSeq: replayPostEdit.targetCallSeq },
|
|
872
|
+
[DERIVED_RUN_KEY]: { code: replayPostEdit.derived.code, description: replayPostEdit.derived.description },
|
|
873
|
+
...(replayPostEdit.derived.recoveryBoundaries === undefined ? {} : {
|
|
874
|
+
[RECOVERY_BOUNDARY_KEY]: replayPostEdit.derived.recoveryBoundaries }),
|
|
875
|
+
...(replayPostEdit.derived.userBindings === undefined ? {} : {
|
|
876
|
+
[USER_BINDINGS_META_KEY]: replayPostEdit.derived.userBindings }),
|
|
877
|
+
...(replayPostEdit.rewrites === undefined ? {} : { [REWRITES_KEY]: replayPostEdit.rewrites }),
|
|
878
|
+
},
|
|
879
|
+
}
|
|
880
|
+
const acceptedCalls = result.journal?.calls?.filter(entry => (
|
|
881
|
+
entry.global === 'tools' && entry.member === 'edit_run_code' && entry.ok === true
|
|
882
|
+
&& decodeValue(entry.value)?.accepted === true
|
|
883
|
+
)) ?? []
|
|
884
|
+
if (post === undefined && acceptedCalls.length === 0) return undefined
|
|
885
|
+
const fail = reason => ({ error: `unproved deferred edit: ${reason}` })
|
|
886
|
+
if (!isRecord(post) || !isCanonicalSequence(post.startSeq)
|
|
887
|
+
|| !isCanonicalSequence(post.executionStartSeq)
|
|
888
|
+
|| post.rootCallSeq !== call.seq || typeof post.subCallId !== 'string'
|
|
889
|
+
|| !isRecord(post.meta) || acceptedCalls.length !== 1) return fail('root metadata or receipt is missing')
|
|
890
|
+
const editorPair = dispatchPairWithinRoot(facts, call, resultEvent, post.startSeq)
|
|
891
|
+
if (editorPair === undefined || editorPair.start.event.data.subCallId !== post.subCallId) {
|
|
892
|
+
return fail('nested dispatch pair is incomplete or ambiguous')
|
|
893
|
+
}
|
|
894
|
+
const start = editorPair.start.event
|
|
895
|
+
const settled = editorPair.settled.event
|
|
896
|
+
const target = editorPair.start.selectedTarget
|
|
897
|
+
if (start.seq <= call.seq || start.seq >= settled.seq || settled.seq >= resultEvent.seq
|
|
898
|
+
|| start.data.rootCallId !== call.data.callId || start.data.parentCallId !== call.data.callId
|
|
899
|
+
|| settled.data.rootCallId !== start.data.rootCallId
|
|
900
|
+
|| settled.data.parentCallId !== start.data.parentCallId
|
|
901
|
+
|| settled.data.name !== 'edit_run_code' || start.data.name !== 'edit_run_code'
|
|
902
|
+
|| !deepEqualJson(start.data.arguments, settled.data.arguments)
|
|
903
|
+
|| settled.data.isError !== false) return fail('nested dispatch relation changed')
|
|
904
|
+
const receipt = decodeValue(acceptedCalls[0].value)
|
|
905
|
+
if (!deepEqualJson(receipt, { accepted: true, scheduled: true,
|
|
906
|
+
target_call_seq: post.meta?.[EDIT_TARGET_KEY]?.targetCallSeq })
|
|
907
|
+
|| !deepEqualJson(decodeValue(acceptedCalls[0].args), start.data.arguments)) {
|
|
908
|
+
return fail('nested receipt does not match root journal')
|
|
909
|
+
}
|
|
910
|
+
let derived
|
|
911
|
+
try {
|
|
912
|
+
derived = normalizeDerivedEditResult(post.meta, target?.callSeq)
|
|
913
|
+
} catch (error) {
|
|
914
|
+
return fail(error.message)
|
|
915
|
+
}
|
|
916
|
+
if (target?.source === undefined || sourceForRunCall(call) === undefined) {
|
|
917
|
+
return fail('nested edit has no selected source')
|
|
918
|
+
}
|
|
919
|
+
const edit = editRejectedCell(start.data.arguments, target.source)
|
|
920
|
+
if (!edit.edited || edit.code !== derived.code || edit.description !== derived.description
|
|
921
|
+
|| (start.data.arguments.expected_target_call_seq !== undefined
|
|
922
|
+
&& start.data.arguments.expected_target_call_seq !== target.callSeq)) {
|
|
923
|
+
return fail('derived source does not match selected edit')
|
|
924
|
+
}
|
|
925
|
+
const executionPair = dispatchPairWithinRoot(facts, call, resultEvent, post.executionStartSeq)
|
|
926
|
+
if (executionPair === undefined) return fail('derived execution dispatch pair is incomplete or ambiguous')
|
|
927
|
+
const executionStart = executionPair.start.event
|
|
928
|
+
const executionSettled = executionPair.settled.event
|
|
929
|
+
const argumentsValue = { code: derived.code, description: derived.description }
|
|
930
|
+
if (executionStart.seq <= settled.seq || executionStart.data.rootCallId !== call.data.callId
|
|
931
|
+
|| executionStart.data.parentCallId !== call.data.callId
|
|
932
|
+
|| executionStart.data.name !== 'run_code' || executionSettled.data.name !== 'run_code'
|
|
933
|
+
|| executionSettled.data.rootCallId !== executionStart.data.rootCallId
|
|
934
|
+
|| executionSettled.data.parentCallId !== executionStart.data.parentCallId
|
|
935
|
+
|| !deepEqualJson(executionStart.data.arguments, argumentsValue)
|
|
936
|
+
|| !deepEqualJson(executionSettled.data.arguments, argumentsValue)
|
|
937
|
+
|| !deepEqualJson(executionSettled.data.meta?.[JOURNAL_KEY], derived.journal)
|
|
938
|
+
|| typeof executionSettled.data.isError !== 'boolean'
|
|
939
|
+
|| executionSettled.data.isError !== (derived.journal.completion?.kind === 'throw'
|
|
940
|
+
|| derived.journal.status === 'discarded')) {
|
|
941
|
+
return fail('derived execution relation changed')
|
|
942
|
+
}
|
|
943
|
+
const rewrites = validatedRewrites(post.meta)
|
|
944
|
+
return { startSeq: start.seq, executionStartSeq: executionStart.seq,
|
|
945
|
+
targetCallSeq: target.callSeq, derived,
|
|
946
|
+
...(rewrites === undefined ? {} : { rewrites }) }
|
|
947
|
+
}
|
|
948
|
+
|
|
790
949
|
function advanceTimelineEvent(previous, inputEvent, eventIndex, replayFact) {
|
|
791
|
-
|
|
950
|
+
const nested = inputEvent?.type === 'tool/ptc-dispatch-start'
|
|
951
|
+
|| inputEvent?.type === 'tool/ptc-dispatch'
|
|
952
|
+
if (nested && inputEvent.data?.name !== 'edit_run_code'
|
|
953
|
+
&& inputEvent.data?.name !== 'run_code'
|
|
954
|
+
&& !previous.eventFacts.some(fact => fact.event.type === 'tool/ptc-dispatch-start'
|
|
955
|
+
&& fact.event.data?.subCallId === inputEvent.data?.subCallId)) return previous
|
|
956
|
+
if (!['turn/start', 'turn/end', RECOVERY_BOUNDARY_EVENT, 'compaction/prune', 'tool/call', 'tool/result',
|
|
957
|
+
'tool/ptc-dispatch-start', 'tool/ptc-dispatch'].includes(inputEvent?.type)) {
|
|
792
958
|
return previous
|
|
793
959
|
}
|
|
794
960
|
const observation = { eventIndex, event: correlationEvent(inputEvent),
|
|
@@ -866,6 +1032,16 @@ function advanceTimelineEvent(previous, inputEvent, eventIndex, replayFact) {
|
|
|
866
1032
|
pruneReplacementWindow = pruneWindowForEvent(event, eventIndex)
|
|
867
1033
|
continue
|
|
868
1034
|
}
|
|
1035
|
+
if (event?.type === 'tool/ptc-dispatch-start') {
|
|
1036
|
+
if (event.data?.name !== 'edit_run_code') continue
|
|
1037
|
+
observation.selectedTarget = state.editableRun?.source === undefined ? null
|
|
1038
|
+
: timelineJson({ callSeq: state.editableRun.callSeq, source: state.editableRun.source })
|
|
1039
|
+
if (replayFact !== undefined && !deepEqualJson(observation.selectedTarget, replayFact.selectedTarget)) {
|
|
1040
|
+
throw new TypeError('invalid PTC session-log projection nested edit target fact')
|
|
1041
|
+
}
|
|
1042
|
+
continue
|
|
1043
|
+
}
|
|
1044
|
+
if (event?.type === 'tool/ptc-dispatch') continue
|
|
869
1045
|
if (event?.type === 'tool/call' && typeof event.data?.callId === 'string') {
|
|
870
1046
|
const executable = REPL_TOOL_NAMES.has(event.data.name)
|
|
871
1047
|
let entry = { event: timelineCallEvent(event), eventIndex, scope }
|
|
@@ -1033,6 +1209,14 @@ function advanceTimelineEvent(previous, inputEvent, eventIndex, replayFact) {
|
|
|
1033
1209
|
journal,
|
|
1034
1210
|
...(userBindings === undefined ? {} : { userBindings }),
|
|
1035
1211
|
}
|
|
1212
|
+
let postEdit
|
|
1213
|
+
try {
|
|
1214
|
+
postEdit = normalizePostEditFact(state.eventFacts, call, event, normalized)
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
postEdit = { error: `unproved deferred edit: ${error.message}` }
|
|
1217
|
+
}
|
|
1218
|
+
if (postEdit?.error !== undefined) normalized.postEditError = postEdit.error
|
|
1219
|
+
else if (postEdit !== undefined) normalized.postEdit = postEdit
|
|
1036
1220
|
}
|
|
1037
1221
|
} catch (error) {
|
|
1038
1222
|
normalized = { ...raw, error: error.message }
|
|
@@ -1050,6 +1234,12 @@ function advanceTimelineEvent(previous, inputEvent, eventIndex, replayFact) {
|
|
|
1050
1234
|
})) throw new TypeError('invalid PTC session-log projection timeline checkpoint result facts')
|
|
1051
1235
|
if (replayFact.boundaries !== undefined) observation.boundaries = replayFact.boundaries
|
|
1052
1236
|
if (replayFact.boundaryFailure !== undefined) observation.boundaryFailure = replayFact.boundaryFailure
|
|
1237
|
+
if (normalized.postEdit !== undefined) {
|
|
1238
|
+
const checked = normalizePostEditFact(state.eventFacts, call, event, normalized, normalized.postEdit)
|
|
1239
|
+
if (checked?.error !== undefined || !deepEqualJson(checked, normalized.postEdit)) {
|
|
1240
|
+
throw new TypeError(`invalid PTC session-log projection deferred edit fact: ${checked?.error ?? 'derived fact changed'}`)
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1053
1243
|
}
|
|
1054
1244
|
observation.normalized = normalized
|
|
1055
1245
|
if (observation.boundaries !== undefined) {
|
|
@@ -1074,6 +1264,10 @@ function advanceTimelineEvent(previous, inputEvent, eventIndex, replayFact) {
|
|
|
1074
1264
|
derived: normalized?.derived,
|
|
1075
1265
|
userBindings: normalized?.userBindings,
|
|
1076
1266
|
rewrites,
|
|
1267
|
+
...(normalized?.postEdit === undefined ? {} : {
|
|
1268
|
+
postEdit: normalized.postEdit,
|
|
1269
|
+
postEditRewrites: normalized.postEdit.rewrites,
|
|
1270
|
+
}),
|
|
1077
1271
|
}
|
|
1078
1272
|
}
|
|
1079
1273
|
if (settlement !== undefined && isCanonicalSequence(event.seq)) {
|
|
@@ -1243,7 +1437,20 @@ export function recoverJournal(session, currentCallSeq, options = {}) {
|
|
|
1243
1437
|
const boundary = boundaries[boundaryIndex++]
|
|
1244
1438
|
const carrierCallSeq = carrierCallSeqForBoundary(boundary)
|
|
1245
1439
|
const failedCall = executableCalls.get(boundary.failedCallSeq)
|
|
1246
|
-
|
|
1440
|
+
// Derived records exist only after the shared timeline has proved both
|
|
1441
|
+
// dispatch pairs and the final root publication. A raw nested start does
|
|
1442
|
+
// not establish a program identity or a completed recovery boundary.
|
|
1443
|
+
const derivedRecords = failedCall === undefined ? orderedRecords.filter(record => (
|
|
1444
|
+
record.call.seq === boundary.failedCallSeq
|
|
1445
|
+
&& record.call.type === 'tool/ptc-dispatch-start'
|
|
1446
|
+
&& record.call.data?.name === 'run_code' && record.result?.derived !== undefined
|
|
1447
|
+
)) : []
|
|
1448
|
+
const failedExecution = failedCall === undefined && derivedRecords.length === 1
|
|
1449
|
+
? derivedRecords[0] : undefined
|
|
1450
|
+
const isEarlierExecution = failedCall === undefined
|
|
1451
|
+
? failedExecution !== undefined && recordEventSeq(failedExecution) < boundary.eventSeq
|
|
1452
|
+
: failedCall.event.seq < boundary.eventSeq
|
|
1453
|
+
if (!isEarlierExecution) {
|
|
1247
1454
|
rejectBoundary(boundary, 'recovery boundary does not identify an earlier executable call')
|
|
1248
1455
|
continue
|
|
1249
1456
|
}
|
|
@@ -1299,6 +1506,22 @@ export function recoverJournal(session, currentCallSeq, options = {}) {
|
|
|
1299
1506
|
if (call.data?.name === 'edit_run_code' && record.code === undefined && result === undefined) continue
|
|
1300
1507
|
if (result?.journal === undefined && confirmedNoops.has(call.seq)) continue
|
|
1301
1508
|
orderedRecords.push(record)
|
|
1509
|
+
if (call.data?.name === 'run_code' && result?.journal !== undefined) {
|
|
1510
|
+
if (result.postEditError !== undefined) {
|
|
1511
|
+
invalidResultReasons.set(call.seq, result.postEditError)
|
|
1512
|
+
registerUnavailable({ seq: call.seq, reason: result.postEditError })
|
|
1513
|
+
} else if (result.postEdit !== undefined) {
|
|
1514
|
+
const postEdit = result.postEdit
|
|
1515
|
+
orderedRecords.push({
|
|
1516
|
+
call: timeline.eventFacts.find(fact => fact.event.seq === postEdit.executionStartSeq).event,
|
|
1517
|
+
code: postEdit.derived.code,
|
|
1518
|
+
result: { eventSeq: result.eventSeq, journal: postEdit.derived.journal,
|
|
1519
|
+
derived: postEdit.derived, userBindings: postEdit.derived.userBindings },
|
|
1520
|
+
hidden: visibleCalls instanceof Set && (!visibleCalls.has(call.seq)
|
|
1521
|
+
|| !visibleCalls.has(postEdit.targetCallSeq)),
|
|
1522
|
+
})
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1302
1525
|
}
|
|
1303
1526
|
orderedRecords.sort((left, right) => (
|
|
1304
1527
|
recordEventSeq(left) - recordEventSeq(right) || left.call.seq - right.call.seq
|
|
@@ -8,13 +8,15 @@ import {
|
|
|
8
8
|
export const JOURNAL_KEY = 'dshPtcPlus'
|
|
9
9
|
export const EDIT_TARGET_KEY = 'dshPtcPlusEdit'
|
|
10
10
|
export const DERIVED_RUN_KEY = 'dshPtcPlusDerivedRun'
|
|
11
|
+
export const POST_EDIT_KEY = 'dshPtcPlusPostEdit'
|
|
11
12
|
export const REWRITES_KEY = 'dshPtcPlusRewrites'
|
|
12
13
|
export const RECOVERY_BOUNDARY_KEY = 'dshPtcPlusRecoveryBoundaries'
|
|
13
14
|
export const IMPORT_BOUNDARY_JOURNAL_VERSION = 7
|
|
14
15
|
export const PER_NAME_USER_BINDINGS_JOURNAL_VERSION = 8
|
|
15
16
|
export const LANGUAGE_SEMANTICS_JOURNAL_VERSION = 9
|
|
16
17
|
export const MODULE_TRANSFORM_JOURNAL_VERSION = 10
|
|
17
|
-
export const
|
|
18
|
+
export const EXECUTION_DIRECTORY_JOURNAL_VERSION = 11
|
|
19
|
+
export const JOURNAL_VERSION = EXECUTION_DIRECTORY_JOURNAL_VERSION
|
|
18
20
|
export const LIVE_USER_BINDINGS_SHADOW_POLICY = 'per-name'
|
|
19
21
|
export const LEGACY_USER_BINDINGS_SHADOW_POLICY = 'whole-entry'
|
|
20
22
|
export const LIVE_USER_BINDINGS_REUSE_POLICY = 'implementation-v1'
|
|
@@ -32,7 +34,8 @@ export const BINDING_MODES = new Set(['loose', 'strict'])
|
|
|
32
34
|
export const WHOLE_ENTRY_JOURNAL_FIELDS = new Set(['version', 'bindingPolicy', 'rewritePolicy', 'moduleSemantics', 'userBindingsFingerprint', 'userBindingsReusePolicy', 'status', 'calls', 'operations', 'confirms', 'diagnostics', 'completion', 'volatileReason'])
|
|
33
35
|
export const PER_NAME_JOURNAL_FIELDS = new Set([...WHOLE_ENTRY_JOURNAL_FIELDS, 'userBindingsShadowPolicy', 'userBindingNames'])
|
|
34
36
|
export const LANGUAGE_SEMANTICS_JOURNAL_FIELDS = new Set([...PER_NAME_JOURNAL_FIELDS, 'languageSemantics'])
|
|
35
|
-
export const
|
|
37
|
+
export const MODULE_TRANSFORM_JOURNAL_FIELDS = new Set([...LANGUAGE_SEMANTICS_JOURNAL_FIELDS, 'moduleTransform'])
|
|
38
|
+
export const JOURNAL_FIELDS = new Set([...MODULE_TRANSFORM_JOURNAL_FIELDS, 'executionCwd'])
|
|
36
39
|
export const FINGERPRINT_REUSE_JOURNAL_FIELDS = new Set([...WHOLE_ENTRY_JOURNAL_FIELDS].filter(field => field !== 'userBindingsReusePolicy'))
|
|
37
40
|
export const RELATIONLESS_JOURNAL_FIELDS = new Set([...FINGERPRINT_REUSE_JOURNAL_FIELDS].filter(field => field !== 'userBindingsFingerprint'))
|
|
38
41
|
export const PREDECESSOR_JOURNAL_FIELDS = new Set(['version', 'bindingMode', 'rewritePolicy', 'status', 'calls', 'operations', 'confirms', 'diagnostics', 'completion', 'volatileReason'])
|
|
@@ -58,7 +61,7 @@ export const ERROR_FIELDS = new Set(['kind', 'message'])
|
|
|
58
61
|
export const EDIT_TARGET_FIELDS = new Set(['targetCallSeq'])
|
|
59
62
|
export const DERIVED_RUN_FIELDS = new Set(['code', 'description'])
|
|
60
63
|
|
|
61
|
-
export const JOURNAL_VERSIONS = new Set([LEGACY_JOURNAL_VERSION, INTERMEDIATE_JOURNAL_VERSION, PREVIOUS_JOURNAL_VERSION, USER_BINDING_RELATIONLESS_JOURNAL_VERSION, FINGERPRINT_REUSE_JOURNAL_VERSION, VERSIONED_BINDING_REUSE_JOURNAL_VERSION, IMPORT_BOUNDARY_JOURNAL_VERSION, PER_NAME_USER_BINDINGS_JOURNAL_VERSION, LANGUAGE_SEMANTICS_JOURNAL_VERSION, JOURNAL_VERSION])
|
|
64
|
+
export const JOURNAL_VERSIONS = new Set([LEGACY_JOURNAL_VERSION, INTERMEDIATE_JOURNAL_VERSION, PREVIOUS_JOURNAL_VERSION, USER_BINDING_RELATIONLESS_JOURNAL_VERSION, FINGERPRINT_REUSE_JOURNAL_VERSION, VERSIONED_BINDING_REUSE_JOURNAL_VERSION, IMPORT_BOUNDARY_JOURNAL_VERSION, PER_NAME_USER_BINDINGS_JOURNAL_VERSION, LANGUAGE_SEMANTICS_JOURNAL_VERSION, MODULE_TRANSFORM_JOURNAL_VERSION, JOURNAL_VERSION])
|
|
62
65
|
export const USER_BINDINGS_REUSE_POLICIES = new Set([LEGACY_USER_BINDINGS_REUSE_POLICY, LIVE_USER_BINDINGS_REUSE_POLICY])
|
|
63
66
|
export const USER_BINDINGS_SHADOW_POLICIES = new Set([LEGACY_USER_BINDINGS_SHADOW_POLICY, LIVE_USER_BINDINGS_SHADOW_POLICY])
|
|
64
67
|
const USER_BINDING_NAME_STATES = new Set(['provider', 'local', 'absent', 'unknown'])
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from 'node:util'
|
|
2
|
+
import { posix, win32 } from 'node:path'
|
|
2
3
|
import { decodeValue, encodeValue, normalizeValueWire } from './value-wire.js'
|
|
3
4
|
import { normalizeDiagnostic } from './diagnostic.js'
|
|
4
5
|
import { assertOwnFields, isRecord } from './record-utils.js'
|
|
@@ -19,6 +20,8 @@ import {
|
|
|
19
20
|
REWRITES_KEY,
|
|
20
21
|
RECOVERY_BOUNDARY_KEY,
|
|
21
22
|
JOURNAL_VERSION,
|
|
23
|
+
MODULE_TRANSFORM_JOURNAL_VERSION,
|
|
24
|
+
MODULE_TRANSFORM_JOURNAL_FIELDS,
|
|
22
25
|
LANGUAGE_SEMANTICS_JOURNAL_VERSION,
|
|
23
26
|
LANGUAGE_SEMANTICS_JOURNAL_FIELDS,
|
|
24
27
|
IMPORT_BOUNDARY_JOURNAL_VERSION,
|
|
@@ -262,6 +265,10 @@ function normalizeUserBindingsFingerprint(value) {
|
|
|
262
265
|
|
|
263
266
|
function migrateJournal(value, resolveLegacyConfirm) {
|
|
264
267
|
if (value.version === JOURNAL_VERSION) return value
|
|
268
|
+
if (value.version === MODULE_TRANSFORM_JOURNAL_VERSION) {
|
|
269
|
+
assertOwnFields(value, MODULE_TRANSFORM_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
270
|
+
return { ...value, version: JOURNAL_VERSION, executionCwd: null }
|
|
271
|
+
}
|
|
265
272
|
if (value.version === LANGUAGE_SEMANTICS_JOURNAL_VERSION) {
|
|
266
273
|
assertOwnFields(value, LANGUAGE_SEMANTICS_JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
267
274
|
const languageSemantics = normalizeLanguageSemantics(value.languageSemantics)
|
|
@@ -332,6 +339,24 @@ function migrateJournal(value, resolveLegacyConfirm) {
|
|
|
332
339
|
}
|
|
333
340
|
}
|
|
334
341
|
|
|
342
|
+
/**
|
|
343
|
+
* A recorded execution directory belongs to the execution world that produced
|
|
344
|
+
* the journal, not to the platform reading it. Judge both absolute spellings so
|
|
345
|
+
* a Windows Host journal stays valid evidence inside a POSIX audit checkout
|
|
346
|
+
* (and the reverse), and never rewrite the recorded bytes.
|
|
347
|
+
*/
|
|
348
|
+
function isRecordedExecutionDirectory(value) {
|
|
349
|
+
return typeof value === 'string' && value.length > 0
|
|
350
|
+
&& (posix.isAbsolute(value) || win32.isAbsolute(value))
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function normalizeExecutionCwd(value) {
|
|
354
|
+
if (value !== null && !isRecordedExecutionDirectory(value)) {
|
|
355
|
+
throw new Error('invalid dsh-ptc-plus journal execution directory')
|
|
356
|
+
}
|
|
357
|
+
return value
|
|
358
|
+
}
|
|
359
|
+
|
|
335
360
|
/** Validate and detach one journal emitted by the runtime. */
|
|
336
361
|
export function normalizeJournal(value, options = {}) {
|
|
337
362
|
if (!isRecord(value)) throw new Error('invalid dsh-ptc-plus journal')
|
|
@@ -339,10 +364,12 @@ export function normalizeJournal(value, options = {}) {
|
|
|
339
364
|
|| !STATUSES.has(value.status)) {
|
|
340
365
|
throw new Error('invalid dsh-ptc-plus journal')
|
|
341
366
|
}
|
|
342
|
-
const migrated = migrateJournal(value, options.resolveLegacyConfirm)
|
|
367
|
+
const migrated = { ...migrateJournal(value, options.resolveLegacyConfirm),
|
|
368
|
+
...(value.version < JOURNAL_VERSION ? { executionCwd: null } : {}) }
|
|
343
369
|
assertOwnFields(migrated, JOURNAL_FIELDS, 'dsh-ptc-plus journal')
|
|
344
370
|
const languageSemantics = normalizeLanguageSemantics(migrated.languageSemantics)
|
|
345
371
|
const moduleTransform = normalizeModuleTransform(migrated.moduleTransform, languageSemantics)
|
|
372
|
+
const executionCwd = normalizeExecutionCwd(migrated.executionCwd)
|
|
346
373
|
const bindingPolicy = normalizeBindingPolicy(migrated.bindingPolicy)
|
|
347
374
|
const rewritePolicy = normalizeRewritePolicy(migrated.rewritePolicy)
|
|
348
375
|
const moduleSemantics = normalizeModuleSemantics(migrated.moduleSemantics)
|
|
@@ -374,6 +401,7 @@ export function normalizeJournal(value, options = {}) {
|
|
|
374
401
|
version: JOURNAL_VERSION,
|
|
375
402
|
languageSemantics,
|
|
376
403
|
moduleTransform,
|
|
404
|
+
executionCwd,
|
|
377
405
|
bindingPolicy,
|
|
378
406
|
rewritePolicy,
|
|
379
407
|
moduleSemantics,
|
|
@@ -1022,7 +1050,7 @@ export function liveToolCallSeq(session, callId, toolName) {
|
|
|
1022
1050
|
|
|
1023
1051
|
/** Start a mutable journal for one live cell. */
|
|
1024
1052
|
export function createJournal(confirms = [], bindingPolicy, rewritePolicy, languageSemantics = LEGACY_LANGUAGE_SEMANTICS,
|
|
1025
|
-
moduleTransform = moduleTransformForLanguage(languageSemantics)) {
|
|
1053
|
+
moduleTransform = moduleTransformForLanguage(languageSemantics), executionCwd = null) {
|
|
1026
1054
|
if (typeof bindingPolicy === 'string') {
|
|
1027
1055
|
if (!BINDING_MODES.has(bindingPolicy)) throw new TypeError('invalid dsh-ptc-plus journal binding mode')
|
|
1028
1056
|
bindingPolicy = {
|
|
@@ -1035,6 +1063,7 @@ export function createJournal(confirms = [], bindingPolicy, rewritePolicy, langu
|
|
|
1035
1063
|
version: JOURNAL_VERSION,
|
|
1036
1064
|
languageSemantics,
|
|
1037
1065
|
moduleTransform: normalizeModuleTransform(moduleTransform, languageSemantics),
|
|
1066
|
+
executionCwd: normalizeExecutionCwd(executionCwd),
|
|
1038
1067
|
bindingPolicy: normalizeBindingPolicy(bindingPolicy),
|
|
1039
1068
|
rewritePolicy: normalizeRewritePolicy(rewritePolicy),
|
|
1040
1069
|
moduleSemantics: normalizeModuleSemantics(LIVE_MODULE_SEMANTICS),
|
|
@@ -118,7 +118,7 @@ function applyLogEvent(state, event, index = event?.seq,
|
|
|
118
118
|
export function createSessionLogProjection() {
|
|
119
119
|
return Object.freeze({
|
|
120
120
|
key: SESSION_LOG_PROJECTION_KEY,
|
|
121
|
-
stateVersion:
|
|
121
|
+
stateVersion: 9,
|
|
122
122
|
stateSchema: Object.freeze({ parse(value) {
|
|
123
123
|
if (!isJsonValue(value) || !isRecord(value) || !isRecord(value.timeline)
|
|
124
124
|
|| typeof value.timeline.openTurn !== 'boolean' || typeof value.timeline.found !== 'boolean'
|