instar 1.3.774 → 1.3.775
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +95 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +7 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +1 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +9 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +175 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/WorkingSetArtifactManager.d.ts +104 -0
- package/dist/core/WorkingSetArtifactManager.d.ts.map +1 -0
- package/dist/core/WorkingSetArtifactManager.js +174 -0
- package/dist/core/WorkingSetArtifactManager.js.map +1 -0
- package/dist/core/WorkingSetArtifactReplicatedStore.d.ts +203 -0
- package/dist/core/WorkingSetArtifactReplicatedStore.d.ts.map +1 -0
- package/dist/core/WorkingSetArtifactReplicatedStore.js +384 -0
- package/dist/core/WorkingSetArtifactReplicatedStore.js.map +1 -0
- package/dist/core/WorkingSetManifest.d.ts +5 -0
- package/dist/core/WorkingSetManifest.d.ts.map +1 -1
- package/dist/core/WorkingSetManifest.js +10 -0
- package/dist/core/WorkingSetManifest.js.map +1 -1
- package/dist/core/WorkingSetPull.d.ts +5 -0
- package/dist/core/WorkingSetPull.d.ts.map +1 -1
- package/dist/core/WorkingSetPull.js +1 -0
- package/dist/core/WorkingSetPull.js.map +1 -1
- package/dist/core/WriteDomainRegistry.d.ts.map +1 -1
- package/dist/core/WriteDomainRegistry.js +12 -0
- package/dist/core/WriteDomainRegistry.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +1 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +7 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +104 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/templates/hooks/settings-template.json +10 -0
- package/upgrades/1.3.775.md +44 -0
- package/upgrades/side-effects/intelligent-working-set-lazy-sync.md +220 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkingSetArtifactManager — the durable, own-origin store of interactive working-set
|
|
3
|
+
* artifact rows (spec: intelligent-working-set-lazy-sync.md, Layer 1). It is the literal
|
|
4
|
+
* analog of `KnowledgeManager` (the local catalog the KnowledgeReplicatedStore rides): it
|
|
5
|
+
* holds THIS machine's per-topic record of files the agent wrote INTERACTIVELY under the
|
|
6
|
+
* `.instar/` jail — the case `WorkingSetManifest.computeWorkingSet` misses — and feeds:
|
|
7
|
+
* (a) the `ReplicatedStoreReader`'s loadOriginRecords/listRecordKeys (own-origin
|
|
8
|
+
* materialization the union reader merges against peer replicas), and
|
|
9
|
+
* (b) `computeWorkingSet`'s new `ready`-row source (component 3).
|
|
10
|
+
*
|
|
11
|
+
* It stores OWN-ORIGIN rows only (producerMachineId === this machine). Peer replicas are
|
|
12
|
+
* NOT stored here — they arrive via the replicated journal and surface through the union
|
|
13
|
+
* reader (read-only, advisory, never clobbering a local file). Row identity is
|
|
14
|
+
* (topicId, relPath, producerMachineId); a re-record of the same triple UPSERTS.
|
|
15
|
+
*
|
|
16
|
+
* Row lifecycle (spec §64): `pendingHash` (recorded, hash deferred) → `ready(contentHash)`
|
|
17
|
+
* (hashed, fetch-eligible) → terminal `tooLarge` / `secretFlagged`. ONLY `ready` rows are
|
|
18
|
+
* returned by getReadyRows() — the fetch-nomination source (the serve-boundary hash-verify
|
|
19
|
+
* remains the authority). Tombstone is OWNER-ONLY (a row is always this machine's, so a
|
|
20
|
+
* local tombstone is legitimate; the replicated tombstone builder re-checks origin ===
|
|
21
|
+
* producer). GC purges rows older than the record TTL (default 30d).
|
|
22
|
+
*
|
|
23
|
+
* Durable + atomic (tmp+rename), mirroring KnowledgeManager. The emit seam is best-effort:
|
|
24
|
+
* a record/tombstone fires the replication emitter when wired (dark by default), else no-op.
|
|
25
|
+
*/
|
|
26
|
+
/** A locally-held working-set artifact row (own-origin). */
|
|
27
|
+
export interface WorkingSetArtifactLocalRow {
|
|
28
|
+
topicId: number;
|
|
29
|
+
relPath: string;
|
|
30
|
+
contentHash: string | null;
|
|
31
|
+
/** ISO-8601 of the last write that produced/updated this row. */
|
|
32
|
+
lastWrittenAt: string;
|
|
33
|
+
producerMachineId: string;
|
|
34
|
+
/** pendingHash | ready | tooLarge | secretFlagged */
|
|
35
|
+
state: string;
|
|
36
|
+
/** ISO-8601 when this row was first recorded — GC anchor (record TTL). */
|
|
37
|
+
recordedAt: string;
|
|
38
|
+
}
|
|
39
|
+
/** The replication emit seam — fired on a record (put) / tombstone (delete). Best-effort;
|
|
40
|
+
* wired ONLY when `multiMachine.stateSync.workingSetArtifact.enabled` (dark by default). */
|
|
41
|
+
export interface WorkingSetArtifactReplicationEmitter {
|
|
42
|
+
emitPut(row: WorkingSetArtifactLocalRow): void;
|
|
43
|
+
emitDelete(row: {
|
|
44
|
+
topicId: number;
|
|
45
|
+
relPath: string;
|
|
46
|
+
producerMachineId: string;
|
|
47
|
+
deletedAt: string;
|
|
48
|
+
}): void;
|
|
49
|
+
}
|
|
50
|
+
export interface RecordArtifactInput {
|
|
51
|
+
topicId: number;
|
|
52
|
+
relPath: string;
|
|
53
|
+
producerMachineId: string;
|
|
54
|
+
/** Defaults to 'pendingHash' — hashing is deferred to the serve boundary / async worker. */
|
|
55
|
+
state?: string;
|
|
56
|
+
contentHash?: string | null;
|
|
57
|
+
/** ISO-8601; defaults to nowIso (injectable for tests). */
|
|
58
|
+
lastWrittenAt?: string;
|
|
59
|
+
}
|
|
60
|
+
/** Default record TTL for GC — distinct from the engine's 7d pending-pull TTL (spec F3). */
|
|
61
|
+
export declare const DEFAULT_RECORD_TTL_MS: number;
|
|
62
|
+
export declare class WorkingSetArtifactManager {
|
|
63
|
+
private readonly dir;
|
|
64
|
+
private readonly catalogPath;
|
|
65
|
+
private replication;
|
|
66
|
+
/** Injectable clock (tests) — returns ISO-8601. */
|
|
67
|
+
private readonly nowIso;
|
|
68
|
+
constructor(stateDir: string, nowIso?: () => string);
|
|
69
|
+
/** Wire the replication emitter (dark by default — only attached when stateSync enabled). */
|
|
70
|
+
setReplicationEmitter(emitter: WorkingSetArtifactReplicationEmitter | null): void;
|
|
71
|
+
/**
|
|
72
|
+
* Upsert a row keyed (topicId, relPath, producerMachineId). A re-record of the same triple
|
|
73
|
+
* updates lastWrittenAt/state/contentHash + preserves the original recordedAt (the GC
|
|
74
|
+
* anchor). Returns the stored row. Fires the emit seam (put) best-effort.
|
|
75
|
+
*/
|
|
76
|
+
record(input: RecordArtifactInput): WorkingSetArtifactLocalRow;
|
|
77
|
+
/**
|
|
78
|
+
* Transition a pendingHash row to ready(contentHash) (or to a terminal tooLarge/secretFlagged).
|
|
79
|
+
* A no-op if the row is absent. Fires the emit seam (put) on a real transition.
|
|
80
|
+
*/
|
|
81
|
+
setState(topicId: number, relPath: string, producerMachineId: string, state: string, contentHash?: string | null): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Owner-only tombstone: remove the row (topicId, relPath, producerMachineId) and fire the
|
|
84
|
+
* replicated tombstone. A row held here is always THIS machine's (own-origin), so a local
|
|
85
|
+
* tombstone is legitimate; the replicated tombstone builder re-checks origin === producer.
|
|
86
|
+
* Returns true if a row was removed.
|
|
87
|
+
*/
|
|
88
|
+
tombstone(topicId: number, relPath: string, producerMachineId: string): boolean;
|
|
89
|
+
/** All own rows for a topic (any state). */
|
|
90
|
+
getRowsForTopic(topicId: number): WorkingSetArtifactLocalRow[];
|
|
91
|
+
/** Own `ready` rows for a topic — the fetch-nomination source (spec §64: only ready nominates). */
|
|
92
|
+
getReadyRows(topicId: number): WorkingSetArtifactLocalRow[];
|
|
93
|
+
/** All own rows (every topic) — the union reader's listRecordKeys source. */
|
|
94
|
+
getAllRows(): WorkingSetArtifactLocalRow[];
|
|
95
|
+
/**
|
|
96
|
+
* GC: purge rows whose recordedAt is older than ttlMs. Returns the count purged. A purge is
|
|
97
|
+
* a LOCAL cleanup (not a tombstone — an expired own row is simply forgotten; a peer's copy
|
|
98
|
+
* ages out under its own TTL). Injectable `nowMs` for tests.
|
|
99
|
+
*/
|
|
100
|
+
gc(ttlMs?: number, nowMs?: number): number;
|
|
101
|
+
private loadCatalog;
|
|
102
|
+
private saveCatalog;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=WorkingSetArtifactManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkingSetArtifactManager.d.ts","sourceRoot":"","sources":["../../src/core/WorkingSetArtifactManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,4DAA4D;AAC5D,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;CACpB;AAMD;6FAC6F;AAC7F,MAAM,WAAW,oCAAoC;IACnD,OAAO,CAAC,GAAG,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC/C,UAAU,CAAC,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC3G;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAKD,4FAA4F;AAC5F,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAE9D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,WAAW,CAAqD;IACxE,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;gBAE1B,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,MAAuC;IAMnF,6FAA6F;IAC7F,qBAAqB,CAAC,OAAO,EAAE,oCAAoC,GAAG,IAAI,GAAG,IAAI;IAIjF;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,0BAA0B;IA8B9D;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;IAc1H;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO;IAc/E,4CAA4C;IAC5C,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,EAAE;IAI9D,mGAAmG;IACnG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,EAAE;IAI3D,6EAA6E;IAC7E,UAAU,IAAI,0BAA0B,EAAE;IAI1C;;;;OAIG;IACH,EAAE,CAAC,KAAK,GAAE,MAA8B,EAAE,KAAK,GAAE,MAAmB,GAAG,MAAM;IAa7E,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,WAAW;CAMpB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkingSetArtifactManager — the durable, own-origin store of interactive working-set
|
|
3
|
+
* artifact rows (spec: intelligent-working-set-lazy-sync.md, Layer 1). It is the literal
|
|
4
|
+
* analog of `KnowledgeManager` (the local catalog the KnowledgeReplicatedStore rides): it
|
|
5
|
+
* holds THIS machine's per-topic record of files the agent wrote INTERACTIVELY under the
|
|
6
|
+
* `.instar/` jail — the case `WorkingSetManifest.computeWorkingSet` misses — and feeds:
|
|
7
|
+
* (a) the `ReplicatedStoreReader`'s loadOriginRecords/listRecordKeys (own-origin
|
|
8
|
+
* materialization the union reader merges against peer replicas), and
|
|
9
|
+
* (b) `computeWorkingSet`'s new `ready`-row source (component 3).
|
|
10
|
+
*
|
|
11
|
+
* It stores OWN-ORIGIN rows only (producerMachineId === this machine). Peer replicas are
|
|
12
|
+
* NOT stored here — they arrive via the replicated journal and surface through the union
|
|
13
|
+
* reader (read-only, advisory, never clobbering a local file). Row identity is
|
|
14
|
+
* (topicId, relPath, producerMachineId); a re-record of the same triple UPSERTS.
|
|
15
|
+
*
|
|
16
|
+
* Row lifecycle (spec §64): `pendingHash` (recorded, hash deferred) → `ready(contentHash)`
|
|
17
|
+
* (hashed, fetch-eligible) → terminal `tooLarge` / `secretFlagged`. ONLY `ready` rows are
|
|
18
|
+
* returned by getReadyRows() — the fetch-nomination source (the serve-boundary hash-verify
|
|
19
|
+
* remains the authority). Tombstone is OWNER-ONLY (a row is always this machine's, so a
|
|
20
|
+
* local tombstone is legitimate; the replicated tombstone builder re-checks origin ===
|
|
21
|
+
* producer). GC purges rows older than the record TTL (default 30d).
|
|
22
|
+
*
|
|
23
|
+
* Durable + atomic (tmp+rename), mirroring KnowledgeManager. The emit seam is best-effort:
|
|
24
|
+
* a record/tombstone fires the replication emitter when wired (dark by default), else no-op.
|
|
25
|
+
*/
|
|
26
|
+
import fs from 'node:fs';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
/** The valid row states (mirrors WORKING_SET_ARTIFACT_STATES in the replicated store). */
|
|
29
|
+
const VALID_STATES = new Set(['pendingHash', 'ready', 'tooLarge', 'secretFlagged']);
|
|
30
|
+
/** Default record TTL for GC — distinct from the engine's 7d pending-pull TTL (spec F3). */
|
|
31
|
+
export const DEFAULT_RECORD_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
32
|
+
export class WorkingSetArtifactManager {
|
|
33
|
+
dir;
|
|
34
|
+
catalogPath;
|
|
35
|
+
replication = null;
|
|
36
|
+
/** Injectable clock (tests) — returns ISO-8601. */
|
|
37
|
+
nowIso;
|
|
38
|
+
constructor(stateDir, nowIso = () => new Date().toISOString()) {
|
|
39
|
+
this.dir = path.join(stateDir, 'working-set');
|
|
40
|
+
this.catalogPath = path.join(this.dir, 'artifacts.json');
|
|
41
|
+
this.nowIso = nowIso;
|
|
42
|
+
}
|
|
43
|
+
/** Wire the replication emitter (dark by default — only attached when stateSync enabled). */
|
|
44
|
+
setReplicationEmitter(emitter) {
|
|
45
|
+
this.replication = emitter;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Upsert a row keyed (topicId, relPath, producerMachineId). A re-record of the same triple
|
|
49
|
+
* updates lastWrittenAt/state/contentHash + preserves the original recordedAt (the GC
|
|
50
|
+
* anchor). Returns the stored row. Fires the emit seam (put) best-effort.
|
|
51
|
+
*/
|
|
52
|
+
record(input) {
|
|
53
|
+
const state = input.state && VALID_STATES.has(input.state) ? input.state : 'pendingHash';
|
|
54
|
+
const lastWrittenAt = input.lastWrittenAt ?? this.nowIso();
|
|
55
|
+
const catalog = this.loadCatalog();
|
|
56
|
+
const existing = catalog.rows.find((r) => r.topicId === input.topicId && r.relPath === input.relPath && r.producerMachineId === input.producerMachineId);
|
|
57
|
+
let row;
|
|
58
|
+
if (existing) {
|
|
59
|
+
existing.state = state;
|
|
60
|
+
existing.lastWrittenAt = lastWrittenAt;
|
|
61
|
+
existing.contentHash = input.contentHash ?? existing.contentHash ?? null;
|
|
62
|
+
row = existing;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
row = {
|
|
66
|
+
topicId: input.topicId,
|
|
67
|
+
relPath: input.relPath,
|
|
68
|
+
contentHash: input.contentHash ?? null,
|
|
69
|
+
lastWrittenAt,
|
|
70
|
+
producerMachineId: input.producerMachineId,
|
|
71
|
+
state,
|
|
72
|
+
recordedAt: this.nowIso(),
|
|
73
|
+
};
|
|
74
|
+
catalog.rows.push(row);
|
|
75
|
+
}
|
|
76
|
+
this.saveCatalog(catalog);
|
|
77
|
+
try {
|
|
78
|
+
this.replication?.emitPut(row);
|
|
79
|
+
}
|
|
80
|
+
catch { /* @silent-fallback-ok: best-effort replication emit (dark by default); a failed emit is re-driven by the store's own reconcile, never a data-loss fallback */ }
|
|
81
|
+
return row;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Transition a pendingHash row to ready(contentHash) (or to a terminal tooLarge/secretFlagged).
|
|
85
|
+
* A no-op if the row is absent. Fires the emit seam (put) on a real transition.
|
|
86
|
+
*/
|
|
87
|
+
setState(topicId, relPath, producerMachineId, state, contentHash) {
|
|
88
|
+
if (!VALID_STATES.has(state))
|
|
89
|
+
return false;
|
|
90
|
+
const catalog = this.loadCatalog();
|
|
91
|
+
const row = catalog.rows.find((r) => r.topicId === topicId && r.relPath === relPath && r.producerMachineId === producerMachineId);
|
|
92
|
+
if (!row)
|
|
93
|
+
return false;
|
|
94
|
+
row.state = state;
|
|
95
|
+
if (contentHash !== undefined)
|
|
96
|
+
row.contentHash = contentHash;
|
|
97
|
+
this.saveCatalog(catalog);
|
|
98
|
+
try {
|
|
99
|
+
this.replication?.emitPut(row);
|
|
100
|
+
}
|
|
101
|
+
catch { /* @silent-fallback-ok: best-effort replication emit (dark by default); a failed emit is re-driven by the store's own reconcile, never a data-loss fallback */ }
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Owner-only tombstone: remove the row (topicId, relPath, producerMachineId) and fire the
|
|
106
|
+
* replicated tombstone. A row held here is always THIS machine's (own-origin), so a local
|
|
107
|
+
* tombstone is legitimate; the replicated tombstone builder re-checks origin === producer.
|
|
108
|
+
* Returns true if a row was removed.
|
|
109
|
+
*/
|
|
110
|
+
tombstone(topicId, relPath, producerMachineId) {
|
|
111
|
+
const catalog = this.loadCatalog();
|
|
112
|
+
const before = catalog.rows.length;
|
|
113
|
+
catalog.rows = catalog.rows.filter((r) => !(r.topicId === topicId && r.relPath === relPath && r.producerMachineId === producerMachineId));
|
|
114
|
+
if (catalog.rows.length === before)
|
|
115
|
+
return false;
|
|
116
|
+
this.saveCatalog(catalog);
|
|
117
|
+
try {
|
|
118
|
+
this.replication?.emitDelete({ topicId, relPath, producerMachineId, deletedAt: this.nowIso() });
|
|
119
|
+
}
|
|
120
|
+
catch { /* @silent-fallback-ok: best-effort replication emit (dark by default); a failed emit is re-driven by the store's own reconcile, never a data-loss fallback */ }
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
/** All own rows for a topic (any state). */
|
|
124
|
+
getRowsForTopic(topicId) {
|
|
125
|
+
return this.loadCatalog().rows.filter((r) => r.topicId === topicId);
|
|
126
|
+
}
|
|
127
|
+
/** Own `ready` rows for a topic — the fetch-nomination source (spec §64: only ready nominates). */
|
|
128
|
+
getReadyRows(topicId) {
|
|
129
|
+
return this.loadCatalog().rows.filter((r) => r.topicId === topicId && r.state === 'ready');
|
|
130
|
+
}
|
|
131
|
+
/** All own rows (every topic) — the union reader's listRecordKeys source. */
|
|
132
|
+
getAllRows() {
|
|
133
|
+
return this.loadCatalog().rows;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* GC: purge rows whose recordedAt is older than ttlMs. Returns the count purged. A purge is
|
|
137
|
+
* a LOCAL cleanup (not a tombstone — an expired own row is simply forgotten; a peer's copy
|
|
138
|
+
* ages out under its own TTL). Injectable `nowMs` for tests.
|
|
139
|
+
*/
|
|
140
|
+
gc(ttlMs = DEFAULT_RECORD_TTL_MS, nowMs = Date.now()) {
|
|
141
|
+
const catalog = this.loadCatalog();
|
|
142
|
+
const before = catalog.rows.length;
|
|
143
|
+
catalog.rows = catalog.rows.filter((r) => {
|
|
144
|
+
const age = nowMs - Date.parse(r.recordedAt ?? '');
|
|
145
|
+
return !(Number.isFinite(age) && age > ttlMs);
|
|
146
|
+
});
|
|
147
|
+
const purged = before - catalog.rows.length;
|
|
148
|
+
if (purged > 0)
|
|
149
|
+
this.saveCatalog(catalog);
|
|
150
|
+
return purged;
|
|
151
|
+
}
|
|
152
|
+
// ── persistence (atomic tmp+rename, mirrors KnowledgeManager) ─────────────────
|
|
153
|
+
loadCatalog() {
|
|
154
|
+
if (!fs.existsSync(this.catalogPath))
|
|
155
|
+
return { rows: [] };
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(fs.readFileSync(this.catalogPath, 'utf-8'));
|
|
158
|
+
return Array.isArray(parsed?.rows) ? { rows: parsed.rows } : { rows: [] };
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// @silent-fallback-ok: a corrupt/unreadable catalog reads as empty (documented contract) — the
|
|
162
|
+
// agent re-records artifacts on the next write; never throws into a caller, never loses live data.
|
|
163
|
+
return { rows: [] };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
saveCatalog(catalog) {
|
|
167
|
+
if (!fs.existsSync(this.dir))
|
|
168
|
+
fs.mkdirSync(this.dir, { recursive: true });
|
|
169
|
+
const tmpPath = `${this.catalogPath}.${process.pid}.tmp`;
|
|
170
|
+
fs.writeFileSync(tmpPath, JSON.stringify(catalog, null, 2), 'utf-8');
|
|
171
|
+
fs.renameSync(tmpPath, this.catalogPath);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=WorkingSetArtifactManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkingSetArtifactManager.js","sourceRoot":"","sources":["../../src/core/WorkingSetArtifactManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkingSetArtifactReplicatedStore — the WS2 replicated kind that lets an agent-produced
|
|
3
|
+
* artifact a conversation wrote INTERACTIVELY under the `.instar/` jail follow that
|
|
4
|
+
* conversation when its topic moves machines (spec: intelligent-working-set-lazy-sync.md,
|
|
5
|
+
* Layer 1). It is the literal analog of `KnowledgeReplicatedStore.ts` (WS2.4) — it layers
|
|
6
|
+
* a `working-set-artifact` replicated kind onto the SAME generic substrate
|
|
7
|
+
* (ReplicatedRecordEnvelope / UnionReader / ConflictStore / ReplicationBudget) so the
|
|
8
|
+
* per-topic record of what an agent wrote is ONE index across machines, not one-per-machine.
|
|
9
|
+
*
|
|
10
|
+
* WHY A NEW KIND (not the computed engine): `WorkingSetManifest.computeWorkingSet` derives
|
|
11
|
+
* the manifest from the `autonomous/<topic>.*` convention dir + `artifactPaths` on
|
|
12
|
+
* `autonomous-run` journal entries. A file the agent wrote INTERACTIVELY (no autonomous
|
|
13
|
+
* run) is invisible to it. This kind is the ONE new manifest source for that case — a
|
|
14
|
+
* durable, replicated per-topic record of `{ relPath, contentHash?, lastWrittenAt, state }`
|
|
15
|
+
* under the EXISTING `.instar/` jail. It does NOT widen the jail, lower a cap, or
|
|
16
|
+
* re-implement the fetch engine (those bind verbatim — spec §50).
|
|
17
|
+
*
|
|
18
|
+
* THE PATH-PAYLOAD / ENVELOPE PATH-JAIL COLLISION (spec §59, M1): this is the FIRST
|
|
19
|
+
* replicated kind whose payload is LEGITIMATELY a path. The envelope structurally auto-jails
|
|
20
|
+
* path-shaped fields (KnowledgeReplicatedStore jails a path-shaped `url` to null). So here:
|
|
21
|
+
* - recordKey = sha256(jailedRelPath) + ':' + origin — a NON-path-shaped derivation
|
|
22
|
+
* (never the raw path), so it survives envelope validation and still gives the
|
|
23
|
+
* (relPath, producerMachine) identity. `origin` IS the authenticated producer machine
|
|
24
|
+
* (spec §62: producerMachineId ≡ the applier's authenticated entry.machine, never a
|
|
25
|
+
* separate trusted content field) — so a peer can never forge which machine produced a
|
|
26
|
+
* row (the envelope origin is authenticated at apply).
|
|
27
|
+
* - `relPath` lives in a store field explicitly CARVED OUT of the path-jail (so it may
|
|
28
|
+
* hold a path) but is STRICTLY validated on RECEIVE by the canonical `jailValidateRelPath`
|
|
29
|
+
* (relative-only; reject abs / drive / UNC / `..`-after-decode / NUL / empty; length-cap)
|
|
30
|
+
* — the filesystem serve-jail is downstream, so an invalid verdict must reject FIRST.
|
|
31
|
+
* This deliberately breaks the "envelope carries identifiers, never paths" invariant;
|
|
32
|
+
* the receive-side relPath validation is the compensating control (spec §61).
|
|
33
|
+
*
|
|
34
|
+
* ROW STATES (spec §64): `pendingHash` (recorded, hash deferred) → `ready(hash)` (hashed,
|
|
35
|
+
* in scope) → terminal `tooLarge` / `secretFlagged`. ONLY `ready` rows enter fetch nominees
|
|
36
|
+
* (the serve-boundary hash-verify remains the authority; a stored hash is advisory until the
|
|
37
|
+
* pull re-reads live) — the wiring into computeWorkingSet (component 3) enforces that.
|
|
38
|
+
*
|
|
39
|
+
* IMPACT: HIGH at the REPLICATION layer (a concurrent divergent edit to the same recordKey
|
|
40
|
+
* — same relPath+producer, different content — goes through APPEND-BOTH-AND-FLAG; both
|
|
41
|
+
* surface, the fetch engine's no-clobber lands the second as `.from-<machine>`). ADVISORY at
|
|
42
|
+
* the READ layer (both variants are hints; the read never blocks, never clobbers a local file).
|
|
43
|
+
*
|
|
44
|
+
* TOMBSTONE AUTHORITY — OWNER-ONLY (spec §91): only the PRODUCER of a row (origin ===
|
|
45
|
+
* this machine) may tombstone it; a receiver deleting its FETCHED local copy is a
|
|
46
|
+
* machine-local suppression, NOT a cross-peer delete. A peer can never tombstone another
|
|
47
|
+
* producer's artifact.
|
|
48
|
+
*
|
|
49
|
+
* SAFETY POSTURE: MECHANISM, dark by default (`multiMachine.stateSync.workingSetArtifact`).
|
|
50
|
+
* PURE LOGIC — no fs, no network, no Date directly.
|
|
51
|
+
*/
|
|
52
|
+
import type { StoreFieldSchema, ReplicatedEnvelope } from './ReplicatedRecordEnvelope.js';
|
|
53
|
+
import type { ImpactTier, OriginRecord, UnionResult } from './UnionReader.js';
|
|
54
|
+
import type { ReplicatedKindBounds } from './ReplicationBudget.js';
|
|
55
|
+
import type { HlcTimestamp } from './HybridLogicalClock.js';
|
|
56
|
+
/** The stateSync config sub-key + advert suffix (`multiMachine.stateSync.workingSetArtifact.enabled`). */
|
|
57
|
+
export declare const WORKING_SET_ARTIFACT_STORE_KEY = "workingSetArtifact";
|
|
58
|
+
/** The JournalKind string this store rides — the DUAL-REGISTRY dynamic half. MUST also be
|
|
59
|
+
* in CoherenceJournal.JOURNAL_KINDS (the static half) or the store advertises receive=true
|
|
60
|
+
* yet serves/applies/pulls nothing (the silent no-replication trap). */
|
|
61
|
+
export declare const WORKING_SET_ARTIFACT_KIND = "working-set-artifact";
|
|
62
|
+
/** HIGH-impact at replication (append-both-and-flag on a concurrent divergent edit to the
|
|
63
|
+
* same relPath+producer); ADVISORY at read. Same posture as knowledge/relationships. */
|
|
64
|
+
export declare const WORKING_SET_ARTIFACT_IMPACT_TIER: ImpactTier;
|
|
65
|
+
/** Row lifecycle states (spec §64). Only `ready` rows are fetch-eligible (enforced by the
|
|
66
|
+
* computeWorkingSet union wiring, component 3). A foreign row whose `state` is outside this
|
|
67
|
+
* set is rejected (markup cannot survive an enum slot). */
|
|
68
|
+
export declare const WORKING_SET_ARTIFACT_STATES: ReadonlyArray<string>;
|
|
69
|
+
/** A relPath under `.instar/` is short; a generous cap that still bounds a smuggled flood. */
|
|
70
|
+
export declare const MAX_RELPATH_LENGTH = 1024;
|
|
71
|
+
/** A content hash is a fixed-width hex digest; bound it hard. */
|
|
72
|
+
export declare const MAX_CONTENT_HASH_LENGTH = 128;
|
|
73
|
+
/**
|
|
74
|
+
* Per-kind replication bounds. Working-set artifact rows are FEW + bounded (a per-topic
|
|
75
|
+
* catalog of interactive writes), coalesced so a churny re-edit loop does not flood the
|
|
76
|
+
* stream. NEVER `rotateKeep: 0`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const WORKING_SET_ARTIFACT_BOUNDS: ReplicatedKindBounds;
|
|
79
|
+
/** Per-entry cap. A row is a tiny projection ({relPath, contentHash, lastWrittenAt, state});
|
|
80
|
+
* 8KB is ample. A record over cap is REJECTED with a named error (never silent-truncate). */
|
|
81
|
+
export declare const WORKING_SET_ARTIFACT_MAX_ENTRY_BYTES: number;
|
|
82
|
+
/** The store-owned VALUE fields (the unknown-field allowlist). `producerMachineId` is
|
|
83
|
+
* DELIBERATELY ABSENT — it is the authenticated envelope `origin`, never a trusted content
|
|
84
|
+
* field (spec §62). `recordKey`/`hlc`/`op`/`origin`/`observed` are reserved envelope fields. */
|
|
85
|
+
export declare const WORKING_SET_ARTIFACT_KNOWN_FIELDS: ReadonlyArray<string>;
|
|
86
|
+
/** The tombstone's only store-owned field beyond the reserved envelope set. */
|
|
87
|
+
export declare const WORKING_SET_ARTIFACT_TOMBSTONE_KNOWN_FIELDS: ReadonlyArray<string>;
|
|
88
|
+
/**
|
|
89
|
+
* The single canonical relative-path validator, used at ALL sites (record, replication
|
|
90
|
+
* receive, serve-jail) so the rules can't drift between callers (spec §64). Returns the
|
|
91
|
+
* relPath UNCHANGED if safe, else null (REJECT). Rules (spec §61/§92): relative-only —
|
|
92
|
+
* reject absolute, Windows drive (`C:`), UNC (`\\`), any `..` segment after decode, a NUL
|
|
93
|
+
* byte (isSafeRelPath does NOT catch NUL, and it would otherwise throw uncaught downstream —
|
|
94
|
+
* fail CLEAN here), empty, or over-cap. This is a STRING check; the realpath / O_NOFOLLOW /
|
|
95
|
+
* containment filesystem jail is the DOWNSTREAM serve-boundary control (this runs first,
|
|
96
|
+
* before any fs touch, because an invalid replication verdict must reject before the fs).
|
|
97
|
+
*/
|
|
98
|
+
export declare function jailValidateRelPath(relPath: unknown): string | null;
|
|
99
|
+
/** Is `v` a clean ISO-8601 date string (no smuggled markup)? Mirrors KnowledgeReplicatedStore. */
|
|
100
|
+
export declare function isIso8601(v: unknown): v is string;
|
|
101
|
+
/**
|
|
102
|
+
* The `working-set-artifact` store schema. Strict typed validation on top of the envelope:
|
|
103
|
+
* TYPE-CLAMP every known field, and — critically — validate `relPath` with the canonical
|
|
104
|
+
* `jailValidateRelPath` (NOT the envelope's auto path-jail, which relPath is carved out of).
|
|
105
|
+
* A record whose relPath fails the jail-validator is REJECTED WHOLE (never landed with a
|
|
106
|
+
* null path). Returns the validated store-specific object, or null to reject the record.
|
|
107
|
+
* PURE (no I/O). The envelope validator has ALREADY validated `op` ∈ {put,delete}.
|
|
108
|
+
*/
|
|
109
|
+
export declare const workingSetArtifactStoreSchema: StoreFieldSchema;
|
|
110
|
+
/**
|
|
111
|
+
* Derive the cross-machine recordKey for a working-set artifact row: a NON-path-shaped
|
|
112
|
+
* `sha256(jailedRelPath) + ':' + producerMachineId` (spec §60). `producerMachineId` IS the
|
|
113
|
+
* authenticated envelope `origin` — so the SAME relPath from the SAME producer collapses to
|
|
114
|
+
* ONE record, while divergent producers of the same path coexist (distinct recordKeys → the
|
|
115
|
+
* fetch engine's no-clobber lands the second as `.from-<machine>`). Returns null when the
|
|
116
|
+
* relPath fails the jail-validator or the producer id is empty (a degenerate row — the caller
|
|
117
|
+
* skips emission; it can never collide a stranger by an empty key).
|
|
118
|
+
*/
|
|
119
|
+
export declare function deriveWorkingSetArtifactRecordKey(relPath: string, producerMachineId: string): string | null;
|
|
120
|
+
/** A local working-set artifact row (the record shape the manager holds). */
|
|
121
|
+
export interface WorkingSetArtifactRow {
|
|
122
|
+
relPath: string;
|
|
123
|
+
contentHash?: string | null;
|
|
124
|
+
lastWrittenAt: string;
|
|
125
|
+
producerMachineId: string;
|
|
126
|
+
state: string;
|
|
127
|
+
}
|
|
128
|
+
/** The `data` object a `working-set-artifact` journal entry carries. */
|
|
129
|
+
export type WorkingSetArtifactData = Record<string, unknown>;
|
|
130
|
+
/** The named error a record-over-cap surfaces (never silent-truncate, never suspect-wedge). */
|
|
131
|
+
export declare class WorkingSetArtifactTooLargeError extends Error {
|
|
132
|
+
readonly recordKey: string;
|
|
133
|
+
readonly bytes: number;
|
|
134
|
+
constructor(recordKey: string, bytes: number);
|
|
135
|
+
}
|
|
136
|
+
export interface BuildWorkingSetArtifactInput {
|
|
137
|
+
row: WorkingSetArtifactRow;
|
|
138
|
+
hlc: HlcTimestamp;
|
|
139
|
+
/** This machine's origin id — the authenticated producer; recordKey binds to it. */
|
|
140
|
+
origin: string;
|
|
141
|
+
observed?: HlcTimestamp;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Build the disclosure-minimized `op:'put'` envelope `data`. recordKey binds relPath to the
|
|
145
|
+
* `origin` (the producer) — NOT to any content field (spec §62). Returns null when the row
|
|
146
|
+
* has no stable identity surface (relPath fails jail / empty origin). Throws
|
|
147
|
+
* WorkingSetArtifactTooLargeError if the projection exceeds the per-entry cap.
|
|
148
|
+
*/
|
|
149
|
+
export declare function buildWorkingSetArtifactData(input: BuildWorkingSetArtifactInput): WorkingSetArtifactData | null;
|
|
150
|
+
/** Throw WorkingSetArtifactTooLargeError if the projected data serializes over the cap. */
|
|
151
|
+
export declare function assertProjectionUnderCap(recordKey: string, data: WorkingSetArtifactData): void;
|
|
152
|
+
export interface BuildWorkingSetArtifactTombstoneInput {
|
|
153
|
+
relPath: string;
|
|
154
|
+
/** The producer of the row being tombstoned — MUST equal `origin` (owner-only, spec §91). */
|
|
155
|
+
producerMachineId: string;
|
|
156
|
+
hlc: HlcTimestamp;
|
|
157
|
+
origin: string;
|
|
158
|
+
deletedAt: string;
|
|
159
|
+
observed?: HlcTimestamp;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Build an `op:'delete'` TOMBSTONE. OWNER-ONLY (spec §91): the recordKey binds relPath to the
|
|
163
|
+
* PRODUCER, and a tombstone is only legitimate from that producer (origin === producerMachineId).
|
|
164
|
+
* Returns null when origin !== producerMachineId (a peer can never tombstone another producer's
|
|
165
|
+
* artifact — a remote-delete authority hole) or the identity surface is degenerate.
|
|
166
|
+
*/
|
|
167
|
+
export declare function buildWorkingSetArtifactTombstoneData(input: BuildWorkingSetArtifactTombstoneInput): WorkingSetArtifactData | null;
|
|
168
|
+
/** A merged working-set artifact view entry. READ-ONLY — never written back locally. */
|
|
169
|
+
export interface MergedWorkingSetArtifactView {
|
|
170
|
+
recordKey: string;
|
|
171
|
+
origin: string;
|
|
172
|
+
data: Record<string, unknown>;
|
|
173
|
+
/** True when this is one of ≥2 concurrent variants of an OPEN conflict (append-both). */
|
|
174
|
+
conflicted: boolean;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Collapse a `Map<recordKey, UnionResult>` into the merged view. HIGH-at-replication /
|
|
178
|
+
* ADVISORY-at-read: an OPEN conflict surfaces BOTH `put` variants (append-both, both are
|
|
179
|
+
* hints, the read never blocks); a delete-resolved key contributes nothing (the
|
|
180
|
+
* delete-resurrection guard). READ-ONLY — never clobbers a local file.
|
|
181
|
+
*/
|
|
182
|
+
export declare function mergeUnionToWorkingSetArtifacts(union: Map<string, UnionResult>): MergedWorkingSetArtifactView[];
|
|
183
|
+
/**
|
|
184
|
+
* Build an OriginRecord for the OWN working-set store (the single-origin materialization the
|
|
185
|
+
* union reader merges against peer replicas). recordKey binds relPath to `origin`; the
|
|
186
|
+
* envelope carries a SYNTHETIC own-origin HLC derived from lastWrittenAt (physical) so the
|
|
187
|
+
* own record has a stable position relative to peer records. Returns null for a degenerate
|
|
188
|
+
* row (relPath fails jail).
|
|
189
|
+
*/
|
|
190
|
+
export declare function workingSetArtifactToOriginRecord(row: WorkingSetArtifactRow, origin: string): OriginRecord | null;
|
|
191
|
+
/** The ReplicatedKindRegistry registration. server.ts registers this; the dual-registry
|
|
192
|
+
* coupling test asserts `kind` is also present in JOURNAL_KINDS. */
|
|
193
|
+
export declare const WORKING_SET_ARTIFACT_KIND_REGISTRATION: {
|
|
194
|
+
readonly kind: "working-set-artifact";
|
|
195
|
+
readonly store: "workingSetArtifact";
|
|
196
|
+
readonly schema: StoreFieldSchema;
|
|
197
|
+
};
|
|
198
|
+
/** The store's contributing journal kinds (for rollback-unmerge's kindsForStore wiring). */
|
|
199
|
+
export declare function workingSetArtifactContributingKinds(): string[];
|
|
200
|
+
/** Impact-tier resolver for ReplicatedStoreReader.tierOf — HIGH (append-both-and-flag). */
|
|
201
|
+
export declare function workingSetArtifactTierOf(_store: string): ImpactTier;
|
|
202
|
+
export type { ReplicatedEnvelope };
|
|
203
|
+
//# sourceMappingURL=WorkingSetArtifactReplicatedStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkingSetArtifactReplicatedStore.d.ts","sourceRoot":"","sources":["../../src/core/WorkingSetArtifactReplicatedStore.ts"],"names":[],"mappings":"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"}
|