@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@llblab/pi-clean-room": "0.1.1",
|
|
45
45
|
"@llblab/pi-codex-usage": "0.10.0",
|
|
46
46
|
"@llblab/pi-grow-loop": "0.8.1",
|
|
47
|
-
"@llblab/pi-state-flow": "0.
|
|
47
|
+
"@llblab/pi-state-flow": "0.17.2",
|
|
48
48
|
"@llblab/pi-telegram": "0.50.1",
|
|
49
49
|
"@llblab/skills": "1.15.0"
|
|
50
50
|
},
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { type ArtifactSourceIdentity } from "./artifact.ts";
|
|
2
|
-
/** Opaque source metadata discovered without decoding or retaining Markdown bodies. */
|
|
3
|
-
export interface ArtifactSourceCandidate extends ArtifactSourceIdentity {
|
|
4
|
-
/** Raw byte count used as a conservative maintenance token-budget ceiling. */
|
|
5
|
-
bytes: number;
|
|
6
|
-
}
|
|
7
|
-
export interface GlobalMarkdownDiscoveryResult {
|
|
8
|
-
sources: ArtifactSourceCandidate[];
|
|
9
|
-
removed: string[];
|
|
10
|
-
unavailable?: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function getKnowledgeRoot(agentDir?: string): string;
|
|
13
|
-
/** Discover regular `*.md` files beneath a Knowledge root without following directory or file symlinks. */
|
|
14
|
-
export declare function discoverGlobalMarkdownSources(knowledgeRoot?: string): ArtifactSourceCandidate[];
|
|
15
|
-
/** Source-owner discovery; retained paths let restart/status re-observe removals until publication. */
|
|
16
|
-
export declare class GlobalMarkdownDiscovery {
|
|
17
|
-
#private;
|
|
18
|
-
readonly knowledgeRoot: string;
|
|
19
|
-
constructor(knowledgeRoot?: string);
|
|
20
|
-
refresh(retainedPaths?: Iterable<string>): GlobalMarkdownDiscoveryResult;
|
|
21
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
|
-
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { hashArtifactSource } from "./artifact.js";
|
|
5
|
-
export function getKnowledgeRoot(agentDir = getAgentDir()) {
|
|
6
|
-
return resolve(agentDir, "knowledge");
|
|
7
|
-
}
|
|
8
|
-
function errorCode(error) {
|
|
9
|
-
return error instanceof Error && "code" in error
|
|
10
|
-
? error.code
|
|
11
|
-
: undefined;
|
|
12
|
-
}
|
|
13
|
-
function isMissing(error) {
|
|
14
|
-
return errorCode(error) === "ENOENT";
|
|
15
|
-
}
|
|
16
|
-
function isInside(root, path) {
|
|
17
|
-
const child = relative(root, path);
|
|
18
|
-
return child === "" || (!isAbsolute(child) && child !== ".." && !child.startsWith(`..${sep}`));
|
|
19
|
-
}
|
|
20
|
-
function canonicalExistingPath(path) {
|
|
21
|
-
try {
|
|
22
|
-
return realpathSync.native(path);
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
if (isMissing(error))
|
|
26
|
-
return undefined;
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function readRegularFile(path) {
|
|
31
|
-
let descriptor;
|
|
32
|
-
try {
|
|
33
|
-
descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
34
|
-
if (!fstatSync(descriptor).isFile())
|
|
35
|
-
return undefined;
|
|
36
|
-
return readFileSync(descriptor);
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
if (isMissing(error) || errorCode(error) === "ELOOP")
|
|
40
|
-
return undefined;
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
finally {
|
|
44
|
-
if (descriptor !== undefined)
|
|
45
|
-
closeSync(descriptor);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
/** Discover regular `*.md` files beneath a Knowledge root without following directory or file symlinks. */
|
|
49
|
-
export function discoverGlobalMarkdownSources(knowledgeRoot = getKnowledgeRoot()) {
|
|
50
|
-
const configuredRoot = resolve(knowledgeRoot);
|
|
51
|
-
const root = canonicalExistingPath(configuredRoot);
|
|
52
|
-
if (root === undefined)
|
|
53
|
-
return [];
|
|
54
|
-
if (!statSync(root).isDirectory())
|
|
55
|
-
throw new Error(`Knowledge root is not a directory: ${configuredRoot}`);
|
|
56
|
-
const sources = [];
|
|
57
|
-
const visit = (directory) => {
|
|
58
|
-
const entries = readdirSync(directory, { withFileTypes: true })
|
|
59
|
-
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
60
|
-
for (const entry of entries) {
|
|
61
|
-
const candidate = join(directory, entry.name);
|
|
62
|
-
let metadata;
|
|
63
|
-
try {
|
|
64
|
-
metadata = lstatSync(candidate);
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
if (isMissing(error))
|
|
68
|
-
continue;
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
if (metadata.isSymbolicLink())
|
|
72
|
-
continue;
|
|
73
|
-
const canonical = canonicalExistingPath(candidate);
|
|
74
|
-
if (canonical === undefined || !isInside(root, canonical))
|
|
75
|
-
continue;
|
|
76
|
-
if (metadata.isDirectory()) {
|
|
77
|
-
visit(canonical);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (!metadata.isFile() || !entry.name.endsWith(".md"))
|
|
81
|
-
continue;
|
|
82
|
-
const body = readRegularFile(canonical);
|
|
83
|
-
if (body === undefined)
|
|
84
|
-
continue;
|
|
85
|
-
sources.push({ path: canonical, hash: hashArtifactSource(body), bytes: body.byteLength });
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
visit(root);
|
|
89
|
-
return sources.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
90
|
-
}
|
|
91
|
-
/** Prove absence inside the current source root without following excluded symlink components. */
|
|
92
|
-
function missingOwnedMarkdown(root, path) {
|
|
93
|
-
if (!isAbsolute(path) || path !== resolve(path) || path === root || !isInside(root, path) || !path.endsWith(".md"))
|
|
94
|
-
return false;
|
|
95
|
-
let candidate = root;
|
|
96
|
-
for (const segment of relative(root, path).split(sep)) {
|
|
97
|
-
candidate = join(candidate, segment);
|
|
98
|
-
const metadata = lstatSync(candidate, { throwIfNoEntry: false });
|
|
99
|
-
if (metadata === undefined)
|
|
100
|
-
return true;
|
|
101
|
-
if (metadata.isSymbolicLink() || !metadata.isDirectory())
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
/** Source-owner discovery; retained paths let restart/status re-observe removals until publication. */
|
|
107
|
-
export class GlobalMarkdownDiscovery {
|
|
108
|
-
knowledgeRoot;
|
|
109
|
-
#knownPaths = new Set();
|
|
110
|
-
constructor(knowledgeRoot = getKnowledgeRoot()) {
|
|
111
|
-
this.knowledgeRoot = resolve(knowledgeRoot);
|
|
112
|
-
}
|
|
113
|
-
refresh(retainedPaths = this.#knownPaths) {
|
|
114
|
-
const root = canonicalExistingPath(this.knowledgeRoot);
|
|
115
|
-
if (root === undefined)
|
|
116
|
-
return { sources: [], removed: [], unavailable: `Knowledge root is unavailable: ${this.knowledgeRoot}` };
|
|
117
|
-
const sources = discoverGlobalMarkdownSources(root);
|
|
118
|
-
const currentPaths = new Set(sources.map((source) => source.path));
|
|
119
|
-
const removed = [...new Set(retainedPaths)]
|
|
120
|
-
.filter((path) => !currentPaths.has(path) && missingOwnedMarkdown(root, path))
|
|
121
|
-
.sort();
|
|
122
|
-
this.#knownPaths = currentPaths;
|
|
123
|
-
return { sources, removed };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { type ArtifactProvenance, type ArtifactSourceIdentity } from "./artifact.ts";
|
|
2
|
-
import type { ArtifactSourceCandidate } from "./discovery.ts";
|
|
3
|
-
export declare const DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS = 1;
|
|
4
|
-
export declare const DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES: number;
|
|
5
|
-
export declare const DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS: number;
|
|
6
|
-
export interface ArtifactMaintenanceOptions {
|
|
7
|
-
/** Stable cycle time supplied by the caller. */
|
|
8
|
-
now?: Date | number | string;
|
|
9
|
-
/** A source must be at least this old. Missing/unparseable timestamps rank as oldest. */
|
|
10
|
-
minimumAgeMs?: number;
|
|
11
|
-
/** Strict source-read count ceiling for this cycle. */
|
|
12
|
-
maxReads?: number;
|
|
13
|
-
/** Strict source-byte ceiling; bytes conservatively upper-bound source tokenizer input. */
|
|
14
|
-
maxSourceBytes?: number;
|
|
15
|
-
}
|
|
16
|
-
export interface ArtifactMaintenanceRequest extends ArtifactSourceIdentity {
|
|
17
|
-
reason: "maintenance";
|
|
18
|
-
sourceBytes: number;
|
|
19
|
-
}
|
|
20
|
-
export interface ArtifactMaintenancePlan {
|
|
21
|
-
requiresCompilation: ArtifactMaintenanceRequest[];
|
|
22
|
-
deferred: ArtifactMaintenanceRequest[];
|
|
23
|
-
budget: {
|
|
24
|
-
maxReads: number;
|
|
25
|
-
maxSourceBytes: number;
|
|
26
|
-
usedReads: number;
|
|
27
|
-
usedSourceBytes: number;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Select a bounded, oldest-first maintenance cohort from otherwise fresh artifacts.
|
|
32
|
-
*
|
|
33
|
-
* This planner is opt-in and side-effect free. Correctness invalidations remain the
|
|
34
|
-
* responsibility of planArtifactInvalidation; they are never displaced by maintenance.
|
|
35
|
-
*/
|
|
36
|
-
export declare function planArtifactMaintenance(sources: readonly ArtifactSourceCandidate[], registry: Readonly<Record<string, unknown>>, compiler: string, options?: ArtifactMaintenanceOptions, provenance?: Readonly<Record<string, ArtifactProvenance>>): ArtifactMaintenancePlan;
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { classifyArtifactFreshness, } from "./artifact.js";
|
|
2
|
-
import { isObject } from "./json.js";
|
|
3
|
-
export const DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS = 1;
|
|
4
|
-
export const DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES = 16 * 1024;
|
|
5
|
-
export const DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS = 30 * 24 * 60 * 60 * 1_000;
|
|
6
|
-
function nonNegativeSafeInteger(value, name) {
|
|
7
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
8
|
-
throw new Error(`${name} must be a non-negative safe integer`);
|
|
9
|
-
return value;
|
|
10
|
-
}
|
|
11
|
-
function cycleTime(value) {
|
|
12
|
-
const timestamp = value === undefined
|
|
13
|
-
? Date.now()
|
|
14
|
-
: value instanceof Date
|
|
15
|
-
? value.getTime()
|
|
16
|
-
: typeof value === "number"
|
|
17
|
-
? value
|
|
18
|
-
: Date.parse(value);
|
|
19
|
-
if (!Number.isFinite(timestamp))
|
|
20
|
-
throw new Error("Artifact maintenance cycle time must be valid");
|
|
21
|
-
return timestamp;
|
|
22
|
-
}
|
|
23
|
-
function compiledTime(entry, legacy) {
|
|
24
|
-
const timestamp = entry?.malformed === true ? undefined
|
|
25
|
-
: entry !== undefined && Object.hasOwn(entry, "compiledAt") ? entry.compiledAt
|
|
26
|
-
: legacy;
|
|
27
|
-
if (typeof timestamp !== "string")
|
|
28
|
-
return Number.NEGATIVE_INFINITY;
|
|
29
|
-
const parsed = Date.parse(timestamp);
|
|
30
|
-
return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
|
|
31
|
-
}
|
|
32
|
-
function request(source) {
|
|
33
|
-
return {
|
|
34
|
-
path: source.path,
|
|
35
|
-
hash: source.hash,
|
|
36
|
-
reason: "maintenance",
|
|
37
|
-
sourceBytes: source.bytes,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Select a bounded, oldest-first maintenance cohort from otherwise fresh artifacts.
|
|
42
|
-
*
|
|
43
|
-
* This planner is opt-in and side-effect free. Correctness invalidations remain the
|
|
44
|
-
* responsibility of planArtifactInvalidation; they are never displaced by maintenance.
|
|
45
|
-
*/
|
|
46
|
-
export function planArtifactMaintenance(sources, registry, compiler, options = {}, provenance = {}) {
|
|
47
|
-
if (!isObject(registry))
|
|
48
|
-
throw new Error("Artifacts must be a path-keyed JSON object");
|
|
49
|
-
const now = cycleTime(options.now);
|
|
50
|
-
const minimumAgeMs = nonNegativeSafeInteger(options.minimumAgeMs ?? DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS, "Artifact maintenance minimum age");
|
|
51
|
-
const maxReads = nonNegativeSafeInteger(options.maxReads ?? DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS, "Artifact maintenance read budget");
|
|
52
|
-
const maxSourceBytes = nonNegativeSafeInteger(options.maxSourceBytes ?? DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES, "Artifact maintenance source-byte budget");
|
|
53
|
-
const seen = new Set();
|
|
54
|
-
const eligible = [];
|
|
55
|
-
for (const source of sources) {
|
|
56
|
-
if (seen.has(source.path))
|
|
57
|
-
throw new Error(`Duplicate artifact source path: ${source.path}`);
|
|
58
|
-
seen.add(source.path);
|
|
59
|
-
nonNegativeSafeInteger(source.bytes, `Artifact source bytes at ${source.path}`);
|
|
60
|
-
const metadata = Object.hasOwn(registry, source.path) ? registry[source.path] : undefined;
|
|
61
|
-
const entry = Object.hasOwn(provenance, source.path) ? provenance[source.path] : undefined;
|
|
62
|
-
const freshness = classifyArtifactFreshness(source, metadata, compiler, false, entry);
|
|
63
|
-
if (freshness.kind !== "fresh")
|
|
64
|
-
continue;
|
|
65
|
-
const compiledAt = compiledTime(entry, isObject(metadata) ? metadata.compiled_at : undefined);
|
|
66
|
-
if (compiledAt !== Number.NEGATIVE_INFINITY && now - compiledAt < minimumAgeMs)
|
|
67
|
-
continue;
|
|
68
|
-
eligible.push({ source, compiledAt });
|
|
69
|
-
}
|
|
70
|
-
eligible.sort((left, right) => {
|
|
71
|
-
if (left.compiledAt !== right.compiledAt)
|
|
72
|
-
return left.compiledAt - right.compiledAt;
|
|
73
|
-
return left.source.path < right.source.path ? -1 : left.source.path > right.source.path ? 1 : 0;
|
|
74
|
-
});
|
|
75
|
-
const requiresCompilation = [];
|
|
76
|
-
const deferred = [];
|
|
77
|
-
let usedSourceBytes = 0;
|
|
78
|
-
for (const candidate of eligible) {
|
|
79
|
-
const next = request(candidate.source);
|
|
80
|
-
if (requiresCompilation.length >= maxReads
|
|
81
|
-
|| candidate.source.bytes > maxSourceBytes - usedSourceBytes) {
|
|
82
|
-
deferred.push(next);
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
requiresCompilation.push(next);
|
|
86
|
-
usedSourceBytes += candidate.source.bytes;
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
requiresCompilation,
|
|
90
|
-
deferred,
|
|
91
|
-
budget: {
|
|
92
|
-
maxReads,
|
|
93
|
-
maxSourceBytes,
|
|
94
|
-
usedReads: requiresCompilation.length,
|
|
95
|
-
usedSourceBytes,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { type DurableFileBase, type OwnedFileUpdate } from "./durable.ts";
|
|
2
|
-
import { type StateScope } from "./state.ts";
|
|
3
|
-
export interface LegacyStorageMigration {
|
|
4
|
-
bases: DurableFileBase[];
|
|
5
|
-
updates: OwnedFileUpdate[];
|
|
6
|
-
scopes: StateScope[];
|
|
7
|
-
}
|
|
8
|
-
/** Read-only activation eligibility, before global migration or any repository publication. */
|
|
9
|
-
export declare function hasCwdMaterialization(cwd: string, repositoryRoot: string): boolean;
|
|
10
|
-
/** Detect predecessor snapshots, temporal envelopes, or pre-intents semantic states without mutating the store. */
|
|
11
|
-
export declare function hasLegacyStateSources(cwd: string, sessionId: string, repositoryRoot: string, sessionKey?: string): boolean;
|
|
12
|
-
/** Plan from current scope snapshots only; old explanatory journals are never replay input. */
|
|
13
|
-
export declare function planLegacyStorageMigration(cwd: string, sessionId: string, repositoryRoot: string, _origin?: string, sessionKey?: string): LegacyStorageMigration;
|
|
@@ -1,167 +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 { captureOwnedFileBases, cwdScopePaths, parseScopeStream, serializeScopeMetadata, serializeScopeStream, sessionScopeKey, sessionScopePaths, } from "./durable.js";
|
|
5
|
-
import { parseSessionRuntime, serializeSessionRuntime } from "./snapshot.js";
|
|
6
|
-
import { migratePreIntentState } from "./state.js";
|
|
7
|
-
/** Discover every owner-proven CWD and session cohort beneath the configured store. */
|
|
8
|
-
function migrationDirectories(cwd, sessionId, root, sessionKey) {
|
|
9
|
-
const selectedCwd = cwdScopePaths(cwd, root).directory;
|
|
10
|
-
const selectedSession = sessionScopePaths(cwd, sessionId, root, sessionKey).directory;
|
|
11
|
-
const directories = [{ directory: root, scope: "global" }];
|
|
12
|
-
const cwdOwners = new Map([[selectedCwd, resolve(cwd)]]);
|
|
13
|
-
if (lstatSync(root, { throwIfNoEntry: false }) !== undefined) {
|
|
14
|
-
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
15
|
-
if (!entry.isDirectory())
|
|
16
|
-
continue;
|
|
17
|
-
const directory = join(root, entry.name);
|
|
18
|
-
let checkpoint;
|
|
19
|
-
let meta;
|
|
20
|
-
try {
|
|
21
|
-
checkpoint = JSON.parse(readFileSync(join(directory, "checkpoint.json"), "utf8"));
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
try {
|
|
27
|
-
meta = JSON.parse(readFileSync(join(directory, "meta.json"), "utf8"));
|
|
28
|
-
}
|
|
29
|
-
catch { /* predecessor metadata may be absent */ }
|
|
30
|
-
const owner = checkpoint && typeof checkpoint === "object" && !Array.isArray(checkpoint) && checkpoint.owner
|
|
31
|
-
? checkpoint.owner
|
|
32
|
-
: meta && typeof meta === "object" && !Array.isArray(meta) ? meta.owner : undefined;
|
|
33
|
-
if (owner && typeof owner.cwd === "string" && resolve(owner.cwd) === owner.cwd)
|
|
34
|
-
cwdOwners.set(directory, owner.cwd);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
for (const [cwdDirectory, cwdOwner] of cwdOwners) {
|
|
38
|
-
directories.push({ directory: cwdDirectory, scope: "cwd", cwdIdentity: cwdOwner });
|
|
39
|
-
if (lstatSync(cwdDirectory, { throwIfNoEntry: false }) === undefined)
|
|
40
|
-
continue;
|
|
41
|
-
for (const entry of readdirSync(cwdDirectory, { withFileTypes: true })) {
|
|
42
|
-
if (!entry.isDirectory())
|
|
43
|
-
continue;
|
|
44
|
-
try {
|
|
45
|
-
sessionScopeKey(entry.name);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
const directory = join(cwdDirectory, entry.name);
|
|
51
|
-
let meta;
|
|
52
|
-
let runtime;
|
|
53
|
-
try {
|
|
54
|
-
meta = JSON.parse(readFileSync(join(directory, "meta.json"), "utf8"));
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
runtime = JSON.parse(readFileSync(join(directory, "runtime.json"), "utf8"));
|
|
61
|
-
}
|
|
62
|
-
catch { /* predecessor sessions keep identity in meta.json */ }
|
|
63
|
-
const identity = meta && typeof meta === "object" && !Array.isArray(meta) && meta.identity !== undefined
|
|
64
|
-
? meta.identity
|
|
65
|
-
: runtime && typeof runtime === "object" && !Array.isArray(runtime) ? runtime.identity : undefined;
|
|
66
|
-
if (!identity || typeof identity !== "object" || Array.isArray(identity))
|
|
67
|
-
continue;
|
|
68
|
-
const owner = identity;
|
|
69
|
-
if (owner.cwd === cwdOwner && typeof owner.sessionId === "string" && owner.sessionId.length > 0) {
|
|
70
|
-
directories.push({ directory, scope: "session", cwdIdentity: cwdOwner, sessionId: owner.sessionId });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (!directories.some(({ directory }) => directory === selectedSession)) {
|
|
75
|
-
directories.push({ directory: selectedSession, scope: "session", cwdIdentity: resolve(cwd), sessionId });
|
|
76
|
-
}
|
|
77
|
-
return directories;
|
|
78
|
-
}
|
|
79
|
-
/** Read-only activation eligibility, before global migration or any repository publication. */
|
|
80
|
-
export function hasCwdMaterialization(cwd, repositoryRoot) {
|
|
81
|
-
const root = resolve(repositoryRoot);
|
|
82
|
-
const directory = cwdScopePaths(cwd, root).directory;
|
|
83
|
-
const [checkpoint, patches, meta] = captureOwnedFileBases([
|
|
84
|
-
join(directory, "checkpoint.json"), join(directory, "patches.jsonl"), join(directory, "meta.json"),
|
|
85
|
-
], root);
|
|
86
|
-
if (checkpoint.content !== undefined)
|
|
87
|
-
return parseScopeStream(checkpoint.content, patches.content, "cwd", cwd, meta.content) !== undefined;
|
|
88
|
-
if (patches.content !== undefined)
|
|
89
|
-
throw new Error(`State Flow tail has no provable checkpoint: ${directory}`);
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
function checkpointNeedsIntentMigration(source) {
|
|
93
|
-
const checkpoint = JSON.parse(source);
|
|
94
|
-
if (migratePreIntentState(checkpoint) !== undefined)
|
|
95
|
-
return true;
|
|
96
|
-
return checkpoint !== null && typeof checkpoint === "object" && !Array.isArray(checkpoint)
|
|
97
|
-
&& migratePreIntentState(checkpoint.state) !== undefined;
|
|
98
|
-
}
|
|
99
|
-
/** Detect predecessor snapshots, temporal envelopes, or pre-intents semantic states without mutating the store. */
|
|
100
|
-
export function hasLegacyStateSources(cwd, sessionId, repositoryRoot, sessionKey = sessionId) {
|
|
101
|
-
const root = resolve(repositoryRoot);
|
|
102
|
-
return migrationDirectories(cwd, sessionId, root, sessionKey).some(({ directory, scope }) => {
|
|
103
|
-
const checkpointPath = join(directory, "checkpoint.json");
|
|
104
|
-
if (lstatSync(checkpointPath, { throwIfNoEntry: false }) === undefined)
|
|
105
|
-
return false;
|
|
106
|
-
try {
|
|
107
|
-
if (checkpointNeedsIntentMigration(readFileSync(checkpointPath, "utf8")))
|
|
108
|
-
return true;
|
|
109
|
-
const meta = JSON.parse(readFileSync(join(directory, "meta.json"), "utf8"));
|
|
110
|
-
if (meta === null || typeof meta !== "object" || !("temporal" in meta))
|
|
111
|
-
return true;
|
|
112
|
-
return scope === "session"
|
|
113
|
-
&& lstatSync(join(directory, "runtime.json"), { throwIfNoEntry: false }) === undefined
|
|
114
|
-
&& ("identity" in meta || "lineage" in meta);
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
/** Plan from current scope snapshots only; old explanatory journals are never replay input. */
|
|
122
|
-
export function planLegacyStorageMigration(cwd, sessionId, repositoryRoot, _origin, sessionKey = sessionId) {
|
|
123
|
-
const root = resolve(repositoryRoot);
|
|
124
|
-
const directories = migrationDirectories(cwd, sessionId, root, sessionKey);
|
|
125
|
-
const paths = directories.flatMap(({ directory, scope }) => [
|
|
126
|
-
join(directory, "checkpoint.json"), join(directory, "patches.jsonl"), join(directory, "meta.json"),
|
|
127
|
-
...(scope === "session" ? [join(directory, "config.json"), join(directory, "runtime.json")] : []),
|
|
128
|
-
]);
|
|
129
|
-
const bases = captureOwnedFileBases(paths, root);
|
|
130
|
-
const byPath = new Map(bases.map((base) => [base.path, base]));
|
|
131
|
-
const updates = [];
|
|
132
|
-
const scopes = [];
|
|
133
|
-
for (const { scope, directory, cwdIdentity, sessionId: ownedSessionId } of directories) {
|
|
134
|
-
const checkpoint = byPath.get(join(directory, "checkpoint.json"));
|
|
135
|
-
const patches = byPath.get(join(directory, "patches.jsonl"));
|
|
136
|
-
const meta = byPath.get(join(directory, "meta.json"));
|
|
137
|
-
if (checkpoint.content !== undefined) {
|
|
138
|
-
const stream = parseScopeStream(checkpoint.content, patches.content, scope, scope === "cwd" ? cwdIdentity : undefined, meta.content);
|
|
139
|
-
const source = serializeScopeStream(stream, scope, scope === "cwd" ? cwdIdentity : undefined);
|
|
140
|
-
const metadata = serializeScopeMetadata(undefined, stream, scope, scope === "cwd" ? cwdIdentity : undefined, meta.content);
|
|
141
|
-
const cohortUpdates = [
|
|
142
|
-
...(checkpoint.content === source.checkpoint ? [] : [{ path: checkpoint.path, content: source.checkpoint }]),
|
|
143
|
-
...(patches.content === source.patches ? [] : [{ path: patches.path, content: source.patches }]),
|
|
144
|
-
...(meta.content === metadata ? [] : [{ path: meta.path, content: metadata }]),
|
|
145
|
-
];
|
|
146
|
-
if (scope === "session") {
|
|
147
|
-
const config = byPath.get(join(directory, "config.json"));
|
|
148
|
-
const runtimeFile = byPath.get(join(directory, "runtime.json"));
|
|
149
|
-
const runtime = parseSessionRuntime(config.content, runtimeFile.content, cwdIdentity, ownedSessionId, meta.content);
|
|
150
|
-
if (runtime !== undefined) {
|
|
151
|
-
const serialized = serializeSessionRuntime(runtime, cwdIdentity, ownedSessionId);
|
|
152
|
-
if (runtimeFile.content !== serialized.runtime)
|
|
153
|
-
cohortUpdates.push({ path: runtimeFile.path, content: serialized.runtime });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
if (cohortUpdates.length > 0) {
|
|
157
|
-
if (!scopes.includes(scope))
|
|
158
|
-
scopes.push(scope);
|
|
159
|
-
updates.push(...cohortUpdates);
|
|
160
|
-
}
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
if (patches.content !== undefined)
|
|
164
|
-
throw new Error(`State Flow tail has no provable checkpoint: ${directory}`);
|
|
165
|
-
}
|
|
166
|
-
return { bases, updates, scopes };
|
|
167
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
export type RemotePublicationMode = "off" | "turn-end" | "transition";
|
|
2
|
-
export interface RemotePublicationDestination {
|
|
3
|
-
gitCommonDir: string;
|
|
4
|
-
remote: string;
|
|
5
|
-
ref: string;
|
|
6
|
-
}
|
|
7
|
-
export interface RemotePublicationPolicy {
|
|
8
|
-
mode: RemotePublicationMode;
|
|
9
|
-
migratedLegacyDefault: boolean;
|
|
10
|
-
}
|
|
11
|
-
export interface RemotePublicationPolicyDocument {
|
|
12
|
-
version: 1;
|
|
13
|
-
mode: RemotePublicationMode;
|
|
14
|
-
}
|
|
15
|
-
/** Missing policy on an existing runtime preserves 0.4 synchronous behavior. */
|
|
16
|
-
export declare function resolveRemotePublicationPolicy(value: unknown, options: {
|
|
17
|
-
legacyRuntime: boolean;
|
|
18
|
-
}): RemotePublicationPolicy;
|
|
19
|
-
export declare function parseRemotePublicationPolicyDocument(value: unknown, options: {
|
|
20
|
-
legacyRuntime: boolean;
|
|
21
|
-
}): RemotePublicationPolicy;
|
|
22
|
-
export declare function serializeRemotePublicationPolicyDocument(policy: RemotePublicationPolicy): RemotePublicationPolicyDocument;
|
|
23
|
-
export declare function remotePublicationDestinationKey(destination: RemotePublicationDestination): string;
|
|
24
|
-
export type PublicationQueueStatus = "pending" | "pushing" | "failed";
|
|
25
|
-
export interface PublicationQueueState {
|
|
26
|
-
version: 1;
|
|
27
|
-
destination: RemotePublicationDestination;
|
|
28
|
-
target: string;
|
|
29
|
-
confirmed?: string;
|
|
30
|
-
status: PublicationQueueStatus;
|
|
31
|
-
attempt: number;
|
|
32
|
-
error?: string;
|
|
33
|
-
}
|
|
34
|
-
export type CommitAncestor = (ancestor: string, descendant: string) => boolean;
|
|
35
|
-
export declare function createPublicationQueue(destination: RemotePublicationDestination, target: string): PublicationQueueState;
|
|
36
|
-
export interface PublicationCoalesceObserver {
|
|
37
|
-
/** Called with the retired record when a journal lineage rewrite orphans its target. */
|
|
38
|
-
onDivergedLineage?: (previous: PublicationQueueState) => void;
|
|
39
|
-
}
|
|
40
|
-
export declare function coalescePublicationTarget(state: PublicationQueueState, destination: RemotePublicationDestination, target: string, isAncestor: CommitAncestor, observer?: PublicationCoalesceObserver): PublicationQueueState;
|
|
41
|
-
export declare function parsePublicationQueue(content: string): PublicationQueueState;
|
|
42
|
-
export declare function serializePublicationQueue(state: PublicationQueueState): string;
|
|
43
|
-
export declare function beginPublicationAttempt(state: PublicationQueueState): PublicationQueueState;
|
|
44
|
-
export declare function failPublicationAttempt(state: PublicationQueueState, error: string): PublicationQueueState;
|
|
45
|
-
export declare function confirmPublicationTarget(state: PublicationQueueState, pushed: string, isAncestor: CommitAncestor): PublicationQueueState | undefined;
|
|
46
|
-
/** A recovered pushing state is unconfirmed and safely retryable after restart. */
|
|
47
|
-
export declare function recoverPublicationQueue(state: PublicationQueueState): PublicationQueueState;
|
|
48
|
-
export declare function validatePublicationQueue(value: unknown): asserts value is PublicationQueueState;
|
|
49
|
-
export declare function publicationQueuePath(destination: RemotePublicationDestination): string;
|
|
50
|
-
export interface PublicationWorkerLease {
|
|
51
|
-
path: string;
|
|
52
|
-
token: string;
|
|
53
|
-
release(): void;
|
|
54
|
-
}
|
|
55
|
-
export declare function acquirePublicationWorkerLease(queuePath: string): PublicationWorkerLease | undefined;
|
|
56
|
-
export declare function loadPublicationQueue(path: string): PublicationQueueState | undefined;
|
|
57
|
-
export declare function removePublicationQueue(path: string, expected: PublicationQueueState): void;
|
|
58
|
-
export interface PublicationQueueReceipt {
|
|
59
|
-
previous?: PublicationQueueState;
|
|
60
|
-
current: PublicationQueueState;
|
|
61
|
-
}
|
|
62
|
-
export declare function savePublicationQueue(path: string, state: PublicationQueueState, expected?: PublicationQueueState): PublicationQueueReceipt;
|
|
63
|
-
export type PublicationPush = (state: Readonly<PublicationQueueState>) => Promise<void>;
|
|
64
|
-
export interface PublicationWorkerResult {
|
|
65
|
-
attempted: PublicationQueueState;
|
|
66
|
-
next?: PublicationQueueState;
|
|
67
|
-
}
|
|
68
|
-
/** Execute one immutable target attempt; persistence/CAS remains the caller's responsibility. */
|
|
69
|
-
export declare function runPublicationWorker(state: PublicationQueueState, push: PublicationPush, current: () => PublicationQueueState, isAncestor: CommitAncestor): Promise<PublicationWorkerResult>;
|
|
70
|
-
export interface PublicationWorkerControllerPorts {
|
|
71
|
-
resolveDestination(): RemotePublicationDestination | undefined;
|
|
72
|
-
isAncestor(ancestor: string, descendant: string): boolean;
|
|
73
|
-
push(destination: RemotePublicationDestination, target: string, signal: AbortSignal): Promise<void>;
|
|
74
|
-
onDiverged(previous: PublicationQueueState, target: string): void;
|
|
75
|
-
}
|
|
76
|
-
/** Own durable queue coalescing, worker leases, retries, generation fencing, and bounded shutdown. */
|
|
77
|
-
export declare class PublicationWorkerController {
|
|
78
|
-
private readonly active;
|
|
79
|
-
private readonly ports;
|
|
80
|
-
private stopped;
|
|
81
|
-
private shutdownPromise;
|
|
82
|
-
constructor(ports: PublicationWorkerControllerPorts);
|
|
83
|
-
enqueue(target: string): void;
|
|
84
|
-
launch(): void;
|
|
85
|
-
shutdown(waitMs: number): Promise<boolean>;
|
|
86
|
-
}
|