@llblab/pi-kit 0.37.1 → 0.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-claude-usage/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-claude-usage/CHANGELOG.md +4 -0
- package/node_modules/@llblab/pi-claude-usage/README.md +3 -3
- package/node_modules/@llblab/pi-claude-usage/lib/status.ts +1 -1
- package/node_modules/@llblab/pi-claude-usage/lib/usage-store.ts +2 -2
- package/node_modules/@llblab/pi-claude-usage/package.json +1 -1
- package/node_modules/@llblab/pi-clean-room/CHANGELOG.md +5 -0
- package/node_modules/@llblab/pi-clean-room/index.ts +39 -9
- package/node_modules/@llblab/pi-clean-room/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +7 -3
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +679 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +17 -0
- package/node_modules/@llblab/pi-state-flow/README.md +30 -14
- package/node_modules/@llblab/pi-state-flow/api/modules.ts +11 -0
- package/node_modules/@llblab/pi-state-flow/dist/api/modules.d.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/api/modules.js +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +7 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +77 -36
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +26 -27
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +13 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +27 -21
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +144 -46
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +14 -18
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +6 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +60 -50
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +1049 -310
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +4 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +15 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +2 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/memory.d.ts +3 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/memory.js +7 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +29 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +186 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/model-interface.d.ts +17 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/model-interface.js +158 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/module-registry.d.ts +21 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/module-registry.js +108 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/operation.js +3 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.d.ts +4 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.js +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/planes.d.ts +29 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/planes.js +84 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +6 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +33 -16
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +21 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +53 -13
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +5 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.d.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.js +10 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +84 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +570 -20
- package/node_modules/@llblab/pi-state-flow/dist/lib/selection.d.ts +10 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/selection.js +32 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/semantic-module.d.ts +79 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/semantic-module.js +187 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +98 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +398 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/skills.d.ts +7 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/skills.js +55 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +67 -14
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +129 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +26 -14
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +61 -19
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +14 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +59 -22
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +111 -10
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +728 -11
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +10 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +50 -14
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +40 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +247 -43
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +41 -8
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +132 -39
- package/node_modules/@llblab/pi-state-flow/dist/package.json +6 -1
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +28 -16
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +12 -8
- package/node_modules/@llblab/pi-state-flow/docs/README.md +9 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +76 -43
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +9 -1
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +6 -0
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +23 -19
- package/node_modules/@llblab/pi-state-flow/docs/protocol-composition-decision.md +66 -0
- package/node_modules/@llblab/pi-state-flow/docs/public-module-contract.md +73 -0
- package/node_modules/@llblab/pi-state-flow/docs/scope-selection-contract.md +814 -0
- package/node_modules/@llblab/pi-state-flow/docs/semantic-compatibility-decision.md +212 -0
- package/node_modules/@llblab/pi-state-flow/docs/semantic-extensibility-decision.md +75 -0
- package/node_modules/@llblab/pi-state-flow/docs/semantic-handoff-contract.md +174 -0
- package/node_modules/@llblab/pi-state-flow/docs/semantic-memory-evaluation.md +166 -0
- package/node_modules/@llblab/pi-state-flow/docs/semantic-module-contract.md +84 -0
- package/node_modules/@llblab/pi-state-flow/docs/single-scope-interface-contract.md +201 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +22 -1
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +32 -18
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +68 -31
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +21 -22
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +14 -1
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +142 -46
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +14 -17
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +53 -44
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +897 -297
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +16 -7
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +2 -1
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +10 -9
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +189 -0
- package/node_modules/@llblab/pi-state-flow/lib/model-interface.ts +136 -0
- package/node_modules/@llblab/pi-state-flow/lib/module-registry.ts +119 -0
- package/node_modules/@llblab/pi-state-flow/lib/operation.ts +3 -2
- package/node_modules/@llblab/pi-state-flow/lib/ownership.ts +6 -6
- package/node_modules/@llblab/pi-state-flow/lib/planes.ts +98 -0
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +37 -16
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +59 -18
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +5 -2
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +10 -1
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +574 -21
- package/node_modules/@llblab/pi-state-flow/lib/selection.ts +39 -0
- package/node_modules/@llblab/pi-state-flow/lib/semantic-module.ts +239 -0
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +387 -3
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +45 -4
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +156 -16
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +69 -28
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +65 -31
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +770 -18
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +58 -20
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +267 -50
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +184 -52
- package/node_modules/@llblab/pi-state-flow/package.json +6 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +28 -16
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +12 -8
- package/package.json +4 -4
|
@@ -3,13 +3,35 @@ import { lstatSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { classifyScopeStream, hasCwdMaterialization, parseScopeProvenance, parseScopeStream, sessionRuntimePaths, temporalScopePaths } from "./durable.js";
|
|
5
5
|
import { pruneArtifactProvenance } from "./artifact.js";
|
|
6
|
-
import { assertTemporalFileBase, captureTemporalFileBase, initializeFileStore, publishTemporalStateToFiles, withStorageTransaction } from "./storage.js";
|
|
6
|
+
import { assertTemporalFileBase, captureSelectedModuleAdditions, captureSelectedProvenanceUpdates, captureTemporalFileBase, temporalFileContents, initializeFileStore, publishTemporalStateToFiles, withStorageTransaction } from "./storage.js";
|
|
7
7
|
import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT } from "./history.js";
|
|
8
|
-
import { hashJson, sameJson } from "./json.js";
|
|
9
|
-
import { RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, preRuntimeCheckpoint, retainedBoundaryCheckpoint } from "./snapshot.js";
|
|
10
|
-
import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView, validateScopeLineage } from "./temporal.js";
|
|
8
|
+
import { hashJson, isObject, sameJson } from "./json.js";
|
|
9
|
+
import { RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, preRuntimeCheckpoint, retainedBoundaryCheckpoint, captureLifecycleSnapshot } from "./snapshot.js";
|
|
10
|
+
import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView, selectTemporalState, validateScopeLineage } from "./temporal.js";
|
|
11
|
+
import { captureScopeSelection } from "./selection.js";
|
|
11
12
|
const SCOPES = ["global", "cwd", "session"];
|
|
12
13
|
const SHARED_SCOPES = ["global", "cwd"];
|
|
14
|
+
function selectedModules(view) {
|
|
15
|
+
return Object.fromEntries(view.selection.filter(scope => view.scopes[scope]?.modules !== undefined)
|
|
16
|
+
.map(scope => [scope, structuredClone(view.scopes[scope].modules)]));
|
|
17
|
+
}
|
|
18
|
+
function selectedRunSnapshot(cohort) {
|
|
19
|
+
const source = cohort.lifecycle.runtime;
|
|
20
|
+
const meta = { step: source.meta.step, ...Object.fromEntries(["specification", "bootstrap", "validation"]
|
|
21
|
+
.filter((key) => Object.hasOwn(source.meta, key)).map((key) => [key, source.meta[key]])) };
|
|
22
|
+
return captureLifecycleSnapshot({ config: { mode: source.config.mode }, meta });
|
|
23
|
+
}
|
|
24
|
+
/** Detached replay inputs; excluded patch bodies must not be touched even for a rejected raw replay. */
|
|
25
|
+
function captureSelectedReplay(accepted, selection) {
|
|
26
|
+
if (accepted === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (!isObject(accepted) || !Array.isArray(accepted.transitions))
|
|
29
|
+
throw new Error("Invalid selected runtime replay");
|
|
30
|
+
for (const item of accepted.transitions)
|
|
31
|
+
if (!isObject(item) || !selection.includes(item.scope))
|
|
32
|
+
throw new Error("Selected runtime replay contains an excluded owner");
|
|
33
|
+
return structuredClone(accepted);
|
|
34
|
+
}
|
|
13
35
|
function emptyProvenance() {
|
|
14
36
|
return { global: {}, cwd: {}, session: {} };
|
|
15
37
|
}
|
|
@@ -45,10 +67,22 @@ function freshEmptyScopeStream(scope, origin, historyLimit) {
|
|
|
45
67
|
}
|
|
46
68
|
/** Cached branch-selected temporal state and publication basis; excludes Pi event policy. */
|
|
47
69
|
export class TemporalRuntime {
|
|
48
|
-
|
|
70
|
+
memory;
|
|
71
|
+
/** Compatibility access to the legacy cache only; selected memory never masquerades as three owners. */
|
|
72
|
+
get view() { return this.memory?.kind === "legacy" ? this.memory.view : undefined; }
|
|
73
|
+
set view(value) {
|
|
74
|
+
if (value !== undefined)
|
|
75
|
+
this.assertLegacyAdmission();
|
|
76
|
+
this.memory = value === undefined ? undefined : { kind: "legacy", view: value };
|
|
77
|
+
}
|
|
49
78
|
base;
|
|
50
79
|
semanticRevision;
|
|
51
80
|
savedRuntime;
|
|
81
|
+
/** Backend choice survives cache clearing but grants no origin, private acquisition or publication authority. */
|
|
82
|
+
selectedBackendChosen = false;
|
|
83
|
+
selectedOriginAccepted = false;
|
|
84
|
+
observedSessionAuthority = false;
|
|
85
|
+
observedPrivateSemantics = false;
|
|
52
86
|
transaction;
|
|
53
87
|
provenanceByScope = emptyProvenance();
|
|
54
88
|
/** Shared scopes whose wholly absent live basis was accepted after one stale-target refusal. */
|
|
@@ -67,18 +101,209 @@ export class TemporalRuntime {
|
|
|
67
101
|
}
|
|
68
102
|
get sessionId() { return this.session.id; }
|
|
69
103
|
get sessionKey() { return this.session.key; }
|
|
104
|
+
rememberSessionAuthority(base) {
|
|
105
|
+
const privatePaths = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
106
|
+
const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
107
|
+
const privateFiles = new Set([privatePaths.checkpoint, privatePaths.patches, privatePaths.meta]);
|
|
108
|
+
for (const file of base?.files ?? [])
|
|
109
|
+
if (file.identity !== "missing") {
|
|
110
|
+
if (privateFiles.has(file.path))
|
|
111
|
+
this.observedPrivateSemantics = true;
|
|
112
|
+
if (privateFiles.has(file.path) || file.path === paths.config || file.path === paths.runtime)
|
|
113
|
+
this.observedSessionAuthority = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
assertLegacyAdmission() {
|
|
117
|
+
if (this.selectedBackendChosen)
|
|
118
|
+
throw new Error("Selected State Flow memory requires selected runtime operations");
|
|
119
|
+
}
|
|
120
|
+
/** Only owned durable acceptance calls this; returned receipts and read-only candidates are not installers. */
|
|
121
|
+
installSelected(cohort) {
|
|
122
|
+
this.memory = { kind: "selected", cohort: structuredClone(cohort) };
|
|
123
|
+
this.selectedBackendChosen = true;
|
|
124
|
+
this.clearLegacyBasis();
|
|
125
|
+
}
|
|
126
|
+
clearLegacyBasis() {
|
|
127
|
+
this.base = undefined;
|
|
128
|
+
this.savedRuntime = undefined;
|
|
129
|
+
this.semanticRevision = undefined;
|
|
130
|
+
this.provenanceByScope = emptyProvenance();
|
|
131
|
+
this.absentSharedScopes.clear();
|
|
132
|
+
}
|
|
133
|
+
installObserved(observation) {
|
|
134
|
+
this.memory = { kind: "observed", observation: structuredClone(observation) };
|
|
135
|
+
this.selectedBackendChosen = true;
|
|
136
|
+
this.clearLegacyBasis();
|
|
137
|
+
}
|
|
138
|
+
observationGuard(revalidateNativePolicy, signal) {
|
|
139
|
+
const memory = this.memory;
|
|
140
|
+
return () => {
|
|
141
|
+
signal?.throwIfAborted();
|
|
142
|
+
if (this.selectedOriginAccepted || this.semanticRevision || this.savedRuntime)
|
|
143
|
+
throw new Error("Accepted State Flow memory requires current acceptance, not observation");
|
|
144
|
+
if (this.memory !== memory)
|
|
145
|
+
throw new Error("Selected State Flow runtime selection or acceptance changed concurrently");
|
|
146
|
+
if (typeof revalidateNativePolicy !== "function")
|
|
147
|
+
throw new Error("Selected observation requires independent native policy authority");
|
|
148
|
+
revalidateNativePolicy();
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
selectedReadCache() {
|
|
152
|
+
if (this.memory?.kind === "selected")
|
|
153
|
+
return this.memory.cohort;
|
|
154
|
+
if (this.memory?.kind === "observed")
|
|
155
|
+
return this.memory.observation;
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
/** Detached read-only selected values; never accepted run fields or a publication/acquisition basis. */
|
|
159
|
+
observedSelectedView() {
|
|
160
|
+
return this.memory?.kind === "observed" ? structuredClone(this.memory.observation.view) : undefined;
|
|
161
|
+
}
|
|
162
|
+
/** Explicit shared inspection before acquisition; missing owners stay unavailable rather than becoming empty streams. */
|
|
163
|
+
async refreshSelectedShared(selection, revalidateNativePolicy, signal) {
|
|
164
|
+
const wanted = captureScopeSelection(selection);
|
|
165
|
+
if (wanted.includes("session"))
|
|
166
|
+
throw new Error("Shared State Flow observation cannot select Session");
|
|
167
|
+
const assertAuthority = this.observationGuard(revalidateNativePolicy, signal);
|
|
168
|
+
const install = (observation) => {
|
|
169
|
+
assertAuthority();
|
|
170
|
+
this.installObserved(observation);
|
|
171
|
+
return observation.view !== undefined;
|
|
172
|
+
};
|
|
173
|
+
assertAuthority();
|
|
174
|
+
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
175
|
+
return install({ selection: wanted, provenance: {} });
|
|
176
|
+
return withStorageTransaction(this.root, storage => {
|
|
177
|
+
assertAuthority();
|
|
178
|
+
return install(storage.observeSelectedShared(this.cwd, wanted, this.root));
|
|
179
|
+
}, signal);
|
|
180
|
+
}
|
|
181
|
+
/** Header-only disposition data; never semantic observation, acceptance, or acquisition permission. */
|
|
182
|
+
async inspectSelectedLifecycle(selection, revalidateNativePolicy, signal) {
|
|
183
|
+
const wanted = captureScopeSelection(selection);
|
|
184
|
+
const memory = this.memory;
|
|
185
|
+
const check = () => {
|
|
186
|
+
signal?.throwIfAborted();
|
|
187
|
+
if (this.memory !== memory)
|
|
188
|
+
throw new Error("State Flow attachment selection changed");
|
|
189
|
+
revalidateNativePolicy();
|
|
190
|
+
};
|
|
191
|
+
check();
|
|
192
|
+
if (!lstatSync(this.root, { throwIfNoEntry: false })) {
|
|
193
|
+
if (this.observedSessionAuthority)
|
|
194
|
+
throw new RevisionUnavailableError("Known selected session authority is unavailable");
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
return withStorageTransaction(this.root, storage => {
|
|
198
|
+
check();
|
|
199
|
+
const lifecycle = storage.inspectSelectedLifecycle(this.cwd, this.sessionId, wanted, this.root, this.sessionKey);
|
|
200
|
+
check();
|
|
201
|
+
if (!lifecycle) {
|
|
202
|
+
if (this.observedSessionAuthority)
|
|
203
|
+
throw new RevisionUnavailableError("Known selected session authority is unavailable");
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
if (this.observedPrivateSemantics && lifecycle.runtime.meta.privateAcquisition !== "acquired")
|
|
207
|
+
throw new Error("Known acquired State Flow private authority cannot be downgraded");
|
|
208
|
+
this.observedSessionAuthority = true;
|
|
209
|
+
if (lifecycle.runtime.meta.privateAcquisition === "acquired")
|
|
210
|
+
this.observedPrivateSemantics = true;
|
|
211
|
+
return { selection: captureScopeSelection(lifecycle.runtime.config.scopes), privateAcquisition: lifecycle.runtime.meta.privateAcquisition,
|
|
212
|
+
snapshot: selectedRunSnapshot({ lifecycle }) };
|
|
213
|
+
}, signal);
|
|
214
|
+
}
|
|
215
|
+
/** Native bookmarks never decide whether current canonical authority exists. */
|
|
216
|
+
async hasSelectedCurrentAuthority(selection, revalidateNativePolicy, signal) {
|
|
217
|
+
return await this.inspectSelectedLifecycle(selection, revalidateNativePolicy, signal) !== undefined;
|
|
218
|
+
}
|
|
219
|
+
/** Fenced inactive restoration/operator inspection: source proof only, without conversion or durable acceptance. */
|
|
220
|
+
async refreshSelectedCurrent(selection, revalidateNativePolicy, signal) {
|
|
221
|
+
const wanted = captureScopeSelection(selection);
|
|
222
|
+
const assertAuthority = this.observationGuard(revalidateNativePolicy, signal);
|
|
223
|
+
assertAuthority();
|
|
224
|
+
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
225
|
+
throw new RevisionUnavailableError("Selected current session storage is unavailable");
|
|
226
|
+
return withStorageTransaction(this.root, storage => {
|
|
227
|
+
assertAuthority();
|
|
228
|
+
const current = storage.prepareSelectedCurrent(this.cwd, this.sessionId, wanted, this.historyLimit, this.root, this.sessionKey);
|
|
229
|
+
if (this.observedPrivateSemantics && current.lifecycle.runtime.meta.privateAcquisition !== "acquired")
|
|
230
|
+
throw new Error("Known acquired State Flow private authority cannot be downgraded");
|
|
231
|
+
// Current acceptance can plan absent shared owners; observation must not expose those planned empty streams.
|
|
232
|
+
const present = wanted.filter(scope => {
|
|
233
|
+
const path = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey).checkpoint;
|
|
234
|
+
return current.basis.files.some(file => file.path === path && file.identity !== "missing");
|
|
235
|
+
});
|
|
236
|
+
const observation = { selection: wanted,
|
|
237
|
+
provenance: Object.fromEntries(present.map(scope => [scope, current.provenance[scope]])),
|
|
238
|
+
...(present.length === 0 ? {} : { view: selectTemporalState(current.view, captureScopeSelection(present), this.historyLimit) }), };
|
|
239
|
+
const snapshot = selectedRunSnapshot(current);
|
|
240
|
+
assertAuthority();
|
|
241
|
+
this.rememberSessionAuthority(current.basis);
|
|
242
|
+
if (current.lifecycle.runtime.meta.privateAcquisition === "acquired")
|
|
243
|
+
this.observedPrivateSemantics = true;
|
|
244
|
+
this.installObserved(observation);
|
|
245
|
+
return snapshot;
|
|
246
|
+
}, signal);
|
|
247
|
+
}
|
|
248
|
+
/** Detached accepted selected values only, never a file receipt or publication capability. */
|
|
249
|
+
selectedView() {
|
|
250
|
+
return this.memory?.kind === "selected" ? structuredClone(this.memory.cohort.view) : undefined;
|
|
251
|
+
}
|
|
252
|
+
/** Accepted run fields only; local policy and pending native prompts remain the adapter's responsibility. */
|
|
253
|
+
selectedSnapshot() {
|
|
254
|
+
if (this.memory?.kind !== "selected")
|
|
255
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
256
|
+
return selectedRunSnapshot(this.memory.cohort);
|
|
257
|
+
}
|
|
258
|
+
/** Native bookkeeping may change mode, never invent accepted run fields or a semantic boundary. */
|
|
259
|
+
retainedSelectedCheckpoint(mode) {
|
|
260
|
+
const memory = this.memory;
|
|
261
|
+
if (memory?.kind !== "selected")
|
|
262
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
263
|
+
const snapshot = selectedRunSnapshot(memory.cohort);
|
|
264
|
+
if (mode !== undefined)
|
|
265
|
+
snapshot.config.mode = mode;
|
|
266
|
+
return { ...retainedBoundaryCheckpoint(snapshot, memory.cohort.view.lineage.at(-1).id), version: 2, scopes: captureScopeSelection(memory.cohort.view.selection) };
|
|
267
|
+
}
|
|
268
|
+
/** Read-only cache identity lease; no source acquisition, storage receipt or publication authority. */
|
|
269
|
+
captureSelectedReadBasis() {
|
|
270
|
+
const memory = this.memory;
|
|
271
|
+
if (!this.selectedReadCache()?.view)
|
|
272
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
273
|
+
return () => { if (this.memory !== memory)
|
|
274
|
+
throw new Error("State Flow module context content basis changed"); };
|
|
275
|
+
}
|
|
276
|
+
/** Exact present owners for the existing selected staging engine; no defaulted disabled scopes. */
|
|
277
|
+
selectedStates(offset = 0) {
|
|
278
|
+
const view = this.selectedReadCache()?.view;
|
|
279
|
+
if (!view)
|
|
280
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
281
|
+
return { selection: captureScopeSelection(view.selection), states: readTemporalScopes(view, offset, this.historyLimit) };
|
|
282
|
+
}
|
|
70
283
|
/** Runtime-owned artifact compilation evidence for one scope; never model-visible state. */
|
|
71
284
|
artifactProvenance(scope) {
|
|
285
|
+
const selected = this.selectedReadCache();
|
|
286
|
+
if (selected) {
|
|
287
|
+
if (!selected.view)
|
|
288
|
+
throw new Error(`State Flow ${scope} scope is excluded or unavailable`);
|
|
289
|
+
if (!selected.view.selection.includes(scope))
|
|
290
|
+
throw new Error(`State Flow ${scope} scope is excluded`);
|
|
291
|
+
return structuredClone(selected.provenance[scope]);
|
|
292
|
+
}
|
|
293
|
+
this.assertLegacyAdmission();
|
|
72
294
|
return structuredClone(this.provenanceByScope[scope]);
|
|
73
295
|
}
|
|
74
296
|
/** Read canonical shared memory without creating, migrating, or publishing storage. */
|
|
75
297
|
loadPassive() {
|
|
298
|
+
this.assertLegacyAdmission();
|
|
76
299
|
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
77
300
|
return false;
|
|
78
301
|
return this.loadPassiveBase(captureTemporalFileBase(this.cwd, this.sessionId, this.root, this.sessionKey));
|
|
79
302
|
}
|
|
80
303
|
loadPassiveBase(base) {
|
|
81
|
-
|
|
304
|
+
this.rememberSessionAuthority(this.base);
|
|
305
|
+
this.rememberSessionAuthority(base);
|
|
306
|
+
const files = temporalFileContents(base);
|
|
82
307
|
const shared = Object.fromEntries(SHARED_SCOPES.map((scope) => {
|
|
83
308
|
const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
|
|
84
309
|
return [scope, parseScopeStream(files.get(paths.checkpoint), files.get(paths.patches), scope, scope === "cwd" ? this.cwd : undefined, files.get(paths.meta))];
|
|
@@ -112,25 +337,30 @@ export class TemporalRuntime {
|
|
|
112
337
|
this.prepareCanonical();
|
|
113
338
|
}
|
|
114
339
|
read(offset = 0, scope) {
|
|
115
|
-
|
|
340
|
+
const view = this.selectedReadCache()?.view ?? this.view;
|
|
341
|
+
if (!view)
|
|
116
342
|
throw new Error("State Flow temporal runtime is unavailable");
|
|
117
|
-
return readTemporalState(
|
|
343
|
+
return readTemporalState(view, offset, scope, this.historyLimit);
|
|
118
344
|
}
|
|
119
345
|
readView(offset = 0, scope) {
|
|
120
|
-
|
|
346
|
+
const view = this.selectedReadCache()?.view ?? this.view;
|
|
347
|
+
if (!view)
|
|
121
348
|
throw new Error("State Flow temporal runtime is unavailable");
|
|
122
|
-
return readTemporalView(
|
|
349
|
+
return readTemporalView(view, offset, scope, this.historyLimit);
|
|
123
350
|
}
|
|
124
351
|
states() {
|
|
352
|
+
this.assertLegacyAdmission();
|
|
125
353
|
return { global: this.read(0, "global"), cwd: this.read(0, "cwd"), session: this.read(0, "session") };
|
|
126
354
|
}
|
|
127
355
|
causalBasis() {
|
|
128
|
-
|
|
356
|
+
const view = this.selectedReadCache()?.view ?? this.view;
|
|
357
|
+
if (!view)
|
|
129
358
|
throw new Error("State Flow temporal runtime is unavailable");
|
|
130
|
-
return
|
|
359
|
+
return view.lineage.at(-1).id;
|
|
131
360
|
}
|
|
132
361
|
/** Encode Pi lifecycle state against the current retained semantic boundary. */
|
|
133
362
|
retainedCheckpoint(snapshot) {
|
|
363
|
+
this.assertLegacyAdmission();
|
|
134
364
|
if (!this.view)
|
|
135
365
|
return preRuntimeCheckpoint(snapshot.config.mode);
|
|
136
366
|
return retainedBoundaryCheckpoint(snapshot, this.causalBasis());
|
|
@@ -140,9 +370,11 @@ export class TemporalRuntime {
|
|
|
140
370
|
}
|
|
141
371
|
recent() {
|
|
142
372
|
const result = [];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
373
|
+
const selected = this.selectedReadCache()?.view;
|
|
374
|
+
const view = selected ?? this.view;
|
|
375
|
+
for (const boundary of view?.lineage.slice(1) ?? []) {
|
|
376
|
+
const transitions = (selected?.selection ?? SCOPES).flatMap((scope) => {
|
|
377
|
+
const record = view.scopes[scope].patches.find(({ transition }) => transition.id === boundary.id);
|
|
146
378
|
return record ? [{ scope, patch: structuredClone(record.patch) }] : [];
|
|
147
379
|
});
|
|
148
380
|
if (transitions.length)
|
|
@@ -153,6 +385,7 @@ export class TemporalRuntime {
|
|
|
153
385
|
/** Await a coherent read-only recovery view; this neither activates policy nor accepts publication authority. */
|
|
154
386
|
async refreshCurrentMemory(signal) {
|
|
155
387
|
signal?.throwIfAborted();
|
|
388
|
+
this.assertLegacyAdmission();
|
|
156
389
|
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
157
390
|
return undefined;
|
|
158
391
|
return withStorageTransaction(this.root, (storage) => {
|
|
@@ -162,11 +395,12 @@ export class TemporalRuntime {
|
|
|
162
395
|
}, signal);
|
|
163
396
|
}
|
|
164
397
|
loadCurrentMemoryBase(base) {
|
|
165
|
-
const files =
|
|
398
|
+
const files = temporalFileContents(base);
|
|
166
399
|
const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
167
400
|
const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
168
401
|
if ([paths.config, paths.runtime, session.checkpoint, session.patches, session.meta].every((path) => files.get(path) === undefined))
|
|
169
402
|
return undefined;
|
|
403
|
+
this.rememberSessionAuthority(base);
|
|
170
404
|
const document = parseSessionRuntime(files.get(paths.config), files.get(paths.runtime), this.cwd, this.sessionId);
|
|
171
405
|
if (!document)
|
|
172
406
|
throw new RevisionUnavailableError("Current State Flow session runtime is unavailable");
|
|
@@ -208,6 +442,8 @@ export class TemporalRuntime {
|
|
|
208
442
|
return this.initializeOrigin(snapshot, { allowCreateCwd, expectedShared, newSessionOrigin });
|
|
209
443
|
}
|
|
210
444
|
initializeOrigin(snapshot, options) {
|
|
445
|
+
if (this.selectedBackendChosen)
|
|
446
|
+
throw new Error("Selected State Flow origin requires selected runtime integration");
|
|
211
447
|
const { allowCreateCwd, expectedShared, newSessionOrigin = false, copy } = options;
|
|
212
448
|
const hasCwd = hasCwdMaterialization(this.cwd, this.root);
|
|
213
449
|
if (!allowCreateCwd && !hasCwd)
|
|
@@ -215,7 +451,7 @@ export class TemporalRuntime {
|
|
|
215
451
|
if (!copy && allowCreateCwd)
|
|
216
452
|
initializeFileStore(this.root);
|
|
217
453
|
const base = captureTemporalFileBase(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
218
|
-
const files =
|
|
454
|
+
const files = temporalFileContents(base);
|
|
219
455
|
if (copy) {
|
|
220
456
|
const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
221
457
|
const owned = [session.checkpoint, session.patches, session.meta, join(session.directory, "config.json"), join(session.directory, "runtime.json"), join(session.directory, "state.json")];
|
|
@@ -361,6 +597,7 @@ export class TemporalRuntime {
|
|
|
361
597
|
/** Await coherent shared inspection, lazily loading an empty private view only when none is selected. */
|
|
362
598
|
async refreshShared(signal) {
|
|
363
599
|
signal?.throwIfAborted();
|
|
600
|
+
this.assertLegacyAdmission();
|
|
364
601
|
if (!this.view && !lstatSync(this.root, { throwIfNoEntry: false }))
|
|
365
602
|
return false;
|
|
366
603
|
return withStorageTransaction(this.root, (storage) => {
|
|
@@ -392,7 +629,7 @@ export class TemporalRuntime {
|
|
|
392
629
|
candidate.provenanceByScope = reconciled.provenance;
|
|
393
630
|
return candidate;
|
|
394
631
|
}
|
|
395
|
-
const files =
|
|
632
|
+
const files = temporalFileContents(current);
|
|
396
633
|
const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
397
634
|
const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
398
635
|
if ([paths.config, paths.runtime, session.checkpoint, session.patches, session.meta].some((path) => files.get(path) !== undefined)) {
|
|
@@ -426,10 +663,315 @@ export class TemporalRuntime {
|
|
|
426
663
|
publish,
|
|
427
664
|
}), { kind: "patch" }, signal);
|
|
428
665
|
}
|
|
666
|
+
/** Authored under exclusion against validated live selected values; use the existing selected staging/commit engine. */
|
|
667
|
+
async withSelectedPatchTransaction(action, revalidateNativePolicy, signal, capturedTargets = []) {
|
|
668
|
+
return this.withSelectedPublicationTransaction(action, "current", revalidateNativePolicy, signal, capturedTargets);
|
|
669
|
+
}
|
|
670
|
+
/** Mode-only Passive persistence: exact accepted sources, no current-origin adoption or caller-authored run fields. */
|
|
671
|
+
async persistSelectedPassive(revalidateNativePolicy, signal) {
|
|
672
|
+
const memory = this.memory;
|
|
673
|
+
if (memory?.kind !== "selected")
|
|
674
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
675
|
+
const assertAuthority = () => {
|
|
676
|
+
signal?.throwIfAborted();
|
|
677
|
+
if (this.memory !== memory)
|
|
678
|
+
throw new Error("Selected State Flow runtime selection or acceptance changed concurrently");
|
|
679
|
+
if (typeof revalidateNativePolicy !== "function")
|
|
680
|
+
throw new Error("Selected Passive persistence requires independent native policy authority");
|
|
681
|
+
revalidateNativePolicy();
|
|
682
|
+
};
|
|
683
|
+
assertAuthority();
|
|
684
|
+
return withStorageTransaction(this.root, storage => {
|
|
685
|
+
assertAuthority();
|
|
686
|
+
const publication = storage.persistSelectedPassive(this.cwd, this.sessionId, memory.cohort, this.root, this.sessionKey);
|
|
687
|
+
if (publication.changed)
|
|
688
|
+
this.installSelected(publication.cohort);
|
|
689
|
+
return publication;
|
|
690
|
+
}, signal);
|
|
691
|
+
}
|
|
692
|
+
/** Precomputed replay remains tied to its accepted cache; changed selected shared targets conflict before publication. */
|
|
693
|
+
async publishSelected(snapshot, accepted, revalidateNativePolicy, options = {}) {
|
|
694
|
+
if (this.memory?.kind !== "selected")
|
|
695
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
696
|
+
const provenance = captureSelectedProvenanceUpdates(options.provenance, this.memory.cohort.view.selection);
|
|
697
|
+
const replay = captureSelectedReplay(accepted, this.memory.cohort.view.selection);
|
|
698
|
+
const additions = captureSelectedModuleAdditions(options.moduleAdditions, this.memory.cohort.view.selection);
|
|
699
|
+
const lifecycle = captureLifecycleSnapshot(snapshot);
|
|
700
|
+
return this.withSelectedPublicationTransaction(transaction => transaction.publish(lifecycle, replay, provenance, additions), "replay", revalidateNativePolicy, options.signal);
|
|
701
|
+
}
|
|
702
|
+
async withSelectedPublicationTransaction(action, kind, revalidateNativePolicy, signal, capturedTargets = []) {
|
|
703
|
+
const memory = this.memory;
|
|
704
|
+
if (memory?.kind !== "selected")
|
|
705
|
+
throw new Error("Selected State Flow runtime is unavailable");
|
|
706
|
+
const selection = captureScopeSelection(memory.cohort.view.selection);
|
|
707
|
+
const targets = capturedTargets.length ? captureScopeSelection(capturedTargets) : undefined;
|
|
708
|
+
if (targets?.some(scope => !selection.includes(scope)))
|
|
709
|
+
throw new Error("Captured State Flow write target is excluded");
|
|
710
|
+
const assertAuthority = () => {
|
|
711
|
+
signal?.throwIfAborted();
|
|
712
|
+
if (this.memory !== memory)
|
|
713
|
+
throw new Error("Selected State Flow runtime selection or acceptance changed concurrently");
|
|
714
|
+
if (typeof revalidateNativePolicy !== "function")
|
|
715
|
+
throw new Error("Selected semantic transaction requires independent native policy authority");
|
|
716
|
+
revalidateNativePolicy();
|
|
717
|
+
};
|
|
718
|
+
assertAuthority();
|
|
719
|
+
return withStorageTransaction(this.root, storage => {
|
|
720
|
+
assertAuthority();
|
|
721
|
+
const current = storage.prepareSelectedCurrent(this.cwd, this.sessionId, selection, this.historyLimit, this.root, this.sessionKey);
|
|
722
|
+
const sharedPaths = new Set(selection.filter(scope => scope !== "session").flatMap(scope => {
|
|
723
|
+
const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
|
|
724
|
+
return [paths.checkpoint, paths.patches, paths.meta];
|
|
725
|
+
}));
|
|
726
|
+
assertTemporalFileBase({ files: memory.cohort.basis.files.filter(file => !sharedPaths.has(file.path)) }, { files: current.basis.files.filter(file => !sharedPaths.has(file.path)) });
|
|
727
|
+
if (!sameJson(memory.cohort.presence, current.presence))
|
|
728
|
+
throw new Error("Selected State Flow presence dependencies changed concurrently");
|
|
729
|
+
if (targets)
|
|
730
|
+
this.assertSelectedReplayTargets(memory.cohort, current, undefined, undefined, undefined, targets);
|
|
731
|
+
let consumed = false;
|
|
732
|
+
let published = false;
|
|
733
|
+
const result = action(Object.freeze({
|
|
734
|
+
selection, states: readTemporalScopes(current.view, 0, this.historyLimit), modules: selectedModules(current.view), snapshot: selectedRunSnapshot(current),
|
|
735
|
+
causalBasis: current.view.lineage.at(-1).id, provenance: structuredClone(current.provenance),
|
|
736
|
+
publish: (snapshot, accepted, requestedProvenance, requestedModules) => {
|
|
737
|
+
if (consumed)
|
|
738
|
+
throw new Error("Selected semantic transaction publication was already consumed");
|
|
739
|
+
consumed = true;
|
|
740
|
+
assertAuthority();
|
|
741
|
+
const provenance = captureSelectedProvenanceUpdates(requestedProvenance, selection);
|
|
742
|
+
const transition = captureSelectedReplay(accepted, selection);
|
|
743
|
+
const moduleAdditions = captureSelectedModuleAdditions(requestedModules, selection);
|
|
744
|
+
if (kind === "replay")
|
|
745
|
+
this.assertSelectedReplayTargets(memory.cohort, current, transition, provenance, moduleAdditions);
|
|
746
|
+
const publication = storage.acceptSelectedCurrent(this.cwd, this.sessionId, { selection, historyLimit: this.historyLimit, lifecycle: captureLifecycleSnapshot(snapshot), transition, provenance, moduleAdditions }, current, this.root, this.sessionKey);
|
|
747
|
+
this.installSelected(publication.cohort);
|
|
748
|
+
published = true;
|
|
749
|
+
return publication;
|
|
750
|
+
},
|
|
751
|
+
}));
|
|
752
|
+
if (!published)
|
|
753
|
+
throw new Error("Selected semantic transaction requires one synchronous publication");
|
|
754
|
+
return result;
|
|
755
|
+
}, signal);
|
|
756
|
+
}
|
|
757
|
+
assertSelectedReplayTargets(base, current, transition, provenance, additions, capturedTargets = []) {
|
|
758
|
+
const targets = new Set([...capturedTargets, ...(transition?.transitions.map(item => item.scope) ?? []), ...Object.keys(provenance ?? {}), ...Object.keys(additions ?? {})]);
|
|
759
|
+
const files = temporalFileContents(current.basis);
|
|
760
|
+
const changed = [], removed = [];
|
|
761
|
+
for (const scope of base.view.selection.filter(scope => scope !== "session" && targets.has(scope))) {
|
|
762
|
+
const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
|
|
763
|
+
const live = classifyScopeStream(files.get(paths.checkpoint), files.get(paths.patches), scope, scope === "cwd" ? this.cwd : undefined, files.get(paths.meta));
|
|
764
|
+
if (live.kind === "absent") {
|
|
765
|
+
removed.push(scope);
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (!sameJson(live.stream, base.view.scopes[scope]) || !sameJson(current.provenance[scope], base.provenance[scope]))
|
|
769
|
+
changed.push(scope);
|
|
770
|
+
}
|
|
771
|
+
if (removed.length > 0)
|
|
772
|
+
throw removedTargetScopeConflict(removed);
|
|
773
|
+
if (changed.length > 0)
|
|
774
|
+
throw targetScopeConflict(changed);
|
|
775
|
+
}
|
|
429
776
|
/** Activate current owned memory; the caller must authorize a wholly absent private origin after waiting. */
|
|
430
777
|
async withStartTransaction(action, signal, allowCreateOrigin = false) {
|
|
431
778
|
return this.withPublicationTransaction((_candidate, publish, current) => action(current, (snapshot) => publish(snapshot)), { kind: "start", allowCreateOrigin }, signal);
|
|
432
779
|
}
|
|
780
|
+
/** New-session shared-only acceptance; caller rechecks native origin authority after waiting. */
|
|
781
|
+
async withSelectedSharedOriginTransaction(selection, snapshot, action, signal, allowCreateOrigin = false) {
|
|
782
|
+
return this.withSelectedOriginAcceptance({ kind: "shared-new", selection, snapshot, historyLimit: this.historyLimit }, action, signal, allowCreateOrigin);
|
|
783
|
+
}
|
|
784
|
+
/** Child-owned deferred marker only; no parent locator or parent memory is acquired here. */
|
|
785
|
+
async withSelectedDeferredForkTransaction(selection, lifecycle, action, signal, allowDeferFork = false, modeAtPublication) {
|
|
786
|
+
if (!lifecycle || Object.keys(lifecycle).some((key) => key !== "mode" && key !== "bootstrap")
|
|
787
|
+
|| (lifecycle.bootstrap !== undefined && lifecycle.bootstrap !== true))
|
|
788
|
+
throw new Error("Invalid selected fork lifecycle");
|
|
789
|
+
if (modeAtPublication !== undefined && typeof modeAtPublication !== "function")
|
|
790
|
+
throw new Error("Invalid selected fork mode sampler");
|
|
791
|
+
const snapshot = { config: { mode: lifecycle.mode }, meta: { step: 0, ...(lifecycle.bootstrap ? { bootstrap: true } : {}) } };
|
|
792
|
+
// Sample mode only; callers cannot replace the fresh child run fields through this hook.
|
|
793
|
+
const author = modeAtPublication ? (basis, publish) => {
|
|
794
|
+
const mode = modeAtPublication();
|
|
795
|
+
signal?.throwIfAborted();
|
|
796
|
+
publish({ config: { mode }, meta: basis.snapshot.meta });
|
|
797
|
+
} : undefined;
|
|
798
|
+
return this.withSelectedOriginAcceptance({ kind: "shared-deferred", selection, snapshot, historyLimit: this.historyLimit, author }, action, signal, allowDeferFork);
|
|
799
|
+
}
|
|
800
|
+
/** Explicit first private acquisition, never deferred-parent recovery or repair of lost acquired state. */
|
|
801
|
+
async withSelectedSessionAcquisitionTransaction(request, action, signal, allowAcquirePrivate = false) {
|
|
802
|
+
return this.withSelectedOriginAcceptance(request, action, signal, allowAcquirePrivate);
|
|
803
|
+
}
|
|
804
|
+
/** Stage first lifecycle/semantic authoring into one origin acceptance, never an empty publication followed by a patch. */
|
|
805
|
+
async withSelectedFirstPatchTransaction(selection, snapshot, action, revalidateNativePolicy, signal, allowCreateOrigin = false) {
|
|
806
|
+
const memory = this.memory;
|
|
807
|
+
const check = () => {
|
|
808
|
+
signal?.throwIfAborted();
|
|
809
|
+
if (!allowCreateOrigin)
|
|
810
|
+
throw new Error("Selected first authoring requires explicit native origin authority");
|
|
811
|
+
if (this.memory !== memory)
|
|
812
|
+
throw new Error("Selected first authoring selection changed concurrently");
|
|
813
|
+
revalidateNativePolicy();
|
|
814
|
+
};
|
|
815
|
+
check();
|
|
816
|
+
const wanted = captureScopeSelection(selection);
|
|
817
|
+
let result;
|
|
818
|
+
const author = (basis, publish) => {
|
|
819
|
+
check();
|
|
820
|
+
result = action(Object.freeze({
|
|
821
|
+
selection: captureScopeSelection(basis.view.selection), states: readTemporalScopes(basis.view, 0, this.historyLimit), modules: selectedModules(basis.view),
|
|
822
|
+
snapshot: basis.snapshot, causalBasis: basis.view.lineage.at(-1).id, provenance: basis.provenance,
|
|
823
|
+
publish: (next, transition, provenance, moduleAdditions) => {
|
|
824
|
+
check();
|
|
825
|
+
return publish(next, transition, provenance, captureSelectedModuleAdditions(moduleAdditions, wanted));
|
|
826
|
+
},
|
|
827
|
+
}));
|
|
828
|
+
};
|
|
829
|
+
await this.withSelectedOriginAcceptance({ kind: wanted.includes("session") ? "new" : "shared-new", selection: wanted, snapshot, historyLimit: this.historyLimit, author }, publish => { check(); publish(); }, signal, allowCreateOrigin);
|
|
830
|
+
return result;
|
|
831
|
+
}
|
|
832
|
+
/** Same-session current acceptance/authoring; native policy checks are synchronous under exclusion. */
|
|
833
|
+
async withSelectedCurrentTransaction(selection, action, revalidateNativePolicy, signal) {
|
|
834
|
+
const wanted = captureScopeSelection(selection);
|
|
835
|
+
const historyLimit = this.historyLimit;
|
|
836
|
+
const assertAuthority = () => {
|
|
837
|
+
signal?.throwIfAborted();
|
|
838
|
+
if (typeof revalidateNativePolicy !== "function")
|
|
839
|
+
throw new Error("Selected current acceptance requires independent native policy authority");
|
|
840
|
+
if (historyLimit !== this.historyLimit)
|
|
841
|
+
throw new Error("Selected current history policy changed concurrently");
|
|
842
|
+
revalidateNativePolicy();
|
|
843
|
+
};
|
|
844
|
+
assertAuthority();
|
|
845
|
+
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
846
|
+
throw new RevisionUnavailableError("Selected current session storage is unavailable");
|
|
847
|
+
return withStorageTransaction(this.root, (storage) => {
|
|
848
|
+
assertAuthority();
|
|
849
|
+
const base = storage.prepareSelectedCurrent(this.cwd, this.sessionId, wanted, historyLimit, this.root, this.sessionKey);
|
|
850
|
+
this.rememberSessionAuthority(base.basis);
|
|
851
|
+
if (this.observedPrivateSemantics && base.lifecycle.runtime.meta.privateAcquisition !== "acquired")
|
|
852
|
+
throw new Error("Known acquired State Flow private authority cannot be downgraded");
|
|
853
|
+
this.observedSessionAuthority = true;
|
|
854
|
+
if (base.lifecycle.runtime.meta.privateAcquisition === "acquired")
|
|
855
|
+
this.observedPrivateSemantics = true;
|
|
856
|
+
const current = selectedRunSnapshot(base);
|
|
857
|
+
let consumed = false, published = false, authorActive = true;
|
|
858
|
+
const publish = (snapshot, accepted, requestedProvenance, requestedModules) => {
|
|
859
|
+
if (!authorActive)
|
|
860
|
+
throw new Error("Selected current authoring has ended");
|
|
861
|
+
if (consumed)
|
|
862
|
+
throw new Error("Selected current acceptance publication was already consumed");
|
|
863
|
+
consumed = true;
|
|
864
|
+
assertAuthority();
|
|
865
|
+
const lifecycle = snapshot === undefined ? undefined : captureLifecycleSnapshot(snapshot);
|
|
866
|
+
const transition = captureSelectedReplay(accepted, wanted);
|
|
867
|
+
const provenance = captureSelectedProvenanceUpdates(requestedProvenance, wanted);
|
|
868
|
+
const moduleAdditions = captureSelectedModuleAdditions(requestedModules, wanted);
|
|
869
|
+
const publication = storage.acceptSelectedCurrent(this.cwd, this.sessionId, { selection: wanted, historyLimit, lifecycle, transition, provenance, moduleAdditions }, base, this.root, this.sessionKey);
|
|
870
|
+
this.selectedOriginAccepted = true;
|
|
871
|
+
this.installSelected(publication.cohort);
|
|
872
|
+
published = true;
|
|
873
|
+
return publication;
|
|
874
|
+
};
|
|
875
|
+
try {
|
|
876
|
+
const result = action(current, snapshot => publish(snapshot), Object.freeze({
|
|
877
|
+
selection: wanted, states: readTemporalScopes(base.view, 0, historyLimit), modules: selectedModules(base.view), snapshot: structuredClone(current),
|
|
878
|
+
causalBasis: base.view.lineage.at(-1).id, provenance: structuredClone(base.provenance), publish,
|
|
879
|
+
}));
|
|
880
|
+
if (!published)
|
|
881
|
+
throw new Error("Selected current acceptance requires one synchronous publication");
|
|
882
|
+
return result;
|
|
883
|
+
}
|
|
884
|
+
finally {
|
|
885
|
+
authorActive = false;
|
|
886
|
+
}
|
|
887
|
+
}, signal);
|
|
888
|
+
}
|
|
889
|
+
/** Prepare may read persisted native identity outside exclusion; recheck must use in-memory native policy only. */
|
|
890
|
+
async withSelectedForkCopyTransaction(request, action, revalidateDirectParent, signal) {
|
|
891
|
+
return this.withSelectedOriginAcceptance(request, action, signal, true, revalidateDirectParent);
|
|
892
|
+
}
|
|
893
|
+
async withSelectedOriginAcceptance(request, action, signal, allowed, revalidateDirectParent) {
|
|
894
|
+
if (!["shared-new", "shared-deferred", "new", "unacquired", "fork-new", "fork-deferred"].includes(request.kind))
|
|
895
|
+
throw new Error("Invalid selected origin admission kind");
|
|
896
|
+
const kind = request.kind;
|
|
897
|
+
const historyLimit = request.historyLimit;
|
|
898
|
+
const wanted = captureScopeSelection(request.selection);
|
|
899
|
+
const lifecycle = captureLifecycleSnapshot(request.snapshot);
|
|
900
|
+
const requestedAuthor = request.author;
|
|
901
|
+
const base = kind === "unacquired" || kind === "fork-deferred" ? structuredClone(request.base) : undefined;
|
|
902
|
+
const copying = kind === "fork-new" || kind === "fork-deferred";
|
|
903
|
+
const source = copying ? Object.freeze({ ...request.source }) : undefined;
|
|
904
|
+
const acquiring = kind === "new" || kind === "unacquired" || copying;
|
|
905
|
+
const install = (accepted) => {
|
|
906
|
+
this.selectedOriginAccepted = true;
|
|
907
|
+
this.observedSessionAuthority = true;
|
|
908
|
+
if (acquiring)
|
|
909
|
+
this.observedPrivateSemantics = true;
|
|
910
|
+
this.installSelected(accepted.cohort);
|
|
911
|
+
};
|
|
912
|
+
const author = requestedAuthor ? (basis, publish) => requestedAuthor(basis, (snapshot, transition, provenance, moduleAdditions) => {
|
|
913
|
+
const accepted = publish(snapshot, transition, provenance, captureSelectedModuleAdditions(moduleAdditions, wanted));
|
|
914
|
+
install(accepted);
|
|
915
|
+
return accepted;
|
|
916
|
+
}) : undefined;
|
|
917
|
+
const assertAuthority = (phase = "recheck") => {
|
|
918
|
+
signal?.throwIfAborted();
|
|
919
|
+
if (!allowed)
|
|
920
|
+
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");
|
|
921
|
+
if (wanted.includes("session") !== acquiring)
|
|
922
|
+
throw new Error(acquiring ? "Selected private acquisition requires Session" : "Selected shared origin cannot acquire Session");
|
|
923
|
+
if (lifecycle.config.mode === "off")
|
|
924
|
+
throw new Error("Off cannot initialize State Flow memory");
|
|
925
|
+
if (historyLimit !== this.historyLimit)
|
|
926
|
+
throw new Error("Selected origin history policy does not match its runtime");
|
|
927
|
+
if (copying) {
|
|
928
|
+
if (!source || typeof source.id !== "string" || typeof source.key !== "string" || source.id === this.sessionId || source.key === this.sessionKey)
|
|
929
|
+
throw new Error("Selected fork requires a distinct direct parent");
|
|
930
|
+
if (typeof revalidateDirectParent !== "function")
|
|
931
|
+
throw new Error("Selected fork copy requires independent native direct-parent authority");
|
|
932
|
+
revalidateDirectParent(source, phase);
|
|
933
|
+
}
|
|
934
|
+
this.rememberSessionAuthority(this.base);
|
|
935
|
+
if (kind === "unacquired" || kind === "fork-deferred") {
|
|
936
|
+
if (this.observedPrivateSemantics)
|
|
937
|
+
throw new Error("Known acquired State Flow private memory cannot become an empty origin");
|
|
938
|
+
}
|
|
939
|
+
else if (this.selectedOriginAccepted || this.observedSessionAuthority || this.semanticRevision || this.savedRuntime) {
|
|
940
|
+
throw new Error("Known State Flow session requires current-memory acceptance, not a new origin");
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
assertAuthority("prepare");
|
|
944
|
+
if (kind === "unacquired" || copying) {
|
|
945
|
+
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
946
|
+
throw new RevisionUnavailableError("Selected private acquisition source storage is unavailable");
|
|
947
|
+
}
|
|
948
|
+
else
|
|
949
|
+
initializeFileStore(this.root);
|
|
950
|
+
return withStorageTransaction(this.root, (storage) => {
|
|
951
|
+
assertAuthority();
|
|
952
|
+
let consumed = false;
|
|
953
|
+
let published = false;
|
|
954
|
+
const result = action(() => {
|
|
955
|
+
if (consumed)
|
|
956
|
+
throw new Error("Selected State Flow origin publication was already consumed");
|
|
957
|
+
consumed = true;
|
|
958
|
+
assertAuthority();
|
|
959
|
+
const input = { selection: wanted, snapshot: lifecycle, historyLimit: this.historyLimit, ...(author ? { author } : {}) };
|
|
960
|
+
const accepted = kind === "shared-new"
|
|
961
|
+
? storage.initializeSelectedSharedOrigin(this.cwd, this.sessionId, input, this.root, this.sessionKey)
|
|
962
|
+
: kind === "shared-deferred" ? storage.deferSelectedFork(this.cwd, this.sessionId, input, this.root, this.sessionKey)
|
|
963
|
+
: 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)
|
|
964
|
+
: storage.acquireSelectedSession(this.cwd, this.sessionId, kind === "new" ? { ...input, kind: "new" } : { ...input, kind: "unacquired", base: base }, this.root, this.sessionKey);
|
|
965
|
+
if (!author)
|
|
966
|
+
install(accepted);
|
|
967
|
+
published = true;
|
|
968
|
+
return accepted;
|
|
969
|
+
});
|
|
970
|
+
if (!published)
|
|
971
|
+
throw new Error("Selected State Flow origin requires one synchronous publication");
|
|
972
|
+
return result;
|
|
973
|
+
}, signal);
|
|
974
|
+
}
|
|
433
975
|
/** Copy the parent's current session memory into an unoccupied child; the caller rechecks native selection after waiting. */
|
|
434
976
|
async withForkTransaction(source, lifecycle, action, signal) {
|
|
435
977
|
signal?.throwIfAborted();
|
|
@@ -441,7 +983,7 @@ export class TemporalRuntime {
|
|
|
441
983
|
if (!parent.loadCurrentMemoryBase(sourceBase))
|
|
442
984
|
throw new RevisionUnavailableError("State Flow fork parent memory is unavailable");
|
|
443
985
|
const base = storage.capture(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
444
|
-
const files =
|
|
986
|
+
const files = temporalFileContents(base);
|
|
445
987
|
const paths = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
446
988
|
if ([paths.checkpoint, paths.patches, paths.meta, join(paths.directory, "config.json"), join(paths.directory, "runtime.json")].some((path) => files.get(path) !== undefined)) {
|
|
447
989
|
throw new Error("State Flow fork target already has session storage");
|
|
@@ -473,6 +1015,8 @@ export class TemporalRuntime {
|
|
|
473
1015
|
const allowCreateOrigin = selection.kind === "start" && selection.allowCreateOrigin;
|
|
474
1016
|
const semantic = kind !== "lifecycle";
|
|
475
1017
|
const assertAuthority = () => {
|
|
1018
|
+
if (this.selectedBackendChosen)
|
|
1019
|
+
throw new Error("Selected State Flow origin requires selected runtime integration");
|
|
476
1020
|
if (selection.kind === "fork") {
|
|
477
1021
|
if (selection.source.id === this.sessionId || selection.source.key === this.sessionKey)
|
|
478
1022
|
throw new Error("State Flow fork requires a distinct session identity and key");
|
|
@@ -573,7 +1117,13 @@ export class TemporalRuntime {
|
|
|
573
1117
|
for (const [path, entry] of Object.entries(updates))
|
|
574
1118
|
nextProvenance[scope][path] = structuredClone(entry);
|
|
575
1119
|
}
|
|
576
|
-
|
|
1120
|
+
const legacyBasis = SCOPES.some((scope) => {
|
|
1121
|
+
const meta = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey).meta;
|
|
1122
|
+
const content = base.files.find((file) => file.path === meta)?.content;
|
|
1123
|
+
return content !== undefined && JSON.parse(content).version < 3;
|
|
1124
|
+
});
|
|
1125
|
+
// A representation-only activation must not turn provenance normalization into migration side effects.
|
|
1126
|
+
if (!runtimeOnly && (!legacyBasis || accepted !== undefined || provenanceScopes.length > 0))
|
|
577
1127
|
for (const scope of SCOPES) {
|
|
578
1128
|
nextProvenance[scope] = pruneArtifactProvenance(nextProvenance[scope], readTemporalState(next, 0, scope, this.historyLimit).artifacts);
|
|
579
1129
|
}
|