dsh-ptc-plus 0.4.8 → 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 +61 -123
- package/README.md +61 -123
- 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 +13 -2
- package/docs/README.md +7 -2
- package/docs/adr/0005-temporary-rejected-cell-edit-transport.md +31 -179
- 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 +14 -3
- package/docs/adr/0020-optional-cordis-tools-in-ptc-mode.md +2 -2
- package/docs/adr/0022-stage-npm-releases-with-oidc.md +4 -2
- package/docs/adr/0023-global-user-bindings.md +3 -1
- 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 +101 -35
- package/docs/adr/0030-serve-both-typert-codec-generations-from-one-codec.md +3 -3
- package/docs/adr/0031-close-semantic-obligations-by-owner.md +1 -1
- 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/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 +25 -27
- package/docs/binding-review-placement-design.md +29 -21
- package/docs/capability-planning-components.md +93 -0
- package/docs/capability-projection.md +7 -0
- package/docs/client-ui.md +88 -29
- package/docs/client-visual-contract.md +65 -0
- 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 +31 -14
- 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 +50 -8
- 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.en.md +2 -2
- package/docs/user-bindings.md +3 -3
- package/docs/value-wire.md +11 -1
- package/docs/verification-optimization-work.md +52 -15
- package/docs/verification.md +30 -86
- package/index.js +147 -23
- 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/compiler-platform.js +5 -3
- package/internal/compiler-service.js +2 -9
- package/internal/config-spec.js +79 -9
- package/internal/cordis-tools-owner.js +22 -19
- package/internal/direct-surface-owner.js +29 -29
- package/internal/dynamic-environment-runtime.js +9 -9
- package/internal/edit-transport-owner.js +183 -139
- package/internal/execution-provider-owner.js +160 -0
- package/internal/execution-seam-compat.js +76 -120
- package/internal/isolated-worker.js +12 -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 +53 -23
- 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 +351 -82
- package/internal/runtime-config.js +58 -0
- package/internal/runtime-contexts.js +85 -16
- 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 +74 -4
- package/internal/session-journal-recovery.js +904 -184
- package/internal/session-journal-schema.js +6 -3
- package/internal/session-journal.js +31 -2
- package/internal/session-log-view.js +189 -43
- package/internal/session-runtime.js +175 -23
- package/internal/settings-compat.js +23 -6
- 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 +47 -7
- 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/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +55 -38
- package/src/client-authoring-view.js +286 -168
- package/src/client-binding-review.js +4 -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-code-editor.js +6 -0
- package/src/client-copy.js +881 -8
- package/src/client-feature-gates.js +15 -5
- package/src/client-host-compat.js +23 -2
- package/src/client-indicator-view.js +4 -6
- package/src/client-primitives.js +280 -0
- package/src/client-repl-view.js +23 -16
- package/src/client-settings-view.js +412 -69
- package/src/client-styles.js +193 -41
- package/src/client-tool-hover.js +185 -0
- package/src/client-tool-view.js +45 -11
- package/src/client-transport.js +44 -9
- package/src/client-workbench.js +9 -10
- package/src/client.js +148 -68
- 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/client.js +0 -31857
|
@@ -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),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
advanceSessionTimeline, createSessionTimelineState, replaySessionTimelineFact, validateSessionTimelineState,
|
|
3
3
|
} from './session-journal-recovery.js'
|
|
4
4
|
import { isCanonicalSequence } from './session-journal.js'
|
|
5
|
-
import { sessionEvents } from './session-events.js'
|
|
5
|
+
import { advanceSessionSurface, readSessionLog, sessionEvents } from './session-events.js'
|
|
6
6
|
import { readRuntimeMessage } from './runtime-messages.js'
|
|
7
|
+
import { deepEqualJson, isJsonValue } from '@deepseek-ai/dsh-util-values'
|
|
8
|
+
import { deepFreeze, isRecord } from './record-utils.js'
|
|
9
|
+
import { advanceRuntimeHistory, createRuntimeHistory } from './recovery-tips.js'
|
|
7
10
|
|
|
8
11
|
import { isHostRuntimeContextSource } from './message-sources.js'
|
|
9
12
|
const SYSTEM_PROMPT_CLEARED = 'Current runtime context: none. Earlier runtime-context snapshots no longer apply.'
|
|
@@ -35,30 +38,185 @@ function isContextStep(event) {
|
|
|
35
38
|
|| (event?.type === 'user/message' && event.data?.source?.kind === 'user')
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
export const SESSION_LOG_PROJECTION_KEY = 'ptcPlusSessionLog'
|
|
42
|
+
|
|
43
|
+
function emptyLogState() {
|
|
44
|
+
return { timeline: createSessionTimelineState(), contextStep: 0,
|
|
45
|
+
runtimeHistory: createRuntimeHistory(), visibleRuntimeFacts: {},
|
|
46
|
+
systemPromptSnapshots: [], ptcMessages: [], runtimeMessagesBySeq: {}, visibleRuntimeMessages: [], surfaceNodes: [], logFacts: [] }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function runtimeMessageFacts(records, initial = {}) {
|
|
50
|
+
const facts = { ...initial }
|
|
51
|
+
for (const record of records) {
|
|
52
|
+
if (record.producer === 'aggregate') facts.aggregate = record
|
|
53
|
+
else if (record.form === 'snapshot') facts.snapshot = record
|
|
54
|
+
else if (record.form === 'catalog') facts.catalog = record
|
|
55
|
+
}
|
|
56
|
+
return facts
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function presentationEvent(event) {
|
|
60
|
+
const recognized = event?.type === 'user/message'
|
|
61
|
+
&& (systemPromptSnapshotSections(event.data) !== undefined || readRuntimeMessage(event.data) !== undefined)
|
|
62
|
+
const data = recognized ? { source: event.data.source, content: event.data.content }
|
|
63
|
+
: event?.type === 'user/message' && typeof event.data?.source?.kind === 'string'
|
|
64
|
+
? { source: { kind: event.data.source.kind } } : undefined
|
|
65
|
+
return JSON.parse(JSON.stringify({ seq: event.seq, type: event.type,
|
|
66
|
+
...(data === undefined ? {} : { data }),
|
|
67
|
+
...(event.surfaceOp === undefined ? {} : { surfaceOp: event.surfaceOp }),
|
|
68
|
+
}))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function applyLogEvent(state, event, index = event?.seq,
|
|
72
|
+
timeline = advanceSessionTimeline(state.timeline, event, index)) {
|
|
73
|
+
const sections = event?.type === 'user/message' ? systemPromptSnapshotSections(event.data) : undefined
|
|
74
|
+
const ptc = event?.type === 'user/message' ? readRuntimeMessage(event.data) : undefined
|
|
75
|
+
const contextStep = state.contextStep + (isContextStep(event) ? 1 : 0)
|
|
76
|
+
const surfaceNodes = advanceSessionSurface(state.surfaceNodes, event)
|
|
77
|
+
let visibleRuntimeMessages = state.visibleRuntimeMessages
|
|
78
|
+
if (timeline === state.timeline && sections === undefined && ptc === undefined && contextStep === state.contextStep
|
|
79
|
+
&& surfaceNodes === state.surfaceNodes) return state
|
|
80
|
+
const next = { ...state, timeline, contextStep,
|
|
81
|
+
logFacts: [...state.logFacts, deepFreeze({ index, event: presentationEvent(event) })],
|
|
82
|
+
}
|
|
83
|
+
next.runtimeHistory = advanceRuntimeHistory(state.runtimeHistory, {
|
|
84
|
+
sections, notice: ptc?.form === 'notice' ? ptc.name : undefined,
|
|
85
|
+
snapshot: ptc?.form === 'snapshot', catalog: ptc?.form === 'catalog',
|
|
86
|
+
index, contextStep: state.contextStep, lastSuccessfulRunIndex: timeline.lastSuccessfulRunIndex,
|
|
87
|
+
resetSuccess: timeline.lastSuccessfulRunIndex !== state.timeline.lastSuccessfulRunIndex,
|
|
88
|
+
})
|
|
89
|
+
if (sections !== undefined) {
|
|
90
|
+
const snapshot = Object.freeze({ index, contextStep: state.contextStep, sections })
|
|
91
|
+
next.systemPromptSnapshots = [...state.systemPromptSnapshots, snapshot]
|
|
92
|
+
next.runtimeMessagesBySeq = { ...state.runtimeMessagesBySeq,
|
|
93
|
+
[`k${event.seq}`]: Object.freeze({ ...snapshot, producer: 'aggregate', form: 'snapshot' }) }
|
|
94
|
+
}
|
|
95
|
+
if (ptc !== undefined) {
|
|
96
|
+
const record = Object.freeze({ ...ptc, index, contextStep: state.contextStep, producer: 'ptc-plus' })
|
|
97
|
+
next.ptcMessages = [...state.ptcMessages, record]
|
|
98
|
+
next.runtimeMessagesBySeq = { ...next.runtimeMessagesBySeq, [`k${event.seq}`]: record }
|
|
99
|
+
}
|
|
100
|
+
if (event?.surfaceOp === 'append') {
|
|
101
|
+
const record = next.runtimeMessagesBySeq[`k${event.seq}`]
|
|
102
|
+
if (record !== undefined) {
|
|
103
|
+
visibleRuntimeMessages = [...visibleRuntimeMessages, record]
|
|
104
|
+
next.visibleRuntimeFacts = runtimeMessageFacts([record], state.visibleRuntimeFacts)
|
|
47
105
|
}
|
|
106
|
+
} else if (surfaceNodes !== state.surfaceNodes) {
|
|
107
|
+
visibleRuntimeMessages = surfaceNodes.flatMap(seq => {
|
|
108
|
+
const record = next.runtimeMessagesBySeq[`k${seq}`]
|
|
109
|
+
return record === undefined ? [] : [record]
|
|
110
|
+
})
|
|
111
|
+
next.visibleRuntimeFacts = runtimeMessageFacts(visibleRuntimeMessages)
|
|
48
112
|
}
|
|
49
|
-
|
|
113
|
+
next.surfaceNodes = surfaceNodes
|
|
114
|
+
next.visibleRuntimeMessages = Object.freeze(visibleRuntimeMessages)
|
|
115
|
+
return next
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function createSessionLogProjection() {
|
|
119
|
+
return Object.freeze({
|
|
120
|
+
key: SESSION_LOG_PROJECTION_KEY,
|
|
121
|
+
stateVersion: 9,
|
|
122
|
+
stateSchema: Object.freeze({ parse(value) {
|
|
123
|
+
if (!isJsonValue(value) || !isRecord(value) || !isRecord(value.timeline)
|
|
124
|
+
|| typeof value.timeline.openTurn !== 'boolean' || typeof value.timeline.found !== 'boolean'
|
|
125
|
+
|| !Number.isSafeInteger(value.timeline.scope) || value.timeline.scope < 0
|
|
126
|
+
|| !Number.isSafeInteger(value.contextStep) || value.contextStep < 0
|
|
127
|
+
|| !Array.isArray(value.systemPromptSnapshots) || !Array.isArray(value.ptcMessages)
|
|
128
|
+
|| !Array.isArray(value.visibleRuntimeMessages)
|
|
129
|
+
|| !isRecord(value.runtimeHistory) || !isRecord(value.runtimeHistory.tips)
|
|
130
|
+
|| !isRecord(value.runtimeHistory.seenNames) || !isRecord(value.visibleRuntimeFacts)
|
|
131
|
+
|| !Array.isArray(value.surfaceNodes) || !value.surfaceNodes.every(isCanonicalSequence)
|
|
132
|
+
|| new Set(value.surfaceNodes).size !== value.surfaceNodes.length
|
|
133
|
+
|| !Array.isArray(value.timeline.calls) || !Array.isArray(value.timeline.boundaries)
|
|
134
|
+
|| !isRecord(value.runtimeMessagesBySeq) || !isRecord(value.timeline.cordisTranscript)) {
|
|
135
|
+
throw new TypeError('invalid PTC session-log projection checkpoint')
|
|
136
|
+
}
|
|
137
|
+
validateSessionTimelineState(value.timeline)
|
|
138
|
+
if (!Array.isArray(value.logFacts)) throw new TypeError('invalid PTC session-log projection checkpoint facts')
|
|
139
|
+
const timelineFacts = new Map(value.timeline.eventFacts.map(fact => [fact.eventIndex, fact]))
|
|
140
|
+
let reconstructed = emptyLogState()
|
|
141
|
+
let lastIndex = -1
|
|
142
|
+
let timelineCount = 0
|
|
143
|
+
for (const fact of value.logFacts) {
|
|
144
|
+
if (!isRecord(fact) || Object.keys(fact).some(key => !['index', 'event'].includes(key))
|
|
145
|
+
|| !isCanonicalSequence(fact.index) || fact.index <= lastIndex
|
|
146
|
+
|| !isRecord(fact.event) || !isCanonicalSequence(fact.event.seq) || typeof fact.event.type !== 'string'
|
|
147
|
+
|| !deepEqualJson(presentationEvent(fact.event), fact.event)) {
|
|
148
|
+
throw new TypeError('invalid PTC session-log projection checkpoint facts')
|
|
149
|
+
}
|
|
150
|
+
lastIndex = fact.index
|
|
151
|
+
const timelineFact = timelineFacts.get(fact.index)
|
|
152
|
+
if (timelineFact !== undefined) {
|
|
153
|
+
if (!deepEqualJson(presentationEvent(timelineFact.event), fact.event)) {
|
|
154
|
+
throw new TypeError('inconsistent PTC session-log projection checkpoint event identity')
|
|
155
|
+
}
|
|
156
|
+
timelineCount += 1
|
|
157
|
+
}
|
|
158
|
+
if (timelineFact === undefined
|
|
159
|
+
&& advanceSessionTimeline(reconstructed.timeline, fact.event, fact.index) !== reconstructed.timeline) {
|
|
160
|
+
throw new TypeError('inconsistent PTC session-log projection checkpoint missing timeline facts')
|
|
161
|
+
}
|
|
162
|
+
const timeline = timelineFact === undefined ? reconstructed.timeline
|
|
163
|
+
: replaySessionTimelineFact(reconstructed.timeline, timelineFact)
|
|
164
|
+
reconstructed = applyLogEvent(reconstructed, fact.event, fact.index, timeline)
|
|
165
|
+
}
|
|
166
|
+
if (timelineCount !== timelineFacts.size || !deepEqualJson(reconstructed, value)) {
|
|
167
|
+
throw new TypeError('inconsistent PTC session-log projection checkpoint facts')
|
|
168
|
+
}
|
|
169
|
+
return deepFreeze(value)
|
|
170
|
+
} }),
|
|
171
|
+
init: emptyLogState,
|
|
172
|
+
apply: applyLogEvent,
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function createSessionLogOwner(ctx) {
|
|
177
|
+
const projection = createSessionLogProjection()
|
|
178
|
+
let registeredService
|
|
179
|
+
const injection = ctx.inject(['sessionProjections'], scope => {
|
|
180
|
+
try {
|
|
181
|
+
scope.effect(() => {
|
|
182
|
+
const registry = scope.sessionProjections
|
|
183
|
+
const unregister = registry.register(projection)
|
|
184
|
+
if (typeof unregister !== 'function') throw new Error('ptc-plus: sessionProjections.register did not return a disposer')
|
|
185
|
+
registeredService = registry
|
|
186
|
+
return async () => {
|
|
187
|
+
await unregister()
|
|
188
|
+
if (registeredService === registry) registeredService = undefined
|
|
189
|
+
}
|
|
190
|
+
}, 'ptc-plus host-only session-log projection')
|
|
191
|
+
} catch (error) {
|
|
192
|
+
ctx.logger?.warn?.('ptc-plus: session-log projection unavailable', error)
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
ctx.effect(() => () => typeof injection === 'function' ? injection() : injection?.dispose?.(),
|
|
196
|
+
'ptc-plus session-log projection injection lifecycle')
|
|
197
|
+
const service = name => typeof ctx.get === 'function' ? ctx.get(name) : ctx[name]
|
|
198
|
+
return Object.freeze({
|
|
199
|
+
read: (session, signal) => readSessionLog(session, service('sessionQuery'), signal),
|
|
200
|
+
project(agent, requestedEdit) {
|
|
201
|
+
if (Array.isArray(agent?.session?.events)) return projectSessionLog(agent, requestedEdit)
|
|
202
|
+
const registry = registeredService ?? service('sessionProjections')
|
|
203
|
+
const state = agent?.session === undefined ? undefined : registry?.stateOf?.(agent.session, SESSION_LOG_PROJECTION_KEY)
|
|
204
|
+
return projectSessionLog(agent, requestedEdit, state)
|
|
205
|
+
},
|
|
206
|
+
})
|
|
50
207
|
}
|
|
51
208
|
|
|
52
209
|
/** Project session events into immutable facts consumed by prompt presentation. */
|
|
53
|
-
export function projectSessionLog(agent, requestedEdit) {
|
|
54
|
-
const events = sessionEvents(agent?.session)
|
|
55
|
-
if (!Array.isArray(events)) {
|
|
210
|
+
export function projectSessionLog(agent, requestedEdit, projectedState) {
|
|
211
|
+
const events = projectedState === undefined ? sessionEvents(agent?.session) : undefined
|
|
212
|
+
if (projectedState === undefined && !Array.isArray(events)) {
|
|
56
213
|
return Object.freeze({
|
|
57
214
|
openTurn: false,
|
|
58
215
|
contextStep: 0,
|
|
59
216
|
systemPromptSnapshots: Object.freeze([]),
|
|
60
217
|
ptcMessages: Object.freeze([]),
|
|
61
218
|
visibleRuntimeMessages: undefined,
|
|
219
|
+
runtimeHistory: Object.freeze(createRuntimeHistory()),
|
|
62
220
|
lastSuccessfulRunIndex: undefined,
|
|
63
221
|
latestRun: undefined,
|
|
64
222
|
editableRun: undefined,
|
|
@@ -66,45 +224,30 @@ export function projectSessionLog(agent, requestedEdit) {
|
|
|
66
224
|
cordisTranscript: Object.freeze({ calls: 0, inspections: 0 }),
|
|
67
225
|
})
|
|
68
226
|
}
|
|
69
|
-
let
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const runtimeMessagesBySeq = new Map()
|
|
73
|
-
for (let index = 0; index < events.length; index += 1) {
|
|
74
|
-
const event = events[index]
|
|
75
|
-
const sections = event?.type === 'user/message' ? systemPromptSnapshotSections(event.data) : undefined
|
|
76
|
-
if (sections !== undefined) {
|
|
77
|
-
const snapshot = Object.freeze({ index, contextStep, sections })
|
|
78
|
-
systemPromptSnapshots.push(snapshot)
|
|
79
|
-
runtimeMessagesBySeq.set(event.seq, Object.freeze({ ...snapshot, producer: 'aggregate', form: 'snapshot' }))
|
|
80
|
-
}
|
|
81
|
-
const ptc = event?.type === 'user/message' ? readRuntimeMessage(event.data) : undefined
|
|
82
|
-
if (ptc !== undefined) {
|
|
83
|
-
const record = Object.freeze({ ...ptc, index, contextStep, producer: 'ptc-plus' })
|
|
84
|
-
ptcMessages.push(record)
|
|
85
|
-
runtimeMessagesBySeq.set(event.seq, record)
|
|
86
|
-
}
|
|
87
|
-
if (isContextStep(event)) contextStep += 1
|
|
227
|
+
let state = projectedState ?? emptyLogState()
|
|
228
|
+
if (projectedState === undefined) {
|
|
229
|
+
for (let index = 0; index < events.length; index += 1) state = applyLogEvent(state, events[index], index)
|
|
88
230
|
}
|
|
89
|
-
const timeline =
|
|
231
|
+
const { timeline, contextStep, systemPromptSnapshots, ptcMessages, runtimeMessagesBySeq } = state
|
|
90
232
|
const requestedEntry = requestedEdit === undefined
|
|
91
233
|
? undefined
|
|
92
|
-
: timeline.executableCalls
|
|
234
|
+
: timeline.executableCalls[`k${requestedEdit.callSeq}`]?.[1]
|
|
93
235
|
const requestedEditTarget = requestedEdit !== undefined
|
|
94
236
|
&& requestedEntry?.event.data?.callId === requestedEdit.callId
|
|
95
237
|
&& requestedEntry.event.data.name === 'edit_run_code'
|
|
96
|
-
? timeline.editTargets
|
|
238
|
+
? timeline.editTargets[`k${requestedEdit.callSeq}`]?.[1] ?? undefined
|
|
97
239
|
: undefined
|
|
98
240
|
const latestRun = timeline.openTurn ? timeline.latestRun : undefined
|
|
99
241
|
const editableRun = timeline.openTurn ? timeline.editableRun : undefined
|
|
100
|
-
const cordisTranscript =
|
|
101
|
-
let visibleRuntimeMessages
|
|
102
|
-
try {
|
|
242
|
+
const cordisTranscript = Object.freeze(timeline.cordisTranscript)
|
|
243
|
+
let visibleRuntimeMessages = projectedState === undefined ? undefined : state.visibleRuntimeMessages
|
|
244
|
+
if (projectedState === undefined) try {
|
|
103
245
|
const nodes = agent?.session?.surface?.nodes
|
|
104
|
-
const knownSeqs = new Set(events.map(event => event?.seq))
|
|
105
|
-
if (Array.isArray(nodes) && nodes.every(seq => isCanonicalSequence(seq)
|
|
246
|
+
const knownSeqs = events === undefined ? undefined : new Set(events.map(event => event?.seq))
|
|
247
|
+
if (Array.isArray(nodes) && nodes.every(seq => isCanonicalSequence(seq)
|
|
248
|
+
&& (knownSeqs === undefined ? seq < agent.session.seq : knownSeqs.has(seq)))) {
|
|
106
249
|
visibleRuntimeMessages = Object.freeze(nodes.flatMap(seq => {
|
|
107
|
-
const record = runtimeMessagesBySeq
|
|
250
|
+
const record = runtimeMessagesBySeq[`k${seq}`]
|
|
108
251
|
return record === undefined ? [] : [record]
|
|
109
252
|
}))
|
|
110
253
|
}
|
|
@@ -115,6 +258,9 @@ export function projectSessionLog(agent, requestedEdit) {
|
|
|
115
258
|
systemPromptSnapshots: Object.freeze(systemPromptSnapshots),
|
|
116
259
|
ptcMessages: Object.freeze(ptcMessages),
|
|
117
260
|
visibleRuntimeMessages,
|
|
261
|
+
visibleRuntimeFacts: projectedState === undefined && visibleRuntimeMessages !== undefined
|
|
262
|
+
? runtimeMessageFacts(visibleRuntimeMessages) : state.visibleRuntimeFacts,
|
|
263
|
+
runtimeHistory: state.runtimeHistory,
|
|
118
264
|
lastSuccessfulRunIndex: timeline.lastSuccessfulRunIndex,
|
|
119
265
|
latestRun,
|
|
120
266
|
editableRun,
|