@llblab/pi-kit 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +2 -2
- package/CHANGELOG.md +5 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +12 -5
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +9 -0
- package/node_modules/@llblab/pi-state-flow/README.md +116 -34
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +24 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +80 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +17 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +40 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +149 -298
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +29 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +58 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/operation.d.ts +37 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/operation.js +59 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.d.ts +31 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.js +117 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +7 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +28 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +6 -1
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +14 -6
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/README.md +19 -9
- package/node_modules/@llblab/pi-state-flow/docs/agent-contract-relocation.md +4 -4
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +644 -91
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +116 -37
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +118 -21
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +68 -8
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +88 -14
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +83 -66
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +391 -62
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +317 -62
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +85 -1
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +47 -0
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +161 -295
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +63 -0
- package/node_modules/@llblab/pi-state-flow/lib/operation.ts +75 -0
- package/node_modules/@llblab/pi-state-flow/lib/ownership.ts +120 -0
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +7 -8
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +1 -2
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +29 -0
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +5 -1
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +14 -6
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +2 -2
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +10 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +4 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +16 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +7 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +12 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +137 -83
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +26 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +57 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +109 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +3 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +31 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +4 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +17 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +48 -12
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +6 -5
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +2 -0
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +4 -0
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +17 -10
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +8 -2
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +135 -97
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +25 -0
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +140 -4
- package/node_modules/@llblab/pi-telegram/lib/model.ts +2 -4
- package/node_modules/@llblab/pi-telegram/lib/status.ts +30 -1
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +21 -3
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +46 -13
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { StringEnum, Type } from "@earendil-works/pi-ai";
|
|
2
2
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { Text } from "@earendil-works/pi-tui";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
4
|
import { existsSync } from "node:fs";
|
|
6
5
|
import { isAbsolute, join, resolve } from "node:path";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { hasCompactionSizedTranscript, planStateFlowCompaction, shouldRequestStateFlowCompaction,
|
|
6
|
+
import { ArtifactAcquisitionState, missingArtifactRemovals } from "./acquisition.js";
|
|
7
|
+
import { inspectRegisteredArtifactPaths, sameArtifactSourceFingerprint } from "./artifact.js";
|
|
8
|
+
import { hasCompactionSizedTranscript, planStateFlowCompaction, shouldRequestStateFlowCompaction, StateFlowCompactionRequests } from "./compaction.js";
|
|
10
9
|
import { inactiveModeFor, loadStateFlowConfig } from "./config.js";
|
|
11
10
|
import { ContextProjection, contextView, createPassiveContinuation, currentRunTrajectory, passiveContinuationMessages, projectSystemProtocol, runtimeContextHead, syntheticUser } from "./context.js";
|
|
12
11
|
import { readNativeSessionHeader } from "./continuation.js";
|
|
13
12
|
import { cwdScopeKey, resolveSessionAddress, sessionScopeKey, } from "./durable.js";
|
|
14
13
|
import { completeRun, deactivateEpisode, prepareRun, resumeEpisode, startEpisode } from "./episode.js";
|
|
15
|
-
import { awaitInFlightBackupPushes, backupCurrentStateFlowFiles, startStateFlowBackupPush } from "./git.js";
|
|
14
|
+
import { awaitInFlightBackupPushes, backupCurrentStateFlowFiles, SettledTurnBackup, startStateFlowBackupPush } from "./git.js";
|
|
16
15
|
import { projectRecentTransitionsWithLimit } from "./history.js";
|
|
17
16
|
import { isObject, presentationJson, sameJson } from "./json.js";
|
|
18
17
|
import { StateFlowDiagnosticWriter, stateFlowLogPath } from "./logging.js";
|
|
19
18
|
import { assistantToolCallCount, conciseDiagnostic, diagnosticText, finalizedAssistantResponse, formatPatchStateArguments, PASSIVE_MEMORY_PROTOCOL, separatedFailure, separatedOutput, stateFlowProtocol } from "./protocol.js";
|
|
19
|
+
import { OwnedOperationSlot, RenewableLifetime } from "./operation.js";
|
|
20
20
|
import { readProjectedState, readStatePath } from "./query.js";
|
|
21
21
|
import { selectedBoundaryFailure, selectRetainedCheckpoint, waitForRecovery } from "./recovery.js";
|
|
22
22
|
import { TemporalRuntime } from "./runtime.js";
|
|
@@ -45,48 +45,46 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
45
45
|
let selectedHistoryExpired = false;
|
|
46
46
|
let modePersistenceError;
|
|
47
47
|
/** One pending inactive-mode persistence; later inactive choices coalesce until it publishes. */
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const inactivePersistence = new OwnedOperationSlot();
|
|
49
|
+
const startActivation = new OwnedOperationSlot();
|
|
50
50
|
let forkInitialization = false;
|
|
51
|
-
|
|
51
|
+
const branchRestoration = new OwnedOperationSlot();
|
|
52
52
|
let deferredBranch;
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let memoryToolLifetime = new AbortController();
|
|
53
|
+
const responseReconciliation = new OwnedOperationSlot();
|
|
54
|
+
const sharedInspectionLifetime = new RenewableLifetime();
|
|
55
|
+
const memoryToolLifetime = new RenewableLifetime();
|
|
57
56
|
let completedRunAccepted = false;
|
|
58
|
-
|
|
59
|
-
let compactionPlan;
|
|
60
|
-
let compactionInFlight = false;
|
|
61
|
-
let compactionStopped = false;
|
|
62
|
-
const compactionPrefix = `state-flow-boundary:${randomUUID()}:`;
|
|
63
|
-
let compactionMarker;
|
|
57
|
+
const compactionRequests = new StateFlowCompactionRequests();
|
|
64
58
|
let passiveContinuation;
|
|
65
59
|
let bootstrapContinuation;
|
|
66
60
|
const contextProjection = new ContextProjection();
|
|
67
|
-
|
|
61
|
+
const inferencePreparation = new OwnedOperationSlot();
|
|
68
62
|
let runAnchorTimestamp;
|
|
69
63
|
let runtime;
|
|
70
64
|
let activeContext;
|
|
71
65
|
let rehydrationPhase;
|
|
72
66
|
const repositoryRoot = resolve(options.repositoryRoot ?? config.directory);
|
|
73
67
|
const diagnosticWriter = new StateFlowDiagnosticWriter(config.logging, stateFlowLogPath(agentDir), repositoryRoot, (message) => notifyActiveContext(message));
|
|
74
|
-
|
|
75
|
-
let backupLifetime = new AbortController();
|
|
76
|
-
const backupOperations = new Set();
|
|
68
|
+
const backup = new SettledTurnBackup();
|
|
77
69
|
let shuttingDown = false;
|
|
78
|
-
let pushFailureNotified = false;
|
|
79
70
|
const skillReads = new SkillReadTracker(hashSkillSource, (path) => activeContext
|
|
80
71
|
? registeredSkillResolver(activeContext.cwd, pi.getCommands())(path)
|
|
81
72
|
: undefined);
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
let artifactHints = {};
|
|
85
|
-
const SOURCE_CHANGED_HINT = "Source changed since this artifact was compiled. Read and recompile it before relying on it.";
|
|
73
|
+
const acquisition = new ArtifactAcquisitionState();
|
|
74
|
+
const artifactReads = acquisition.reads;
|
|
86
75
|
let telegramStartPending = false;
|
|
87
76
|
function sessionAddress(ctx) {
|
|
88
77
|
return resolveSessionAddress(ctx.sessionManager.getSessionFile(), ctx.sessionManager.getSessionId(), ctx.sessionManager.getHeader()?.timestamp);
|
|
89
78
|
}
|
|
79
|
+
/** Native session identity captured before an await; any change supersedes the waiting operation. */
|
|
80
|
+
function sessionIdentity(ctx) {
|
|
81
|
+
const cwd = ctx.cwd;
|
|
82
|
+
const owner = ctx.sessionManager.getSessionId();
|
|
83
|
+
const file = ctx.sessionManager.getSessionFile();
|
|
84
|
+
const timestamp = ctx.sessionManager.getHeader()?.timestamp;
|
|
85
|
+
return () => ctx.cwd === cwd && ctx.sessionManager.getSessionId() === owner && ctx.sessionManager.getSessionFile() === file
|
|
86
|
+
&& ctx.sessionManager.getHeader()?.timestamp === timestamp;
|
|
87
|
+
}
|
|
90
88
|
function notifyProblem(ctx, message, level) {
|
|
91
89
|
ctx.ui.notify(conciseDiagnostic(message), level);
|
|
92
90
|
}
|
|
@@ -103,7 +101,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
103
101
|
return new TemporalRuntime(ctx.cwd, sessionAddress(ctx), repositoryRoot, undefined, config.historyLimit);
|
|
104
102
|
}
|
|
105
103
|
function projectModelState(state) {
|
|
106
|
-
return projectStateForModel(state,
|
|
104
|
+
return projectStateForModel(state, acquisition.hints);
|
|
107
105
|
}
|
|
108
106
|
function assertSelectedBranchAvailable() {
|
|
109
107
|
if (snapshot.meta.validation?.attempt === 0) {
|
|
@@ -147,110 +145,29 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
147
145
|
function updateUi(ctx) {
|
|
148
146
|
ctx.ui.setStatus(STATUS_KEY, compactStatus(snapshot, scopeRevisions(), (color, text) => ctx.ui.theme.fg(color, text)));
|
|
149
147
|
}
|
|
150
|
-
function cancelInactivePersistence() {
|
|
151
|
-
const pending = inactivePersistence;
|
|
152
|
-
pending?.controller.abort();
|
|
153
|
-
inactivePersistence = undefined;
|
|
154
|
-
return pending?.operation;
|
|
155
|
-
}
|
|
156
|
-
function cancelStartActivation() {
|
|
157
|
-
const pending = startActivation;
|
|
158
|
-
pending?.controller.abort();
|
|
159
|
-
startActivation = undefined;
|
|
160
|
-
return pending?.operation;
|
|
161
|
-
}
|
|
162
|
-
function cancelBranchRestoration() {
|
|
163
|
-
const pending = branchRestoration;
|
|
164
|
-
pending?.controller.abort();
|
|
165
|
-
branchRestoration = undefined;
|
|
166
|
-
return pending?.operation;
|
|
167
|
-
}
|
|
168
|
-
function cancelResponseReconciliation() {
|
|
169
|
-
responseReconciliation?.abort();
|
|
170
|
-
responseReconciliation = undefined;
|
|
171
|
-
}
|
|
172
148
|
function cancelSharedInspections() {
|
|
173
|
-
sharedInspectionLifetime.
|
|
174
|
-
sharedInspectionLifetime = new AbortController();
|
|
175
|
-
}
|
|
176
|
-
function cancelBackupWork() {
|
|
177
|
-
backupLifetime.abort();
|
|
178
|
-
backupLifetime = new AbortController();
|
|
179
|
-
backupPending = false;
|
|
180
|
-
}
|
|
181
|
-
function cancelInferencePreparation() {
|
|
182
|
-
inferencePreparation?.controller.abort();
|
|
183
|
-
inferencePreparation = undefined;
|
|
149
|
+
sharedInspectionLifetime.renew(new Error("State Flow inspection was cancelled; select the scope again"));
|
|
184
150
|
}
|
|
185
151
|
function clearRunTransient() {
|
|
186
|
-
|
|
187
|
-
|
|
152
|
+
inferencePreparation.cancel();
|
|
153
|
+
responseReconciliation.cancel();
|
|
188
154
|
cancelSharedInspections();
|
|
189
155
|
completedRunAccepted = false;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
compactionInFlight = false;
|
|
156
|
+
backup.clearTurn();
|
|
157
|
+
compactionRequests.clear();
|
|
193
158
|
skillReads.clear();
|
|
194
159
|
artifactReads.clear();
|
|
195
160
|
}
|
|
196
161
|
function deferInferencePreparation(prompt) {
|
|
197
|
-
|
|
198
|
-
inferencePreparation
|
|
199
|
-
|
|
200
|
-
artifactReads.setCandidates([]);
|
|
162
|
+
inferencePreparation.cancel();
|
|
163
|
+
inferencePreparation.claim({ controller: new AbortController(), prompt });
|
|
164
|
+
acquisition.clearInvalidations();
|
|
201
165
|
}
|
|
202
166
|
function refreshArtifactHints() {
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const paths = new Set();
|
|
210
|
-
for (const scope of ["global", "cwd", "session"])
|
|
211
|
-
for (const path of Object.keys(scopeStates[scope].artifacts))
|
|
212
|
-
paths.add(path);
|
|
213
|
-
const observations = new Map(inspectRegisteredArtifactPaths(paths).map((observation) => [observation.path, observation]));
|
|
214
|
-
const nextHints = {};
|
|
215
|
-
const nextInvalidations = new Map();
|
|
216
|
-
for (const scope of ["global", "cwd", "session"]) {
|
|
217
|
-
const provenance = runtime.artifactProvenance(scope);
|
|
218
|
-
for (const [path, metadata] of Object.entries(scopeStates[scope].artifacts)) {
|
|
219
|
-
delete nextHints[path];
|
|
220
|
-
nextInvalidations.delete(path);
|
|
221
|
-
if (metadata.kind === "skill")
|
|
222
|
-
continue;
|
|
223
|
-
const observed = observations.get(path);
|
|
224
|
-
if (observed?.kind !== "present")
|
|
225
|
-
continue;
|
|
226
|
-
const need = classifyArtifactCompilationNeed({ path, scope, sourceFingerprint: observed.fingerprint }, metadata, ORDINARY_ARTIFACT_COMPILER, false, provenance[path]);
|
|
227
|
-
if (need.kind !== "requires-compilation")
|
|
228
|
-
continue;
|
|
229
|
-
if (need.reason === "source-changed")
|
|
230
|
-
nextHints[path] = SOURCE_CHANGED_HINT;
|
|
231
|
-
nextInvalidations.set(path, { path, scope, reason: need.reason });
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
artifactHints = nextHints;
|
|
235
|
-
artifactInvalidations = [...nextInvalidations.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
236
|
-
artifactReads.setCandidates(artifactInvalidations);
|
|
237
|
-
}
|
|
238
|
-
function missingArtifactRemovals(states) {
|
|
239
|
-
const owners = new Map();
|
|
240
|
-
for (const scope of ["global", "cwd", "session"])
|
|
241
|
-
for (const path of Object.keys(states[scope].artifacts ?? {})) {
|
|
242
|
-
owners.set(path, [...owners.get(path) ?? [], scope]);
|
|
243
|
-
}
|
|
244
|
-
const removals = {};
|
|
245
|
-
for (const observation of inspectRegisteredArtifactPaths(owners.keys())) {
|
|
246
|
-
if (observation.kind !== "missing")
|
|
247
|
-
continue;
|
|
248
|
-
for (const scope of owners.get(observation.path) ?? []) {
|
|
249
|
-
const artifacts = (removals[scope]?.artifacts ?? {});
|
|
250
|
-
removals[scope] = { ...(removals[scope] ?? {}), artifacts: { ...artifacts, [observation.path]: null } };
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return removals;
|
|
167
|
+
if (runtime?.view)
|
|
168
|
+
acquisition.refresh(scopeStates, (scope) => runtime.artifactProvenance(scope));
|
|
169
|
+
else
|
|
170
|
+
acquisition.reset();
|
|
254
171
|
}
|
|
255
172
|
function installScopeStates() {
|
|
256
173
|
scopeStates = runtime?.view ? runtime.states() : { global: emptyState(), cwd: emptyState(), session: emptyState() };
|
|
@@ -307,15 +224,11 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
307
224
|
? [...new Set([...active, ...owned])]
|
|
308
225
|
: active.filter((name) => !owned.includes(name)));
|
|
309
226
|
}
|
|
310
|
-
|
|
227
|
+
/** Accepted canonical publication establishes runtime authority and makes the settled-turn Git backup due. */
|
|
228
|
+
function recordPublication() {
|
|
311
229
|
// A passive view is not runtime authority; only accepted canonical publication establishes it.
|
|
312
230
|
branchStartsWithoutRuntime = false;
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
/** Record accepted lifecycle persistence; Git backup is scheduled only after an accepted turn settles. */
|
|
316
|
-
function recordPolicyPublication(publication, ctx) {
|
|
317
|
-
if (publication)
|
|
318
|
-
recordPublication(publication, ctx);
|
|
231
|
+
backup.markPublished();
|
|
319
232
|
}
|
|
320
233
|
function skillReadIsCurrent(read) {
|
|
321
234
|
return read.hash !== undefined && runtime?.view !== undefined && hasCompiledSkillArtifact(scopeStates[read.scope].artifacts, runtime.artifactProvenance(read.scope)[read.path], read.path, read.hash);
|
|
@@ -332,11 +245,9 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
332
245
|
const target = `${read.scope}.artifacts[${JSON.stringify(read.path)}]`;
|
|
333
246
|
return `State Flow acquisition: this registered Skill belongs at ${target}. If durable compiled guidance is useful, include a non-empty description, kind:"skill", and compilation object there. Unrelated semantic patches do not need to include it.`;
|
|
334
247
|
}
|
|
335
|
-
function clearAcceptedAcquisitions(acquiredArtifactPaths
|
|
336
|
-
|
|
337
|
-
artifactReads.setCandidates(artifactInvalidations);
|
|
248
|
+
function clearAcceptedAcquisitions(acquiredArtifactPaths) {
|
|
249
|
+
acquisition.acceptAcquired(acquiredArtifactPaths);
|
|
338
250
|
dropCurrentSkillReads();
|
|
339
|
-
artifactReads.clear();
|
|
340
251
|
}
|
|
341
252
|
async function prepareInference(pending, selected, ctx, operationSignal) {
|
|
342
253
|
const signal = AbortSignal.any([pending.controller.signal, operationSignal]);
|
|
@@ -345,7 +256,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
345
256
|
throw new Error("Temporal State Flow runtime is unavailable; reload before publishing");
|
|
346
257
|
await selected.withPatchTransaction((transaction) => {
|
|
347
258
|
signal.throwIfAborted();
|
|
348
|
-
if (runtime !== selected || inferencePreparation
|
|
259
|
+
if (runtime !== selected || !inferencePreparation.owns(pending) || !isActive() || shuttingDown) {
|
|
349
260
|
throw new Error("State Flow inference selection changed while awaiting publication");
|
|
350
261
|
}
|
|
351
262
|
assertPublicationAvailable();
|
|
@@ -372,7 +283,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
372
283
|
if (rotatesRun && rehydrationPhase !== "new-bootstrap" && rehydrationPhase !== "resume-bootstrap")
|
|
373
284
|
rehydrationPhase = "step";
|
|
374
285
|
if (publication?.changed)
|
|
375
|
-
recordPublication(
|
|
286
|
+
recordPublication();
|
|
376
287
|
if (pending.prompt !== undefined || publication?.changed)
|
|
377
288
|
appendCheckpoint();
|
|
378
289
|
if (Object.keys(removals).length > 0)
|
|
@@ -381,7 +292,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
381
292
|
}, signal);
|
|
382
293
|
}
|
|
383
294
|
catch (error) {
|
|
384
|
-
if (signal.aborted || runtime !== selected || inferencePreparation
|
|
295
|
+
if (signal.aborted || runtime !== selected || !inferencePreparation.owns(pending) || shuttingDown)
|
|
385
296
|
return;
|
|
386
297
|
// Pi reports context-hook exceptions and continues. Abort through its public port rather than infer from stale preparation.
|
|
387
298
|
ctx.abort();
|
|
@@ -392,25 +303,21 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
392
303
|
: `State Flow inference preparation failed: ${cause}`, "error");
|
|
393
304
|
}
|
|
394
305
|
}
|
|
395
|
-
function currentRehydrationPhase() {
|
|
396
|
-
return rehydrationPhase;
|
|
397
|
-
}
|
|
398
306
|
function statusDiagnostics(ctx) {
|
|
399
307
|
const cwd = ctx.cwd;
|
|
400
|
-
const diagnosticStates = scopeStates;
|
|
401
308
|
const view = runtime?.view;
|
|
402
309
|
const diagnosticRecent = runtime?.recent() ?? [];
|
|
403
310
|
const durableStateError = snapshot.meta.validation?.attempt === 0 ? snapshot.meta.validation.error
|
|
404
311
|
: view ? undefined : "no temporal runtime is selected on this branch";
|
|
405
312
|
const staleArtifacts = durableStateError === undefined
|
|
406
|
-
?
|
|
313
|
+
? acquisition.invalidations.map(({ path, scope, reason }) => ({ scope: scope ?? "global", path, reason }))
|
|
407
314
|
: [];
|
|
408
315
|
const session = sessionAddress(ctx);
|
|
409
316
|
return {
|
|
410
317
|
repositoryRoot,
|
|
411
318
|
cwdScopeKey: cwdScopeKey(cwd),
|
|
412
319
|
sessionScopeKey: sessionScopeKey(session.key),
|
|
413
|
-
scopeStates
|
|
320
|
+
scopeStates,
|
|
414
321
|
effectiveState,
|
|
415
322
|
recent: diagnosticRecent,
|
|
416
323
|
historyLimit: config.historyLimit,
|
|
@@ -437,19 +344,17 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
437
344
|
/** Select the active native branch under one owned restoration lifetime; only current accepted work installs memory. */
|
|
438
345
|
function restoreActiveBranch(ctx, sessionStartReason, notifyRecovery = true, startOwner, requestedMode) {
|
|
439
346
|
contextProjection.reset();
|
|
440
|
-
memoryToolLifetime.
|
|
441
|
-
|
|
442
|
-
cancelBranchRestoration();
|
|
347
|
+
memoryToolLifetime.renew();
|
|
348
|
+
branchRestoration.cancel();
|
|
443
349
|
// Start-owned attachment/fork recovery keeps its owner; only accepted Start cancels Stop.
|
|
444
350
|
if (!startOwner) {
|
|
445
|
-
|
|
446
|
-
|
|
351
|
+
startActivation.cancel();
|
|
352
|
+
inactivePersistence.cancel();
|
|
447
353
|
}
|
|
448
354
|
clearRunTransient();
|
|
449
355
|
passiveContinuation = undefined;
|
|
450
356
|
bootstrapContinuation = undefined;
|
|
451
|
-
|
|
452
|
-
artifactReads.setCandidates([]);
|
|
357
|
+
acquisition.clearInvalidations();
|
|
453
358
|
telegramStartPending = false;
|
|
454
359
|
activeContext = ctx;
|
|
455
360
|
if (!startOwner) {
|
|
@@ -475,7 +380,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
475
380
|
// Off needs only policy; unavailable native evidence grants no memory authority.
|
|
476
381
|
}
|
|
477
382
|
if (mode === "off") {
|
|
478
|
-
|
|
383
|
+
backup.cancel();
|
|
479
384
|
const owner = sessionAddress(ctx).key;
|
|
480
385
|
const reason = pendingFork || (deferredBranch?.owner === owner && deferredBranch.cwd === ctx.cwd && deferredBranch.reason === "fork"
|
|
481
386
|
&& retainsPhysicalSessionProjection(sessionStartReason)) ? "fork" : sessionStartReason;
|
|
@@ -489,7 +394,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
489
394
|
modePersistenceError = persistenceError;
|
|
490
395
|
forkInitialization = reason === "fork";
|
|
491
396
|
installScopeStates();
|
|
492
|
-
|
|
397
|
+
acquisition.reset();
|
|
493
398
|
if (adoptDefault)
|
|
494
399
|
appendCheckpoint();
|
|
495
400
|
syncStateFlowTools();
|
|
@@ -577,25 +482,18 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
577
482
|
awaitingAcceptance: selection.kind !== "settled" && selection.kind !== "current",
|
|
578
483
|
...(requestedMode === undefined ? {} : { requestedMode }),
|
|
579
484
|
};
|
|
580
|
-
branchRestoration
|
|
581
|
-
|
|
582
|
-
pending
|
|
583
|
-
restorationOperations.add(operation);
|
|
584
|
-
const finished = () => { restorationOperations.delete(operation); };
|
|
585
|
-
void operation.then(finished, finished);
|
|
586
|
-
return operation;
|
|
485
|
+
branchRestoration.claim(pending);
|
|
486
|
+
// attachBranch releases its own ownership; settlement only drains it for shutdown.
|
|
487
|
+
return branchRestoration.track(pending, attachBranch(ctx, pending, selection, sessionStartReason, notifyRecovery, skipped), false);
|
|
587
488
|
}
|
|
588
489
|
async function attachBranch(ctx, pending, selection, reason, notifyRecovery, skipped) {
|
|
589
490
|
const placeholder = runtime;
|
|
590
491
|
let selected = runtime;
|
|
591
492
|
const owner = ctx.sessionManager.getSessionId();
|
|
592
|
-
const
|
|
593
|
-
const file = ctx.sessionManager.getSessionFile();
|
|
594
|
-
const timestamp = ctx.sessionManager.getHeader()?.timestamp;
|
|
493
|
+
const sameSession = sessionIdentity(ctx);
|
|
595
494
|
const signal = AbortSignal.any([pending.controller.signal, ...(ctx.signal ? [ctx.signal] : [])]);
|
|
596
|
-
const isCurrent = () => branchRestoration
|
|
597
|
-
&&
|
|
598
|
-
&& ctx.sessionManager.getHeader()?.timestamp === timestamp;
|
|
495
|
+
const isCurrent = () => branchRestoration.owns(pending) && !pending.controller.signal.aborted && !shuttingDown && runtime === selected
|
|
496
|
+
&& sameSession();
|
|
599
497
|
const assertCurrent = () => {
|
|
600
498
|
signal.throwIfAborted();
|
|
601
499
|
if (!isCurrent())
|
|
@@ -603,13 +501,13 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
603
501
|
};
|
|
604
502
|
let accepted = false;
|
|
605
503
|
// Install accepted memory before native writes; later ancillary failures cannot revert or replay it.
|
|
606
|
-
const accept = (candidate, next,
|
|
504
|
+
const accept = (candidate, next, nativeWrites) => {
|
|
607
505
|
accepted = true;
|
|
608
506
|
pending.awaitingAcceptance = false;
|
|
609
507
|
runtime = selected = candidate;
|
|
610
508
|
snapshot = next;
|
|
611
509
|
installScopeStates();
|
|
612
|
-
|
|
510
|
+
recordPublication();
|
|
613
511
|
let failure;
|
|
614
512
|
try {
|
|
615
513
|
nativeWrites();
|
|
@@ -645,7 +543,8 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
645
543
|
&& retainsPhysicalSessionProjection(reason) && hasUncheckpointedConversation(ctx.sessionManager.getBranch()))
|
|
646
544
|
restored.meta.bootstrap = true;
|
|
647
545
|
const next = pending.requestedMode ? deactivateEpisode(restored, pending.requestedMode) : restored;
|
|
648
|
-
|
|
546
|
+
publish(next);
|
|
547
|
+
accept(candidate, next, appendCheckpoint);
|
|
649
548
|
}, signal);
|
|
650
549
|
}
|
|
651
550
|
else if (selection.kind === "fork") {
|
|
@@ -653,9 +552,9 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
653
552
|
assertCurrent();
|
|
654
553
|
// Mode is selected independently of creating the child's private memory.
|
|
655
554
|
const next = pending.requestedMode ? deactivateEpisode(child, pending.requestedMode) : child;
|
|
656
|
-
|
|
555
|
+
publish(next);
|
|
657
556
|
forkInitialization = false;
|
|
658
|
-
accept(candidate, next,
|
|
557
|
+
accept(candidate, next, () => {
|
|
659
558
|
pi.appendEntry(PASSIVE_STOP_ENTRY_TYPE, { reset: true, owner });
|
|
660
559
|
appendCheckpoint();
|
|
661
560
|
});
|
|
@@ -671,7 +570,8 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
671
570
|
throw new Error("Existing session runtime requires retained-boundary restoration");
|
|
672
571
|
const activated = startEpisode(hasPriorConversation(branch));
|
|
673
572
|
const next = pending.requestedMode ? deactivateEpisode(activated, pending.requestedMode) : activated;
|
|
674
|
-
|
|
573
|
+
publish(next);
|
|
574
|
+
accept(candidate, next, appendCheckpoint);
|
|
675
575
|
}, signal, true);
|
|
676
576
|
}
|
|
677
577
|
}
|
|
@@ -717,8 +617,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
717
617
|
}
|
|
718
618
|
finally {
|
|
719
619
|
pending.awaitingAcceptance = false;
|
|
720
|
-
|
|
721
|
-
branchRestoration = undefined;
|
|
620
|
+
branchRestoration.release(pending);
|
|
722
621
|
}
|
|
723
622
|
}
|
|
724
623
|
/** Load current shared memory for passive projection without initializing or publishing it. */
|
|
@@ -769,7 +668,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
769
668
|
pi.registerTool({
|
|
770
669
|
name: READ_STATE_TOOL_NAME,
|
|
771
670
|
label: "Read State",
|
|
772
|
-
description: "Read exact State Flow values or historical semantic patches with one path or an ordered path list. Unscoped semantic paths read the effective overlay; effective makes that overlay explicit, while global, cwd, and session select ownership. Array selectors support zero-based indices and half-open [start..end] ranges. Projection value returns semantic data; keys returns minimal structure; patch intersects the selected path at its boundary.",
|
|
671
|
+
description: "Read exact State Flow values or historical semantic patches with one path or an ordered path list. Unscoped semantic paths read the effective overlay; effective makes that overlay explicit, while global, cwd, and session select ownership. Array selectors support zero-based indices and half-open [start..end] ranges. Projection value returns semantic data; keys returns minimal structure; patch intersects the selected path at its boundary. Path object keys must be ASCII identifiers ([A-Za-z_$][A-Za-z0-9_$-]*); non-ASCII keys cannot be addressed.",
|
|
773
672
|
promptSnippet: "Read exact state values, structures, or historical semantic patches",
|
|
774
673
|
parameters: Type.Object({
|
|
775
674
|
path: Type.Optional(Type.String({ description: "One semantic path; unscoped paths use effective, explicit roots may use effective/global/cwd/session and historical [N], and arrays support half-open [start..end] ranges" })),
|
|
@@ -818,7 +717,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
818
717
|
pi.registerTool({
|
|
819
718
|
name: PATCH_STATE_TOOL_NAME,
|
|
820
719
|
label: "Patch State",
|
|
821
|
-
description: "The sole State Flow semantic mutation protocol. Supply one or more global, cwd, or session patches; all supplied scopes commit atomically. This call must be the only State Flow barrier in its assistant response.",
|
|
720
|
+
description: "The sole State Flow semantic mutation protocol. Supply one or more global, cwd, or session patches; all supplied scopes commit atomically. This call must be the only State Flow barrier in its assistant response. Deleting an intent also deletes same-scope working/lazy keys its {\"$ref\"} values own unless another intent refs them. Name keys in ASCII ([A-Za-z_$][A-Za-z0-9_$-]*) so read paths and refs resolve; values may use any language.",
|
|
822
721
|
promptSnippet: "Atomically patch one or more global/cwd/session scopes",
|
|
823
722
|
promptGuidelines: [
|
|
824
723
|
"Use patch_state only for material durable semantic changes; ordinary answers need no finalization call.",
|
|
@@ -897,12 +796,12 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
897
796
|
snapshot = nextSnapshot;
|
|
898
797
|
installScopeStates();
|
|
899
798
|
if (publication?.changed) {
|
|
900
|
-
recordPublication(
|
|
799
|
+
recordPublication();
|
|
901
800
|
appendCheckpoint();
|
|
902
801
|
}
|
|
903
802
|
clearAcceptedAcquisitions(new Set(acquiredArtifacts.map(({ path }) => path)));
|
|
904
803
|
updateUi(ctx);
|
|
905
|
-
const updates = contextProjection.acceptPatch(previousEffective, effectiveState, patches,
|
|
804
|
+
const updates = contextProjection.acceptPatch(previousEffective, effectiveState, patches, acquisition.hints);
|
|
906
805
|
const acknowledgement = changed
|
|
907
806
|
? `\nState materialized atomically at ${scopes.join("+")} scope${scopes.length === 1 ? "" : "s"}.`
|
|
908
807
|
: "\nState already current.";
|
|
@@ -934,31 +833,20 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
934
833
|
function startStateFlow(ctx) {
|
|
935
834
|
if (shuttingDown)
|
|
936
835
|
return Promise.resolve({ ok: false, message: "State Flow is shutting down" });
|
|
937
|
-
if (startActivation?.operation)
|
|
938
|
-
return startActivation.operation;
|
|
836
|
+
if (startActivation.current?.operation)
|
|
837
|
+
return startActivation.current.operation;
|
|
939
838
|
telegramStartPending = false;
|
|
940
|
-
if (activeContext && !branchRestoration && isActive()) {
|
|
839
|
+
if (activeContext && !branchRestoration.current && isActive()) {
|
|
941
840
|
return Promise.resolve({ ok: true, message: "State Flow is already active", signal: sharedInspectionLifetime.signal });
|
|
942
841
|
}
|
|
943
|
-
const pending = { controller: new AbortController() };
|
|
944
|
-
startActivation
|
|
945
|
-
const operation = runStart(ctx, pending.controller);
|
|
946
|
-
pending.operation = operation;
|
|
947
|
-
const finished = () => { if (startActivation === pending)
|
|
948
|
-
startActivation = undefined; };
|
|
949
|
-
void operation.then(finished, finished);
|
|
950
|
-
return operation;
|
|
842
|
+
const pending = startActivation.claim({ controller: new AbortController() });
|
|
843
|
+
return startActivation.track(pending, runStart(ctx, pending.controller));
|
|
951
844
|
}
|
|
952
845
|
/** Await restoration-owned attachment and exact-source fork recovery before current-head activation. */
|
|
953
846
|
async function runStart(ctx, pending) {
|
|
954
|
-
const
|
|
955
|
-
const cwd = ctx.cwd;
|
|
956
|
-
const file = ctx.sessionManager.getSessionFile();
|
|
957
|
-
const timestamp = ctx.sessionManager.getHeader()?.timestamp;
|
|
847
|
+
const sameSession = sessionIdentity(ctx);
|
|
958
848
|
const signal = ctx.signal ? AbortSignal.any([pending.signal, ctx.signal]) : pending.signal;
|
|
959
|
-
const isOwner = () => startActivation?.controller === pending && !signal.aborted && !shuttingDown
|
|
960
|
-
&& ctx.cwd === cwd && ctx.sessionManager.getSessionId() === owner && ctx.sessionManager.getSessionFile() === file
|
|
961
|
-
&& ctx.sessionManager.getHeader()?.timestamp === timestamp;
|
|
849
|
+
const isOwner = () => startActivation.current?.controller === pending && !signal.aborted && !shuttingDown && sameSession();
|
|
962
850
|
const superseded = () => {
|
|
963
851
|
pending.abort();
|
|
964
852
|
return { ok: false, message: "State Flow activation was superseded", signal: pending.signal };
|
|
@@ -968,15 +856,15 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
968
856
|
await waitForRecovery(restoreActiveBranch(ctx, "fork", false, pending), signal);
|
|
969
857
|
else if (!activeContext)
|
|
970
858
|
await waitForRecovery(restoreActiveBranch(ctx, undefined, false, pending), signal);
|
|
971
|
-
else if (branchRestoration?.operation)
|
|
972
|
-
await waitForRecovery(branchRestoration.operation, signal);
|
|
859
|
+
else if (branchRestoration.current?.operation)
|
|
860
|
+
await waitForRecovery(branchRestoration.current.operation, signal);
|
|
973
861
|
if (!isOwner())
|
|
974
862
|
return superseded();
|
|
975
863
|
if (isActive())
|
|
976
864
|
return { ok: true, message: "State Flow is already active", signal: sharedInspectionLifetime.signal };
|
|
977
865
|
if (forkInitialization) {
|
|
978
866
|
// Withdraw this join on cancellation without revoking the independently owned mode persistence.
|
|
979
|
-
const stopping = inactivePersistence?.operation;
|
|
867
|
+
const stopping = inactivePersistence.current?.operation;
|
|
980
868
|
if (stopping)
|
|
981
869
|
await waitForRecovery(stopping, signal);
|
|
982
870
|
if (!isOwner())
|
|
@@ -999,16 +887,13 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
999
887
|
async function activateCurrentState(ctx, pending) {
|
|
1000
888
|
let selected = runtime;
|
|
1001
889
|
const owner = ctx.sessionManager.getSessionId();
|
|
1002
|
-
const
|
|
1003
|
-
const file = ctx.sessionManager.getSessionFile();
|
|
1004
|
-
const timestamp = ctx.sessionManager.getHeader()?.timestamp;
|
|
890
|
+
const sameSession = sessionIdentity(ctx);
|
|
1005
891
|
const initiallyActive = isActive();
|
|
1006
892
|
let accepted = false;
|
|
1007
893
|
let receipt = AbortSignal.any([pending.signal, sharedInspectionLifetime.signal]);
|
|
1008
894
|
const signal = ctx.signal ? AbortSignal.any([pending.signal, ctx.signal]) : pending.signal;
|
|
1009
|
-
const isCurrent = () => startActivation?.controller === pending && !signal.aborted && runtime === selected && !shuttingDown
|
|
1010
|
-
&&
|
|
1011
|
-
&& ctx.sessionManager.getHeader()?.timestamp === timestamp && isActive() === (accepted || initiallyActive);
|
|
895
|
+
const isCurrent = () => startActivation.current?.controller === pending && !signal.aborted && runtime === selected && !shuttingDown
|
|
896
|
+
&& sameSession() && isActive() === (accepted || initiallyActive);
|
|
1012
897
|
const superseded = () => {
|
|
1013
898
|
pending.abort();
|
|
1014
899
|
return { ok: false, message: "State Flow activation was superseded", signal: pending.signal };
|
|
@@ -1033,9 +918,9 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1033
918
|
const bootstrap = hasPriorConversation(ctx.sessionManager.getBranch()) || passiveContinuation !== undefined || boundary !== undefined;
|
|
1034
919
|
const activated = current ? resumeEpisode(current, bootstrap) : startEpisode(bootstrap);
|
|
1035
920
|
const recoveredCurrent = selectedHistoryExpired;
|
|
1036
|
-
|
|
921
|
+
publish(activated);
|
|
1037
922
|
accepted = true;
|
|
1038
|
-
|
|
923
|
+
inactivePersistence.cancel();
|
|
1039
924
|
runtime = selected = activation;
|
|
1040
925
|
snapshot = activated;
|
|
1041
926
|
activeContext = ctx;
|
|
@@ -1050,7 +935,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1050
935
|
bootstrapContinuation = snapshot.meta.bootstrap ? continuation : undefined;
|
|
1051
936
|
deferInferencePreparation();
|
|
1052
937
|
receipt = AbortSignal.any([pending.signal, sharedInspectionLifetime.signal]);
|
|
1053
|
-
|
|
938
|
+
recordPublication();
|
|
1054
939
|
syncStateFlowTools();
|
|
1055
940
|
updateUi(ctx);
|
|
1056
941
|
if (forkInitialization) {
|
|
@@ -1110,9 +995,9 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1110
995
|
return Promise.resolve({ ok: false, message: "State Flow is shutting down" });
|
|
1111
996
|
if (mode === "off")
|
|
1112
997
|
return selectOff(ctx);
|
|
1113
|
-
|
|
998
|
+
startActivation.cancel();
|
|
1114
999
|
telegramStartPending = false;
|
|
1115
|
-
const persisting = inactivePersistence;
|
|
1000
|
+
const persisting = inactivePersistence.current;
|
|
1116
1001
|
if (persisting?.operation && !persisting.published) {
|
|
1117
1002
|
// Pending inactive choices coalesce: acceptance persists whichever inactive mode is current then.
|
|
1118
1003
|
if (snapshot.config.mode !== mode)
|
|
@@ -1121,7 +1006,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1121
1006
|
}
|
|
1122
1007
|
if (!activeContext)
|
|
1123
1008
|
void restoreActiveBranch(ctx, undefined, false);
|
|
1124
|
-
const restoring = branchRestoration;
|
|
1009
|
+
const restoring = branchRestoration.current;
|
|
1125
1010
|
// Read-only recovery also retains the latest policy; its native write fence is updated below.
|
|
1126
1011
|
if (restoring)
|
|
1127
1012
|
restoring.requestedMode = mode;
|
|
@@ -1136,17 +1021,11 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1136
1021
|
if (modePersistenceError || recordedMode === mode)
|
|
1137
1022
|
return Promise.resolve({ ok: true, message: `State Flow is already ${mode}` });
|
|
1138
1023
|
}
|
|
1139
|
-
|
|
1140
|
-
const pending = { controller: new AbortController() };
|
|
1141
|
-
inactivePersistence
|
|
1142
|
-
const operation = deferredBranch && mode === "passive" && !branchStartsWithoutRuntime
|
|
1024
|
+
inactivePersistence.cancel();
|
|
1025
|
+
const pending = inactivePersistence.claim({ controller: new AbortController() });
|
|
1026
|
+
return inactivePersistence.track(pending, deferredBranch && mode === "passive" && !branchStartsWithoutRuntime
|
|
1143
1027
|
? acquirePassiveMode(ctx, pending, deferredBranch.reason)
|
|
1144
|
-
: persistInactiveMode(ctx, pending, mode);
|
|
1145
|
-
pending.operation = operation;
|
|
1146
|
-
const finished = () => { if (inactivePersistence === pending)
|
|
1147
|
-
inactivePersistence = undefined; };
|
|
1148
|
-
void operation.then(finished, finished);
|
|
1149
|
-
return operation;
|
|
1028
|
+
: persistInactiveMode(ctx, pending, mode));
|
|
1150
1029
|
}
|
|
1151
1030
|
/** Off owns cancellation and native policy only; no canonical transaction or semantic handoff. */
|
|
1152
1031
|
function selectOff(ctx) {
|
|
@@ -1164,7 +1043,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1164
1043
|
modePersistenceError = policy?.persistenceError;
|
|
1165
1044
|
const alreadyOff = current.config.mode === "off" && policy?.mode === "off";
|
|
1166
1045
|
const incoming = sameOwner || sameDeferred ? current.meta.bootstrap ? bootstrapContinuation : passiveContinuation : undefined;
|
|
1167
|
-
const unfinished = current.meta.specification !== undefined || (inferencePreparation?.prompt !== undefined && !inferencePreparation.accepted)
|
|
1046
|
+
const unfinished = current.meta.specification !== undefined || (inferencePreparation.current?.prompt !== undefined && !inferencePreparation.current.accepted)
|
|
1168
1047
|
|| (isActive() && hasUncheckpointedConversation(branch));
|
|
1169
1048
|
const at = incoming?.startedAt ?? Date.now();
|
|
1170
1049
|
const from = incoming ? incoming.activeRunStartedAt : (sameOwner || sameDeferred) && (!ctx.isIdle() || unfinished) ? runAnchorTimestamp : undefined;
|
|
@@ -1180,20 +1059,17 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1180
1059
|
catch {
|
|
1181
1060
|
// An unusable cached bookmark cannot prevent native Off policy or authorize replacement memory.
|
|
1182
1061
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
memoryToolLifetime.
|
|
1188
|
-
memoryToolLifetime = new AbortController();
|
|
1062
|
+
startActivation.cancel();
|
|
1063
|
+
branchRestoration.cancel();
|
|
1064
|
+
inactivePersistence.cancel();
|
|
1065
|
+
backup.cancel();
|
|
1066
|
+
memoryToolLifetime.renew();
|
|
1189
1067
|
clearRunTransient();
|
|
1190
1068
|
contextProjection.reset();
|
|
1191
1069
|
telegramStartPending = false;
|
|
1192
1070
|
bootstrapContinuation = undefined;
|
|
1193
1071
|
passiveContinuation = undefined;
|
|
1194
|
-
|
|
1195
|
-
artifactHints = {};
|
|
1196
|
-
artifactReads.setCandidates([]);
|
|
1072
|
+
acquisition.reset();
|
|
1197
1073
|
runtime = undefined;
|
|
1198
1074
|
snapshot = emptySnapshot("off");
|
|
1199
1075
|
selectedHistoryExpired = false;
|
|
@@ -1227,7 +1103,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1227
1103
|
}
|
|
1228
1104
|
async function acquirePassiveMode(ctx, pending, reason) {
|
|
1229
1105
|
await restoreActiveBranch(ctx, reason, true, pending.controller, "passive");
|
|
1230
|
-
if (inactivePersistence
|
|
1106
|
+
if (!inactivePersistence.owns(pending) || pending.controller.signal.aborted || shuttingDown) {
|
|
1231
1107
|
return { ok: false, message: "State Flow mode change was superseded" };
|
|
1232
1108
|
}
|
|
1233
1109
|
return persistInactiveMode(ctx, pending, "passive");
|
|
@@ -1238,7 +1114,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1238
1114
|
const current = snapshot;
|
|
1239
1115
|
const wasActive = current.config.mode === "active";
|
|
1240
1116
|
let unfinished = current.meta.specification !== undefined
|
|
1241
|
-
|| (inferencePreparation?.prompt !== undefined && !inferencePreparation.accepted);
|
|
1117
|
+
|| (inferencePreparation.current?.prompt !== undefined && !inferencePreparation.current.accepted);
|
|
1242
1118
|
applyInactiveMode(ctx, mode);
|
|
1243
1119
|
const stoppedAt = Date.now();
|
|
1244
1120
|
if (modePersistenceError) {
|
|
@@ -1254,7 +1130,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1254
1130
|
const incomingBoundary = current.meta.bootstrap ? bootstrapContinuation : undefined;
|
|
1255
1131
|
const anchor = runAnchorTimestamp;
|
|
1256
1132
|
const idle = ctx.isIdle();
|
|
1257
|
-
const isCurrent = () => inactivePersistence
|
|
1133
|
+
const isCurrent = () => inactivePersistence.owns(pending) && runtime === selected
|
|
1258
1134
|
&& !shuttingDown && ctx.sessionManager.getSessionId() === owner && !isActive();
|
|
1259
1135
|
const superseded = () => ({ ok: false, message: "State Flow mode change was superseded" });
|
|
1260
1136
|
const freezeHandoff = () => {
|
|
@@ -1291,8 +1167,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1291
1167
|
// Projection changes before any wait; capture the old run's boundary before later native input can replace it.
|
|
1292
1168
|
freezeHandoff();
|
|
1293
1169
|
bootstrapContinuation = undefined;
|
|
1294
|
-
|
|
1295
|
-
artifactReads.setCandidates([]);
|
|
1170
|
+
acquisition.clearInvalidations();
|
|
1296
1171
|
assertPublicationAvailable();
|
|
1297
1172
|
const signal = ctx.signal ? AbortSignal.any([pending.controller.signal, ctx.signal]) : pending.controller.signal;
|
|
1298
1173
|
if (branchStartsWithoutRuntime || !selected?.view) {
|
|
@@ -1314,11 +1189,11 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1314
1189
|
throw new Error("State Flow mode selection changed while awaiting publication");
|
|
1315
1190
|
assertPublicationAvailable();
|
|
1316
1191
|
const next = structuredClone(snapshot);
|
|
1317
|
-
|
|
1192
|
+
publish(next);
|
|
1318
1193
|
accepted = true;
|
|
1319
1194
|
snapshot = next;
|
|
1320
1195
|
installScopeStates();
|
|
1321
|
-
|
|
1196
|
+
recordPublication();
|
|
1322
1197
|
return complete();
|
|
1323
1198
|
}, signal);
|
|
1324
1199
|
return isCurrent() ? result : superseded();
|
|
@@ -1334,8 +1209,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1334
1209
|
}
|
|
1335
1210
|
modePersistenceError = cause.trim() ? cause : "Canonical State Flow persistence failed";
|
|
1336
1211
|
bootstrapContinuation = undefined;
|
|
1337
|
-
|
|
1338
|
-
artifactReads.setCandidates([]);
|
|
1212
|
+
acquisition.clearInvalidations();
|
|
1339
1213
|
return complete();
|
|
1340
1214
|
}
|
|
1341
1215
|
}
|
|
@@ -1371,7 +1245,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1371
1245
|
},
|
|
1372
1246
|
cancelStart: () => {
|
|
1373
1247
|
telegramStartPending = false;
|
|
1374
|
-
|
|
1248
|
+
startActivation.cancel();
|
|
1375
1249
|
},
|
|
1376
1250
|
},
|
|
1377
1251
|
});
|
|
@@ -1388,7 +1262,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1388
1262
|
contextProjection.reset();
|
|
1389
1263
|
skillReads.clear();
|
|
1390
1264
|
artifactReads.clear();
|
|
1391
|
-
|
|
1265
|
+
responseReconciliation.cancel();
|
|
1392
1266
|
completedRunAccepted = false;
|
|
1393
1267
|
// This native hook has no operation signal. Capture only; the first active context owns acceptance.
|
|
1394
1268
|
deferInferencePreparation(event.prompt);
|
|
@@ -1409,12 +1283,12 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1409
1283
|
if (!isActive() && !passiveContinuation && !passiveMemoryAvailable())
|
|
1410
1284
|
return;
|
|
1411
1285
|
// Idle inspection must not freeze a pre-acceptance snapshot for the live inference.
|
|
1412
|
-
const projection = isActive() && inferencePreparation && !inferencePreparation.accepted
|
|
1286
|
+
const projection = isActive() && inferencePreparation.current && !inferencePreparation.current.accepted
|
|
1413
1287
|
? new ContextProjection() : contextProjection;
|
|
1414
1288
|
const effective = effectiveState;
|
|
1415
|
-
const invalidations =
|
|
1416
|
-
const phase = isActive() ?
|
|
1417
|
-
const view = contextView(effective,
|
|
1289
|
+
const invalidations = acquisition.invalidations.map(({ path, scope, reason }) => ({ path, ...(scope === undefined ? {} : { scope }), reason }));
|
|
1290
|
+
const phase = isActive() ? rehydrationPhase : undefined;
|
|
1291
|
+
const view = contextView(effective, acquisition.hints, invalidations, phase);
|
|
1418
1292
|
if (passiveContinuation) {
|
|
1419
1293
|
const retained = passiveContinuationMessages(messages, passiveContinuation);
|
|
1420
1294
|
if (!runtime?.view)
|
|
@@ -1431,7 +1305,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1431
1305
|
return { messages: projection.project(source, view, () => runtimeContextHead(snapshot, view, projectRecentTransitionsWithLimit(config.historyLimit, runtime?.recent() ?? []))) };
|
|
1432
1306
|
}
|
|
1433
1307
|
function prepareContext(messages, ctx) {
|
|
1434
|
-
const pending = inferencePreparation;
|
|
1308
|
+
const pending = inferencePreparation.current;
|
|
1435
1309
|
const selected = runtime;
|
|
1436
1310
|
const operationSignal = ctx.signal;
|
|
1437
1311
|
// Idle projections remain observational; only a live native operation may await preparation.
|
|
@@ -1439,15 +1313,15 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1439
1313
|
return projectContext(messages);
|
|
1440
1314
|
pending.operation ??= prepareInference(pending, selected, ctx, operationSignal).finally(() => {
|
|
1441
1315
|
// A late withdrawal must not clear newer work; accepted lifecycle is never replayed after an error.
|
|
1442
|
-
if (!pending.accepted && inferencePreparation
|
|
1316
|
+
if (!pending.accepted && inferencePreparation.owns(pending))
|
|
1443
1317
|
pending.operation = undefined;
|
|
1444
1318
|
});
|
|
1445
1319
|
return pending.operation.then(() => {
|
|
1446
1320
|
if (shuttingDown || operationSignal.aborted || runtime !== selected || ctx.signal !== operationSignal)
|
|
1447
1321
|
return;
|
|
1448
|
-
if (isActive() && inferencePreparation
|
|
1322
|
+
if (isActive() && !inferencePreparation.owns(pending)) {
|
|
1449
1323
|
// Start may require same-run maintenance. A new captured user prompt instead owns a different context request.
|
|
1450
|
-
if (inferencePreparation?.prompt === undefined)
|
|
1324
|
+
if (inferencePreparation.current?.prompt === undefined)
|
|
1451
1325
|
return prepareContext(messages, ctx);
|
|
1452
1326
|
return;
|
|
1453
1327
|
}
|
|
@@ -1512,7 +1386,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1512
1386
|
return;
|
|
1513
1387
|
if (event.message.role !== "assistant")
|
|
1514
1388
|
return;
|
|
1515
|
-
|
|
1389
|
+
responseReconciliation.cancel();
|
|
1516
1390
|
const message = event.message;
|
|
1517
1391
|
if (message.stopReason === "aborted" || assistantToolCallCount(message.content) > 0 || message.stopReason === "toolUse")
|
|
1518
1392
|
return;
|
|
@@ -1520,18 +1394,18 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1520
1394
|
recordDiagnostic(`Assistant response ended with ${message.stopReason}`, "finalization", ctx, { content: message.content });
|
|
1521
1395
|
return;
|
|
1522
1396
|
}
|
|
1523
|
-
responseReconciliation
|
|
1397
|
+
responseReconciliation.claim({ controller: new AbortController() });
|
|
1524
1398
|
});
|
|
1525
1399
|
pi.on("turn_end", async (event, ctx) => {
|
|
1526
1400
|
if (shuttingDown)
|
|
1527
1401
|
return;
|
|
1528
|
-
const pending = responseReconciliation;
|
|
1402
|
+
const pending = responseReconciliation.current;
|
|
1529
1403
|
if (!isActive() || !pending) {
|
|
1530
1404
|
updateUi(ctx);
|
|
1531
1405
|
return;
|
|
1532
1406
|
}
|
|
1533
1407
|
const selected = runtime;
|
|
1534
|
-
const signal = ctx.signal ? AbortSignal.any([pending.signal, ctx.signal]) : pending.signal;
|
|
1408
|
+
const signal = ctx.signal ? AbortSignal.any([pending.controller.signal, ctx.signal]) : pending.controller.signal;
|
|
1535
1409
|
let responseCommitted = false;
|
|
1536
1410
|
try {
|
|
1537
1411
|
const response = finalizedAssistantResponse(event.message);
|
|
@@ -1539,7 +1413,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1539
1413
|
throw new Error("Temporal State Flow runtime is unavailable; reload before publishing");
|
|
1540
1414
|
await selected.withPatchTransaction((transaction) => {
|
|
1541
1415
|
signal.throwIfAborted();
|
|
1542
|
-
if (runtime !== selected || responseReconciliation
|
|
1416
|
+
if (runtime !== selected || !responseReconciliation.owns(pending) || !isActive()) {
|
|
1543
1417
|
throw new Error("State Flow response selection changed while awaiting publication");
|
|
1544
1418
|
}
|
|
1545
1419
|
assertPublicationAvailable();
|
|
@@ -1556,19 +1430,19 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1556
1430
|
responseCommitted = true;
|
|
1557
1431
|
contextProjection.reset();
|
|
1558
1432
|
if (publication?.changed)
|
|
1559
|
-
recordPublication(
|
|
1433
|
+
recordPublication();
|
|
1560
1434
|
appendCheckpoint();
|
|
1561
1435
|
clearAcceptedAcquisitions();
|
|
1562
1436
|
bootstrapContinuation = undefined;
|
|
1563
1437
|
rehydrationPhase = "step";
|
|
1564
1438
|
completedRunAccepted = !wasBootstrap;
|
|
1565
|
-
|
|
1439
|
+
backup.markTurnAccepted();
|
|
1566
1440
|
updateUi(ctx);
|
|
1567
1441
|
}, signal);
|
|
1568
1442
|
}
|
|
1569
1443
|
catch (error) {
|
|
1570
1444
|
// Superseded completion owns neither the new lifecycle nor its diagnostics/UI.
|
|
1571
|
-
if (signal.aborted || runtime !== selected || responseReconciliation
|
|
1445
|
+
if (signal.aborted || runtime !== selected || !responseReconciliation.owns(pending))
|
|
1572
1446
|
return;
|
|
1573
1447
|
const cause = diagnosticText(error);
|
|
1574
1448
|
recordDiagnostic(cause, "finalization", ctx);
|
|
@@ -1577,30 +1451,20 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1577
1451
|
: `State Flow response reconciliation failed: ${cause}`, "error");
|
|
1578
1452
|
}
|
|
1579
1453
|
finally {
|
|
1580
|
-
|
|
1581
|
-
responseReconciliation = undefined;
|
|
1454
|
+
responseReconciliation.release(pending);
|
|
1582
1455
|
}
|
|
1583
1456
|
});
|
|
1584
1457
|
pi.on("session_before_compact", (event) => {
|
|
1585
|
-
|
|
1586
|
-
return;
|
|
1587
|
-
const marker = compactionMarker;
|
|
1588
|
-
if (shuttingDown || compactionStopped || !isActive() || !compactionPlan || !marker || event.customInstructions !== marker)
|
|
1589
|
-
return { cancel: true };
|
|
1590
|
-
const result = stateFlowCompactionResult(compactionPlan, marker, event);
|
|
1458
|
+
const result = compactionRequests.resolve(event, !shuttingDown && isActive());
|
|
1591
1459
|
if (result === undefined || "cancel" in result)
|
|
1592
1460
|
return result;
|
|
1593
1461
|
return { compaction: result };
|
|
1594
1462
|
});
|
|
1595
1463
|
pi.on("agent_before_settle", async (_event, ctx) => {
|
|
1596
|
-
if (shuttingDown || !memoryToolsAvailable() || !
|
|
1597
|
-
return;
|
|
1598
|
-
turnAcceptedForBackup = false;
|
|
1599
|
-
if (!backupPending)
|
|
1464
|
+
if (shuttingDown || !memoryToolsAvailable() || !backup.takeSettledTurn())
|
|
1600
1465
|
return;
|
|
1601
|
-
backupPending = false;
|
|
1602
1466
|
const operationSignal = ctx.signal;
|
|
1603
|
-
const lifetime =
|
|
1467
|
+
const lifetime = backup.lifetime;
|
|
1604
1468
|
const signal = operationSignal ? AbortSignal.any([lifetime, operationSignal]) : lifetime;
|
|
1605
1469
|
const selected = runtime;
|
|
1606
1470
|
try {
|
|
@@ -1608,14 +1472,7 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1608
1472
|
const pushSessionId = sessionAddress(ctx).id;
|
|
1609
1473
|
const pushCwd = ctx.cwd;
|
|
1610
1474
|
// Pi 0.87 settles after its agent signal ends; waiting there would strand native Abort.
|
|
1611
|
-
|
|
1612
|
-
backupOperations.add(operation);
|
|
1613
|
-
try {
|
|
1614
|
-
await operation;
|
|
1615
|
-
}
|
|
1616
|
-
finally {
|
|
1617
|
-
backupOperations.delete(operation);
|
|
1618
|
-
}
|
|
1475
|
+
await backup.track(backupCurrentStateFlowFiles(repositoryRoot, signal, operationSignal !== undefined));
|
|
1619
1476
|
if (signal.aborted || snapshot.config.mode === "off")
|
|
1620
1477
|
return;
|
|
1621
1478
|
startStateFlowBackupPush(repositoryRoot, (error) => {
|
|
@@ -1623,13 +1480,12 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1623
1480
|
return;
|
|
1624
1481
|
const message = diagnosticText(error);
|
|
1625
1482
|
const recorded = diagnosticWriter.recordBackupPushFailure(pushSessionId, pushCwd, message);
|
|
1626
|
-
if (
|
|
1483
|
+
if (!backup.claimPushFailureNotice())
|
|
1627
1484
|
return;
|
|
1628
|
-
pushFailureNotified = true;
|
|
1629
1485
|
notifyActiveContext(recorded
|
|
1630
1486
|
? `State Flow Git backup push failed; state is saved locally. Details: ${stateFlowLogPath(agentDir)}. A later accepted turn retries.`
|
|
1631
1487
|
: `State Flow Git backup push failed; local diagnostics unavailable: ${message}`);
|
|
1632
|
-
}, () =>
|
|
1488
|
+
}, () => backup.resetPushFailureNotice(), lifetime);
|
|
1633
1489
|
}
|
|
1634
1490
|
}
|
|
1635
1491
|
catch (error) {
|
|
@@ -1648,8 +1504,8 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1648
1504
|
await startStateFlow(ctx);
|
|
1649
1505
|
return;
|
|
1650
1506
|
}
|
|
1651
|
-
if (!completedRunAccepted ||
|
|
1652
|
-
||
|
|
1507
|
+
if (!completedRunAccepted || compactionRequests.stopped || !isActive() || snapshot.meta.bootstrap
|
|
1508
|
+
|| compactionRequests.inFlight || !ctx.isIdle() || ctx.hasPendingMessages() || !runtime?.view
|
|
1653
1509
|
|| !shouldRequestStateFlowCompaction(ctx.getContextUsage()))
|
|
1654
1510
|
return;
|
|
1655
1511
|
completedRunAccepted = false;
|
|
@@ -1659,16 +1515,11 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1659
1515
|
const plan = planStateFlowCompaction(entries, runtime.causalBasis(), snapshot.meta.step, runAnchorTimestamp);
|
|
1660
1516
|
if (!plan)
|
|
1661
1517
|
return;
|
|
1662
|
-
|
|
1663
|
-
const marker = compactionMarker = `${compactionPrefix}${randomUUID()}`;
|
|
1664
|
-
compactionInFlight = true;
|
|
1518
|
+
const marker = compactionRequests.begin(plan);
|
|
1665
1519
|
// Pi dispatches deferred companion prompts after all settled handlers return.
|
|
1666
1520
|
await new Promise((resolve) => {
|
|
1667
1521
|
const finished = () => {
|
|
1668
|
-
|
|
1669
|
-
compactionPlan = undefined;
|
|
1670
|
-
compactionInFlight = false;
|
|
1671
|
-
}
|
|
1522
|
+
compactionRequests.finish(marker);
|
|
1672
1523
|
resolve();
|
|
1673
1524
|
};
|
|
1674
1525
|
ctx.compact({ customInstructions: marker, onComplete: finished, onError: finished });
|
|
@@ -1688,21 +1539,21 @@ export default function stateFlowExtension(pi, options = {}) {
|
|
|
1688
1539
|
});
|
|
1689
1540
|
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
1690
1541
|
shuttingDown = true;
|
|
1691
|
-
memoryToolLifetime.
|
|
1542
|
+
memoryToolLifetime.end();
|
|
1692
1543
|
contextProjection.reset();
|
|
1693
|
-
const restoring =
|
|
1694
|
-
const starting =
|
|
1695
|
-
const stopping =
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1544
|
+
const restoring = branchRestoration.cancel();
|
|
1545
|
+
const starting = startActivation.cancel();
|
|
1546
|
+
const stopping = inactivePersistence.cancel();
|
|
1547
|
+
backup.abort();
|
|
1548
|
+
inferencePreparation.cancel();
|
|
1549
|
+
responseReconciliation.cancel();
|
|
1699
1550
|
cancelSharedInspections();
|
|
1700
1551
|
telegramStartPending = false;
|
|
1701
|
-
|
|
1552
|
+
compactionRequests.stop();
|
|
1702
1553
|
completedRunAccepted = false;
|
|
1703
1554
|
activeContext = undefined;
|
|
1704
1555
|
telegram.dispose();
|
|
1705
|
-
await Promise.allSettled([...
|
|
1556
|
+
await Promise.allSettled([...backup.operations, ...branchRestoration.inflight, ...[restoring, stopping, starting].filter((operation) => operation !== undefined)]);
|
|
1706
1557
|
await awaitInFlightBackupPushes(repositoryRoot);
|
|
1707
1558
|
});
|
|
1708
1559
|
}
|