dsh-ptc-plus 0.4.3 → 0.4.10
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 +15 -3
- package/README.md +15 -3
- package/client.js +1157 -533
- package/cordis.patch.yml +12 -2
- package/docs/README.md +2 -0
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +60 -4
- package/docs/adr/0017-track-the-latest-dsh-public-surface.md +5 -1
- package/docs/adr/0019-plugin-settings-and-kill-switch.md +14 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0023-global-user-bindings.md +1 -1
- package/docs/adr/0028-attach-to-the-host-ptc-execution-seam.md +82 -18
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
- package/docs/adr/0032-own-session-log-facts-in-a-host-only-projection.md +27 -0
- package/docs/adr/0033-gate-client-visual-usability-in-a-browser.md +23 -0
- package/docs/architecture.md +6 -4
- package/docs/binding-review-placement-design.md +29 -21
- package/docs/client-ui.md +20 -8
- package/docs/client-visual-contract.md +48 -0
- package/docs/durability-design.md +12 -0
- package/docs/installation.md +48 -9
- package/docs/publishing.md +4 -4
- package/docs/release-notes.md +109 -0
- package/docs/runtime-reference.md +8 -2
- package/docs/user-bindings.en.md +2 -2
- package/docs/user-bindings.md +3 -3
- package/docs/verification-optimization-work.md +26 -0
- package/docs/verification.md +7 -1
- package/index.js +28 -7
- package/internal/compiler-platform.js +5 -3
- package/internal/compiler-service.js +2 -9
- package/internal/cordis-tools-owner.js +75 -16
- package/internal/direct-surface-owner.js +333 -7
- package/internal/edit-transport-owner.js +8 -5
- package/internal/execution-provider-owner.js +157 -0
- package/internal/execution-seam-compat.js +69 -113
- package/internal/isolated-worker.js +12 -8
- package/internal/recovery-tips.js +50 -21
- package/internal/rejected-cell-editor.js +11 -21
- package/internal/runtime-bridge-owner.js +88 -57
- package/internal/runtime-contexts.js +13 -13
- package/internal/session-events.js +40 -4
- package/internal/session-journal-recovery.js +678 -181
- package/internal/session-log-view.js +189 -43
- package/internal/session-runtime.js +58 -10
- package/internal/settings-compat.js +12 -2
- package/internal/user-bindings-owner.js +6 -5
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +18 -13
- package/src/client-authoring-view.js +222 -141
- package/src/client-binding-review.js +2 -0
- package/src/client-code-editor.js +6 -0
- package/src/client-copy.js +10 -0
- package/src/client-feature-gates.js +12 -5
- package/src/client-host-compat.js +23 -2
- package/src/client-primitives.js +249 -0
- package/src/client-repl-view.js +3 -3
- package/src/client-settings-view.js +2 -29
- package/src/client-styles.js +92 -33
- package/src/client-tool-view.js +8 -3
- package/src/client-workbench.js +9 -7
- package/src/client.js +72 -46
- 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
|
@@ -147,6 +147,7 @@ export function createRuntimeBridgeOwner({
|
|
|
147
147
|
sessionConfig,
|
|
148
148
|
userBindingsCwd,
|
|
149
149
|
observeSession,
|
|
150
|
+
readSessionLog = async session => session,
|
|
150
151
|
maxNestedRunCodeDepth,
|
|
151
152
|
presentationGeneration,
|
|
152
153
|
sessionId,
|
|
@@ -172,7 +173,7 @@ export function createRuntimeBridgeOwner({
|
|
|
172
173
|
const withInitiator = ctx.agents === undefined || typeof ctx.agents.withInitiator !== 'function'
|
|
173
174
|
? undefined
|
|
174
175
|
: (agent, operation) => ctx.agents.withInitiator(agent, operation)
|
|
175
|
-
const sessions = new SessionRuntime(sessionConfig, { withInitiator, userBindingsCwd, observeSession })
|
|
176
|
+
const sessions = new SessionRuntime(sessionConfig, { withInitiator, userBindingsCwd, observeSession, readSessionLog })
|
|
176
177
|
let currentConfig = sessions.config
|
|
177
178
|
const patchedDefinitions = new Map()
|
|
178
179
|
const pending = new WeakMap()
|
|
@@ -364,16 +365,18 @@ export function createRuntimeBridgeOwner({
|
|
|
364
365
|
}
|
|
365
366
|
if (patchedDefinitions.has(definition)) return
|
|
366
367
|
const output = definition.output
|
|
367
|
-
const
|
|
368
|
+
const ownPresentationMeta = Object.getOwnPropertyDescriptor(output, 'presentationMeta')
|
|
368
369
|
const patchedPresentationMeta = (args, value) => {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
370
|
+
const prototype = Object.getPrototypeOf(output)
|
|
371
|
+
const originalPresentationMeta = ownPresentationMeta === undefined
|
|
372
|
+
? prototype === null ? undefined : Reflect.get(prototype, 'presentationMeta', output)
|
|
373
|
+
: Object.hasOwn(ownPresentationMeta, 'value')
|
|
374
|
+
? ownPresentationMeta.value
|
|
375
|
+
: ownPresentationMeta.get?.call(output)
|
|
374
376
|
const base = originalPresentationMeta === undefined
|
|
375
377
|
? undefined
|
|
376
|
-
: originalPresentationMeta(args, value)
|
|
378
|
+
: originalPresentationMeta.call(output, args, value)
|
|
379
|
+
if (!active) return base
|
|
377
380
|
const current = scope.getStore()
|
|
378
381
|
const settlement = current?.settlement
|
|
379
382
|
if (settlement === undefined) return generatedRunCodeDescriptionMeta(args, base)
|
|
@@ -417,15 +420,15 @@ export function createRuntimeBridgeOwner({
|
|
|
417
420
|
patchedDefinitions.set(definition, {
|
|
418
421
|
definition,
|
|
419
422
|
output,
|
|
420
|
-
|
|
423
|
+
ownPresentationMeta,
|
|
421
424
|
patchedPresentationMeta,
|
|
422
425
|
ownExecute,
|
|
423
426
|
patchedExecute,
|
|
424
427
|
})
|
|
425
428
|
} catch (error) {
|
|
426
429
|
if (presentationPatched && output.presentationMeta === patchedPresentationMeta) {
|
|
427
|
-
if (
|
|
428
|
-
else output
|
|
430
|
+
if (ownPresentationMeta === undefined) delete output.presentationMeta
|
|
431
|
+
else Object.defineProperty(output, 'presentationMeta', ownPresentationMeta)
|
|
429
432
|
}
|
|
430
433
|
throw new Error(`ptc-plus: cannot attach the session journal to run_code results: ${error.message}`)
|
|
431
434
|
}
|
|
@@ -439,10 +442,10 @@ export function createRuntimeBridgeOwner({
|
|
|
439
442
|
return runIsolated(request, child.executionToken, child.depth, child.cellConfig, child.functions)
|
|
440
443
|
}
|
|
441
444
|
}
|
|
442
|
-
if (!active) return seam.
|
|
445
|
+
if (!active) return seam.runUpstream(request)
|
|
443
446
|
const current = scope.getStore()
|
|
444
|
-
if (current === undefined) return seam.
|
|
445
|
-
const projected = projectBindings(request, 0, current)
|
|
447
|
+
if (current === undefined) return seam.runUpstream(request)
|
|
448
|
+
const projected = projectBindings(request, 0, current, undefined, current.submission?.config ?? currentConfig)
|
|
446
449
|
return sessions.runTentative(current, {
|
|
447
450
|
...projected.request,
|
|
448
451
|
executionToken: current,
|
|
@@ -455,7 +458,26 @@ export function createRuntimeBridgeOwner({
|
|
|
455
458
|
.finally(projected.release)
|
|
456
459
|
}
|
|
457
460
|
|
|
458
|
-
const releaseSeam = seam.
|
|
461
|
+
const releaseSeam = seam.installExecute(patchedRun)
|
|
462
|
+
|
|
463
|
+
const withSubmission = async (exec, operation) => {
|
|
464
|
+
const id = sessionId(exec.agent)
|
|
465
|
+
const inherited = scope.getStore()
|
|
466
|
+
if (exec.parent !== undefined || id === undefined || inherited?.submission?.sessionId === id) return operation()
|
|
467
|
+
const submission = sessions.beginSubmission({ id }, exec.signal)
|
|
468
|
+
try {
|
|
469
|
+
return await scope.run({ ...inherited, submission }, operation)
|
|
470
|
+
} finally {
|
|
471
|
+
sessions.releaseSubmission(submission)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const readSubmissionLog = async (session, signal) => {
|
|
476
|
+
const inherited = scope.getStore()
|
|
477
|
+
const sessionLog = inherited?.sessionLog ?? await readSessionLog(session, signal)
|
|
478
|
+
if (inherited?.submission !== undefined) sessions.assertSubmission(inherited.submission)
|
|
479
|
+
return sessionLog
|
|
480
|
+
}
|
|
459
481
|
|
|
460
482
|
return Object.freeze({
|
|
461
483
|
config: sessions.config,
|
|
@@ -464,8 +486,10 @@ export function createRuntimeBridgeOwner({
|
|
|
464
486
|
currentConfig = sessions.config
|
|
465
487
|
},
|
|
466
488
|
observeRepl(id, memory, signal) { return sessions.observe(id, memory, signal) },
|
|
489
|
+
withSubmission,
|
|
490
|
+
readSubmissionLog,
|
|
467
491
|
// A composite tool's outer result owns the final durability decision.
|
|
468
|
-
async executeTentative(callSeq, operation) {
|
|
492
|
+
async executeTentative(callSeq, operation, sessionLog) {
|
|
469
493
|
const settlement = {
|
|
470
494
|
current: undefined,
|
|
471
495
|
innerConfirmed: false,
|
|
@@ -480,7 +504,9 @@ export function createRuntimeBridgeOwner({
|
|
|
480
504
|
}
|
|
481
505
|
try {
|
|
482
506
|
const result = await scope.run({
|
|
507
|
+
...(scope.getStore()?.submission === undefined ? {} : { submission: scope.getStore().submission }),
|
|
483
508
|
...(callSeq === undefined ? {} : { persistedCallSeq: callSeq }),
|
|
509
|
+
...(sessionLog === undefined ? {} : { sessionLog }),
|
|
484
510
|
deferredSettlement: settlement,
|
|
485
511
|
}, operation)
|
|
486
512
|
return Object.freeze({ result, finalize })
|
|
@@ -489,46 +515,51 @@ export function createRuntimeBridgeOwner({
|
|
|
489
515
|
throw error
|
|
490
516
|
}
|
|
491
517
|
},
|
|
492
|
-
handleExecute(exec, next, executionArguments = exec.arguments, userBindings = undefined) {
|
|
518
|
+
async handleExecute(exec, next, executionArguments = exec.arguments, userBindings = undefined) {
|
|
493
519
|
if (exec.parent !== undefined) return scope.run(undefined, next)
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
? {}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
const settlement = current.settlement
|
|
518
|
-
if (result?.isError === true && settlement !== undefined) {
|
|
519
|
-
let meta = withJournal(result.meta, settlement.journal)
|
|
520
|
-
if (settlement.recoveryBoundaries !== undefined) {
|
|
521
|
-
meta = withRecoveryBoundaries(meta, settlement.recoveryBoundaries)
|
|
522
|
-
}
|
|
523
|
-
if (settlement.rewrites !== undefined) meta = withRewrites(meta, settlement.rewrites)
|
|
524
|
-
if (settlement.userBindings !== undefined) {
|
|
525
|
-
meta = withUserBindingsSnapshot(meta, settlement.userBindings)
|
|
526
|
-
}
|
|
527
|
-
meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
|
|
528
|
-
meta = withDraftCapability(meta, current)
|
|
529
|
-
return { ...result, meta }
|
|
520
|
+
return withSubmission(exec, async () => {
|
|
521
|
+
const id = sessionId(exec.agent)
|
|
522
|
+
if (id === undefined) return next()
|
|
523
|
+
patchRunCodeDefinition(exec.agent)
|
|
524
|
+
const inherited = scope.getStore()
|
|
525
|
+
const persistedCallSeq = inherited?.persistedCallSeq
|
|
526
|
+
const current = {
|
|
527
|
+
id,
|
|
528
|
+
submission: inherited?.submission,
|
|
529
|
+
callId: String(exec.callId),
|
|
530
|
+
...(persistedCallSeq === undefined ? {} : { persistedCallSeq }),
|
|
531
|
+
...(inherited?.deferredSettlement === undefined
|
|
532
|
+
? {}
|
|
533
|
+
: { deferredSettlement: inherited.deferredSettlement }),
|
|
534
|
+
session: exec.agent?.session,
|
|
535
|
+
sessionLog: await readSubmissionLog(exec.agent?.session, exec.signal),
|
|
536
|
+
agent: exec.agent,
|
|
537
|
+
executionArguments,
|
|
538
|
+
...(userBindings === undefined ? {} : { userBindings }),
|
|
539
|
+
}
|
|
540
|
+
sessions.assertSubmission(current.submission)
|
|
541
|
+
if (current.deferredSettlement !== undefined) {
|
|
542
|
+
current.deferredSettlement.current = current
|
|
530
543
|
}
|
|
531
|
-
|
|
544
|
+
pending.set(exec, current)
|
|
545
|
+
return scope.run(current, async () => {
|
|
546
|
+
const result = await next()
|
|
547
|
+
const settlement = current.settlement
|
|
548
|
+
if (result?.isError === true && settlement !== undefined) {
|
|
549
|
+
let meta = withJournal(result.meta, settlement.journal)
|
|
550
|
+
if (settlement.recoveryBoundaries !== undefined) {
|
|
551
|
+
meta = withRecoveryBoundaries(meta, settlement.recoveryBoundaries)
|
|
552
|
+
}
|
|
553
|
+
if (settlement.rewrites !== undefined) meta = withRewrites(meta, settlement.rewrites)
|
|
554
|
+
if (settlement.userBindings !== undefined) {
|
|
555
|
+
meta = withUserBindingsSnapshot(meta, settlement.userBindings)
|
|
556
|
+
}
|
|
557
|
+
meta = withReplMemorySnapshot(meta, settlement.replMemory, presentationGeneration)
|
|
558
|
+
meta = withDraftCapability(meta, current)
|
|
559
|
+
return { ...result, meta }
|
|
560
|
+
}
|
|
561
|
+
return result
|
|
562
|
+
})
|
|
532
563
|
})
|
|
533
564
|
},
|
|
534
565
|
handleResult(exec, result) {
|
|
@@ -539,7 +570,7 @@ export function createRuntimeBridgeOwner({
|
|
|
539
570
|
pending.delete(exec)
|
|
540
571
|
const settlement = current?.settlement
|
|
541
572
|
if (settlement === undefined) {
|
|
542
|
-
sessions.noteNoop(id,
|
|
573
|
+
sessions.noteNoop(id, current?.sessionLog, exec.callId)
|
|
543
574
|
return
|
|
544
575
|
}
|
|
545
576
|
const meta = result?.meta
|
|
@@ -586,8 +617,8 @@ export function createRuntimeBridgeOwner({
|
|
|
586
617
|
releaseSeam()
|
|
587
618
|
for (const patched of patchedDefinitions.values()) {
|
|
588
619
|
if (patched.output.presentationMeta === patched.patchedPresentationMeta) {
|
|
589
|
-
if (patched.
|
|
590
|
-
else patched.output
|
|
620
|
+
if (patched.ownPresentationMeta === undefined) delete patched.output.presentationMeta
|
|
621
|
+
else Object.defineProperty(patched.output, 'presentationMeta', patched.ownPresentationMeta)
|
|
591
622
|
}
|
|
592
623
|
if (patched.patchedExecute !== undefined
|
|
593
624
|
&& patched.definition.execute === patched.patchedExecute) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { latestRecoveryTip } from './recovery-tips.js'
|
|
2
|
-
import { projectSessionLog, systemPromptSnapshotSections } from './session-log-view.js'
|
|
1
|
+
import { latestRecoveryTip, runtimeHistoryForView } from './recovery-tips.js'
|
|
2
|
+
import { projectSessionLog, runtimeMessageFacts, systemPromptSnapshotSections } from './session-log-view.js'
|
|
3
3
|
import {
|
|
4
4
|
PTC_BINDING_CATALOG, PTC_DELIVERY_CONTEXT, PTC_STATE_NAMES, readRuntimeMessage,
|
|
5
5
|
runtimeBindingCatalogMessage, runtimeNoticeMessage, runtimeStateMessage,
|
|
@@ -21,7 +21,7 @@ function continuationFeedback(view) {
|
|
|
21
21
|
|
|
22
22
|
/** Build all dynamic PTC contexts from one session-log projection. */
|
|
23
23
|
export function sessionRuntimeContexts(agent, tipConfig, options = {}) {
|
|
24
|
-
const view = projectSessionLog(agent)
|
|
24
|
+
const view = (options.sessionLogView ?? projectSessionLog)(agent)
|
|
25
25
|
const rewrite = continuationFeedback(view)
|
|
26
26
|
const tip = latestRecoveryTip(view, tipConfig)
|
|
27
27
|
const cordisRecovery = options.cordisRecoveryRequired?.(view) === true
|
|
@@ -42,13 +42,14 @@ export function projectRuntimeMessages(view, contexts, pending = []) {
|
|
|
42
42
|
const sections = contexts.filter(context => PTC_STATE_NAMES.includes(context.name) && context.name !== PTC_BINDING_CATALOG)
|
|
43
43
|
const catalog = contexts.find(context => context.name === PTC_BINDING_CATALOG)
|
|
44
44
|
const records = view.visibleRuntimeMessages
|
|
45
|
-
const
|
|
46
|
-
const
|
|
45
|
+
const facts = view.visibleRuntimeFacts ?? runtimeMessageFacts(records)
|
|
46
|
+
const history = runtimeHistoryForView(view)
|
|
47
|
+
const owned = facts.snapshot
|
|
48
|
+
const legacy = facts.aggregate
|
|
47
49
|
const pendingAggregate = pending.map(systemPromptSnapshotSections).filter(sections => sections !== undefined).at(-1)
|
|
48
50
|
const retained = owned?.sections
|
|
49
51
|
?? (pendingAggregate ?? legacy?.sections)?.filter(section => PTC_STATE_NAMES.includes(section.name))
|
|
50
|
-
const hadState =
|
|
51
|
-
|| view.systemPromptSnapshots.some(record => record.sections.some(section => PTC_STATE_NAMES.includes(section.name)))
|
|
52
|
+
const hadState = history.hadState
|
|
52
53
|
const messages = []
|
|
53
54
|
const proposed = pending.map(readRuntimeMessage).filter(record => record !== undefined)
|
|
54
55
|
const pendingState = proposed.filter(record => record.form === 'snapshot').at(-1)
|
|
@@ -58,23 +59,22 @@ export function projectRuntimeMessages(view, contexts, pending = []) {
|
|
|
58
59
|
messages.push(runtimeStateMessage(sections))
|
|
59
60
|
}
|
|
60
61
|
const previousCatalog = proposed.filter(record => record.form === 'catalog').at(-1)
|
|
61
|
-
??
|
|
62
|
-
const hadCatalog =
|
|
62
|
+
?? facts.catalog
|
|
63
|
+
const hadCatalog = history.hadCatalog
|
|
63
64
|
if ((catalog !== undefined || previousCatalog !== undefined || hadCatalog)
|
|
64
65
|
&& JSON.stringify(previousCatalog?.sections) !== JSON.stringify(catalog === undefined ? [] : [catalog])) {
|
|
65
66
|
messages.push(runtimeBindingCatalogMessage(catalog))
|
|
66
67
|
}
|
|
67
68
|
for (const tip of contexts.filter(context => context.name.startsWith('tools:ptc-plus-tip/'))) {
|
|
68
69
|
if (proposed.some(record => record.form === 'notice' && record.name === tip.name)) continue
|
|
69
|
-
if (
|
|
70
|
-
|| view.systemPromptSnapshots.some(snapshot => snapshot.sections.some(section => section.name === tip.name))) continue
|
|
70
|
+
if (Object.hasOwn(history.seenNames, tip.name)) continue
|
|
71
71
|
messages.push(runtimeNoticeMessage(tip))
|
|
72
72
|
}
|
|
73
73
|
return messages
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/** Passive message delivery follows the actual public assembly and accepted step. */
|
|
77
|
-
export function createRuntimeMessageOwner(contextsForRequest) {
|
|
77
|
+
export function createRuntimeMessageOwner(contextsForRequest, sessionLogView = projectSessionLog) {
|
|
78
78
|
const requests = new WeakMap()
|
|
79
79
|
const disposedAgents = new WeakSet()
|
|
80
80
|
let disposed = false
|
|
@@ -99,7 +99,7 @@ export function createRuntimeMessageOwner(contextsForRequest) {
|
|
|
99
99
|
if (disposed || decision.kind !== 'enter' || payload.signal?.aborted === true
|
|
100
100
|
|| request?.allowed !== true || request.context.signal !== payload.signal) return decision
|
|
101
101
|
const messages = projectRuntimeMessages(
|
|
102
|
-
|
|
102
|
+
sessionLogView(payload.agent), contextsForRequest(request.context), decision.messages,
|
|
103
103
|
)
|
|
104
104
|
return messages.length === 0 ? decision : { ...decision, messages: [...decision.messages, ...messages] }
|
|
105
105
|
},
|
|
@@ -1,7 +1,43 @@
|
|
|
1
|
-
/** Read
|
|
1
|
+
/** Read an explicit observation or the historical array-backed session contract. */
|
|
2
2
|
export function sessionEvents(session) {
|
|
3
|
-
if (
|
|
4
|
-
|
|
3
|
+
if (session === undefined || session === null) return undefined
|
|
4
|
+
const events = session.events
|
|
5
|
+
if (Array.isArray(events)) return events
|
|
6
|
+
if (Object.hasOwn(session, 'events')) throw new Error('ptc-plus: legacy session.events is not an event array')
|
|
7
|
+
throw new Error('ptc-plus: session history requires a public sessionQuery observation')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function advanceSessionSurface(nodes, event) {
|
|
11
|
+
if (event?.surfaceOp === 'append') return [...nodes, event.seq]
|
|
12
|
+
if (event?.surfaceOp?.op !== 'replace') return nodes
|
|
13
|
+
const first = nodes.indexOf(event.surfaceOp.startSeq ?? event.surfaceOp.start)
|
|
14
|
+
const last = nodes.indexOf(event.surfaceOp.endSeq ?? event.surfaceOp.end)
|
|
15
|
+
if (first === -1 || last < first) return nodes
|
|
16
|
+
const next = [...nodes]
|
|
17
|
+
next.splice(first, last - first + 1, event.seq)
|
|
18
|
+
return next
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function readSessionLog(session, query, signal) {
|
|
22
|
+
if (session === undefined || session === null || Array.isArray(session.events)) return session
|
|
23
|
+
if (typeof query?.observeSession !== 'function') {
|
|
24
|
+
throw new Error('ptc-plus: sessionQuery.observeSession is unavailable for this session')
|
|
25
|
+
}
|
|
26
|
+
const observation = await query.observeSession(session.id, { signal, projectionMode: 'none' })
|
|
27
|
+
try {
|
|
28
|
+
if (!Array.isArray(observation.events)) throw new Error('ptc-plus: sessionQuery observation did not publish events')
|
|
29
|
+
let nodes = session.surface?.nodes
|
|
30
|
+
if (!Array.isArray(nodes) || session.seq - 1 !== observation.cursor) {
|
|
31
|
+
nodes = []
|
|
32
|
+
for (const event of observation.events) nodes = advanceSessionSurface(nodes, event)
|
|
33
|
+
}
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
id: session.id, header: observation.header, events: observation.events,
|
|
36
|
+
surface: Object.freeze({
|
|
37
|
+
nodes: Object.freeze([...nodes]),
|
|
38
|
+
}),
|
|
39
|
+
})
|
|
40
|
+
} finally {
|
|
41
|
+
observation[Symbol.dispose]()
|
|
5
42
|
}
|
|
6
|
-
return session?.events
|
|
7
43
|
}
|