@llblab/pi-kit 0.24.1 → 0.26.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/AGENTS.md +1 -1
- package/BACKLOG.md +5 -1
- package/CHANGELOG.md +12 -0
- package/README.md +11 -8
- package/node_modules/@llblab/pi-actors/AGENTS.md +2 -0
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +4 -1
- package/node_modules/@llblab/pi-actors/LICENSE +21 -0
- package/node_modules/@llblab/pi-actors/README.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +1 -1
- package/node_modules/@llblab/pi-actors/package.json +4 -3
- package/node_modules/@llblab/pi-claude-usage/AGENTS.md +23 -0
- package/node_modules/@llblab/pi-claude-usage/BACKLOG.md +4 -0
- package/node_modules/@llblab/pi-claude-usage/CHANGELOG.md +21 -0
- package/node_modules/@llblab/pi-claude-usage/LICENSE +22 -0
- package/node_modules/@llblab/pi-claude-usage/README.md +155 -0
- package/node_modules/@llblab/pi-claude-usage/banner.jpg +0 -0
- package/node_modules/@llblab/pi-claude-usage/index.ts +8 -0
- package/node_modules/@llblab/pi-claude-usage/lib/extension.ts +30 -0
- package/node_modules/@llblab/pi-claude-usage/lib/fast.ts +24 -0
- package/node_modules/@llblab/pi-claude-usage/lib/query.ts +146 -0
- package/node_modules/@llblab/pi-claude-usage/lib/status-format.ts +297 -0
- package/node_modules/@llblab/pi-claude-usage/lib/status.ts +366 -0
- package/node_modules/@llblab/pi-claude-usage/lib/telegram.ts +44 -0
- package/node_modules/@llblab/pi-claude-usage/lib/usage-store.ts +221 -0
- package/node_modules/@llblab/pi-claude-usage/lib/usage.ts +128 -0
- package/node_modules/@llblab/pi-claude-usage/package.json +64 -0
- package/node_modules/@llblab/pi-clean-room/AGENTS.md +1 -0
- package/node_modules/@llblab/pi-clean-room/CHANGELOG.md +5 -0
- package/node_modules/@llblab/pi-clean-room/LICENSE +21 -0
- package/node_modules/@llblab/pi-clean-room/README.md +1 -1
- package/node_modules/@llblab/pi-clean-room/package.json +3 -2
- package/node_modules/@llblab/pi-codex-usage/AGENTS.md +9 -6
- package/node_modules/@llblab/pi-codex-usage/BACKLOG.md +2 -1
- package/node_modules/@llblab/pi-codex-usage/CHANGELOG.md +17 -0
- package/node_modules/@llblab/pi-codex-usage/README.md +75 -17
- package/node_modules/@llblab/pi-codex-usage/index.ts +8 -1602
- package/node_modules/@llblab/pi-codex-usage/lib/extension.ts +25 -0
- package/node_modules/@llblab/pi-codex-usage/lib/fast.ts +23 -0
- package/node_modules/@llblab/pi-codex-usage/lib/query.ts +368 -0
- package/node_modules/@llblab/pi-codex-usage/lib/status-format.ts +347 -0
- package/node_modules/@llblab/pi-codex-usage/lib/status.ts +435 -0
- package/node_modules/@llblab/pi-codex-usage/lib/telegram.ts +45 -0
- package/node_modules/@llblab/pi-codex-usage/lib/usage-store.ts +229 -0
- package/node_modules/@llblab/pi-codex-usage/lib/usage.ts +425 -0
- package/node_modules/@llblab/pi-codex-usage/package.json +11 -6
- package/node_modules/@llblab/pi-command-fast/AGENTS.md +7 -0
- package/node_modules/@llblab/pi-command-fast/BACKLOG.md +9 -0
- package/node_modules/@llblab/pi-command-fast/CHANGELOG.md +7 -0
- package/node_modules/@llblab/pi-command-fast/LICENSE +21 -0
- package/node_modules/@llblab/pi-command-fast/README.md +42 -0
- package/node_modules/@llblab/pi-command-fast/dist/command.d.ts +8 -0
- package/node_modules/@llblab/pi-command-fast/dist/command.js +52 -0
- package/node_modules/@llblab/pi-command-fast/dist/index.d.ts +3 -0
- package/node_modules/@llblab/pi-command-fast/dist/index.js +3 -0
- package/node_modules/@llblab/pi-command-fast/dist/models-json.d.ts +10 -0
- package/node_modules/@llblab/pi-command-fast/dist/models-json.js +81 -0
- package/node_modules/@llblab/pi-command-fast/package.json +49 -0
- package/node_modules/@llblab/pi-grow-loop/AGENTS.md +1 -0
- package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -1
- package/node_modules/@llblab/pi-grow-loop/LICENSE +21 -0
- package/node_modules/@llblab/pi-grow-loop/README.md +1 -1
- package/node_modules/@llblab/pi-grow-loop/package.json +3 -2
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +43 -56
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +17 -3
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +25 -0
- package/node_modules/@llblab/pi-state-flow/LICENSE +21 -0
- package/node_modules/@llblab/pi-state-flow/README.md +18 -15
- package/node_modules/@llblab/pi-state-flow/dist/index.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/index.js +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +7 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +16 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +9 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +5 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +7 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +3 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +3 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +503 -235
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +16 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +11 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +6 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +4 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +1 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +4 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +13 -13
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +7 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +9 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +3 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +17 -12
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +13 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +62 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +23 -8
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +52 -20
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +22 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +30 -10
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +5 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +19 -28
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +17 -15
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +52 -52
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +8 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +34 -18
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +13 -19
- package/node_modules/@llblab/pi-state-flow/dist/package.json +12 -11
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/README.md +2 -1
- package/node_modules/@llblab/pi-state-flow/docs/agent-contract-relocation.md +72 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +44 -36
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +14 -6
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +7 -5
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +6 -6
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +13 -12
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +37 -33
- package/node_modules/@llblab/pi-state-flow/index.ts +3 -2
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +20 -10
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +15 -14
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +8 -6
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +6 -6
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +484 -232
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +14 -5
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +16 -11
- package/node_modules/@llblab/pi-state-flow/lib/logging.ts +5 -1
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +16 -16
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +11 -11
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +19 -13
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +57 -3
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +57 -22
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +46 -13
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +23 -32
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +66 -65
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +39 -19
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +19 -27
- package/node_modules/@llblab/pi-state-flow/package.json +12 -11
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +2 -2
- package/node_modules/jsonc-parser/CHANGELOG.md +76 -0
- package/node_modules/jsonc-parser/LICENSE.md +21 -0
- package/node_modules/jsonc-parser/README.md +364 -0
- package/node_modules/jsonc-parser/SECURITY.md +41 -0
- package/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
- package/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
- package/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
- package/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
- package/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
- package/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
- package/node_modules/jsonc-parser/lib/esm/main.js +178 -0
- package/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
- package/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
- package/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
- package/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
- package/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
- package/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
- package/node_modules/jsonc-parser/lib/umd/main.js +194 -0
- package/node_modules/jsonc-parser/package.json +37 -0
- package/package.json +10 -6
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Await a coherent capture; hosts without cancellation may refuse contention instead of hanging Abort. */
|
|
2
2
|
export declare function backupCurrentStateFlowFiles(repositoryRoot: string, signal?: AbortSignal, waitForLock?: boolean): Promise<string | undefined>;
|
|
3
3
|
/** Skip overlapping pushes; the next accepted turn can push the latest HEAD. */
|
|
4
|
-
export declare function startStateFlowBackupPush(repositoryRoot: string, onFailure: (error: unknown) => void, onSuccess?: () => void): boolean;
|
|
4
|
+
export declare function startStateFlowBackupPush(repositoryRoot: string, onFailure: (error: unknown) => void, onSuccess?: () => void, signal?: AbortSignal): boolean;
|
|
5
5
|
/** Resolve only after the push process has closed (including timeout termination). */
|
|
6
6
|
export declare function awaitInFlightBackupPushes(repositoryRoot: string): Promise<void>;
|
|
7
7
|
/** Push the current backup commit to its explicitly configured branch remote without blocking settlement. */
|
|
8
|
-
export declare function pushCurrentStateFlowBackup(repositoryRoot: string): Promise<{
|
|
8
|
+
export declare function pushCurrentStateFlowBackup(repositoryRoot: string, signal?: AbortSignal): Promise<{
|
|
9
9
|
commit: string;
|
|
10
10
|
remote: string;
|
|
11
11
|
ref: string;
|
|
@@ -204,18 +204,20 @@ export function backupCurrentStateFlowFiles(repositoryRoot, signal, waitForLock
|
|
|
204
204
|
return withBackupLock(repositoryRoot, (root) => commitCurrentOwnedFiles(root, currentHead(root), signal, waitForLock), signal, waitForLock);
|
|
205
205
|
}
|
|
206
206
|
/** Skip overlapping pushes; the next accepted turn can push the latest HEAD. */
|
|
207
|
-
export function startStateFlowBackupPush(repositoryRoot, onFailure, onSuccess) {
|
|
207
|
+
export function startStateFlowBackupPush(repositoryRoot, onFailure, onSuccess, signal) {
|
|
208
208
|
const root = resolve(repositoryRoot);
|
|
209
|
-
if (activePushes.has(root))
|
|
209
|
+
if (signal?.aborted || activePushes.has(root))
|
|
210
210
|
return false;
|
|
211
|
-
const push = pushCurrentStateFlowBackup(root).then((result) => {
|
|
212
|
-
if (result) {
|
|
211
|
+
const push = pushCurrentStateFlowBackup(root, signal).then((result) => {
|
|
212
|
+
if (result && !signal?.aborted) {
|
|
213
213
|
try {
|
|
214
214
|
onSuccess?.();
|
|
215
215
|
}
|
|
216
216
|
catch { /* Reporting cannot change push acceptance. */ }
|
|
217
217
|
}
|
|
218
218
|
}, (error) => {
|
|
219
|
+
if (signal?.aborted)
|
|
220
|
+
return;
|
|
219
221
|
try {
|
|
220
222
|
onFailure(error);
|
|
221
223
|
}
|
|
@@ -231,15 +233,17 @@ export async function awaitInFlightBackupPushes(repositoryRoot) {
|
|
|
231
233
|
await push;
|
|
232
234
|
}
|
|
233
235
|
/** Push the current backup commit to its explicitly configured branch remote without blocking settlement. */
|
|
234
|
-
export function pushCurrentStateFlowBackup(repositoryRoot) {
|
|
236
|
+
export function pushCurrentStateFlowBackup(repositoryRoot, signal) {
|
|
235
237
|
return new Promise((resolvePush, rejectPush) => {
|
|
236
238
|
let root;
|
|
237
239
|
let commit;
|
|
238
240
|
let destination;
|
|
239
241
|
try {
|
|
242
|
+
signal?.throwIfAborted();
|
|
240
243
|
root = assertRepositoryRoot(repositoryRoot);
|
|
241
244
|
commit = currentHead(root);
|
|
242
245
|
destination = configuredPushDestination(root);
|
|
246
|
+
signal?.throwIfAborted();
|
|
243
247
|
}
|
|
244
248
|
catch (error) {
|
|
245
249
|
rejectPush(new Error(redactGitDiagnostic(error instanceof Error ? error.message : String(error))));
|
|
@@ -282,6 +286,7 @@ export function pushCurrentStateFlowBackup(repositoryRoot) {
|
|
|
282
286
|
return;
|
|
283
287
|
settled = true;
|
|
284
288
|
clearTimeout(timeout);
|
|
289
|
+
signal?.removeEventListener("abort", cancelled);
|
|
285
290
|
if (error)
|
|
286
291
|
rejectPush(error);
|
|
287
292
|
else
|
|
@@ -297,5 +302,11 @@ export function pushCurrentStateFlowBackup(repositoryRoot) {
|
|
|
297
302
|
child.stderr?.destroy(); });
|
|
298
303
|
child.once("close", (code, endedBy) => finish(failure ?? (code === 0 ? undefined
|
|
299
304
|
: new Error(`Git backup push failed (${endedBy ?? code}): ${redactGitDiagnostic(stderr.trim()) || "no diagnostic output"}`))));
|
|
305
|
+
function cancelled() {
|
|
306
|
+
terminate(signal?.reason instanceof Error ? signal.reason : new Error("Git backup push cancelled"));
|
|
307
|
+
}
|
|
308
|
+
signal?.addEventListener("abort", cancelled, { once: true });
|
|
309
|
+
if (signal?.aborted)
|
|
310
|
+
cancelled();
|
|
300
311
|
});
|
|
301
312
|
}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type JsonObject, type JsonValue } from "./json.ts";
|
|
2
|
+
import type { ScopedSemanticStates, StateScope } from "./state.ts";
|
|
2
3
|
export declare const DEFAULT_HISTORY_LIMIT = 7;
|
|
3
4
|
export declare const MAX_HISTORY_LIMIT = 100;
|
|
4
5
|
export interface RecentScopePatch {
|
|
5
6
|
scope: StateScope;
|
|
6
|
-
patch:
|
|
7
|
-
|
|
7
|
+
patch: {
|
|
8
|
+
[key: string]: JsonValue | undefined;
|
|
9
|
+
intents?: JsonObject | null;
|
|
10
|
+
contract?: JsonObject | null;
|
|
11
|
+
working?: JsonObject | null;
|
|
12
|
+
artifacts?: JsonObject | null;
|
|
13
|
+
lazy?: JsonObject | null;
|
|
14
|
+
response?: string | null;
|
|
8
15
|
};
|
|
9
16
|
}
|
|
10
17
|
/** Exact accepted replay cohort; temporal runtime owns its causal boundary. */
|
|
@@ -18,6 +25,6 @@ export interface RecentTransition extends AcceptedTransition {
|
|
|
18
25
|
}
|
|
19
26
|
export type RecentTransitionWindow = RecentTransition[];
|
|
20
27
|
export declare function validateRecentTransition(value: unknown): asserts value is RecentTransition;
|
|
21
|
-
export declare function createAcceptedTransition(currentStates:
|
|
28
|
+
export declare function createAcceptedTransition(currentStates: ScopedSemanticStates, nextStates: ScopedSemanticStates, id?: string): AcceptedTransition | undefined;
|
|
22
29
|
/** Preserve the configured per-scope budget, filtering in selected-lineage order. */
|
|
23
30
|
export declare function projectRecentTransitionsWithLimit(limit: number, lineage: readonly RecentTransition[]): RecentTransitionWindow;
|
|
@@ -30,13 +30,10 @@ function validateScopedPatch(value) {
|
|
|
30
30
|
validatePatch(value.patch);
|
|
31
31
|
for (const [key, field] of Object.entries(value.patch)) {
|
|
32
32
|
const valid = key === "response"
|
|
33
|
-
? value.scope === "session" && typeof field === "string"
|
|
34
|
-
: key ===
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (!PATCH_KEYS.has(key) || !valid) {
|
|
38
|
-
throw new Error("Recent State Flow patches may contain hot object planes, ordinary-JSON lazy state, and a session response string");
|
|
39
|
-
}
|
|
33
|
+
? value.scope === "session" && (field === null || typeof field === "string")
|
|
34
|
+
: !PATCH_KEYS.has(key) || field === null || isObject(field);
|
|
35
|
+
if (!valid)
|
|
36
|
+
throw new Error("Recent State Flow patches require object planes or deletion, and a session response string or deletion");
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
export function validateRecentTransition(value) {
|
|
@@ -60,6 +57,8 @@ export function createAcceptedTransition(currentStates, nextStates, id) {
|
|
|
60
57
|
const transitions = [];
|
|
61
58
|
for (const scope of SCOPES) {
|
|
62
59
|
const patch = replayPatch(currentStates[scope], nextStates[scope]);
|
|
60
|
+
if ((currentStates[scope].response ?? "") === (nextStates[scope].response ?? ""))
|
|
61
|
+
delete patch.response;
|
|
63
62
|
if (Object.keys(patch).length > 0)
|
|
64
63
|
transitions.push({ scope, patch });
|
|
65
64
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type StateFlowDiagnosticCategory = "invalid-patch" | "publication-conflict" | "finalization";
|
|
1
|
+
export type StateFlowDiagnosticCategory = "invalid-patch" | "publication-conflict" | "finalization" | "barrier-block";
|
|
2
2
|
/** Minimal structural block; only ordinary text keeps its exact content. */
|
|
3
3
|
export interface StateFlowDiagnosticBlock {
|
|
4
4
|
type: string;
|
|
@@ -15,6 +15,8 @@ export interface StateFlowDiagnosticRecord {
|
|
|
15
15
|
input?: unknown;
|
|
16
16
|
tool?: string;
|
|
17
17
|
toolCallId?: string;
|
|
18
|
+
/** Names only, in native assistant-batch order; never sibling arguments. */
|
|
19
|
+
batchToolNames?: string[];
|
|
18
20
|
}
|
|
19
21
|
/** Preserve exact text blocks and block boundaries; reasoning bodies are never duplicated. */
|
|
20
22
|
export declare function projectDiagnosticContent(content: unknown): StateFlowDiagnosticBlock[];
|
|
@@ -26,6 +28,7 @@ export interface DiagnosticExtras {
|
|
|
26
28
|
input?: unknown;
|
|
27
29
|
tool?: string;
|
|
28
30
|
toolCallId?: string;
|
|
31
|
+
batchToolNames?: readonly string[];
|
|
29
32
|
}
|
|
30
33
|
/** Own diagnostic path safety, projection, persistence, and one-shot failure reporting. */
|
|
31
34
|
export declare class StateFlowDiagnosticWriter {
|
|
@@ -77,6 +77,7 @@ export class StateFlowDiagnosticWriter {
|
|
|
77
77
|
...(extras.input === undefined ? {} : { input: extras.input }),
|
|
78
78
|
...(extras.tool === undefined ? {} : { tool: extras.tool }),
|
|
79
79
|
...(extras.toolCallId === undefined ? {} : { toolCallId: extras.toolCallId }),
|
|
80
|
+
...(extras.batchToolNames === undefined ? {} : { batchToolNames: [...extras.batchToolNames] }),
|
|
80
81
|
});
|
|
81
82
|
return true;
|
|
82
83
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type RecentScopePatch } from "./history.ts";
|
|
1
2
|
import { type JsonValue } from "./json.ts";
|
|
2
|
-
import { type
|
|
3
|
+
import { type SemanticState, type StateScope } from "./state.ts";
|
|
3
4
|
import { type TemporalState, type TransitionBoundary } from "./temporal.ts";
|
|
4
5
|
export type StateReadQuery = {
|
|
5
6
|
kind: "state";
|
|
@@ -15,13 +16,11 @@ export type StateReadQuery = {
|
|
|
15
16
|
export type StateReadResult = {
|
|
16
17
|
path: string;
|
|
17
18
|
boundary: TransitionBoundary;
|
|
18
|
-
state:
|
|
19
|
+
state: SemanticState;
|
|
19
20
|
} | {
|
|
20
21
|
path: string;
|
|
21
22
|
boundary: TransitionBoundary;
|
|
22
|
-
patch:
|
|
23
|
-
response?: string;
|
|
24
|
-
};
|
|
23
|
+
patch: RecentScopePatch["patch"];
|
|
25
24
|
};
|
|
26
25
|
export type StateReadProjection = "value" | "keys" | "patch";
|
|
27
26
|
type StateReadMeta = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT } from "./history.js";
|
|
2
2
|
import { isObject, sameJson } from "./json.js";
|
|
3
|
-
import { projectStateForModel } from "./state.js";
|
|
4
|
-
import {
|
|
3
|
+
import { emptyState, projectSemanticPatch, projectStateForModel } from "./state.js";
|
|
4
|
+
import { readTemporalView } from "./temporal.js";
|
|
5
5
|
const MAX_REFERENCE_SOURCES = 3;
|
|
6
6
|
const MAX_REFERENCE_SCAN_NODES = 10_000;
|
|
7
7
|
const PATH_PATTERN = /^(effective|global|cwd|session)(?:\[(\d+)\])?(?:\.patches(?:\[(\d+)\])?)?$/;
|
|
@@ -32,12 +32,12 @@ export function readStatePath(view, path, historyLimit = DEFAULT_HISTORY_LIMIT)
|
|
|
32
32
|
const boundary = view.lineage[view.lineage.length - 1 - query.offset];
|
|
33
33
|
if (!boundary)
|
|
34
34
|
throw new Error("Requested history predates the proven temporal origin");
|
|
35
|
-
return { path, boundary: structuredClone(boundary), state: projectStateForModel(
|
|
35
|
+
return { path, boundary: structuredClone(boundary), state: projectStateForModel(readTemporalView(view, query.offset, query.scope, historyLimit)) };
|
|
36
36
|
}
|
|
37
37
|
const record = view.scopes[query.scope].patches.at(-1 - query.offset);
|
|
38
38
|
if (!record)
|
|
39
39
|
throw new Error(`Requested ${query.scope} patch predates retained hot history`);
|
|
40
|
-
return { path, boundary: structuredClone(record.transition), patch:
|
|
40
|
+
return { path, boundary: structuredClone(record.transition), patch: projectSemanticPatch(record.patch) };
|
|
41
41
|
}
|
|
42
42
|
function parseValuePath(path) {
|
|
43
43
|
const explicitRoot = /^(?:effective|global|cwd|session)(?:\[\d+\])?(?=\.|$)/.exec(path)?.[0];
|
|
@@ -151,7 +151,7 @@ export function findStateReferenceSources(view, path, historyLimit = DEFAULT_HIS
|
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
for (const scope of ["global", "cwd", "session"]) {
|
|
154
|
-
const state =
|
|
154
|
+
const state = readTemporalView(view, 0, scope, historyLimit);
|
|
155
155
|
for (const plane of ["artifacts", "contract", "working", "intents", "lazy"]) {
|
|
156
156
|
const value = state[plane];
|
|
157
157
|
if (value !== undefined)
|
|
@@ -201,13 +201,13 @@ function patchAtPath(view, root, selectors, path, historyLimit) {
|
|
|
201
201
|
throw new Error("Requested history predates the proven temporal origin");
|
|
202
202
|
let patch = {};
|
|
203
203
|
if (rootQuery.scope) {
|
|
204
|
-
patch =
|
|
204
|
+
patch = projectSemanticPatch((view.scopes[rootQuery.scope].patches.find((record) => record.transition.id === boundary.id)?.patch ?? {}));
|
|
205
205
|
}
|
|
206
206
|
else {
|
|
207
207
|
const before = view.lineage.at(-2 - rootQuery.offset);
|
|
208
208
|
if (before) {
|
|
209
|
-
const current =
|
|
210
|
-
const previous =
|
|
209
|
+
const current = readTemporalView(view, rootQuery.offset, undefined, historyLimit);
|
|
210
|
+
const previous = readTemporalView(view, rootQuery.offset + 1, undefined, historyLimit);
|
|
211
211
|
patch = diffObjects(previous, current);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
@@ -270,11 +270,11 @@ export function readProjectedState(view, paths, projection = "value", historyLim
|
|
|
270
270
|
if (query.kind !== "state")
|
|
271
271
|
throw new Error("Value and keys projections require a state path");
|
|
272
272
|
const readsLazy = selectors[0]?.kind === "key" && selectors[0].key === "lazy";
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (
|
|
277
|
-
|
|
273
|
+
const raw = readTemporalView(view, query.offset, query.scope, historyLimit);
|
|
274
|
+
const state = readsLazy ? raw : projectStateForModel(raw);
|
|
275
|
+
const field = selectors.length === 1 && selectors[0]?.kind === "key" ? selectors[0].key : undefined;
|
|
276
|
+
if (projection === "value" && field !== undefined && Object.hasOwn(emptyState(), field) && !Object.hasOwn(state, field))
|
|
277
|
+
return { value: null };
|
|
278
278
|
return projectValue(selectValue(state, selectors, path), projection);
|
|
279
279
|
}
|
|
280
280
|
catch (error) {
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { type RetainedBoundaryCheckpoint, type Snapshot } from "./snapshot.ts";
|
|
1
|
+
import { type InactiveMode, type RetainedBoundaryCheckpoint, type Snapshot } from "./snapshot.ts";
|
|
2
2
|
export type RetainedCheckpointSelection = {
|
|
3
3
|
kind: "boundary";
|
|
4
4
|
checkpoint: RetainedBoundaryCheckpoint;
|
|
5
5
|
skipped: string[];
|
|
6
6
|
} | {
|
|
7
|
-
kind: "
|
|
7
|
+
kind: "pre-runtime";
|
|
8
|
+
mode: InactiveMode;
|
|
8
9
|
skipped: string[];
|
|
9
10
|
} | {
|
|
10
11
|
kind: "unavailable";
|
|
11
12
|
snapshot: Snapshot;
|
|
12
13
|
skipped: string[];
|
|
13
14
|
};
|
|
14
|
-
/** Select the newest supported retained-boundary checkpoint or
|
|
15
|
-
export declare function selectRetainedCheckpoint(candidates: readonly unknown[]): RetainedCheckpointSelection;
|
|
16
|
-
/** Withdraw a caller's join without cancelling independently owned recovery or
|
|
15
|
+
/** Select the newest supported retained-boundary checkpoint or pre-runtime mode; unsupported pointers fail closed. */
|
|
16
|
+
export declare function selectRetainedCheckpoint(candidates: readonly unknown[], inactiveMode?: InactiveMode): RetainedCheckpointSelection;
|
|
17
|
+
/** Withdraw a caller's join without cancelling independently owned recovery or mode persistence. */
|
|
17
18
|
export declare function waitForRecovery<T>(operation: Promise<T>, signal: AbortSignal): Promise<T>;
|
|
18
19
|
/** A selected boundary that cannot be resolved stays unavailable; callers never fall through to older evidence. */
|
|
19
|
-
export declare function selectedBoundaryFailure(cause: string): Snapshot;
|
|
20
|
+
export declare function selectedBoundaryFailure(cause: string, mode?: InactiveMode): Snapshot;
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { parseRetainedPiCheckpoint, migrationFailure } from "./snapshot.js";
|
|
2
|
-
/** Select the newest supported retained-boundary checkpoint or
|
|
3
|
-
export function selectRetainedCheckpoint(candidates) {
|
|
2
|
+
/** Select the newest supported retained-boundary checkpoint or pre-runtime mode; unsupported pointers fail closed. */
|
|
3
|
+
export function selectRetainedCheckpoint(candidates, inactiveMode = "passive") {
|
|
4
4
|
const skipped = [];
|
|
5
5
|
for (const candidate of candidates) {
|
|
6
6
|
let retained;
|
|
7
7
|
try {
|
|
8
8
|
if (typeof candidate === "object" && candidate !== null && Object.hasOwn(candidate, "revision")) {
|
|
9
|
-
return { kind: "unavailable", snapshot: migrationFailure({}, "Snapshot restoration failed: revision-pointer checkpoints are unsupported"), skipped };
|
|
9
|
+
return { kind: "unavailable", snapshot: migrationFailure({}, "Snapshot restoration failed: revision-pointer checkpoints are unsupported", inactiveMode), skipped };
|
|
10
10
|
}
|
|
11
|
-
retained = parseRetainedPiCheckpoint(candidate);
|
|
11
|
+
retained = parseRetainedPiCheckpoint(candidate, inactiveMode);
|
|
12
12
|
}
|
|
13
13
|
catch (error) {
|
|
14
14
|
skipped.push(`Snapshot restoration failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
17
|
-
return "
|
|
17
|
+
return "boundary" in retained ? { kind: "boundary", checkpoint: retained, skipped } : { kind: "pre-runtime", mode: retained.mode, skipped };
|
|
18
18
|
}
|
|
19
19
|
return {
|
|
20
20
|
kind: "unavailable",
|
|
21
|
-
snapshot: migrationFailure({}, skipped[0] ?? "Snapshot restoration failed: no supported checkpoint"),
|
|
21
|
+
snapshot: migrationFailure({}, skipped[0] ?? "Snapshot restoration failed: no supported checkpoint", inactiveMode),
|
|
22
22
|
skipped,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
/** Withdraw a caller's join without cancelling independently owned recovery or
|
|
25
|
+
/** Withdraw a caller's join without cancelling independently owned recovery or mode persistence. */
|
|
26
26
|
export function waitForRecovery(operation, signal) {
|
|
27
27
|
return new Promise((resolve, reject) => {
|
|
28
28
|
const aborted = () => reject(signal.reason);
|
|
@@ -42,6 +42,6 @@ export function waitForRecovery(operation, signal) {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
/** A selected boundary that cannot be resolved stays unavailable; callers never fall through to older evidence. */
|
|
45
|
-
export function selectedBoundaryFailure(cause) {
|
|
46
|
-
return migrationFailure({}, `Snapshot restoration failed: ${cause}
|
|
45
|
+
export function selectedBoundaryFailure(cause, mode = "passive") {
|
|
46
|
+
return migrationFailure({}, `Snapshot restoration failed: ${cause}`, mode);
|
|
47
47
|
}
|
|
@@ -3,11 +3,11 @@ import { type ArtifactProvenance, type ArtifactProvenanceRegistry } from "./arti
|
|
|
3
3
|
import { publishTemporalStateToFiles } from "./storage.ts";
|
|
4
4
|
import { type AcceptedTransition, type RecentTransitionWindow } from "./history.ts";
|
|
5
5
|
import { type RetainedBoundaryCheckpoint, type RetainedPiCheckpoint, type Snapshot } from "./snapshot.ts";
|
|
6
|
-
import { type MaterializedState, type ScopedStates, type StateScope } from "./state.ts";
|
|
6
|
+
import { type MaterializedState, type SemanticState, type ScopedSemanticStates, type ScopedStates, type StateScope } from "./state.ts";
|
|
7
7
|
import { type TemporalState } from "./temporal.ts";
|
|
8
8
|
export type RuntimePublication = ReturnType<typeof publishTemporalStateToFiles>;
|
|
9
9
|
export interface RuntimePatchTransaction {
|
|
10
|
-
readonly states:
|
|
10
|
+
readonly states: ScopedSemanticStates;
|
|
11
11
|
readonly causalBasis: string;
|
|
12
12
|
readonly provenance: Record<StateScope, ArtifactProvenanceRegistry>;
|
|
13
13
|
publish(snapshot: Snapshot, accepted?: AcceptedTransition, provenance?: Partial<Record<StateScope, Record<string, ArtifactProvenance>>>): RuntimePublication;
|
|
@@ -45,6 +45,7 @@ export declare class TemporalRuntime {
|
|
|
45
45
|
/** Canonical preparation is the default; Git backup is a later independent concern. */
|
|
46
46
|
prepare(): void;
|
|
47
47
|
read(offset?: number, scope?: StateScope): MaterializedState;
|
|
48
|
+
readView(offset?: number, scope?: StateScope): SemanticState;
|
|
48
49
|
states(): ScopedStates;
|
|
49
50
|
causalBasis(): string;
|
|
50
51
|
/** Encode Pi lifecycle state against the current retained semantic boundary. */
|
|
@@ -6,9 +6,8 @@ import { pruneArtifactProvenance } from "./artifact.js";
|
|
|
6
6
|
import { assertTemporalFileBase, captureTemporalFileBase, initializeFileStore, publishTemporalStateToFiles, withStorageTransaction } from "./storage.js";
|
|
7
7
|
import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT } from "./history.js";
|
|
8
8
|
import { hashJson, sameJson } from "./json.js";
|
|
9
|
-
import { HistoryBoundaryExpiredError, RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, retainedBoundaryCheckpoint } from "./snapshot.js";
|
|
10
|
-
import {
|
|
11
|
-
import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalState, selectScopeStreamAtBoundary, validateScopeLineage } from "./temporal.js";
|
|
9
|
+
import { HistoryBoundaryExpiredError, RevisionUnavailableError, createSessionRuntime, parseSessionRuntime, preRuntimeCheckpoint, retainedBoundaryCheckpoint } from "./snapshot.js";
|
|
10
|
+
import { adoptTemporalStreams, advanceTemporalState, constrainTemporalState, createTemporalState, readTemporalScopes, readTemporalState, readTemporalView, selectScopeStreamAtBoundary, validateScopeLineage } from "./temporal.js";
|
|
12
11
|
const SCOPES = ["global", "cwd", "session"];
|
|
13
12
|
const SHARED_SCOPES = ["global", "cwd"];
|
|
14
13
|
function emptyProvenance() {
|
|
@@ -42,7 +41,7 @@ function removedTargetScopeConflict(scopes) {
|
|
|
42
41
|
return new SharedScopeRemovalConflictError(scopes);
|
|
43
42
|
}
|
|
44
43
|
function freshEmptyScopeStream(scope, origin, historyLimit) {
|
|
45
|
-
return createTemporalState({ global:
|
|
44
|
+
return createTemporalState({ global: {}, cwd: {}, session: {} }, origin, historyLimit).scopes[scope];
|
|
46
45
|
}
|
|
47
46
|
/** Cached branch-selected temporal state and publication basis; excludes Pi event policy. */
|
|
48
47
|
export class TemporalRuntime {
|
|
@@ -89,7 +88,7 @@ export class TemporalRuntime {
|
|
|
89
88
|
return false;
|
|
90
89
|
if (!shared.global)
|
|
91
90
|
throw new Error("Incomplete passive State Flow shared storage: CWD state exists without global state");
|
|
92
|
-
const fresh = createTemporalState({ global:
|
|
91
|
+
const fresh = createTemporalState({ global: {}, cwd: {}, session: {} }, randomUUID(), this.historyLimit);
|
|
93
92
|
// Global memory is valid before this CWD has ever materialized its own scope.
|
|
94
93
|
const view = adoptTemporalStreams({ global: shared.global, cwd: shared.cwd ?? fresh.scopes.cwd, session: fresh.scopes.session }, `passive:files:${randomUUID()}`, this.historyLimit);
|
|
95
94
|
const provenance = {
|
|
@@ -119,6 +118,11 @@ export class TemporalRuntime {
|
|
|
119
118
|
throw new Error("State Flow temporal runtime is unavailable");
|
|
120
119
|
return readTemporalState(this.view, offset, scope, this.historyLimit);
|
|
121
120
|
}
|
|
121
|
+
readView(offset = 0, scope) {
|
|
122
|
+
if (!this.view)
|
|
123
|
+
throw new Error("State Flow temporal runtime is unavailable");
|
|
124
|
+
return readTemporalView(this.view, offset, scope, this.historyLimit);
|
|
125
|
+
}
|
|
122
126
|
states() {
|
|
123
127
|
return { global: this.read(0, "global"), cwd: this.read(0, "cwd"), session: this.read(0, "session") };
|
|
124
128
|
}
|
|
@@ -130,7 +134,7 @@ export class TemporalRuntime {
|
|
|
130
134
|
/** Encode Pi lifecycle state against the current retained semantic boundary. */
|
|
131
135
|
retainedCheckpoint(snapshot) {
|
|
132
136
|
if (!this.view)
|
|
133
|
-
return
|
|
137
|
+
return preRuntimeCheckpoint(snapshot.config.mode);
|
|
134
138
|
return retainedBoundaryCheckpoint(snapshot, this.causalBasis());
|
|
135
139
|
}
|
|
136
140
|
usesCanonicalFiles() {
|
|
@@ -179,7 +183,7 @@ export class TemporalRuntime {
|
|
|
179
183
|
session: selectedSession,
|
|
180
184
|
}, `restore:${checkpoint.boundary}:${randomUUID()}`, this.historyLimit);
|
|
181
185
|
const snapshot = {
|
|
182
|
-
config: {
|
|
186
|
+
config: { mode: checkpoint.mode },
|
|
183
187
|
meta: {
|
|
184
188
|
step: checkpoint.step,
|
|
185
189
|
...(checkpoint.bootstrap === true ? { bootstrap: true } : {}),
|
|
@@ -195,7 +199,7 @@ export class TemporalRuntime {
|
|
|
195
199
|
for (const record of scopes.session.patches) {
|
|
196
200
|
if (record.transition.position <= boundary.position)
|
|
197
201
|
continue;
|
|
198
|
-
for (const path of Object.keys(record.patch.artifacts ?? {}))
|
|
202
|
+
for (const path of Object.keys(record.patch.artifacts === null ? retained : record.patch.artifacts ?? {}))
|
|
199
203
|
delete retained[path];
|
|
200
204
|
}
|
|
201
205
|
}
|
|
@@ -266,8 +270,9 @@ export class TemporalRuntime {
|
|
|
266
270
|
const meta = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey).meta;
|
|
267
271
|
return [scope, parseScopeProvenance(files.get(meta), meta)];
|
|
268
272
|
}));
|
|
273
|
+
// Read-only recovery grants no active policy; callers select the inactive mode.
|
|
269
274
|
const snapshot = {
|
|
270
|
-
config: {
|
|
275
|
+
config: { mode: "passive" },
|
|
271
276
|
meta: { step: document.meta.step, ...(document.meta.bootstrap === true ? { bootstrap: true } : {}) },
|
|
272
277
|
};
|
|
273
278
|
this.view = view;
|
|
@@ -355,7 +360,7 @@ export class TemporalRuntime {
|
|
|
355
360
|
streams.session = undefined;
|
|
356
361
|
if (copy)
|
|
357
362
|
streams.session = copy.stream;
|
|
358
|
-
const fresh = createTemporalState({ global:
|
|
363
|
+
const fresh = createTemporalState({ global: {}, cwd: {}, session: {} }, randomUUID(), this.historyLimit);
|
|
359
364
|
const candidate = new TemporalRuntime(this.cwd, this.session, this.root, undefined, this.historyLimit);
|
|
360
365
|
candidate.base = base;
|
|
361
366
|
candidate.view = adoptTemporalStreams({ global: streams.global ?? fresh.scopes.global, cwd: streams.cwd ?? fresh.scopes.cwd, session: streams.session ?? fresh.scopes.session }, `files:${randomUUID()}`, this.historyLimit);
|
|
@@ -512,7 +517,7 @@ export class TemporalRuntime {
|
|
|
512
517
|
if (!document || !stream)
|
|
513
518
|
throw new RevisionUnavailableError("Current State Flow session memory is incomplete");
|
|
514
519
|
validateScopeLineage(stream, "session", document.meta.lineage, MAX_HISTORY_LIMIT);
|
|
515
|
-
throw new RevisionUnavailableError("Existing State Flow session memory is not selected; select an accepted boundary or use /state-flow-
|
|
520
|
+
throw new RevisionUnavailableError("Existing State Flow session memory is not selected; select an accepted boundary or use /state-flow-active");
|
|
516
521
|
}
|
|
517
522
|
const origin = `patch:${randomUUID()}`;
|
|
518
523
|
const streams = Object.fromEntries(SCOPES.map((scope) => {
|
|
@@ -531,7 +536,7 @@ export class TemporalRuntime {
|
|
|
531
536
|
/** Stage and accept synchronously inside an awaited lock; expose neither selection nor raw storage operations. */
|
|
532
537
|
async withPatchTransaction(action, signal) {
|
|
533
538
|
return this.withPublicationTransaction((candidate, publish) => action({
|
|
534
|
-
states: candidate.
|
|
539
|
+
states: readTemporalScopes(candidate.view, 0, this.historyLimit),
|
|
535
540
|
causalBasis: candidate.causalBasis(),
|
|
536
541
|
provenance: structuredClone(candidate.provenanceByScope),
|
|
537
542
|
publish,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type InactiveMode, type StateFlowMode } from "./snapshot.ts";
|
|
1
2
|
export declare const SNAPSHOT_ENTRY_TYPE = "state-flow-snapshot";
|
|
2
3
|
interface BranchEntry {
|
|
3
4
|
type?: unknown;
|
|
@@ -21,8 +22,12 @@ export interface PassiveStopBoundary {
|
|
|
21
22
|
at: number;
|
|
22
23
|
from?: number;
|
|
23
24
|
preserveContext?: true;
|
|
24
|
-
/** A same-owner failed
|
|
25
|
+
/** A same-owner failed mode change remains a write fence until a later accepted checkpoint. */
|
|
25
26
|
persistenceError?: string;
|
|
27
|
+
/** The inactive mode selected by that failed change; legacy markers omit it. */
|
|
28
|
+
mode?: InactiveMode;
|
|
29
|
+
/** Native-only Off choice; canonical runtime mode is deliberately not updated. */
|
|
30
|
+
memoryDeferred?: true;
|
|
26
31
|
}
|
|
27
32
|
export interface SnapshotDiscovery {
|
|
28
33
|
candidates: unknown[];
|
|
@@ -30,6 +35,13 @@ export interface SnapshotDiscovery {
|
|
|
30
35
|
}
|
|
31
36
|
/** Enumerate active-branch snapshots newest-first while containing hostile entries. */
|
|
32
37
|
export declare function discoverSnapshotData(branch: readonly BranchEntry[]): SnapshotDiscovery;
|
|
38
|
+
/** Read native mode policy only; semantic checkpoint validity remains the recovery owner's concern. */
|
|
39
|
+
export declare function findBranchPolicy(branch: readonly BranchEntry[], sessionId: string | undefined, stopEntryType: string | undefined, inactiveMode: InactiveMode): {
|
|
40
|
+
mode: StateFlowMode;
|
|
41
|
+
persistenceError?: string;
|
|
42
|
+
} | undefined;
|
|
43
|
+
/** Native policy bookkeeping retains an unacquired fork across extension reloads. */
|
|
44
|
+
export declare function hasPendingFork(branch: readonly BranchEntry[], sessionId: string, entryType: string): boolean;
|
|
33
45
|
export declare function snapshotDataNewestFirst(branch: readonly BranchEntry[]): unknown[];
|
|
34
46
|
export declare function latestSnapshotData(branch: readonly BranchEntry[]): unknown;
|
|
35
47
|
export declare function hasPriorConversation(branch: readonly BranchEntry[]): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseRetainedPiCheckpoint } from "./snapshot.js";
|
|
1
|
+
import { parseRetainedPiCheckpoint, readCheckpointMode } from "./snapshot.js";
|
|
2
2
|
export const SNAPSHOT_ENTRY_TYPE = "state-flow-snapshot";
|
|
3
3
|
/** Enumerate active-branch snapshots newest-first while containing hostile entries. */
|
|
4
4
|
export function discoverSnapshotData(branch) {
|
|
@@ -16,6 +16,65 @@ export function discoverSnapshotData(branch) {
|
|
|
16
16
|
}
|
|
17
17
|
return { candidates, errors };
|
|
18
18
|
}
|
|
19
|
+
/** Read native mode policy only; semantic checkpoint validity remains the recovery owner's concern. */
|
|
20
|
+
export function findBranchPolicy(branch, sessionId, stopEntryType, inactiveMode) {
|
|
21
|
+
let stopReset = false;
|
|
22
|
+
for (let index = branch.length - 1; index >= 0; index--) {
|
|
23
|
+
try {
|
|
24
|
+
const entry = branch[index];
|
|
25
|
+
if (entry?.type !== "custom")
|
|
26
|
+
continue;
|
|
27
|
+
if (entry.customType === SNAPSHOT_ENTRY_TYPE) {
|
|
28
|
+
const mode = readCheckpointMode(entry.data, inactiveMode);
|
|
29
|
+
if (mode !== undefined)
|
|
30
|
+
return { mode };
|
|
31
|
+
const data = entry.data;
|
|
32
|
+
if (data?.disabled === true && !Object.hasOwn(data, "mode") && !Object.hasOwn(data, "enabled"))
|
|
33
|
+
return { mode: inactiveMode };
|
|
34
|
+
}
|
|
35
|
+
else if (stopEntryType !== undefined && entry.customType === stopEntryType) {
|
|
36
|
+
const data = entry.data;
|
|
37
|
+
if (!data || stopReset || (sessionId === undefined ? typeof data.owner !== "string" || !data.owner.trim() : data.owner !== sessionId))
|
|
38
|
+
continue;
|
|
39
|
+
if (data.reset === true) {
|
|
40
|
+
stopReset = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (Number.isSafeInteger(data.at) && data.at >= 0 && data.memoryDeferred === true && data.mode === "off") {
|
|
44
|
+
return { mode: "off", ...(typeof data.persistenceError === "string" && data.persistenceError.trim() ? { persistenceError: data.persistenceError } : {}) };
|
|
45
|
+
}
|
|
46
|
+
if (Number.isSafeInteger(data.at) && data.at >= 0 && typeof data.persistenceError === "string" && data.persistenceError.trim()) {
|
|
47
|
+
return { mode: data.mode === "off" || data.mode === "passive" ? data.mode : inactiveMode, persistenceError: data.persistenceError };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Malformed native policy cannot prevent another explicit retained choice from being read.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
/** Native policy bookkeeping retains an unacquired fork across extension reloads. */
|
|
58
|
+
export function hasPendingFork(branch, sessionId, entryType) {
|
|
59
|
+
for (let index = branch.length - 1; index >= 0; index--) {
|
|
60
|
+
try {
|
|
61
|
+
const entry = branch[index];
|
|
62
|
+
if (entry?.type !== "custom" || entry.customType !== entryType)
|
|
63
|
+
continue;
|
|
64
|
+
const data = entry.data;
|
|
65
|
+
if (data?.owner !== sessionId)
|
|
66
|
+
continue;
|
|
67
|
+
if (data.reset === true)
|
|
68
|
+
return false;
|
|
69
|
+
if (data.forkPending === true)
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Unrelated native entries grant no fork authority.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
19
78
|
export function snapshotDataNewestFirst(branch) {
|
|
20
79
|
return discoverSnapshotData(branch).candidates;
|
|
21
80
|
}
|
|
@@ -92,7 +151,7 @@ export function findPassiveStopBoundary(branch, sessionId, entryType) {
|
|
|
92
151
|
}
|
|
93
152
|
if (entry.customType !== entryType)
|
|
94
153
|
continue;
|
|
95
|
-
const { at, from, reset, owner, preserveContext, persistenceError } = entry.data ?? {};
|
|
154
|
+
const { at, from, reset, owner, preserveContext, persistenceError, mode } = entry.data ?? {};
|
|
96
155
|
if (reset === true && owner === sessionId)
|
|
97
156
|
return undefined;
|
|
98
157
|
if (owner !== undefined && owner !== sessionId)
|
|
@@ -103,6 +162,7 @@ export function findPassiveStopBoundary(branch, sessionId, entryType) {
|
|
|
103
162
|
...(typeof from === "number" && Number.isSafeInteger(from) && from >= 0 ? { from } : {}),
|
|
104
163
|
...(preserveContext === true ? { preserveContext: true } : {}),
|
|
105
164
|
...(!checkpointSeen && owner === sessionId && typeof persistenceError === "string" && persistenceError.trim().length > 0 ? { persistenceError } : {}),
|
|
165
|
+
...(mode === "passive" || mode === "off" ? { mode } : {}),
|
|
106
166
|
};
|
|
107
167
|
}
|
|
108
168
|
catch {
|