@llblab/pi-kit 0.15.0 → 0.17.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 +12 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +12 -12
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +2 -13
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +31 -0
- package/node_modules/@llblab/pi-state-flow/README.md +11 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +5 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +17 -16
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +24 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +2 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +15 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +4 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +7 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +124 -274
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +20 -23
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +8 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +29 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +18 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +44 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +55 -21
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +7 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +65 -17
- package/node_modules/@llblab/pi-state-flow/dist/lib/publication.d.ts +17 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/publication.js +102 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +43 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +268 -10
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +34 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +17 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +38 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +20 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +11 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +14 -4
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +55 -20
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +9 -6
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +10 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +8 -3
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +79 -0
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +23 -121
- package/node_modules/@llblab/pi-state-flow/docs/README.md +1 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +27 -20
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +3 -2
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +513 -0
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +13 -10
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +18 -15
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +14 -5
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +5 -0
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +119 -262
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +23 -22
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +8 -4
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +31 -3
- package/node_modules/@llblab/pi-state-flow/lib/logging.ts +53 -1
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +51 -20
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +61 -17
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +96 -0
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +261 -9
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +32 -4
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +45 -0
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +22 -25
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +22 -6
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +46 -18
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +20 -6
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +8 -3
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +79 -0
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +23 -121
- package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +7 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +134 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +297 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +11 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +68 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +17 -13
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +55 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +23 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +18 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +152 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +5 -3
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +4 -3
- package/node_modules/@llblab/pi-telegram/docs/sections.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +2 -1
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +3 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +466 -21
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +15 -0
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +77 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +25 -10
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +3 -0
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +84 -17
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +30 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +199 -6
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +5 -2
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
|
@@ -333,3 +333,105 @@ export async function runPublicationWorker(state, push, current, isAncestor) {
|
|
|
333
333
|
};
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
|
+
/** Own durable queue coalescing, worker leases, retries, generation fencing, and bounded shutdown. */
|
|
337
|
+
export class PublicationWorkerController {
|
|
338
|
+
active = new Map();
|
|
339
|
+
ports;
|
|
340
|
+
stopped = false;
|
|
341
|
+
shutdownPromise;
|
|
342
|
+
constructor(ports) {
|
|
343
|
+
this.ports = ports;
|
|
344
|
+
}
|
|
345
|
+
enqueue(target) {
|
|
346
|
+
const destination = this.ports.resolveDestination();
|
|
347
|
+
if (!destination)
|
|
348
|
+
return;
|
|
349
|
+
const path = publicationQueuePath(destination);
|
|
350
|
+
const previous = loadPublicationQueue(path);
|
|
351
|
+
const next = previous
|
|
352
|
+
? coalescePublicationTarget(previous, destination, target, this.ports.isAncestor, {
|
|
353
|
+
onDivergedLineage: (dropped) => this.ports.onDiverged(dropped, target),
|
|
354
|
+
})
|
|
355
|
+
: createPublicationQueue(destination, target);
|
|
356
|
+
savePublicationQueue(path, next, previous);
|
|
357
|
+
}
|
|
358
|
+
launch() {
|
|
359
|
+
if (this.stopped)
|
|
360
|
+
return;
|
|
361
|
+
let destination;
|
|
362
|
+
try {
|
|
363
|
+
destination = this.ports.resolveDestination();
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
if (error instanceof Error && /ENOENT/.test(error.message))
|
|
367
|
+
return;
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
if (!destination)
|
|
371
|
+
return;
|
|
372
|
+
const path = publicationQueuePath(destination);
|
|
373
|
+
if (this.active.has(path))
|
|
374
|
+
return;
|
|
375
|
+
let queued;
|
|
376
|
+
let lease;
|
|
377
|
+
try {
|
|
378
|
+
queued = loadPublicationQueue(path);
|
|
379
|
+
if (!queued)
|
|
380
|
+
return;
|
|
381
|
+
lease = acquirePublicationWorkerLease(path);
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (!lease)
|
|
387
|
+
return;
|
|
388
|
+
const controller = new AbortController();
|
|
389
|
+
const done = runPublicationWorker(queued, ({ target }) => this.ports.push(destination, target, controller.signal), () => loadPublicationQueue(path) ?? queued, this.ports.isAncestor).then((result) => {
|
|
390
|
+
if (this.stopped)
|
|
391
|
+
return;
|
|
392
|
+
const current = loadPublicationQueue(path);
|
|
393
|
+
if (!current)
|
|
394
|
+
return;
|
|
395
|
+
if (result.next === undefined) {
|
|
396
|
+
if (current.target === result.attempted.target)
|
|
397
|
+
removePublicationQueue(path, current);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (current.target === result.attempted.target || result.next.target === current.target)
|
|
401
|
+
savePublicationQueue(path, result.next, current);
|
|
402
|
+
}).catch(() => {
|
|
403
|
+
// Queue/CAS truth remains durable; status and a later activation expose retry.
|
|
404
|
+
}).finally(() => {
|
|
405
|
+
this.active.delete(path);
|
|
406
|
+
try {
|
|
407
|
+
lease.release();
|
|
408
|
+
if (!this.stopped && loadPublicationQueue(path)?.status === "pending")
|
|
409
|
+
this.launch();
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
// Failed lease cleanup or malformed persistence stays inert until retry or repair.
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
this.active.set(path, { controller, done });
|
|
416
|
+
}
|
|
417
|
+
shutdown(waitMs) {
|
|
418
|
+
this.stopped = true;
|
|
419
|
+
return this.shutdownPromise ??= (async () => {
|
|
420
|
+
const workers = [...this.active.values()];
|
|
421
|
+
for (const { controller } of workers)
|
|
422
|
+
controller.abort();
|
|
423
|
+
if (workers.length === 0)
|
|
424
|
+
return true;
|
|
425
|
+
let timeout;
|
|
426
|
+
try {
|
|
427
|
+
return await Promise.race([
|
|
428
|
+
Promise.all(workers.map(({ done }) => done)).then(() => true),
|
|
429
|
+
new Promise((resolve) => { timeout = setTimeout(() => resolve(false), waitMs); }),
|
|
430
|
+
]);
|
|
431
|
+
}
|
|
432
|
+
finally {
|
|
433
|
+
clearTimeout(timeout);
|
|
434
|
+
}
|
|
435
|
+
})();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type JsonValue } from "./json.ts";
|
|
1
2
|
import { type MaterializedState, type ScopePatch, type StateScope } from "./state.ts";
|
|
2
3
|
import { type TemporalState, type TransitionBoundary } from "./temporal.ts";
|
|
3
4
|
export type StateReadQuery = {
|
|
@@ -22,6 +23,47 @@ export type StateReadResult = {
|
|
|
22
23
|
response?: string;
|
|
23
24
|
};
|
|
24
25
|
};
|
|
25
|
-
|
|
26
|
+
export type StateReadProjection = "value" | "keys" | "patch";
|
|
27
|
+
type StateReadMeta = {
|
|
28
|
+
type: "object";
|
|
29
|
+
size: number;
|
|
30
|
+
} | {
|
|
31
|
+
type: "array";
|
|
32
|
+
length: number;
|
|
33
|
+
} | {
|
|
34
|
+
type: "string";
|
|
35
|
+
length: number;
|
|
36
|
+
} | {
|
|
37
|
+
type: "number" | "boolean";
|
|
38
|
+
};
|
|
39
|
+
type StateReadKeys = Record<string, string> | [];
|
|
40
|
+
export interface StateReadHint {
|
|
41
|
+
type: "dangling-reference";
|
|
42
|
+
message: string;
|
|
43
|
+
paths: string[];
|
|
44
|
+
}
|
|
45
|
+
export type ProjectedStateRead = {
|
|
46
|
+
value: JsonValue | JsonValue[];
|
|
47
|
+
hint?: StateReadHint[];
|
|
48
|
+
} | {
|
|
49
|
+
meta: StateReadMeta | StateReadMeta[];
|
|
50
|
+
keys: StateReadKeys | StateReadKeys[];
|
|
51
|
+
} | {
|
|
52
|
+
patch: JsonValue | JsonValue[];
|
|
53
|
+
};
|
|
54
|
+
export interface StateReferenceSource {
|
|
55
|
+
scope: StateScope;
|
|
56
|
+
path: string;
|
|
57
|
+
form: "structured" | "text";
|
|
58
|
+
}
|
|
59
|
+
/** Resolve a projection root without repeating the tool name in every path. */
|
|
26
60
|
export declare function parseStateReadPath(path: string): StateReadQuery;
|
|
27
61
|
export declare function readStatePath(view: TemporalState, path: string): StateReadResult;
|
|
62
|
+
/** Reactively locate exact durable sources for one failed state-path resolution. */
|
|
63
|
+
export declare function findStateReferenceSources(view: TemporalState, path: string): {
|
|
64
|
+
sources: StateReferenceSource[];
|
|
65
|
+
truncated: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** Project exact current/historical state paths without exposing temporal metadata. */
|
|
68
|
+
export declare function readProjectedState(view: TemporalState, paths: readonly string[], projection?: StateReadProjection): ProjectedStateRead;
|
|
69
|
+
export {};
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
+
import { isObject, sameJson } from "./json.js";
|
|
1
2
|
import { projectStateForModel } from "./state.js";
|
|
2
3
|
import { readTemporalState } from "./temporal.js";
|
|
3
|
-
const
|
|
4
|
-
|
|
4
|
+
const MAX_REFERENCE_SOURCES = 3;
|
|
5
|
+
const MAX_REFERENCE_SCAN_NODES = 10_000;
|
|
6
|
+
const PATH_PATTERN = /^(effective|global|cwd|session)(?:\[(\d+)\])?(?:\.patches(?:\[(\d+)\])?)?$/;
|
|
7
|
+
/** Resolve a projection root without repeating the tool name in every path. */
|
|
5
8
|
export function parseStateReadPath(path) {
|
|
6
9
|
const match = PATH_PATTERN.exec(path);
|
|
7
10
|
if (!match)
|
|
8
11
|
throw new Error("Invalid State Flow read path");
|
|
9
|
-
const [,
|
|
10
|
-
if (
|
|
11
|
-
throw new Error("State Flow read path cannot index both state and a scope");
|
|
12
|
-
if (path.includes(".patches") && scopeOffset !== undefined)
|
|
12
|
+
const [, root, rootOffset, patchOffset] = match;
|
|
13
|
+
if (path.includes(".patches") && rootOffset !== undefined)
|
|
13
14
|
throw new Error("Index patches after .patches, not after the scope");
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (path.includes(".patches") && root === "effective")
|
|
16
|
+
throw new Error("Patch history requires an explicit scope");
|
|
17
|
+
const offset = Number(patchOffset ?? rootOffset ?? "0");
|
|
16
18
|
if (!Number.isSafeInteger(offset) || offset < 0 || offset > 7)
|
|
17
19
|
throw new Error("State Flow read path index must be an integer from 0 to 7");
|
|
18
20
|
if (patchOffset !== undefined || path.endsWith(".patches")) {
|
|
19
|
-
return { kind: "patch", path, offset, scope:
|
|
21
|
+
return { kind: "patch", path, offset, scope: root };
|
|
20
22
|
}
|
|
21
|
-
return { kind: "state", path, offset, ...(
|
|
23
|
+
return { kind: "state", path, offset, ...(root === "effective" ? {} : { scope: root }) };
|
|
22
24
|
}
|
|
23
25
|
export function readStatePath(view, path) {
|
|
24
26
|
const query = parseStateReadPath(path);
|
|
@@ -33,3 +35,259 @@ export function readStatePath(view, path) {
|
|
|
33
35
|
throw new Error(`Requested ${query.scope} patch predates retained hot history`);
|
|
34
36
|
return { path, boundary: structuredClone(record.transition), patch: structuredClone(record.patch) };
|
|
35
37
|
}
|
|
38
|
+
function parseValuePath(path) {
|
|
39
|
+
const explicitRoot = /^(?:effective|global|cwd|session)(?:\[\d+\])?(?=\.|$)/.exec(path)?.[0];
|
|
40
|
+
const implicitRoot = /^(?:artifacts|contract|working|intents|response|lazy)(?=\.|\[|$)/.exec(path)?.[0];
|
|
41
|
+
if (explicitRoot === undefined && implicitRoot === undefined) {
|
|
42
|
+
throw new Error("State Flow read path requires a semantic path or an effective, global, cwd, or session root");
|
|
43
|
+
}
|
|
44
|
+
const root = explicitRoot ?? "effective";
|
|
45
|
+
const selectors = implicitRoot === undefined ? [] : [{ kind: "key", key: implicitRoot }];
|
|
46
|
+
let rest = path.slice((explicitRoot ?? implicitRoot).length);
|
|
47
|
+
while (rest.length > 0) {
|
|
48
|
+
const key = /^\.([A-Za-z_$][A-Za-z0-9_$-]*)/.exec(rest);
|
|
49
|
+
if (key) {
|
|
50
|
+
selectors.push({ kind: "key", key: key[1] });
|
|
51
|
+
rest = rest.slice(key[0].length);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const range = /^\[(\d+)(?::|\.\.)(\d+)\]/.exec(rest);
|
|
55
|
+
if (range) {
|
|
56
|
+
selectors.push({ kind: "range", start: Number(range[1]), end: Number(range[2]) });
|
|
57
|
+
rest = rest.slice(range[0].length);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const index = /^\[(\d+)\]/.exec(rest);
|
|
61
|
+
if (index) {
|
|
62
|
+
selectors.push({ kind: "index", index: Number(index[1]) });
|
|
63
|
+
rest = rest.slice(index[0].length);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
throw new Error("Invalid State Flow read path selector");
|
|
67
|
+
}
|
|
68
|
+
return { root, selectors };
|
|
69
|
+
}
|
|
70
|
+
function selectValue(root, selectors, path) {
|
|
71
|
+
let value = root;
|
|
72
|
+
for (const selector of selectors) {
|
|
73
|
+
if (selector.kind === "key") {
|
|
74
|
+
if (!isObject(value) || !Object.hasOwn(value, selector.key))
|
|
75
|
+
throw new Error(`State Flow read path does not exist: ${path}`);
|
|
76
|
+
value = value[selector.key];
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!Array.isArray(value))
|
|
80
|
+
throw new Error(`State Flow array selector requires an array: ${path}`);
|
|
81
|
+
if (selector.kind === "index") {
|
|
82
|
+
if (selector.index >= value.length)
|
|
83
|
+
throw new Error(`Index ${selector.index} is outside ${path} with length ${value.length}`);
|
|
84
|
+
value = value[selector.index];
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (selector.start > selector.end || selector.end > value.length)
|
|
88
|
+
throw new Error(`Range [${selector.start}:${selector.end}] is outside ${path} with length ${value.length}`);
|
|
89
|
+
value = value.slice(selector.start, selector.end);
|
|
90
|
+
}
|
|
91
|
+
return structuredClone(value);
|
|
92
|
+
}
|
|
93
|
+
function valueKind(value) {
|
|
94
|
+
if (Array.isArray(value))
|
|
95
|
+
return "array";
|
|
96
|
+
if (isObject(value))
|
|
97
|
+
return "object";
|
|
98
|
+
return typeof value;
|
|
99
|
+
}
|
|
100
|
+
function referenceCandidates(path) {
|
|
101
|
+
if (/^(?:artifacts|contract|working|intents|response|lazy)(?=\.|\[|$)/.test(path))
|
|
102
|
+
return [path, `effective.${path}`];
|
|
103
|
+
return [path];
|
|
104
|
+
}
|
|
105
|
+
function inlineReferencePattern(path) {
|
|
106
|
+
const escaped = path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
107
|
+
return new RegExp(`(?:^|[^A-Za-z0-9_$.[\\]:-])\\$${escaped}(?=$|[^A-Za-z0-9_$.[\\]:-])`, "u");
|
|
108
|
+
}
|
|
109
|
+
/** Reactively locate exact durable sources for one failed state-path resolution. */
|
|
110
|
+
export function findStateReferenceSources(view, path) {
|
|
111
|
+
const candidates = referenceCandidates(path);
|
|
112
|
+
const patterns = candidates.map(inlineReferencePattern);
|
|
113
|
+
const sources = [];
|
|
114
|
+
let visited = 0;
|
|
115
|
+
let truncated = false;
|
|
116
|
+
const visit = (value, owner, ownerPath) => {
|
|
117
|
+
if (sources.length >= MAX_REFERENCE_SOURCES || visited >= MAX_REFERENCE_SCAN_NODES) {
|
|
118
|
+
truncated = true;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
visited += 1;
|
|
122
|
+
if (typeof value === "string") {
|
|
123
|
+
if (patterns.some((pattern) => pattern.test(value)))
|
|
124
|
+
sources.push({ scope: owner, path: ownerPath, form: "text" });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
for (let index = 0; index < value.length && !truncated; index++)
|
|
129
|
+
visit(value[index], owner, `${ownerPath}[${index}]`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!isObject(value))
|
|
133
|
+
return;
|
|
134
|
+
if (typeof value.$ref === "string" && candidates.includes(value.$ref)) {
|
|
135
|
+
sources.push({ scope: owner, path: ownerPath, form: "structured" });
|
|
136
|
+
if (sources.length >= MAX_REFERENCE_SOURCES) {
|
|
137
|
+
truncated = true;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const key of Object.keys(value).sort()) {
|
|
142
|
+
if (key === "response" || (key === "$ref" && typeof value[key] === "string"))
|
|
143
|
+
continue;
|
|
144
|
+
visit(value[key], owner, ownerPath ? `${ownerPath}.${key}` : `${owner}.${key}`);
|
|
145
|
+
if (truncated)
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
for (const scope of ["global", "cwd", "session"]) {
|
|
150
|
+
const state = readTemporalState(view, 0, scope);
|
|
151
|
+
for (const plane of ["artifacts", "contract", "working", "intents", "lazy"]) {
|
|
152
|
+
const value = state[plane];
|
|
153
|
+
if (value !== undefined)
|
|
154
|
+
visit(value, scope, `${scope}.${plane}`);
|
|
155
|
+
if (truncated)
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
if (truncated)
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
sources.sort((left, right) => (left.form === right.form ? left.path.localeCompare(right.path) : left.form === "structured" ? -1 : 1));
|
|
162
|
+
return { sources, truncated };
|
|
163
|
+
}
|
|
164
|
+
function missingReferenceHint(view, path) {
|
|
165
|
+
const { sources, truncated } = findStateReferenceSources(view, path);
|
|
166
|
+
if (sources.length === 0)
|
|
167
|
+
return undefined;
|
|
168
|
+
return [{
|
|
169
|
+
type: "dangling-reference",
|
|
170
|
+
message: `Reconcile the verified current values that reference this path${truncated ? "; additional sources may exist beyond the bounded scan" : ""}.`,
|
|
171
|
+
paths: sources.map(({ path: sourcePath }) => sourcePath),
|
|
172
|
+
}];
|
|
173
|
+
}
|
|
174
|
+
function projectValue(value, projection) {
|
|
175
|
+
if (projection === "value")
|
|
176
|
+
return { value: structuredClone(value) };
|
|
177
|
+
if (isObject(value)) {
|
|
178
|
+
return {
|
|
179
|
+
meta: { type: "object", size: Object.keys(value).length },
|
|
180
|
+
keys: Object.fromEntries(Object.entries(value).map(([key, child]) => [key, valueKind(child)])),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(value))
|
|
184
|
+
return { meta: { type: "array", length: value.length }, keys: [] };
|
|
185
|
+
if (typeof value === "string")
|
|
186
|
+
return { meta: { type: "string", length: value.length }, keys: [] };
|
|
187
|
+
if (value === null)
|
|
188
|
+
throw new Error("State Flow semantic state cannot contain null");
|
|
189
|
+
return { meta: { type: typeof value }, keys: [] };
|
|
190
|
+
}
|
|
191
|
+
function patchAtPath(view, root, selectors, path) {
|
|
192
|
+
const rootQuery = parseStateReadPath(root);
|
|
193
|
+
if (rootQuery.kind !== "state")
|
|
194
|
+
throw new Error("Patch projection requires a state path");
|
|
195
|
+
const boundary = view.lineage.at(-1 - rootQuery.offset);
|
|
196
|
+
if (!boundary)
|
|
197
|
+
throw new Error("Requested history predates the proven temporal origin");
|
|
198
|
+
let patch = {};
|
|
199
|
+
if (rootQuery.scope) {
|
|
200
|
+
patch = structuredClone(view.scopes[rootQuery.scope].patches.find((record) => record.transition.id === boundary.id)?.patch ?? {});
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const before = view.lineage.at(-2 - rootQuery.offset);
|
|
204
|
+
if (before) {
|
|
205
|
+
const current = readTemporalState(view, rootQuery.offset);
|
|
206
|
+
const previous = readTemporalState(view, rootQuery.offset + 1);
|
|
207
|
+
patch = diffObjects(previous, current);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (let index = 0; index < selectors.length; index++) {
|
|
211
|
+
const selector = selectors[index];
|
|
212
|
+
if (patch === null)
|
|
213
|
+
return null;
|
|
214
|
+
if (selector.kind === "key" && isObject(patch) && Object.hasOwn(patch, selector.key)) {
|
|
215
|
+
patch = patch[selector.key];
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (selector.kind !== "key" && isObject(patch)) {
|
|
219
|
+
const key = selector.kind === "index" ? `[${selector.index}]` : undefined;
|
|
220
|
+
if (key && Object.hasOwn(patch, key)) {
|
|
221
|
+
patch = patch[key];
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(patch))
|
|
226
|
+
return selectValue(patch, selectors.slice(index), path);
|
|
227
|
+
if (!isObject(patch)) {
|
|
228
|
+
const result = readStatePath(view, root);
|
|
229
|
+
if (!("state" in result))
|
|
230
|
+
throw new Error("Patch projection requires a state path");
|
|
231
|
+
return selectValue(result.state, selectors, path);
|
|
232
|
+
}
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
return structuredClone(patch);
|
|
236
|
+
}
|
|
237
|
+
function diffObjects(previous, current) {
|
|
238
|
+
if (!isObject(previous) || !isObject(current))
|
|
239
|
+
return structuredClone(current);
|
|
240
|
+
const patch = {};
|
|
241
|
+
for (const key of new Set([...Object.keys(previous), ...Object.keys(current)])) {
|
|
242
|
+
if (!Object.hasOwn(current, key))
|
|
243
|
+
patch[key] = null;
|
|
244
|
+
else if (!Object.hasOwn(previous, key))
|
|
245
|
+
patch[key] = structuredClone(current[key]);
|
|
246
|
+
else if (!sameJson(previous[key], current[key]))
|
|
247
|
+
patch[key] = diffObjects(previous[key], current[key]);
|
|
248
|
+
}
|
|
249
|
+
return patch;
|
|
250
|
+
}
|
|
251
|
+
/** Project exact current/historical state paths without exposing temporal metadata. */
|
|
252
|
+
export function readProjectedState(view, paths, projection = "value") {
|
|
253
|
+
if (paths.length === 0)
|
|
254
|
+
throw new Error("read_state requires at least one path");
|
|
255
|
+
if (projection === "patch") {
|
|
256
|
+
const patches = paths.map((path) => {
|
|
257
|
+
const { root, selectors } = parseValuePath(path);
|
|
258
|
+
return patchAtPath(view, root, selectors, path);
|
|
259
|
+
});
|
|
260
|
+
return { patch: patches.length === 1 ? patches[0] : patches };
|
|
261
|
+
}
|
|
262
|
+
const projected = paths.map((path) => {
|
|
263
|
+
try {
|
|
264
|
+
const { root, selectors } = parseValuePath(path);
|
|
265
|
+
const query = parseStateReadPath(root);
|
|
266
|
+
if (query.kind !== "state")
|
|
267
|
+
throw new Error("Value and keys projections require a state path");
|
|
268
|
+
const readsLazy = selectors[0]?.kind === "key" && selectors[0].key === "lazy";
|
|
269
|
+
const state = readsLazy
|
|
270
|
+
? readTemporalState(view, query.offset, query.scope)
|
|
271
|
+
: projectStateForModel(readTemporalState(view, query.offset, query.scope));
|
|
272
|
+
if (readsLazy && !Object.hasOwn(state, "lazy"))
|
|
273
|
+
state.lazy = {};
|
|
274
|
+
return projectValue(selectValue(state, selectors, path), projection);
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
278
|
+
const missing = /does not exist| is outside /.test(message);
|
|
279
|
+
const hint = missing && projection === "value" && paths.length === 1 ? missingReferenceHint(view, path) : undefined;
|
|
280
|
+
if (hint)
|
|
281
|
+
return { value: null, hint };
|
|
282
|
+
throw new Error(message, error instanceof Error ? { cause: error } : undefined);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
if (projected.length === 1)
|
|
286
|
+
return projected[0];
|
|
287
|
+
if (projection === "value")
|
|
288
|
+
return { value: projected.map((result) => result.value) };
|
|
289
|
+
return {
|
|
290
|
+
meta: projected.map((result) => result.meta),
|
|
291
|
+
keys: projected.map((result) => result.keys),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
@@ -97,6 +97,8 @@ export declare class TemporalRuntime {
|
|
|
97
97
|
get sessionKey(): string;
|
|
98
98
|
/** Runtime-owned artifact freshness evidence for one scope; never model-visible state. */
|
|
99
99
|
artifactProvenance(scope: StateScope): ArtifactProvenanceRegistry;
|
|
100
|
+
/** Read canonical shared memory without creating, migrating, or publishing storage. */
|
|
101
|
+
loadPassive(): boolean;
|
|
100
102
|
/** Explicit start owns directory/repository creation; reads never call this. */
|
|
101
103
|
prepare(): void;
|
|
102
104
|
/** Explicit start migrates a predecessor envelope even when passive restore already populated the cache. */
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { lstatSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { classifyScopeStream, parseScopeProvenance, parseScopeStream, sessionRuntimePaths, temporalScopePaths } from "./durable.js";
|
|
5
|
-
import {
|
|
5
|
+
import { pruneArtifactProvenance } from "./artifact.js";
|
|
6
6
|
import { adoptFileStateToGit, initializeGitRepository, isLocalGitRepository, captureTemporalGitBase, loadTemporalRevision, migrateLegacyStorageToGit, publishTemporalStateToGit } from "./git.js";
|
|
7
7
|
import { captureTemporalFileBase, detectGitCapability, initializeFileStore, loadTemporalFileRevision, migrateLegacyStorageToFiles, publishTemporalStateToFiles } from "./storage.js";
|
|
8
8
|
import { hashJson, sameJson } from "./json.js";
|
|
@@ -95,6 +95,35 @@ export class TemporalRuntime {
|
|
|
95
95
|
artifactProvenance(scope) {
|
|
96
96
|
return structuredClone(this.provenanceByScope[scope]);
|
|
97
97
|
}
|
|
98
|
+
/** Read canonical shared memory without creating, migrating, or publishing storage. */
|
|
99
|
+
loadPassive() {
|
|
100
|
+
if (!lstatSync(this.root, { throwIfNoEntry: false }))
|
|
101
|
+
return false;
|
|
102
|
+
const backend = detectGitCapability() === "git" && lstatSync(join(this.root, ".git"), { throwIfNoEntry: false }) ? "git" : "files";
|
|
103
|
+
const base = backend === "git"
|
|
104
|
+
? captureTemporalGitBase(this.cwd, this.sessionId, this.root, this.sessionKey)
|
|
105
|
+
: captureTemporalFileBase(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
106
|
+
const files = new Map(base.files.map((file) => [file.path, file.content]));
|
|
107
|
+
const shared = Object.fromEntries(SHARED_SCOPES.map((scope) => {
|
|
108
|
+
const paths = temporalScopePaths(this.cwd, this.sessionId, scope, this.root, this.sessionKey);
|
|
109
|
+
return [scope, parseScopeStream(files.get(paths.checkpoint), files.get(paths.patches), scope, scope === "cwd" ? this.cwd : undefined, files.get(paths.meta))];
|
|
110
|
+
}));
|
|
111
|
+
if (!shared.global && !shared.cwd)
|
|
112
|
+
return false;
|
|
113
|
+
if (!shared.global || !shared.cwd)
|
|
114
|
+
throw new Error("Incomplete passive State Flow shared storage");
|
|
115
|
+
const fresh = createTemporalState({ global: emptyState(), cwd: emptyState(), session: emptyState() }, randomUUID());
|
|
116
|
+
const basis = backend === "git" ? base.head ?? "unborn" : "files";
|
|
117
|
+
this.view = adoptTemporalStreams({ global: shared.global, cwd: shared.cwd, session: fresh.scopes.session }, `passive:${basis}:${randomUUID()}`);
|
|
118
|
+
this.base = base;
|
|
119
|
+
this.backend = backend;
|
|
120
|
+
this.provenanceByScope = {
|
|
121
|
+
global: parseScopeProvenance(files.get(temporalScopePaths(this.cwd, this.sessionId, "global", this.root, this.sessionKey).meta), "State Flow global metadata"),
|
|
122
|
+
cwd: parseScopeProvenance(files.get(temporalScopePaths(this.cwd, this.sessionId, "cwd", this.root, this.sessionKey).meta), "State Flow CWD metadata"),
|
|
123
|
+
session: {},
|
|
124
|
+
};
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
98
127
|
/** Explicit start owns directory/repository creation; reads never call this. */
|
|
99
128
|
prepare() {
|
|
100
129
|
const backend = detectGitCapability();
|
|
@@ -309,7 +338,7 @@ export class TemporalRuntime {
|
|
|
309
338
|
const files = new Map(base.files.map((file) => [file.path, file.content]));
|
|
310
339
|
if (copy) {
|
|
311
340
|
const session = temporalScopePaths(this.cwd, this.sessionId, "session", this.root, this.sessionKey);
|
|
312
|
-
const owned = [session.checkpoint, session.patches, session.meta, join(session.directory, "config.json"), join(session.directory, "state.json")];
|
|
341
|
+
const owned = [session.checkpoint, session.patches, session.meta, join(session.directory, "config.json"), join(session.directory, "runtime.json"), join(session.directory, "state.json")];
|
|
313
342
|
const occupied = owned.some((path) => files.get(path) !== undefined);
|
|
314
343
|
const historical = !occupied && backend === "git" && base.head ? loadTemporalRevision(this.cwd, this.sessionId, this.root, base.head, this.sessionKey) : undefined;
|
|
315
344
|
if (occupied || historical?.runtime || historical?.scopes.session) {
|
|
@@ -330,7 +359,7 @@ export class TemporalRuntime {
|
|
|
330
359
|
if (copy && !streams.global)
|
|
331
360
|
throw new Error("State Flow fork requires existing shared scope storage");
|
|
332
361
|
const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
333
|
-
const existingRuntime = parseSessionRuntime(files.get(paths.config), files.get(paths.
|
|
362
|
+
const existingRuntime = parseSessionRuntime(files.get(paths.config), files.get(paths.runtime), this.cwd, this.sessionId, files.get(paths.meta));
|
|
334
363
|
if (existingRuntime && !newSessionOrigin)
|
|
335
364
|
throw new Error("Existing session runtime requires a branch revision pointer");
|
|
336
365
|
// Explicit start before any branch runtime is a new origin, never inheritance of a later session layer.
|
|
@@ -348,7 +377,7 @@ export class TemporalRuntime {
|
|
|
348
377
|
candidate.provenanceByScope = {
|
|
349
378
|
global: parseScopeProvenance(files.get(globalMeta), globalMeta),
|
|
350
379
|
cwd: parseScopeProvenance(files.get(cwdMeta), cwdMeta),
|
|
351
|
-
session: copy ? structuredClone(copy.provenance) : streams.session === undefined ? {} :
|
|
380
|
+
session: copy ? structuredClone(copy.provenance) : streams.session === undefined ? {} : parseScopeProvenance(files.get(paths.meta), paths.meta),
|
|
352
381
|
};
|
|
353
382
|
if (expectedShared && ["global", "cwd"].some((scope) => !sameJson(candidate.read(0, scope), expectedShared[scope]))) {
|
|
354
383
|
throw new Error("Legacy branch shared scopes diverged from the selected revision; migration cannot overwrite them");
|
|
@@ -507,7 +536,7 @@ export class TemporalRuntime {
|
|
|
507
536
|
if (!scopedWrite) {
|
|
508
537
|
const current = captureTemporalGitBase(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
509
538
|
const paths = sessionRuntimePaths(this.cwd, this.sessionId, this.root, this.sessionKey);
|
|
510
|
-
for (const path of [paths.config, paths.
|
|
539
|
+
for (const path of [paths.config, paths.runtime]) {
|
|
511
540
|
if (current.files.find((file) => file.path === path)?.identity !== base.files.find((file) => file.path === path)?.identity) {
|
|
512
541
|
throw new Error("Temporal State Flow runtime changed concurrently");
|
|
513
542
|
}
|
|
@@ -7,6 +7,20 @@ interface BranchEntry {
|
|
|
7
7
|
role?: unknown;
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
+
export interface SessionEntryLookup {
|
|
11
|
+
getLeafEntry(): (BranchEntry & {
|
|
12
|
+
id?: string;
|
|
13
|
+
parentId?: string | null;
|
|
14
|
+
}) | undefined;
|
|
15
|
+
getEntry(id: string): (BranchEntry & {
|
|
16
|
+
id?: string;
|
|
17
|
+
parentId?: string | null;
|
|
18
|
+
}) | undefined;
|
|
19
|
+
}
|
|
20
|
+
export interface PassiveStopBoundary {
|
|
21
|
+
at: number;
|
|
22
|
+
from?: number;
|
|
23
|
+
}
|
|
10
24
|
export interface SnapshotDiscovery {
|
|
11
25
|
candidates: unknown[];
|
|
12
26
|
errors: string[];
|
|
@@ -18,4 +32,7 @@ export declare function latestSnapshotData(branch: readonly BranchEntry[]): unkn
|
|
|
18
32
|
export declare function hasPriorConversation(branch: readonly BranchEntry[]): boolean;
|
|
19
33
|
/** Auto-start eligibility is session identity/lifecycle, not the presence of CWD materialization. */
|
|
20
34
|
export declare function isNewSession(reason: unknown, branch: readonly BranchEntry[]): boolean;
|
|
35
|
+
export declare function findAssistantToolBatch(session: SessionEntryLookup, toolCallId: string): string[] | undefined;
|
|
36
|
+
export declare function findPassiveStopBoundary(branch: readonly BranchEntry[], sessionId: string, entryType: string): PassiveStopBoundary | undefined;
|
|
37
|
+
export declare function retainsPhysicalSessionProjection(reason: unknown): boolean;
|
|
21
38
|
export {};
|
|
@@ -42,3 +42,41 @@ export function isNewSession(reason, branch) {
|
|
|
42
42
|
return true;
|
|
43
43
|
return reason === "startup" && !hasPriorConversation(branch);
|
|
44
44
|
}
|
|
45
|
+
export function findAssistantToolBatch(session, toolCallId) {
|
|
46
|
+
for (let cursor = session.getLeafEntry(); cursor; cursor = cursor.parentId ? session.getEntry(cursor.parentId) : undefined) {
|
|
47
|
+
if (cursor.type !== "message" || cursor.message?.role !== "assistant" || !Array.isArray(cursor.message.content))
|
|
48
|
+
continue;
|
|
49
|
+
const calls = cursor.message.content.filter((block) => {
|
|
50
|
+
return typeof block === "object" && block !== null
|
|
51
|
+
&& block.type === "toolCall"
|
|
52
|
+
&& typeof block.id === "string"
|
|
53
|
+
&& typeof block.name === "string";
|
|
54
|
+
});
|
|
55
|
+
if (calls.some(({ id }) => id === toolCallId))
|
|
56
|
+
return calls.map(({ name }) => name);
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
export function findPassiveStopBoundary(branch, sessionId, entryType) {
|
|
61
|
+
for (const entry of [...branch].reverse()) {
|
|
62
|
+
try {
|
|
63
|
+
if (entry?.type !== "custom" || entry.customType !== entryType)
|
|
64
|
+
continue;
|
|
65
|
+
const { at, from, reset, owner } = entry.data ?? {};
|
|
66
|
+
if (reset === true && owner === sessionId)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (typeof at === "number" && Number.isSafeInteger(at) && at >= 0)
|
|
69
|
+
return {
|
|
70
|
+
at,
|
|
71
|
+
...(typeof from === "number" && Number.isSafeInteger(from) && from >= 0 ? { from } : {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// A hostile unrelated branch entry cannot manufacture or suppress a valid marker.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
export function retainsPhysicalSessionProjection(reason) {
|
|
81
|
+
return reason === undefined || reason === "startup" || reason === "reload" || reason === "resume";
|
|
82
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ArtifactProvenanceRegistry } from "./artifact.ts";
|
|
2
2
|
import { type RemotePublicationPolicyDocument } from "./publication.ts";
|
|
3
3
|
import { type JsonObject } from "./json.ts";
|
|
4
|
-
import { type
|
|
4
|
+
import { type TransitionBoundary } from "./temporal.ts";
|
|
5
5
|
/** Missing operational capability is not evidence that a checkpoint target is invalid. */
|
|
6
6
|
export declare class RevisionUnavailableError extends Error {
|
|
7
7
|
}
|
|
@@ -57,12 +57,12 @@ export interface SessionRuntime {
|
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
export declare function validateSessionRuntime(value: unknown, cwd: string, sessionId: string): asserts value is SessionRuntime;
|
|
60
|
-
export declare function createSessionRuntime(snapshot: Snapshot, cwd: string, sessionId: string, lineage: readonly TransitionBoundary[], publication?: SessionRuntime["meta"]["publication"],
|
|
61
|
-
export declare function serializeSessionRuntime(runtime: SessionRuntime, cwd: string, sessionId: string
|
|
60
|
+
export declare function createSessionRuntime(snapshot: Snapshot, cwd: string, sessionId: string, lineage: readonly TransitionBoundary[], publication?: SessionRuntime["meta"]["publication"], _artifacts?: ArtifactProvenanceRegistry): SessionRuntime;
|
|
61
|
+
export declare function serializeSessionRuntime(runtime: SessionRuntime, cwd: string, sessionId: string): {
|
|
62
62
|
config: string;
|
|
63
|
-
|
|
63
|
+
runtime: string;
|
|
64
64
|
};
|
|
65
|
-
export declare function parseSessionRuntime(config: string | undefined,
|
|
65
|
+
export declare function parseSessionRuntime(config: string | undefined, runtimeSource: string | undefined, cwd: string, sessionId: string, legacyMetaSource?: string): SessionRuntime | undefined;
|
|
66
66
|
export declare function resolveSessionRuntime(runtime: SessionRuntime, revision: string): {
|
|
67
67
|
snapshot: Snapshot;
|
|
68
68
|
lineage: TransitionBoundary[];
|