@llblab/pi-kit 0.37.0 → 0.38.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.
Files changed (152) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -3
  3. package/node_modules/@llblab/pi-state-flow/AGENTS.md +7 -3
  4. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +679 -2
  5. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +17 -0
  6. package/node_modules/@llblab/pi-state-flow/README.md +30 -14
  7. package/node_modules/@llblab/pi-state-flow/api/modules.ts +11 -0
  8. package/node_modules/@llblab/pi-state-flow/dist/api/modules.d.ts +3 -0
  9. package/node_modules/@llblab/pi-state-flow/dist/api/modules.js +3 -0
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +7 -4
  11. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +77 -36
  12. package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +2 -0
  13. package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +26 -27
  14. package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +3 -0
  15. package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +13 -1
  16. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +27 -21
  17. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +144 -46
  18. package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +14 -18
  19. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +6 -0
  20. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +60 -50
  21. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +1049 -310
  22. package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +4 -4
  23. package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +15 -3
  24. package/node_modules/@llblab/pi-state-flow/dist/lib/json.d.ts +2 -0
  25. package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +2 -1
  26. package/node_modules/@llblab/pi-state-flow/dist/lib/memory.d.ts +3 -1
  27. package/node_modules/@llblab/pi-state-flow/dist/lib/memory.js +7 -7
  28. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +29 -0
  29. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +186 -0
  30. package/node_modules/@llblab/pi-state-flow/dist/lib/model-interface.d.ts +17 -0
  31. package/node_modules/@llblab/pi-state-flow/dist/lib/model-interface.js +158 -0
  32. package/node_modules/@llblab/pi-state-flow/dist/lib/module-registry.d.ts +21 -0
  33. package/node_modules/@llblab/pi-state-flow/dist/lib/module-registry.js +108 -0
  34. package/node_modules/@llblab/pi-state-flow/dist/lib/operation.js +3 -2
  35. package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.d.ts +4 -4
  36. package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.js +5 -5
  37. package/node_modules/@llblab/pi-state-flow/dist/lib/planes.d.ts +29 -0
  38. package/node_modules/@llblab/pi-state-flow/dist/lib/planes.js +84 -0
  39. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +6 -3
  40. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +33 -16
  41. package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +21 -4
  42. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +53 -13
  43. package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +5 -2
  44. package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.d.ts +3 -0
  45. package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.js +10 -1
  46. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +84 -5
  47. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +570 -20
  48. package/node_modules/@llblab/pi-state-flow/dist/lib/selection.d.ts +10 -0
  49. package/node_modules/@llblab/pi-state-flow/dist/lib/selection.js +32 -0
  50. package/node_modules/@llblab/pi-state-flow/dist/lib/semantic-module.d.ts +79 -0
  51. package/node_modules/@llblab/pi-state-flow/dist/lib/semantic-module.js +187 -0
  52. package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +98 -1
  53. package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +398 -3
  54. package/node_modules/@llblab/pi-state-flow/dist/lib/skills.d.ts +7 -2
  55. package/node_modules/@llblab/pi-state-flow/dist/lib/skills.js +55 -3
  56. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +67 -14
  57. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +129 -9
  58. package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +26 -14
  59. package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +61 -19
  60. package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +14 -9
  61. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +59 -22
  62. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +111 -10
  63. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +728 -11
  64. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +10 -7
  65. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +50 -14
  66. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +40 -4
  67. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +247 -43
  68. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +41 -8
  69. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +132 -39
  70. package/node_modules/@llblab/pi-state-flow/dist/package.json +6 -1
  71. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +28 -16
  72. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +12 -8
  73. package/node_modules/@llblab/pi-state-flow/docs/README.md +9 -0
  74. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +76 -43
  75. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +9 -1
  76. package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +6 -0
  77. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +23 -19
  78. package/node_modules/@llblab/pi-state-flow/docs/protocol-composition-decision.md +66 -0
  79. package/node_modules/@llblab/pi-state-flow/docs/public-module-contract.md +73 -0
  80. package/node_modules/@llblab/pi-state-flow/docs/scope-selection-contract.md +814 -0
  81. package/node_modules/@llblab/pi-state-flow/docs/semantic-compatibility-decision.md +212 -0
  82. package/node_modules/@llblab/pi-state-flow/docs/semantic-extensibility-decision.md +75 -0
  83. package/node_modules/@llblab/pi-state-flow/docs/semantic-handoff-contract.md +174 -0
  84. package/node_modules/@llblab/pi-state-flow/docs/semantic-memory-evaluation.md +166 -0
  85. package/node_modules/@llblab/pi-state-flow/docs/semantic-module-contract.md +84 -0
  86. package/node_modules/@llblab/pi-state-flow/docs/single-scope-interface-contract.md +201 -0
  87. package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +22 -1
  88. package/node_modules/@llblab/pi-state-flow/docs/usage.md +32 -18
  89. package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +68 -31
  90. package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +21 -22
  91. package/node_modules/@llblab/pi-state-flow/lib/config.ts +14 -1
  92. package/node_modules/@llblab/pi-state-flow/lib/context.ts +142 -46
  93. package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +14 -17
  94. package/node_modules/@llblab/pi-state-flow/lib/durable.ts +53 -44
  95. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +897 -297
  96. package/node_modules/@llblab/pi-state-flow/lib/history.ts +16 -7
  97. package/node_modules/@llblab/pi-state-flow/lib/json.ts +2 -1
  98. package/node_modules/@llblab/pi-state-flow/lib/memory.ts +10 -9
  99. package/node_modules/@llblab/pi-state-flow/lib/migration.ts +189 -0
  100. package/node_modules/@llblab/pi-state-flow/lib/model-interface.ts +136 -0
  101. package/node_modules/@llblab/pi-state-flow/lib/module-registry.ts +119 -0
  102. package/node_modules/@llblab/pi-state-flow/lib/operation.ts +3 -2
  103. package/node_modules/@llblab/pi-state-flow/lib/ownership.ts +6 -6
  104. package/node_modules/@llblab/pi-state-flow/lib/planes.ts +98 -0
  105. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +37 -16
  106. package/node_modules/@llblab/pi-state-flow/lib/query.ts +59 -18
  107. package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +5 -2
  108. package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +10 -1
  109. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +574 -21
  110. package/node_modules/@llblab/pi-state-flow/lib/selection.ts +39 -0
  111. package/node_modules/@llblab/pi-state-flow/lib/semantic-module.ts +239 -0
  112. package/node_modules/@llblab/pi-state-flow/lib/session.ts +387 -3
  113. package/node_modules/@llblab/pi-state-flow/lib/skills.ts +45 -4
  114. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +156 -16
  115. package/node_modules/@llblab/pi-state-flow/lib/state.ts +69 -28
  116. package/node_modules/@llblab/pi-state-flow/lib/status.ts +65 -31
  117. package/node_modules/@llblab/pi-state-flow/lib/storage.ts +770 -18
  118. package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +58 -20
  119. package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +267 -50
  120. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +184 -52
  121. package/node_modules/@llblab/pi-state-flow/package.json +6 -1
  122. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +28 -16
  123. package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +12 -8
  124. package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
  125. package/node_modules/@llblab/pi-telegram/BACKLOG.md +8 -10
  126. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +10 -0
  127. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +20 -18
  128. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +6 -6
  129. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +23 -65
  130. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +3 -3
  131. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.d.ts +1 -1
  132. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +1 -1
  133. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -0
  134. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.js +1 -0
  135. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +4 -2
  136. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +24 -13
  137. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  138. package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/SKILL.md +5 -5
  139. package/node_modules/@llblab/pi-telegram/docs/architecture.md +5 -5
  140. package/node_modules/@llblab/pi-telegram/docs/compact-matrix-literal.md +1 -1
  141. package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
  142. package/node_modules/@llblab/pi-telegram/docs/outbound.md +8 -8
  143. package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -2
  144. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +17 -15
  145. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +28 -68
  146. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +3 -3
  147. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +1 -1
  148. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +2 -1
  149. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +25 -12
  150. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  151. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +5 -5
  152. package/package.json +3 -3
