@llblab/pi-kit 0.18.2 → 0.19.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/CHANGELOG.md +6 -0
- package/README.md +3 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +35 -37
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +5 -3
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +19 -1
- package/node_modules/@llblab/pi-state-flow/README.md +102 -48
- package/node_modules/@llblab/pi-state-flow/dist/index.d.ts +6 -9
- package/node_modules/@llblab/pi-state-flow/dist/index.js +6 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +21 -8
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +40 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +93 -63
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +9 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +6 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +56 -58
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +12 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +7 -18
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +47 -78
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +4 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +241 -437
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +2 -72
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +120 -499
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +2 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.d.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +43 -22
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +1 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +0 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/memory.d.ts +1 -14
- package/node_modules/@llblab/pi-state-flow/dist/lib/memory.js +5 -37
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +1 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +13 -32
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +6 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +25 -21
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +3 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +15 -12
- package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.d.ts +2 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.js +5 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +26 -88
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +159 -255
- package/node_modules/@llblab/pi-state-flow/dist/lib/skills.d.ts +0 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/skills.js +1 -47
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +16 -33
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +48 -137
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +16 -11
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +13 -14
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +1 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +15 -43
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +5 -9
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +13 -45
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +15 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +104 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +0 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +11 -14
- package/node_modules/@llblab/pi-state-flow/dist/package.json +9 -6
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +11 -17
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +8 -8
- package/node_modules/@llblab/pi-state-flow/docs/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +64 -67
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +24 -6
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +12 -15
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +22 -27
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +25 -41
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +38 -421
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +33 -46
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +37 -61
- package/node_modules/@llblab/pi-state-flow/index.ts +7 -71
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +26 -11
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +116 -88
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +12 -10
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +6 -6
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +52 -59
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +11 -20
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +44 -86
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +237 -460
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +110 -552
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +39 -23
- package/node_modules/@llblab/pi-state-flow/lib/logging.ts +1 -7
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +5 -44
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +14 -25
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +26 -22
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +17 -11
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +7 -5
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +154 -254
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +1 -49
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +58 -142
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +27 -19
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +16 -54
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +12 -40
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +104 -22
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +16 -26
- package/node_modules/@llblab/pi-state-flow/package.json +9 -6
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +11 -17
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +8 -8
- package/package.json +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/discovery.d.ts +0 -21
- package/node_modules/@llblab/pi-state-flow/dist/lib/discovery.js +0 -125
- package/node_modules/@llblab/pi-state-flow/dist/lib/maintenance.d.ts +0 -36
- package/node_modules/@llblab/pi-state-flow/dist/lib/maintenance.js +0 -98
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +0 -13
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +0 -167
- package/node_modules/@llblab/pi-state-flow/dist/lib/publication.d.ts +0 -86
- package/node_modules/@llblab/pi-state-flow/dist/lib/publication.js +0 -437
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +0 -133
- package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +0 -147
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +0 -171
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +0 -458
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
classifyArtifactFreshness,
|
|
3
|
-
type ArtifactMetadata,
|
|
4
|
-
type ArtifactProvenance,
|
|
5
|
-
type ArtifactRegistry,
|
|
6
|
-
type ArtifactSourceIdentity,
|
|
7
|
-
} from "./artifact.ts";
|
|
8
|
-
import type { ArtifactSourceCandidate } from "./discovery.ts";
|
|
9
|
-
import { isObject } from "./json.ts";
|
|
10
|
-
|
|
11
|
-
export const DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS = 1;
|
|
12
|
-
export const DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES = 16 * 1024;
|
|
13
|
-
export const DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS = 30 * 24 * 60 * 60 * 1_000;
|
|
14
|
-
|
|
15
|
-
export interface ArtifactMaintenanceOptions {
|
|
16
|
-
/** Stable cycle time supplied by the caller. */
|
|
17
|
-
now?: Date | number | string;
|
|
18
|
-
/** A source must be at least this old. Missing/unparseable timestamps rank as oldest. */
|
|
19
|
-
minimumAgeMs?: number;
|
|
20
|
-
/** Strict source-read count ceiling for this cycle. */
|
|
21
|
-
maxReads?: number;
|
|
22
|
-
/** Strict source-byte ceiling; bytes conservatively upper-bound source tokenizer input. */
|
|
23
|
-
maxSourceBytes?: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface ArtifactMaintenanceRequest extends ArtifactSourceIdentity {
|
|
27
|
-
reason: "maintenance";
|
|
28
|
-
sourceBytes: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface ArtifactMaintenancePlan {
|
|
32
|
-
requiresCompilation: ArtifactMaintenanceRequest[];
|
|
33
|
-
deferred: ArtifactMaintenanceRequest[];
|
|
34
|
-
budget: {
|
|
35
|
-
maxReads: number;
|
|
36
|
-
maxSourceBytes: number;
|
|
37
|
-
usedReads: number;
|
|
38
|
-
usedSourceBytes: number;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function nonNegativeSafeInteger(value: number, name: string): number {
|
|
43
|
-
if (!Number.isSafeInteger(value) || value < 0) throw new Error(`${name} must be a non-negative safe integer`);
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function cycleTime(value: Date | number | string | undefined): number {
|
|
48
|
-
const timestamp = value === undefined
|
|
49
|
-
? Date.now()
|
|
50
|
-
: value instanceof Date
|
|
51
|
-
? value.getTime()
|
|
52
|
-
: typeof value === "number"
|
|
53
|
-
? value
|
|
54
|
-
: Date.parse(value);
|
|
55
|
-
if (!Number.isFinite(timestamp)) throw new Error("Artifact maintenance cycle time must be valid");
|
|
56
|
-
return timestamp;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function compiledTime(entry: ArtifactProvenance | undefined, legacy: unknown): number {
|
|
60
|
-
const timestamp = entry?.malformed === true ? undefined
|
|
61
|
-
: entry !== undefined && Object.hasOwn(entry, "compiledAt") ? entry.compiledAt
|
|
62
|
-
: legacy;
|
|
63
|
-
if (typeof timestamp !== "string") return Number.NEGATIVE_INFINITY;
|
|
64
|
-
const parsed = Date.parse(timestamp);
|
|
65
|
-
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function request(source: ArtifactSourceCandidate): ArtifactMaintenanceRequest {
|
|
69
|
-
return {
|
|
70
|
-
path: source.path,
|
|
71
|
-
hash: source.hash,
|
|
72
|
-
reason: "maintenance",
|
|
73
|
-
sourceBytes: source.bytes,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Select a bounded, oldest-first maintenance cohort from otherwise fresh artifacts.
|
|
79
|
-
*
|
|
80
|
-
* This planner is opt-in and side-effect free. Correctness invalidations remain the
|
|
81
|
-
* responsibility of planArtifactInvalidation; they are never displaced by maintenance.
|
|
82
|
-
*/
|
|
83
|
-
export function planArtifactMaintenance(
|
|
84
|
-
sources: readonly ArtifactSourceCandidate[],
|
|
85
|
-
registry: Readonly<Record<string, unknown>>,
|
|
86
|
-
compiler: string,
|
|
87
|
-
options: ArtifactMaintenanceOptions = {},
|
|
88
|
-
provenance: Readonly<Record<string, ArtifactProvenance>> = {},
|
|
89
|
-
): ArtifactMaintenancePlan {
|
|
90
|
-
if (!isObject(registry)) throw new Error("Artifacts must be a path-keyed JSON object");
|
|
91
|
-
const now = cycleTime(options.now);
|
|
92
|
-
const minimumAgeMs = nonNegativeSafeInteger(
|
|
93
|
-
options.minimumAgeMs ?? DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS,
|
|
94
|
-
"Artifact maintenance minimum age",
|
|
95
|
-
);
|
|
96
|
-
const maxReads = nonNegativeSafeInteger(
|
|
97
|
-
options.maxReads ?? DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS,
|
|
98
|
-
"Artifact maintenance read budget",
|
|
99
|
-
);
|
|
100
|
-
const maxSourceBytes = nonNegativeSafeInteger(
|
|
101
|
-
options.maxSourceBytes ?? DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES,
|
|
102
|
-
"Artifact maintenance source-byte budget",
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
const seen = new Set<string>();
|
|
106
|
-
const eligible: { source: ArtifactSourceCandidate; compiledAt: number }[] = [];
|
|
107
|
-
for (const source of sources) {
|
|
108
|
-
if (seen.has(source.path)) throw new Error(`Duplicate artifact source path: ${source.path}`);
|
|
109
|
-
seen.add(source.path);
|
|
110
|
-
nonNegativeSafeInteger(source.bytes, `Artifact source bytes at ${source.path}`);
|
|
111
|
-
const metadata = Object.hasOwn(registry, source.path) ? registry[source.path] : undefined;
|
|
112
|
-
const entry = Object.hasOwn(provenance, source.path) ? provenance[source.path] : undefined;
|
|
113
|
-
const freshness = classifyArtifactFreshness(source, metadata, compiler, false, entry);
|
|
114
|
-
if (freshness.kind !== "fresh") continue;
|
|
115
|
-
const compiledAt = compiledTime(entry, isObject(metadata) ? (metadata as ArtifactMetadata).compiled_at : undefined);
|
|
116
|
-
if (compiledAt !== Number.NEGATIVE_INFINITY && now - compiledAt < minimumAgeMs) continue;
|
|
117
|
-
eligible.push({ source, compiledAt });
|
|
118
|
-
}
|
|
119
|
-
eligible.sort((left, right) => {
|
|
120
|
-
if (left.compiledAt !== right.compiledAt) return left.compiledAt - right.compiledAt;
|
|
121
|
-
return left.source.path < right.source.path ? -1 : left.source.path > right.source.path ? 1 : 0;
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const requiresCompilation: ArtifactMaintenanceRequest[] = [];
|
|
125
|
-
const deferred: ArtifactMaintenanceRequest[] = [];
|
|
126
|
-
let usedSourceBytes = 0;
|
|
127
|
-
for (const candidate of eligible) {
|
|
128
|
-
const next = request(candidate.source);
|
|
129
|
-
if (requiresCompilation.length >= maxReads
|
|
130
|
-
|| candidate.source.bytes > maxSourceBytes - usedSourceBytes) {
|
|
131
|
-
deferred.push(next);
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
requiresCompilation.push(next);
|
|
135
|
-
usedSourceBytes += candidate.source.bytes;
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
requiresCompilation,
|
|
139
|
-
deferred,
|
|
140
|
-
budget: {
|
|
141
|
-
maxReads,
|
|
142
|
-
maxSourceBytes,
|
|
143
|
-
usedReads: requiresCompilation.length,
|
|
144
|
-
usedSourceBytes,
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
// Domain: predecessor temporal-envelope migration planning; Git/files backends own publication and rollback.
|
|
2
|
-
import { lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
3
|
-
import { join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
captureOwnedFileBases,
|
|
6
|
-
cwdScopePaths,
|
|
7
|
-
parseScopeStream,
|
|
8
|
-
serializeScopeMetadata,
|
|
9
|
-
serializeScopeStream,
|
|
10
|
-
sessionScopeKey,
|
|
11
|
-
sessionScopePaths,
|
|
12
|
-
type DurableFileBase,
|
|
13
|
-
type OwnedFileUpdate,
|
|
14
|
-
} from "./durable.ts";
|
|
15
|
-
import { parseSessionRuntime, serializeSessionRuntime } from "./snapshot.ts";
|
|
16
|
-
import { migratePreIntentState, type StateScope } from "./state.ts";
|
|
17
|
-
|
|
18
|
-
export interface LegacyStorageMigration {
|
|
19
|
-
bases: DurableFileBase[];
|
|
20
|
-
updates: OwnedFileUpdate[];
|
|
21
|
-
scopes: StateScope[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface MigrationDirectory {
|
|
25
|
-
directory: string;
|
|
26
|
-
scope: StateScope;
|
|
27
|
-
cwdIdentity?: string;
|
|
28
|
-
sessionId?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Discover every owner-proven CWD and session cohort beneath the configured store. */
|
|
32
|
-
function migrationDirectories(cwd: string, sessionId: string, root: string, sessionKey: string): MigrationDirectory[] {
|
|
33
|
-
const selectedCwd = cwdScopePaths(cwd, root).directory;
|
|
34
|
-
const selectedSession = sessionScopePaths(cwd, sessionId, root, sessionKey).directory;
|
|
35
|
-
const directories: MigrationDirectory[] = [{ directory: root, scope: "global" }];
|
|
36
|
-
const cwdOwners = new Map<string, string>([[selectedCwd, resolve(cwd)]]);
|
|
37
|
-
if (lstatSync(root, { throwIfNoEntry: false }) !== undefined) {
|
|
38
|
-
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
39
|
-
if (!entry.isDirectory()) continue;
|
|
40
|
-
const directory = join(root, entry.name);
|
|
41
|
-
let checkpoint: unknown;
|
|
42
|
-
let meta: unknown;
|
|
43
|
-
try { checkpoint = JSON.parse(readFileSync(join(directory, "checkpoint.json"), "utf8")); }
|
|
44
|
-
catch { continue; }
|
|
45
|
-
try { meta = JSON.parse(readFileSync(join(directory, "meta.json"), "utf8")); } catch { /* predecessor metadata may be absent */ }
|
|
46
|
-
const owner = checkpoint && typeof checkpoint === "object" && !Array.isArray(checkpoint) && (checkpoint as { owner?: unknown }).owner
|
|
47
|
-
? (checkpoint as { owner: { cwd?: unknown } }).owner
|
|
48
|
-
: meta && typeof meta === "object" && !Array.isArray(meta) ? (meta as { owner?: { cwd?: unknown } }).owner : undefined;
|
|
49
|
-
if (owner && typeof owner.cwd === "string" && resolve(owner.cwd) === owner.cwd) cwdOwners.set(directory, owner.cwd);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
for (const [cwdDirectory, cwdOwner] of cwdOwners) {
|
|
53
|
-
directories.push({ directory: cwdDirectory, scope: "cwd", cwdIdentity: cwdOwner });
|
|
54
|
-
if (lstatSync(cwdDirectory, { throwIfNoEntry: false }) === undefined) continue;
|
|
55
|
-
for (const entry of readdirSync(cwdDirectory, { withFileTypes: true })) {
|
|
56
|
-
if (!entry.isDirectory()) continue;
|
|
57
|
-
try { sessionScopeKey(entry.name); } catch { continue; }
|
|
58
|
-
const directory = join(cwdDirectory, entry.name);
|
|
59
|
-
let meta: unknown;
|
|
60
|
-
let runtime: unknown;
|
|
61
|
-
try { meta = JSON.parse(readFileSync(join(directory, "meta.json"), "utf8")); }
|
|
62
|
-
catch { continue; }
|
|
63
|
-
try { runtime = JSON.parse(readFileSync(join(directory, "runtime.json"), "utf8")); }
|
|
64
|
-
catch { /* predecessor sessions keep identity in meta.json */ }
|
|
65
|
-
const identity = meta && typeof meta === "object" && !Array.isArray(meta) && (meta as { identity?: unknown }).identity !== undefined
|
|
66
|
-
? (meta as { identity?: unknown }).identity
|
|
67
|
-
: runtime && typeof runtime === "object" && !Array.isArray(runtime) ? (runtime as { identity?: unknown }).identity : undefined;
|
|
68
|
-
if (!identity || typeof identity !== "object" || Array.isArray(identity)) continue;
|
|
69
|
-
const owner = identity as { cwd?: unknown; sessionId?: unknown };
|
|
70
|
-
if (owner.cwd === cwdOwner && typeof owner.sessionId === "string" && owner.sessionId.length > 0) {
|
|
71
|
-
directories.push({ directory, scope: "session", cwdIdentity: cwdOwner, sessionId: owner.sessionId });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (!directories.some(({ directory }) => directory === selectedSession)) {
|
|
76
|
-
directories.push({ directory: selectedSession, scope: "session", cwdIdentity: resolve(cwd), sessionId });
|
|
77
|
-
}
|
|
78
|
-
return directories;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Read-only activation eligibility, before global migration or any repository publication. */
|
|
82
|
-
export function hasCwdMaterialization(cwd: string, repositoryRoot: string): boolean {
|
|
83
|
-
const root = resolve(repositoryRoot);
|
|
84
|
-
const directory = cwdScopePaths(cwd, root).directory;
|
|
85
|
-
const [checkpoint, patches, meta] = captureOwnedFileBases([
|
|
86
|
-
join(directory, "checkpoint.json"), join(directory, "patches.jsonl"), join(directory, "meta.json"),
|
|
87
|
-
], root);
|
|
88
|
-
if (checkpoint!.content !== undefined) return parseScopeStream(checkpoint!.content, patches!.content, "cwd", cwd, meta!.content) !== undefined;
|
|
89
|
-
if (patches!.content !== undefined) throw new Error(`State Flow tail has no provable checkpoint: ${directory}`);
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function checkpointNeedsIntentMigration(source: string): boolean {
|
|
94
|
-
const checkpoint = JSON.parse(source) as unknown;
|
|
95
|
-
if (migratePreIntentState(checkpoint) !== undefined) return true;
|
|
96
|
-
return checkpoint !== null && typeof checkpoint === "object" && !Array.isArray(checkpoint)
|
|
97
|
-
&& migratePreIntentState((checkpoint as { state?: unknown }).state) !== undefined;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Detect predecessor snapshots, temporal envelopes, or pre-intents semantic states without mutating the store. */
|
|
101
|
-
export function hasLegacyStateSources(
|
|
102
|
-
cwd: string,
|
|
103
|
-
sessionId: string,
|
|
104
|
-
repositoryRoot: string,
|
|
105
|
-
sessionKey = sessionId,
|
|
106
|
-
): boolean {
|
|
107
|
-
const root = resolve(repositoryRoot);
|
|
108
|
-
return migrationDirectories(cwd, sessionId, root, sessionKey).some(({ directory, scope }) => {
|
|
109
|
-
const checkpointPath = join(directory, "checkpoint.json");
|
|
110
|
-
if (lstatSync(checkpointPath, { throwIfNoEntry: false }) === undefined) return false;
|
|
111
|
-
try {
|
|
112
|
-
if (checkpointNeedsIntentMigration(readFileSync(checkpointPath, "utf8"))) return true;
|
|
113
|
-
const meta = JSON.parse(readFileSync(join(directory, "meta.json"), "utf8"));
|
|
114
|
-
if (meta === null || typeof meta !== "object" || !("temporal" in meta)) return true;
|
|
115
|
-
return scope === "session"
|
|
116
|
-
&& lstatSync(join(directory, "runtime.json"), { throwIfNoEntry: false }) === undefined
|
|
117
|
-
&& ("identity" in meta || "lineage" in meta);
|
|
118
|
-
} catch { return true; }
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/** Plan from current scope snapshots only; old explanatory journals are never replay input. */
|
|
123
|
-
export function planLegacyStorageMigration(
|
|
124
|
-
cwd: string,
|
|
125
|
-
sessionId: string,
|
|
126
|
-
repositoryRoot: string,
|
|
127
|
-
_origin?: string,
|
|
128
|
-
sessionKey = sessionId,
|
|
129
|
-
): LegacyStorageMigration {
|
|
130
|
-
const root = resolve(repositoryRoot);
|
|
131
|
-
const directories = migrationDirectories(cwd, sessionId, root, sessionKey);
|
|
132
|
-
const paths = directories.flatMap(({ directory, scope }) => [
|
|
133
|
-
join(directory, "checkpoint.json"), join(directory, "patches.jsonl"), join(directory, "meta.json"),
|
|
134
|
-
...(scope === "session" ? [join(directory, "config.json"), join(directory, "runtime.json")] : []),
|
|
135
|
-
]);
|
|
136
|
-
const bases = captureOwnedFileBases(paths, root);
|
|
137
|
-
const byPath = new Map(bases.map((base) => [base.path, base]));
|
|
138
|
-
const updates: OwnedFileUpdate[] = [];
|
|
139
|
-
const scopes: StateScope[] = [];
|
|
140
|
-
for (const { scope, directory, cwdIdentity, sessionId: ownedSessionId } of directories) {
|
|
141
|
-
const checkpoint = byPath.get(join(directory, "checkpoint.json"))!;
|
|
142
|
-
const patches = byPath.get(join(directory, "patches.jsonl"))!;
|
|
143
|
-
const meta = byPath.get(join(directory, "meta.json"))!;
|
|
144
|
-
if (checkpoint.content !== undefined) {
|
|
145
|
-
const stream = parseScopeStream(checkpoint.content, patches.content, scope, scope === "cwd" ? cwdIdentity : undefined, meta.content)!;
|
|
146
|
-
const source = serializeScopeStream(stream, scope, scope === "cwd" ? cwdIdentity : undefined);
|
|
147
|
-
const metadata = serializeScopeMetadata(undefined, stream, scope, scope === "cwd" ? cwdIdentity : undefined, meta.content);
|
|
148
|
-
const cohortUpdates: OwnedFileUpdate[] = [
|
|
149
|
-
...(checkpoint.content === source.checkpoint ? [] : [{ path: checkpoint.path, content: source.checkpoint }]),
|
|
150
|
-
...(patches.content === source.patches ? [] : [{ path: patches.path, content: source.patches }]),
|
|
151
|
-
...(meta.content === metadata ? [] : [{ path: meta.path, content: metadata }]),
|
|
152
|
-
];
|
|
153
|
-
if (scope === "session") {
|
|
154
|
-
const config = byPath.get(join(directory, "config.json"))!;
|
|
155
|
-
const runtimeFile = byPath.get(join(directory, "runtime.json"))!;
|
|
156
|
-
const runtime = parseSessionRuntime(config.content, runtimeFile.content, cwdIdentity!, ownedSessionId!, meta.content);
|
|
157
|
-
if (runtime !== undefined) {
|
|
158
|
-
const serialized = serializeSessionRuntime(runtime, cwdIdentity!, ownedSessionId!);
|
|
159
|
-
if (runtimeFile.content !== serialized.runtime) cohortUpdates.push({ path: runtimeFile.path, content: serialized.runtime });
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (cohortUpdates.length > 0) {
|
|
163
|
-
if (!scopes.includes(scope)) scopes.push(scope);
|
|
164
|
-
updates.push(...cohortUpdates);
|
|
165
|
-
}
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
if (patches.content !== undefined) throw new Error(`State Flow tail has no provable checkpoint: ${directory}`);
|
|
169
|
-
}
|
|
170
|
-
return { bases, updates, scopes };
|
|
171
|
-
}
|