@@ -3,12 +3,14 @@ import { lstatSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { classifyScopeStream, hasCwdMaterialization, parseScopeProvenance, parseScopeStream, sessionRuntimePaths, temporalScopePaths, type SessionAddress } from "./durable.ts";
5
5
  import { pruneArtifactProvenance, type ArtifactProvenance, type ArtifactProvenanceRegistry } from "./artifact.ts";
6
- import { assertTemporalFileBase, captureTemporalFileBase, initializeFileStore, publishTemporalStateToFiles, withStorageTransaction, type StorageTransaction, type TemporalFileBase } from "./storage.ts";
6
+ import { assertTemporalFileBase, captureSelectedModuleAdditions, captureSelectedProvenanceUpdates, captureTemporalFileBase, temporalFileContents, initializeFileStore, publishTemporalStateToFiles, withStorageTransaction, type StorageTransaction, type TemporalFileBase, type SelectedFilePublication, type SelectedOriginRequest, type SelectedSessionAcquisitionRequest, type SelectedForkCopyRequest, type SelectedFileCohort, type SelectedModuleAdditions, type SelectedProvenanceUpdates, type SelectedReadObservation } from "./storage.ts";
7
7
  import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT, type AcceptedTransition, type RecentTransitionWindow } from "./history.ts";
8
- import { hashJson, sameJson } from "./json.ts";
9
- import { RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, preRuntimeCheckpoint, retainedBoundaryCheckpoint, type RetainedPiCheckpoint, type Snapshot, type StateFlowMode } from "./snapshot.ts";
10
- import { type MaterializedState, type SemanticState, type ScopedSemanticStates, type ScopedStates, type StateScope } from "./state.ts";
11
- import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView, validateScopeLineage, type ScopeStream, type TemporalState } from "./temporal.ts";
8
+ import { hashJson, isObject, sameJson } from "./json.ts";
9
+ import { RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, preRuntimeCheckpoint, retainedBoundaryCheckpoint, captureLifecycleSnapshot, type RetainedPiCheckpoint, type SelectedRetainedPiCheckpoint, type Snapshot, type StateFlowMode, type PrivateAcquisition } from "./snapshot.ts";
10
+ import { type MaterializedState, type SemanticState, type ScopedSemanticStates, type ScopedStates, type ScopeStateValues, type StateScope } from "./state.ts";
11
+ import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView, selectTemporalState, validateScopeLineage, type ScopeStream, type TemporalState, type SelectedTemporalState } from "./temporal.ts";
12
+
13
+ import { captureScopeSelection, type ScopeSelection } from "./selection.ts";
12
14
 
13
15
  const SCOPES = ["global", "cwd", "session"] as const;
14
16
  const SHARED_SCOPES = ["global", "cwd"] as const;
@@ -21,6 +23,42 @@ export interface RuntimePatchTransaction {
21
23
  publish(snapshot: Snapshot, accepted?: AcceptedTransition, provenance?: Partial<Record<StateScope, Record<string, ArtifactProvenance>>>): RuntimePublication;
22
24
  }
23
25
 
26
+ export interface SelectedRuntimePatchTransaction {
27
+ readonly selection: ScopeSelection;
28
+ readonly states: ScopeStateValues;
29
+ readonly modules?: SelectedModuleAdditions;
30
+ readonly snapshot: Snapshot;
31
+ readonly causalBasis: string;
32
+ readonly provenance: SelectedProvenanceUpdates;
33
+ publish(snapshot: Snapshot, accepted?: AcceptedTransition, provenance?: SelectedProvenanceUpdates, moduleAdditions?: SelectedModuleAdditions): SelectedFilePublication;
34
+ }
35
+
36
+ export interface SelectedLifecycleInspection {
37
+ readonly selection: ScopeSelection;
38
+ readonly privateAcquisition: PrivateAcquisition;
39
+ readonly snapshot: Snapshot;
40
+ }
41
+
42
+ function selectedModules(view: SelectedTemporalState): SelectedModuleAdditions {
43
+ return Object.fromEntries(view.selection.filter(scope => view.scopes[scope]?.modules !== undefined)
44
+ .map(scope => [scope, structuredClone(view.scopes[scope]!.modules!)]));
45
+ }
46
+
47
+ function selectedRunSnapshot(cohort: Pick<SelectedFileCohort, "lifecycle">): Snapshot {
48
+ const source = cohort.lifecycle.runtime;
49
+ const meta = { step: source.meta.step, ...Object.fromEntries(["specification", "bootstrap", "validation"]
50
+ .filter((key) => Object.hasOwn(source.meta, key)).map((key) => [key, source.meta[key]])) };
51
+ return captureLifecycleSnapshot({ config: { mode: source.config.mode }, meta });
52
+ }
53
+
54
+ /** Detached replay inputs; excluded patch bodies must not be touched even for a rejected raw replay. */
55
+ function captureSelectedReplay(accepted: AcceptedTransition | undefined, selection: ScopeSelection): AcceptedTransition | undefined {
56
+ if (accepted === undefined) return undefined;
57
+ if (!isObject(accepted) || !Array.isArray(accepted.transitions)) throw new Error("Invalid selected runtime replay");
58
+ for (const item of accepted.transitions) if (!isObject(item) || !selection.includes(item.scope as StateScope)) throw new Error("Selected runtime replay contains an excluded owner");
59
+ return structuredClone(accepted);
60
+ }
61
+
24
62
  type PublicationSelection =
25
63
  | { kind: "patch" | "lifecycle" }
26
64
  | { kind: "start"; allowCreateOrigin: boolean }
@@ -78,10 +116,22 @@ function freshEmptyScopeStream(scope: StateScope, origin: string, historyLimit:
78
116
 
79
117
  /** Cached branch-selected temporal state and publication basis; excludes Pi event policy. */
80
118
  export class TemporalRuntime {
81
- view: TemporalState | undefined;
119
+ private memory: { kind: "legacy"; view: TemporalState } | { kind: "selected"; cohort: SelectedFileCohort }
120
+ | { kind: "observed"; observation: SelectedReadObservation } | undefined;
121
+ /** Compatibility access to the legacy cache only; selected memory never masquerades as three owners. */
122
+ get view(): TemporalState | undefined { return this.memory?.kind === "legacy" ? this.memory.view : undefined; }
123
+ set view(value: TemporalState | undefined) {
124
+ if (value !== undefined) this.assertLegacyAdmission();
125
+ this.memory = value === undefined ? undefined : { kind: "legacy", view: value };
126
+ }
82
127
  private base: TemporalFileBase | undefined;
83
128
  private semanticRevision: string | undefined;
84
129
  private savedRuntime: string | undefined;
130
+ /** Backend choice survives cache clearing but grants no origin, private acquisition or publication authority. */
131
+ private selectedBackendChosen = false;
132
+ private selectedOriginAccepted = false;
133
+ private observedSessionAuthority = false;
134
+ private observedPrivateSemantics = false;
85
135
  private transaction: StorageTransaction | undefined;
86
136
  private provenanceByScope: Record<StateScope, ArtifactProvenanceRegistry> = emptyProvenance();
87
137
  /** Shared scopes whose wholly absent live basis was accepted after one stale-target refusal. */
@@ -101,19 +151,201 @@ export class TemporalRuntime {
101
151
  get sessionId(): string { return this.session.id; }
102
152
  get sessionKey(): string { return this.session.key; }
103
153
 
154
+ private rememberSessionAuthority(base: TemporalFileBase | undefined): void {
155
+ const privatePaths = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
156
+ const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
157
+ const privateFiles = new Set([privatePaths.checkpoint, privatePaths.patches, privatePaths.meta]);
158
+ for (const file of base?.files ?? []) if (file.identity !== "missing") {
159
+ if (privateFiles.has(file.path)) this.observedPrivateSemantics = true;
160
+ if (privateFiles.has(file.path) || file.path === paths.config || file.path === paths.runtime) this.observedSessionAuthority = true;
161
+ }
162
+ }
163
+
164
+ private assertLegacyAdmission(): void {
165
+ if (this.selectedBackendChosen) throw new Error("Selected State Flow memory requires selected runtime operations");
166
+ }
167
+
168
+ /** Only owned durable acceptance calls this; returned receipts and read-only candidates are not installers. */
169
+ private installSelected(cohort: SelectedFileCohort): void {
170
+ this.memory = { kind: "selected", cohort: structuredClone(cohort) };
171
+ this.selectedBackendChosen = true;
172
+ this.clearLegacyBasis();
173
+ }
174
+
175
+ private clearLegacyBasis(): void {
176
+ this.base = undefined;
177
+ this.savedRuntime = undefined;
178
+ this.semanticRevision = undefined;
179
+ this.provenanceByScope = emptyProvenance();
180
+ this.absentSharedScopes.clear();
181
+ }
182
+
183
+ private installObserved(observation: SelectedReadObservation): void {
184
+ this.memory = { kind: "observed", observation: structuredClone(observation) };
185
+ this.selectedBackendChosen = true;
186
+ this.clearLegacyBasis();
187
+ }
188
+
189
+ private observationGuard(revalidateNativePolicy: () => void, signal?: AbortSignal): () => void {
190
+ const memory = this.memory;
191
+ return () => {
192
+ signal?.throwIfAborted();
193
+ if (this.selectedOriginAccepted || this.semanticRevision || this.savedRuntime) throw new Error("Accepted State Flow memory requires current acceptance, not observation");
194
+ if (this.memory !== memory) throw new Error("Selected State Flow runtime selection or acceptance changed concurrently");
195
+ if (typeof revalidateNativePolicy !== "function") throw new Error("Selected observation requires independent native policy authority");
196
+ revalidateNativePolicy();
197
+ };
198
+ }
199
+
200
+ private selectedReadCache(): Pick<SelectedReadObservation, "view" | "provenance"> | undefined {
201
+ if (this.memory?.kind === "selected") return this.memory.cohort;
202
+ if (this.memory?.kind === "observed") return this.memory.observation;
203
+ return undefined;
204
+ }
205
+
206
+ /** Detached read-only selected values; never accepted run fields or a publication/acquisition basis. */
207
+ observedSelectedView(): SelectedTemporalState | undefined {
208
+ return this.memory?.kind === "observed" ? structuredClone(this.memory.observation.view) : undefined;
209
+ }
210
+
211
+ /** Explicit shared inspection before acquisition; missing owners stay unavailable rather than becoming empty streams. */
212
+ async refreshSelectedShared(selection: ScopeSelection, revalidateNativePolicy: () => void, signal?: AbortSignal): Promise<boolean> {
213
+ const wanted = captureScopeSelection(selection);
214
+ if (wanted.includes("session")) throw new Error("Shared State Flow observation cannot select Session");
215
+ const assertAuthority = this.observationGuard(revalidateNativePolicy, signal);
216
+ const install = (observation: SelectedReadObservation): boolean => {
217
+ assertAuthority();
218
+ this.installObserved(observation);
219
+ return observation.view !== undefined;
220
+ };
221
+ assertAuthority();
222
+ if (!lstatSync(this.root, { throwIfNoEntry: false })) return install({ selection: wanted, provenance: {} });
223
+ return withStorageTransaction(this.root, storage => {
224
+ assertAuthority();
225
+ return install(storage.observeSelectedShared(this.cwd, wanted, this.root));
226
+ }, signal);
227
+ }
228
+
229
+ /** Header-only disposition data; never semantic observation, acceptance, or acquisition permission. */
230
+ async inspectSelectedLifecycle(selection: ScopeSelection, revalidateNativePolicy: () => void, signal?: AbortSignal): Promise<SelectedLifecycleInspection | undefined> {
231
+ const wanted = captureScopeSelection(selection);
232
+ const memory = this.memory;
233
+ const check = () => {
234
+ signal?.throwIfAborted();
235
+ if (this.memory !== memory) throw new Error("State Flow attachment selection changed");
236
+ revalidateNativePolicy();
237
+ };
238
+ check();
239
+ if (!lstatSync(this.root, { throwIfNoEntry: false })) {
240
+ if (this.observedSessionAuthority) throw new RevisionUnavailableError("Known selected session authority is unavailable");
241
+ return undefined;
242
+ }
243
+ return withStorageTransaction(this.root, storage => {
244
+ check();
245
+ const lifecycle = storage.inspectSelectedLifecycle(this.cwd, this.sessionId, wanted, this.root, this.sessionKey);
246
+ check();
247
+ if (!lifecycle) {
248
+ if (this.observedSessionAuthority) throw new RevisionUnavailableError("Known selected session authority is unavailable");
249
+ return undefined;
250
+ }
251
+ if (this.observedPrivateSemantics && lifecycle.runtime.meta.privateAcquisition !== "acquired") throw new Error("Known acquired State Flow private authority cannot be downgraded");
252
+ this.observedSessionAuthority = true;
253
+ if (lifecycle.runtime.meta.privateAcquisition === "acquired") this.observedPrivateSemantics = true;
254
+ return { selection: captureScopeSelection(lifecycle.runtime.config.scopes), privateAcquisition: lifecycle.runtime.meta.privateAcquisition,
255
+ snapshot: selectedRunSnapshot({ lifecycle }) };
256
+ }, signal);
257
+ }
258
+
259
+ /** Native bookmarks never decide whether current canonical authority exists. */
260
+ async hasSelectedCurrentAuthority(selection: ScopeSelection, revalidateNativePolicy: () => void, signal?: AbortSignal): Promise<boolean> {
261
+ return await this.inspectSelectedLifecycle(selection, revalidateNativePolicy, signal) !== undefined;
262
+ }
263
+
264
+ /** Fenced inactive restoration/operator inspection: source proof only, without conversion or durable acceptance. */
265
+ async refreshSelectedCurrent(selection: ScopeSelection, revalidateNativePolicy: () => void, signal?: AbortSignal): Promise<Snapshot> {
266
+ const wanted = captureScopeSelection(selection);
267
+ const assertAuthority = this.observationGuard(revalidateNativePolicy, signal);
268
+ assertAuthority();
269
+ if (!lstatSync(this.root, { throwIfNoEntry: false })) throw new RevisionUnavailableError("Selected current session storage is unavailable");
270
+ return withStorageTransaction(this.root, storage => {
271
+ assertAuthority();
272
+ const current = storage.prepareSelectedCurrent(this.cwd, this.sessionId, wanted, this.historyLimit, this.root, this.sessionKey);
273
+ if (this.observedPrivateSemantics && current.lifecycle.runtime.meta.privateAcquisition !== "acquired") throw new Error("Known acquired State Flow private authority cannot be downgraded");
274
+ // Current acceptance can plan absent shared owners; observation must not expose those planned empty streams.
275
+ const present = wanted.filter(scope => {
276
+ const path = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey).checkpoint;
277
+ return current.basis.files.some(file => file.path === path && file.identity !== "missing");
278
+ });
279
+ const observation: SelectedReadObservation = { selection: wanted,
280
+ provenance: Object.fromEntries(present.map(scope => [scope, current.provenance[scope]!])),
281
+ ...(present.length === 0 ? {} : { view: selectTemporalState(current.view, captureScopeSelection(present), this.historyLimit) }),
282
+ };
283
+ const snapshot = selectedRunSnapshot(current);
284
+ assertAuthority();
285
+ this.rememberSessionAuthority(current.basis);
286
+ if (current.lifecycle.runtime.meta.privateAcquisition === "acquired") this.observedPrivateSemantics = true;
287
+ this.installObserved(observation);
288
+ return snapshot;
289
+ }, signal);
290
+ }
291
+
292
+ /** Detached accepted selected values only, never a file receipt or publication capability. */
293
+ selectedView(): SelectedTemporalState | undefined {
294
+ return this.memory?.kind === "selected" ? structuredClone(this.memory.cohort.view) : undefined;
295
+ }
296
+
297
+ /** Accepted run fields only; local policy and pending native prompts remain the adapter's responsibility. */
298
+ selectedSnapshot(): Snapshot {
299
+ if (this.memory?.kind !== "selected") throw new Error("Selected State Flow runtime is unavailable");
300
+ return selectedRunSnapshot(this.memory.cohort);
301
+ }
302
+
303
+ /** Native bookkeeping may change mode, never invent accepted run fields or a semantic boundary. */
304
+ retainedSelectedCheckpoint(mode?: StateFlowMode): SelectedRetainedPiCheckpoint {
305
+ const memory = this.memory;
306
+ if (memory?.kind !== "selected") throw new Error("Selected State Flow runtime is unavailable");
307
+ const snapshot = selectedRunSnapshot(memory.cohort);
308
+ if (mode !== undefined) snapshot.config.mode = mode;
309
+ return { ...retainedBoundaryCheckpoint(snapshot, memory.cohort.view.lineage.at(-1)!.id), version: 2, scopes: captureScopeSelection(memory.cohort.view.selection) };
310
+ }
311
+
312
+ /** Read-only cache identity lease; no source acquisition, storage receipt or publication authority. */
313
+ captureSelectedReadBasis(): () => void {
314
+ const memory = this.memory;
315
+ if (!this.selectedReadCache()?.view) throw new Error("Selected State Flow runtime is unavailable");
316
+ return () => { if (this.memory !== memory) throw new Error("State Flow module context content basis changed"); };
317
+ }
318
+
319
+ /** Exact present owners for the existing selected staging engine; no defaulted disabled scopes. */
320
+ selectedStates(offset = 0): { selection: ScopeSelection; states: ScopeStateValues } {
321
+ const view = this.selectedReadCache()?.view;
322
+ if (!view) throw new Error("Selected State Flow runtime is unavailable");
323
+ return { selection: captureScopeSelection(view.selection), states: readTemporalScopes(view, offset, this.historyLimit) };
324
+ }
325
+
104
326
  /** Runtime-owned artifact compilation evidence for one scope; never model-visible state. */
105
327
  artifactProvenance(scope: StateScope): ArtifactProvenanceRegistry {
328
+ const selected = this.selectedReadCache();
329
+ if (selected) {
330
+ if (!selected.view) throw new Error(`State Flow ${scope} scope is excluded or unavailable`);
331
+ if (!selected.view.selection.includes(scope)) throw new Error(`State Flow ${scope} scope is excluded`);
332
+ return structuredClone(selected.provenance[scope]!);
333
+ }
334
+ this.assertLegacyAdmission();
106
335
  return structuredClone(this.provenanceByScope[scope]);
107
336
  }
108
337
 
109
338
  /** Read canonical shared memory without creating, migrating, or publishing storage. */
110
339
  loadPassive(): boolean {
340
+ this.assertLegacyAdmission();
111
341
  if (!lstatSync(this.root, { throwIfNoEntry: false })) return false;
112
342
  return this.loadPassiveBase(captureTemporalFileBase(this.cwd, this.sessionId, this.root, this.sessionKey));
113
343
  }
114
344
 
115
345
  private loadPassiveBase(base: TemporalFileBase): boolean {
116
- const files = new Map(base.files.map((file) => [file.path, file.content]));
346
+ this.rememberSessionAuthority(this.base);
347
+ this.rememberSessionAuthority(base);
348
+ const files = temporalFileContents(base);
117
349
  const shared = Object.fromEntries(SHARED_SCOPES.map((scope) => {
118
350
  const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
119
351
  return [scope, parseScopeStream(files.get(paths.checkpoint), files.get(paths.patches), scope,
@@ -150,26 +382,31 @@ export class TemporalRuntime {
150
382
 
151
383
 
152
384
  read(offset = 0, scope?: StateScope): MaterializedState {
153
- if (!this.view) throw new Error("State Flow temporal runtime is unavailable");
154
- return readTemporalState(this.view, offset, scope, this.historyLimit);
385
+ const view = this.selectedReadCache()?.view ?? this.view;
386
+ if (!view) throw new Error("State Flow temporal runtime is unavailable");
387
+ return readTemporalState(view, offset, scope, this.historyLimit);
155
388
  }
156
389
 
157
390
  readView(offset = 0, scope?: StateScope): SemanticState {
158
- if (!this.view) throw new Error("State Flow temporal runtime is unavailable");
159
- return readTemporalView(this.view, offset, scope, this.historyLimit);
391
+ const view = this.selectedReadCache()?.view ?? this.view;
392
+ if (!view) throw new Error("State Flow temporal runtime is unavailable");
393
+ return readTemporalView(view, offset, scope, this.historyLimit);
160
394
  }
161
395
 
162
396
  states(): ScopedStates {
397
+ this.assertLegacyAdmission();
163
398
  return { global: this.read(0, "global"), cwd: this.read(0, "cwd"), session: this.read(0, "session") };
164
399
  }
165
400
 
166
401
  causalBasis(): string {
167
- if (!this.view) throw new Error("State Flow temporal runtime is unavailable");
168
- return this.view.lineage.at(-1)!.id;
402
+ const view = this.selectedReadCache()?.view ?? this.view;
403
+ if (!view) throw new Error("State Flow temporal runtime is unavailable");
404
+ return view.lineage.at(-1)!.id;
169
405
  }
170
406
 
171
407
  /** Encode Pi lifecycle state against the current retained semantic boundary. */
172
408
  retainedCheckpoint(snapshot: Snapshot): RetainedPiCheckpoint {
409
+ this.assertLegacyAdmission();
173
410
  if (!this.view) return preRuntimeCheckpoint(snapshot.config.mode);
174
411
  return retainedBoundaryCheckpoint(snapshot, this.causalBasis());
175
412
  }
@@ -180,9 +417,11 @@ export class TemporalRuntime {
180
417
 
181
418
  recent(): RecentTransitionWindow {
182
419
  const result: RecentTransitionWindow = [];
183
- for (const boundary of this.view?.lineage.slice(1) ?? []) {
184
- const transitions = SCOPES.flatMap((scope) => {
185
- const record = this.view!.scopes[scope].patches.find(({ transition }) => transition.id === boundary.id);
420
+ const selected = this.selectedReadCache()?.view;
421
+ const view = selected ?? this.view;
422
+ for (const boundary of view?.lineage.slice(1) ?? []) {
423
+ const transitions = (selected?.selection ?? SCOPES).flatMap((scope) => {
424
+ const record = view!.scopes[scope]!.patches.find(({ transition }) => transition.id === boundary.id);
186
425
  return record ? [{ scope, patch: structuredClone(record.patch) }] : [];
187
426
  });
188
427
  if (transitions.length) result.push({ id: boundary.id, at: boundary.position, transitions });
@@ -193,6 +432,7 @@ export class TemporalRuntime {
193
432
  /** Await a coherent read-only recovery view; this neither activates policy nor accepts publication authority. */
194
433
  async refreshCurrentMemory(signal?: AbortSignal): Promise<Snapshot | undefined> {
195
434
  signal?.throwIfAborted();
435
+ this.assertLegacyAdmission();
196
436
  if (!lstatSync(this.root, { throwIfNoEntry: false })) return undefined;
197
437
  return withStorageTransaction(this.root, (storage) => {
198
438
  const base = storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
@@ -202,10 +442,11 @@ export class TemporalRuntime {
202
442
  }
203
443
 
204
444
  private loadCurrentMemoryBase(base: TemporalFileBase): Snapshot | undefined {
205
- const files = new Map(base.files.map((file) => [file.path, file.content]));
445
+ const files = temporalFileContents(base);
206
446
  const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
207
447
  const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
208
448
  if ([paths.config, paths.runtime, session.checkpoint, session.patches, session.meta].every((path) => files.get(path) === undefined)) return undefined;
449
+ this.rememberSessionAuthority(base);
209
450
  const document = parseSessionRuntime(files.get(paths.config), files.get(paths.runtime), this.cwd, this.sessionId);
210
451
  if (!document) throw new RevisionUnavailableError("Current State Flow session runtime is unavailable");
211
452
  const origin = `start:${randomUUID()}`;
@@ -252,12 +493,13 @@ export class TemporalRuntime {
252
493
  newSessionOrigin?: boolean;
253
494
  copy?: SessionCopy;
254
495
  }): RuntimePublication | undefined {
496
+ if (this.selectedBackendChosen) throw new Error("Selected State Flow origin requires selected runtime integration");
255
497
  const { allowCreateCwd, expectedShared, newSessionOrigin = false, copy } = options;
256
498
  const hasCwd = hasCwdMaterialization(this.cwd, this.root);
257
499
  if (!allowCreateCwd && !hasCwd) return undefined;
258
500
  if (!copy && allowCreateCwd) initializeFileStore(this.root);
259
501
  const base = captureTemporalFileBase(this.cwd, this.sessionId, this.root, this.sessionKey);
260
- const files = new Map(base.files.map((file) => [file.path, file.content]));
502
+ const files = temporalFileContents(base);
261
503
  if (copy) {
262
504
  const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
263
505
  const owned = [session.checkpoint, session.patches, session.meta, join(session.directory, "config.json"), join(session.directory, "runtime.json"), join(session.directory, "state.json")];
@@ -399,6 +641,7 @@ export class TemporalRuntime {
399
641
  /** Await coherent shared inspection, lazily loading an empty private view only when none is selected. */
400
642
  async refreshShared(signal?: AbortSignal): Promise<boolean> {
401
643
  signal?.throwIfAborted();
644
+ this.assertLegacyAdmission();
402
645
  if (!this.view && !lstatSync(this.root, { throwIfNoEntry: false })) return false;
403
646
  return withStorageTransaction(this.root, (storage) => {
404
647
  const current = storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
@@ -428,7 +671,7 @@ export class TemporalRuntime {
428
671
  candidate.provenanceByScope = reconciled.provenance;
429
672
  return candidate;
430
673
  }
431
- const files = new Map(current.files.map((file) => [file.path, file.content]));
674
+ const files = temporalFileContents(current);
432
675
  const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
433
676
  const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
434
677
  if ([paths.config, paths.runtime, session.checkpoint, session.patches, session.meta].some((path) => files.get(path) !== undefined)) {
@@ -464,11 +707,314 @@ export class TemporalRuntime {
464
707
  }), { kind: "patch" }, signal);
465
708
  }
466
709
 
710
+ /** Authored under exclusion against validated live selected values; use the existing selected staging/commit engine. */
711
+ async withSelectedPatchTransaction<T>(
712
+ action: (transaction: SelectedRuntimePatchTransaction) => T, revalidateNativePolicy: () => void, signal?: AbortSignal, capturedTargets: readonly StateScope[] = [],
713
+ ): Promise<T> {
714
+ return this.withSelectedPublicationTransaction(action, "current", revalidateNativePolicy, signal, capturedTargets);
715
+ }
716
+
717
+ /** Mode-only Passive persistence: exact accepted sources, no current-origin adoption or caller-authored run fields. */
718
+ async persistSelectedPassive(revalidateNativePolicy: () => void, signal?: AbortSignal): Promise<SelectedFilePublication> {
719
+ const memory = this.memory;
720
+ if (memory?.kind !== "selected") throw new Error("Selected State Flow runtime is unavailable");
721
+ const assertAuthority = (): void => {
722
+ signal?.throwIfAborted();
723
+ if (this.memory !== memory) throw new Error("Selected State Flow runtime selection or acceptance changed concurrently");
724
+ if (typeof revalidateNativePolicy !== "function") throw new Error("Selected Passive persistence requires independent native policy authority");
725
+ revalidateNativePolicy();
726
+ };
727
+ assertAuthority();
728
+ return withStorageTransaction(this.root, storage => {
729
+ assertAuthority();
730
+ const publication = storage.persistSelectedPassive(this.cwd, this.sessionId, memory.cohort, this.root, this.sessionKey);
731
+ if (publication.changed) this.installSelected(publication.cohort);
732
+ return publication;
733
+ }, signal);
734
+ }
735
+
736
+ /** Precomputed replay remains tied to its accepted cache; changed selected shared targets conflict before publication. */
737
+ async publishSelected(
738
+ snapshot: Snapshot, accepted: AcceptedTransition | undefined, revalidateNativePolicy: () => void,
739
+ options: { provenance?: SelectedProvenanceUpdates; moduleAdditions?: SelectedModuleAdditions; signal?: AbortSignal } = {},
740
+ ): Promise<SelectedFilePublication> {
741
+ if (this.memory?.kind !== "selected") throw new Error("Selected State Flow runtime is unavailable");
742
+ const provenance = captureSelectedProvenanceUpdates(options.provenance, this.memory.cohort.view.selection);
743
+ const replay = captureSelectedReplay(accepted, this.memory.cohort.view.selection);
744
+ const additions = captureSelectedModuleAdditions(options.moduleAdditions, this.memory.cohort.view.selection);
745
+ const lifecycle = captureLifecycleSnapshot(snapshot);
746
+ return this.withSelectedPublicationTransaction(transaction => transaction.publish(lifecycle, replay, provenance, additions), "replay", revalidateNativePolicy, options.signal);
747
+ }
748
+
749
+ private async withSelectedPublicationTransaction<T>(
750
+ action: (transaction: SelectedRuntimePatchTransaction) => T, kind: "current" | "replay",
751
+ revalidateNativePolicy: () => void, signal?: AbortSignal, capturedTargets: readonly StateScope[] = [],
752
+ ): Promise<T> {
753
+ const memory = this.memory;
754
+ if (memory?.kind !== "selected") throw new Error("Selected State Flow runtime is unavailable");
755
+ const selection = captureScopeSelection(memory.cohort.view.selection);
756
+ const targets = capturedTargets.length ? captureScopeSelection(capturedTargets) : undefined;
757
+ if (targets?.some(scope => !selection.includes(scope))) throw new Error("Captured State Flow write target is excluded");
758
+ const assertAuthority = (): void => {
759
+ signal?.throwIfAborted();
760
+ if (this.memory !== memory) throw new Error("Selected State Flow runtime selection or acceptance changed concurrently");
761
+ if (typeof revalidateNativePolicy !== "function") throw new Error("Selected semantic transaction requires independent native policy authority");
762
+ revalidateNativePolicy();
763
+ };
764
+ assertAuthority();
765
+ return withStorageTransaction(this.root, storage => {
766
+ assertAuthority();
767
+ const current = storage.prepareSelectedCurrent(this.cwd, this.sessionId, selection, this.historyLimit, this.root, this.sessionKey);
768
+ const sharedPaths = new Set(selection.filter(scope => scope !== "session").flatMap(scope => {
769
+ const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
770
+ return [paths.checkpoint, paths.patches, paths.meta];
771
+ }));
772
+ assertTemporalFileBase({ files: memory.cohort.basis.files.filter(file => !sharedPaths.has(file.path)) },
773
+ { files: current.basis.files.filter(file => !sharedPaths.has(file.path)) });
774
+ if (!sameJson(memory.cohort.presence, current.presence)) throw new Error("Selected State Flow presence dependencies changed concurrently");
775
+ if (targets) this.assertSelectedReplayTargets(memory.cohort, current, undefined, undefined, undefined, targets);
776
+ let consumed = false;
777
+ let published = false;
778
+ const result = action(Object.freeze<SelectedRuntimePatchTransaction>({
779
+ selection, states: readTemporalScopes(current.view, 0, this.historyLimit), modules: selectedModules(current.view), snapshot: selectedRunSnapshot(current),
780
+ causalBasis: current.view.lineage.at(-1)!.id, provenance: structuredClone(current.provenance),
781
+ publish: (snapshot, accepted, requestedProvenance, requestedModules) => {
782
+ if (consumed) throw new Error("Selected semantic transaction publication was already consumed");
783
+ consumed = true;
784
+ assertAuthority();
785
+ const provenance = captureSelectedProvenanceUpdates(requestedProvenance, selection);
786
+ const transition = captureSelectedReplay(accepted, selection);
787
+ const moduleAdditions = captureSelectedModuleAdditions(requestedModules, selection);
788
+ if (kind === "replay") this.assertSelectedReplayTargets(memory.cohort, current, transition, provenance, moduleAdditions);
789
+ const publication = storage.acceptSelectedCurrent(this.cwd, this.sessionId,
790
+ { selection, historyLimit: this.historyLimit, lifecycle: captureLifecycleSnapshot(snapshot), transition, provenance, moduleAdditions }, current, this.root, this.sessionKey);
791
+ this.installSelected(publication.cohort);
792
+ published = true;
793
+ return publication;
794
+ },
795
+ }));
796
+ if (!published) throw new Error("Selected semantic transaction requires one synchronous publication");
797
+ return result;
798
+ }, signal);
799
+ }
800
+
801
+ private assertSelectedReplayTargets(
802
+ base: SelectedFileCohort, current: SelectedFileCohort, transition?: AcceptedTransition, provenance?: SelectedProvenanceUpdates, additions?: SelectedModuleAdditions, capturedTargets: readonly StateScope[] = [],
803
+ ): void {
804
+ const targets = new Set([...capturedTargets, ...(transition?.transitions.map(item => item.scope) ?? []), ...Object.keys(provenance ?? {}) as StateScope[], ...Object.keys(additions ?? {}) as StateScope[]]);
805
+ const files = temporalFileContents(current.basis);
806
+ const changed: StateScope[] = [], removed: StateScope[] = [];
807
+ for (const scope of base.view.selection.filter(scope => scope !== "session" && targets.has(scope))) {
808
+ const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
809
+ const live = classifyScopeStream(files.get(paths.checkpoint), files.get(paths.patches), scope, scope === "cwd" ? this.cwd : undefined, files.get(paths.meta));
810
+ if (live.kind === "absent") { removed.push(scope); continue; }
811
+ if (!sameJson(live.stream, base.view.scopes[scope]) || !sameJson(current.provenance[scope], base.provenance[scope])) changed.push(scope);
812
+ }
813
+ if (removed.length > 0) throw removedTargetScopeConflict(removed);
814
+ if (changed.length > 0) throw targetScopeConflict(changed);
815
+ }
816
+
467
817
  /** Activate current owned memory; the caller must authorize a wholly absent private origin after waiting. */
468
818
  async withStartTransaction<T>(action: (current: Snapshot | undefined, publish: (snapshot: Snapshot) => RuntimePublication) => T, signal?: AbortSignal, allowCreateOrigin = false): Promise<T> {
469
819
  return this.withPublicationTransaction((_candidate, publish, current) => action(current, (snapshot) => publish(snapshot)), { kind: "start", allowCreateOrigin }, signal);
470
820
  }
471
821
 
822
+ /** New-session shared-only acceptance; caller rechecks native origin authority after waiting. */
823
+ async withSelectedSharedOriginTransaction<T>(
824
+ selection: ScopeSelection, snapshot: Snapshot, action: (publish: () => SelectedFilePublication) => T,
825
+ signal?: AbortSignal, allowCreateOrigin = false,
826
+ ): Promise<T> {
827
+ return this.withSelectedOriginAcceptance({ kind: "shared-new", selection, snapshot, historyLimit: this.historyLimit }, action, signal, allowCreateOrigin);
828
+ }
829
+
830
+ /** Child-owned deferred marker only; no parent locator or parent memory is acquired here. */
831
+ async withSelectedDeferredForkTransaction<T>(
832
+ selection: ScopeSelection, lifecycle: ForkLifecycle, action: (publish: () => SelectedFilePublication) => T,
833
+ signal?: AbortSignal, allowDeferFork = false, modeAtPublication?: () => StateFlowMode,
834
+ ): Promise<T> {
835
+ if (!lifecycle || Object.keys(lifecycle).some((key) => key !== "mode" && key !== "bootstrap")
836
+ || (lifecycle.bootstrap !== undefined && lifecycle.bootstrap !== true)) throw new Error("Invalid selected fork lifecycle");
837
+ if (modeAtPublication !== undefined && typeof modeAtPublication !== "function") throw new Error("Invalid selected fork mode sampler");
838
+ const snapshot: Snapshot = { config: { mode: lifecycle.mode }, meta: { step: 0, ...(lifecycle.bootstrap ? { bootstrap: true } : {}) } };
839
+ // Sample mode only; callers cannot replace the fresh child run fields through this hook.
840
+ const author: SelectedOriginRequest["author"] = modeAtPublication ? (basis, publish) => {
841
+ const mode = modeAtPublication();
842
+ signal?.throwIfAborted();
843
+ publish({ config: { mode }, meta: basis.snapshot.meta });
844
+ } : undefined;
845
+ return this.withSelectedOriginAcceptance({ kind: "shared-deferred", selection, snapshot, historyLimit: this.historyLimit, author }, action, signal, allowDeferFork);
846
+ }
847
+
848
+ /** Explicit first private acquisition, never deferred-parent recovery or repair of lost acquired state. */
849
+ async withSelectedSessionAcquisitionTransaction<T>(
850
+ request: SelectedSessionAcquisitionRequest, action: (publish: () => SelectedFilePublication) => T,
851
+ signal?: AbortSignal, allowAcquirePrivate = false,
852
+ ): Promise<T> {
853
+ return this.withSelectedOriginAcceptance(request, action, signal, allowAcquirePrivate);
854
+ }
855
+
856
+ /** Stage first lifecycle/semantic authoring into one origin acceptance, never an empty publication followed by a patch. */
857
+ async withSelectedFirstPatchTransaction<T>(
858
+ selection: ScopeSelection, snapshot: Snapshot, action: (transaction: SelectedRuntimePatchTransaction) => T,
859
+ revalidateNativePolicy: () => void, signal?: AbortSignal, allowCreateOrigin = false,
860
+ ): Promise<T> {
861
+ const memory = this.memory;
862
+ const check = () => {
863
+ signal?.throwIfAborted();
864
+ if (!allowCreateOrigin) throw new Error("Selected first authoring requires explicit native origin authority");
865
+ if (this.memory !== memory) throw new Error("Selected first authoring selection changed concurrently");
866
+ revalidateNativePolicy();
867
+ };
868
+ check();
869
+ const wanted = captureScopeSelection(selection);
870
+ let result: T;
871
+ const author: NonNullable<SelectedOriginRequest["author"]> = (basis, publish) => {
872
+ check();
873
+ result = action(Object.freeze<SelectedRuntimePatchTransaction>({
874
+ selection: captureScopeSelection(basis.view.selection), states: readTemporalScopes(basis.view, 0, this.historyLimit), modules: selectedModules(basis.view),
875
+ snapshot: basis.snapshot, causalBasis: basis.view.lineage.at(-1)!.id, provenance: basis.provenance,
876
+ publish: (next, transition, provenance, moduleAdditions) => {
877
+ check();
878
+ return publish(next, transition, provenance, captureSelectedModuleAdditions(moduleAdditions, wanted));
879
+ },
880
+ }));
881
+ };
882
+ await this.withSelectedOriginAcceptance({ kind: wanted.includes("session") ? "new" : "shared-new", selection: wanted, snapshot, historyLimit: this.historyLimit, author },
883
+ publish => { check(); publish(); }, signal, allowCreateOrigin);
884
+ return result!;
885
+ }
886
+
887
+ /** Same-session current acceptance/authoring; native policy checks are synchronous under exclusion. */
888
+ async withSelectedCurrentTransaction<T>(
889
+ selection: ScopeSelection, action: (current: Snapshot,
890
+ publish: (snapshot?: Snapshot) => SelectedFilePublication,
891
+ transaction: SelectedRuntimePatchTransaction) => T,
892
+ revalidateNativePolicy: () => void, signal?: AbortSignal,
893
+ ): Promise<T> {
894
+ const wanted = captureScopeSelection(selection);
895
+ const historyLimit = this.historyLimit;
896
+ const assertAuthority = (): void => {
897
+ signal?.throwIfAborted();
898
+ if (typeof revalidateNativePolicy !== "function") throw new Error("Selected current acceptance requires independent native policy authority");
899
+ if (historyLimit !== this.historyLimit) throw new Error("Selected current history policy changed concurrently");
900
+ revalidateNativePolicy();
901
+ };
902
+ assertAuthority();
903
+ if (!lstatSync(this.root, { throwIfNoEntry: false })) throw new RevisionUnavailableError("Selected current session storage is unavailable");
904
+ return withStorageTransaction(this.root, (storage) => {
905
+ assertAuthority();
906
+ const base = storage.prepareSelectedCurrent(this.cwd, this.sessionId, wanted, historyLimit, this.root, this.sessionKey);
907
+ this.rememberSessionAuthority(base.basis);
908
+ if (this.observedPrivateSemantics && base.lifecycle.runtime.meta.privateAcquisition !== "acquired") throw new Error("Known acquired State Flow private authority cannot be downgraded");
909
+ this.observedSessionAuthority = true;
910
+ if (base.lifecycle.runtime.meta.privateAcquisition === "acquired") this.observedPrivateSemantics = true;
911
+ const current = selectedRunSnapshot(base);
912
+ let consumed = false, published = false, authorActive = true;
913
+ const publish = (snapshot?: Snapshot, accepted?: AcceptedTransition, requestedProvenance?: SelectedProvenanceUpdates, requestedModules?: SelectedModuleAdditions): SelectedFilePublication => {
914
+ if (!authorActive) throw new Error("Selected current authoring has ended");
915
+ if (consumed) throw new Error("Selected current acceptance publication was already consumed");
916
+ consumed = true;
917
+ assertAuthority();
918
+ const lifecycle = snapshot === undefined ? undefined : captureLifecycleSnapshot(snapshot);
919
+ const transition = captureSelectedReplay(accepted, wanted);
920
+ const provenance = captureSelectedProvenanceUpdates(requestedProvenance, wanted);
921
+ const moduleAdditions = captureSelectedModuleAdditions(requestedModules, wanted);
922
+ const publication = storage.acceptSelectedCurrent(this.cwd, this.sessionId, { selection: wanted, historyLimit, lifecycle, transition, provenance, moduleAdditions }, base, this.root, this.sessionKey);
923
+ this.selectedOriginAccepted = true;
924
+ this.installSelected(publication.cohort);
925
+ published = true;
926
+ return publication;
927
+ };
928
+ try {
929
+ const result = action(current, snapshot => publish(snapshot), Object.freeze<SelectedRuntimePatchTransaction>({
930
+ selection: wanted, states: readTemporalScopes(base.view, 0, historyLimit), modules: selectedModules(base.view), snapshot: structuredClone(current),
931
+ causalBasis: base.view.lineage.at(-1)!.id, provenance: structuredClone(base.provenance), publish,
932
+ }));
933
+ if (!published) throw new Error("Selected current acceptance requires one synchronous publication");
934
+ return result;
935
+ } finally { authorActive = false; }
936
+ }, signal);
937
+ }
938
+
939
+ /** Prepare may read persisted native identity outside exclusion; recheck must use in-memory native policy only. */
940
+ async withSelectedForkCopyTransaction<T>(
941
+ request: SelectedForkCopyRequest, action: (publish: () => SelectedFilePublication) => T,
942
+ revalidateDirectParent: (source: Readonly<SessionAddress>, phase: "prepare" | "recheck") => void, signal?: AbortSignal,
943
+ ): Promise<T> {
944
+ return this.withSelectedOriginAcceptance(request, action, signal, true, revalidateDirectParent);
945
+ }
946
+
947
+ private async withSelectedOriginAcceptance<T>(
948
+ request: SelectedSessionAcquisitionRequest | SelectedForkCopyRequest | (SelectedOriginRequest & { kind: "shared-new" | "shared-deferred" }),
949
+ action: (publish: () => SelectedFilePublication) => T, signal: AbortSignal | undefined, allowed: boolean,
950
+ revalidateDirectParent?: (source: Readonly<SessionAddress>, phase: "prepare" | "recheck") => void,
951
+ ): Promise<T> {
952
+ if (!["shared-new", "shared-deferred", "new", "unacquired", "fork-new", "fork-deferred"].includes(request.kind)) throw new Error("Invalid selected origin admission kind");
953
+ const kind = request.kind;
954
+ const historyLimit = request.historyLimit;
955
+ const wanted = captureScopeSelection(request.selection);
956
+ const lifecycle = captureLifecycleSnapshot(request.snapshot);
957
+ const requestedAuthor = request.author;
958
+ const base = kind === "unacquired" || kind === "fork-deferred" ? structuredClone(request.base) : undefined;
959
+ const copying = kind === "fork-new" || kind === "fork-deferred";
960
+ const source = copying ? Object.freeze({ ...request.source }) : undefined;
961
+ const acquiring = kind === "new" || kind === "unacquired" || copying;
962
+ const install = (accepted: SelectedFilePublication) => {
963
+ this.selectedOriginAccepted = true;
964
+ this.observedSessionAuthority = true;
965
+ if (acquiring) this.observedPrivateSemantics = true;
966
+ this.installSelected(accepted.cohort);
967
+ };
968
+ const author: SelectedOriginRequest["author"] = requestedAuthor ? (basis, publish) => requestedAuthor(basis, (snapshot, transition, provenance, moduleAdditions) => {
969
+ const accepted = publish(snapshot, transition, provenance, captureSelectedModuleAdditions(moduleAdditions, wanted));
970
+ install(accepted);
971
+ return accepted;
972
+ }) : undefined;
973
+ const assertAuthority = (phase: "prepare" | "recheck" = "recheck"): void => {
974
+ signal?.throwIfAborted();
975
+ if (!allowed) throw new Error(acquiring ? "Selected private acquisition requires explicit native authority" : kind === "shared-deferred" ? "Selected fork deferral requires explicit native fork authority" : "Selected State Flow shared origin requires explicit new-session authority");
976
+ if (wanted.includes("session") !== acquiring) throw new Error(acquiring ? "Selected private acquisition requires Session" : "Selected shared origin cannot acquire Session");
977
+ if (lifecycle.config.mode === "off") throw new Error("Off cannot initialize State Flow memory");
978
+ if (historyLimit !== this.historyLimit) throw new Error("Selected origin history policy does not match its runtime");
979
+ if (copying) {
980
+ if (!source || typeof source.id !== "string" || typeof source.key !== "string" || source.id === this.sessionId || source.key === this.sessionKey) throw new Error("Selected fork requires a distinct direct parent");
981
+ if (typeof revalidateDirectParent !== "function") throw new Error("Selected fork copy requires independent native direct-parent authority");
982
+ revalidateDirectParent(source, phase);
983
+ }
984
+ this.rememberSessionAuthority(this.base);
985
+ if (kind === "unacquired" || kind === "fork-deferred") {
986
+ if (this.observedPrivateSemantics) throw new Error("Known acquired State Flow private memory cannot become an empty origin");
987
+ } else if (this.selectedOriginAccepted || this.observedSessionAuthority || this.semanticRevision || this.savedRuntime) {
988
+ throw new Error("Known State Flow session requires current-memory acceptance, not a new origin");
989
+ }
990
+ };
991
+ assertAuthority("prepare");
992
+ if (kind === "unacquired" || copying) {
993
+ if (!lstatSync(this.root, { throwIfNoEntry: false })) throw new RevisionUnavailableError("Selected private acquisition source storage is unavailable");
994
+ } else initializeFileStore(this.root);
995
+ return withStorageTransaction(this.root, (storage) => {
996
+ assertAuthority();
997
+ let consumed = false;
998
+ let published = false;
999
+ const result = action(() => {
1000
+ if (consumed) throw new Error("Selected State Flow origin publication was already consumed");
1001
+ consumed = true;
1002
+ assertAuthority();
1003
+ const input = { selection: wanted, snapshot: lifecycle, historyLimit: this.historyLimit, ...(author ? { author } : {}) };
1004
+ const accepted = kind === "shared-new"
1005
+ ? storage.initializeSelectedSharedOrigin(this.cwd, this.sessionId, input, this.root, this.sessionKey)
1006
+ : kind === "shared-deferred" ? storage.deferSelectedFork(this.cwd, this.sessionId, input, this.root, this.sessionKey)
1007
+ : copying ? storage.copySelectedFork(this.cwd, this.sessionId, kind === "fork-new" ? { ...input, kind, source: source! } : { ...input, kind: "fork-deferred", source: source!, base: base! }, this.root, this.sessionKey)
1008
+ : storage.acquireSelectedSession(this.cwd, this.sessionId, kind === "new" ? { ...input, kind: "new" } : { ...input, kind: "unacquired", base: base! }, this.root, this.sessionKey);
1009
+ if (!author) install(accepted);
1010
+ published = true;
1011
+ return accepted;
1012
+ });
1013
+ if (!published) throw new Error("Selected State Flow origin requires one synchronous publication");
1014
+ return result;
1015
+ }, signal);
1016
+ }
1017
+
472
1018
  /** Copy the parent's current session memory into an unoccupied child; the caller rechecks native selection after waiting. */
473
1019
  async withForkTransaction<T>(source: SessionAddress, lifecycle: ForkLifecycle, action: (selected: Snapshot, publish: (snapshot: Snapshot) => RuntimePublication) => T, signal?: AbortSignal): Promise<T> {
474
1020
  signal?.throwIfAborted();
@@ -483,7 +1029,7 @@ export class TemporalRuntime {
483
1029
  const parent = new TemporalRuntime(this.cwd, source, this.root, undefined, this.historyLimit);
484
1030
  if (!parent.loadCurrentMemoryBase(sourceBase)) throw new RevisionUnavailableError("State Flow fork parent memory is unavailable");
485
1031
  const base = storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
486
- const files = new Map(base.files.map((file) => [file.path, file.content]));
1032
+ const files = temporalFileContents(base);
487
1033
  const paths = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
488
1034
  if ([paths.checkpoint, paths.patches, paths.meta, join(paths.directory, "config.json"), join(paths.directory, "runtime.json")].some((path) => files.get(path) !== undefined)) {
489
1035
  throw new Error("State Flow fork target already has session storage");
@@ -520,6 +1066,7 @@ export class TemporalRuntime {
520
1066
  const allowCreateOrigin = selection.kind === "start" && selection.allowCreateOrigin;
521
1067
  const semantic = kind !== "lifecycle";
522
1068
  const assertAuthority = (): void => {
1069
+ if (this.selectedBackendChosen) throw new Error("Selected State Flow origin requires selected runtime integration");
523
1070
  if (selection.kind === "fork") {
524
1071
  if (selection.source.id === this.sessionId || selection.source.key === this.sessionKey) throw new Error("State Flow fork requires a distinct session identity and key");
525
1072
  if (this.view) throw new Error("State Flow fork target already has session storage");
@@ -613,7 +1160,13 @@ export class TemporalRuntime {
613
1160
  const updates = options.provenance?.[scope];
614
1161
  if (updates) for (const [path, entry] of Object.entries(updates)) nextProvenance[scope][path] = structuredClone(entry);
615
1162
  }
616
- if (!runtimeOnly) for (const scope of SCOPES) {
1163
+ const legacyBasis = SCOPES.some((scope) => {
1164
+ const meta = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey).meta;
1165
+ const content = base.files.find((file) => file.path === meta)?.content;
1166
+ return content !== undefined && JSON.parse(content).version < 3;
1167
+ });
1168
+ // A representation-only activation must not turn provenance normalization into migration side effects.
1169
+ if (!runtimeOnly && (!legacyBasis || accepted !== undefined || provenanceScopes.length > 0)) for (const scope of SCOPES) {
617
1170
  nextProvenance[scope] = pruneArtifactProvenance(nextProvenance[scope], readTemporalState(next, 0, scope, this.historyLimit).artifacts);
618
1171
  }
619
1172
  const provenanceChanged = !sameJson(nextProvenance, this.provenanceByScope);