@seanhogg/builderforce-memory 2026.9.34 → 2026.10.2
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/errors/SSMError.d.ts +1 -1
- package/dist/errors/SSMError.d.ts.map +1 -1
- package/dist/errors/SSMError.js.map +1 -1
- package/dist/handoff/index.d.ts +5 -5
- package/dist/handoff/index.js +5 -5
- package/dist/handoff/note.d.ts +1 -1
- package/dist/handoff/note.js +1 -1
- package/dist/handoff/pack.d.ts +13 -4
- package/dist/handoff/pack.d.ts.map +1 -1
- package/dist/handoff/pack.js +31 -10
- package/dist/handoff/pack.js.map +1 -1
- package/dist/memory/MemoryStore.d.ts +120 -181
- package/dist/memory/MemoryStore.d.ts.map +1 -1
- package/dist/memory/MemoryStore.js +201 -389
- package/dist/memory/MemoryStore.js.map +1 -1
- package/dist/memory/SemanticRecall.d.ts +64 -0
- package/dist/memory/SemanticRecall.d.ts.map +1 -0
- package/dist/memory/SemanticRecall.js +188 -0
- package/dist/memory/SemanticRecall.js.map +1 -0
- package/dist/memory/records.d.ts +26 -0
- package/dist/memory/records.d.ts.map +1 -0
- package/dist/memory/records.js +71 -0
- package/dist/memory/records.js.map +1 -0
- package/dist/memory/ttlSweeper.d.ts +12 -0
- package/dist/memory/ttlSweeper.d.ts.map +1 -0
- package/dist/memory/ttlSweeper.js +28 -0
- package/dist/memory/ttlSweeper.js.map +1 -0
- package/dist/memory/types.d.ts +142 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +6 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/storage/ScopedFactRepository.d.ts +156 -0
- package/dist/storage/ScopedFactRepository.d.ts.map +1 -0
- package/dist/storage/ScopedFactRepository.js +278 -0
- package/dist/storage/ScopedFactRepository.js.map +1 -0
- package/dist/storage/factImport.d.ts +21 -0
- package/dist/storage/factImport.d.ts.map +1 -0
- package/dist/storage/factImport.js +61 -0
- package/dist/storage/factImport.js.map +1 -0
- package/dist/storage/factMutations.d.ts +46 -0
- package/dist/storage/factMutations.d.ts.map +1 -0
- package/dist/storage/factMutations.js +73 -0
- package/dist/storage/factMutations.js.map +1 -0
- package/dist/storage/factQueries.d.ts +16 -0
- package/dist/storage/factQueries.d.ts.map +1 -0
- package/dist/storage/factQueries.js +36 -0
- package/dist/storage/factQueries.js.map +1 -0
- package/dist/storage/factRetention.d.ts +30 -0
- package/dist/storage/factRetention.d.ts.map +1 -0
- package/dist/storage/factRetention.js +65 -0
- package/dist/storage/factRetention.js.map +1 -0
- package/dist/storage/fileLock.d.ts +56 -0
- package/dist/storage/fileLock.d.ts.map +1 -0
- package/dist/storage/fileLock.js +138 -0
- package/dist/storage/fileLock.js.map +1 -0
- package/dist/storage/fileTransaction.d.ts +33 -0
- package/dist/storage/fileTransaction.d.ts.map +1 -0
- package/dist/storage/fileTransaction.js +108 -0
- package/dist/storage/fileTransaction.js.map +1 -0
- package/dist/storage/idbAdapter.d.ts +95 -0
- package/dist/storage/idbAdapter.d.ts.map +1 -0
- package/dist/storage/idbAdapter.js +311 -0
- package/dist/storage/idbAdapter.js.map +1 -0
- package/dist/storage/nodeFileAdapter.d.ts +127 -0
- package/dist/storage/nodeFileAdapter.d.ts.map +1 -0
- package/dist/storage/nodeFileAdapter.js +275 -0
- package/dist/storage/nodeFileAdapter.js.map +1 -0
- package/dist/storage/syncJournal.d.ts +19 -0
- package/dist/storage/syncJournal.d.ts.map +1 -0
- package/dist/storage/syncJournal.js +46 -0
- package/dist/storage/syncJournal.js.map +1 -0
- package/dist/storage/types.d.ts +207 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +90 -0
- package/dist/storage/types.js.map +1 -0
- package/package.json +3 -3
- package/src/errors/SSMError.ts +1 -0
- package/src/handoff/handoff.test.ts +52 -2
- package/src/handoff/index.ts +5 -5
- package/src/handoff/note.ts +1 -1
- package/src/handoff/pack.ts +32 -9
- package/src/memory/MemoryStore.ts +245 -525
- package/src/memory/SemanticRecall.ts +204 -0
- package/src/memory/records.ts +79 -0
- package/src/memory/ttlSweeper.ts +29 -0
- package/src/memory/types.ts +149 -0
- package/src/storage/ScopedFactRepository.ts +373 -0
- package/src/storage/factImport.ts +73 -0
- package/src/storage/factMutations.ts +106 -0
- package/src/storage/factQueries.ts +41 -0
- package/src/storage/factRetention.ts +82 -0
- package/src/storage/fileLock.ts +162 -0
- package/src/storage/fileTransaction.ts +116 -0
- package/src/storage/idbAdapter.ts +373 -0
- package/src/storage/nodeFileAdapter.ts +331 -0
- package/src/storage/syncJournal.ts +47 -0
- package/src/storage/types.ts +262 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storage/ScopedFactRepository.ts – Scoped, journaled, revision-tracked fact
|
|
3
|
+
* repository over a {@link FactStoragePort}.
|
|
4
|
+
*
|
|
5
|
+
* This is the unit-of-work owner the offline-memory scope requires:
|
|
6
|
+
*
|
|
7
|
+
* - Scope partitions (M03): every operation takes an explicit
|
|
8
|
+
* {@link MemoryScope}; records are physically partitioned by scoped record
|
|
9
|
+
* id and logically filtered by partition, so no code path can recall across
|
|
10
|
+
* account/tenant/project/repository/chat boundaries. The unbound scope is
|
|
11
|
+
* an explicit partition for personal, never-connected memory.
|
|
12
|
+
* - Atomic commits (M02): fact mutation + provenance + tombstone + sync
|
|
13
|
+
* journal + durable counters commit in ONE storage transaction.
|
|
14
|
+
* - Durable identity (M04): every record carries a durable `revision` (the
|
|
15
|
+
* store generation that wrote it) and `opId`; deletes write tombstones;
|
|
16
|
+
* writes may be compare-and-set against a base revision, so concurrent
|
|
17
|
+
* evidence conflicts surface as explicit conflicts instead of unconditional
|
|
18
|
+
* last-write-wins.
|
|
19
|
+
* - Sync-aware retention (M06): facts with `pendingSync` are never evicted by
|
|
20
|
+
* caps and are never dropped by import; TTL expiry is an intentional
|
|
21
|
+
* deletion and writes a tombstone when sync is enabled.
|
|
22
|
+
*
|
|
23
|
+
* The shared write rules live in `factMutations.ts`, retention in
|
|
24
|
+
* `factRetention.ts`, import in `factImport.ts`, sync acknowledgement in
|
|
25
|
+
* `syncJournal.ts`.
|
|
26
|
+
*/
|
|
27
|
+
import { type ImportResult } from './factImport.js';
|
|
28
|
+
import { type ConsistencyNote, type FactProvenance, type FactStoragePort, type JournalEntry, type MemoryScope, type StoredFactRecord, type TombstoneRecord } from './types.js';
|
|
29
|
+
export interface ScopedFactRepositoryOptions {
|
|
30
|
+
/**
|
|
31
|
+
* When true, mutations are journaled (unsynced) until {@link markSynced}
|
|
32
|
+
* acknowledges and prunes them, deletes write tombstones, and cap eviction
|
|
33
|
+
* skips unsynced facts. When false (default — no sync target configured)
|
|
34
|
+
* nothing is journaled and retention behaves exactly like the legacy store.
|
|
35
|
+
*/
|
|
36
|
+
syncEnabled?: boolean;
|
|
37
|
+
/** Identity of this repository context for opIds and change notes. */
|
|
38
|
+
contextId?: string;
|
|
39
|
+
/** Injectable clock (ms). Default: Date.now. */
|
|
40
|
+
now?: () => number;
|
|
41
|
+
}
|
|
42
|
+
export interface RememberRecordInput {
|
|
43
|
+
content: string;
|
|
44
|
+
/** Explicit source time (import). Default: now. */
|
|
45
|
+
timestamp?: number;
|
|
46
|
+
/** Explicit source sequence (import). Default: next durable sequence. */
|
|
47
|
+
seq?: number;
|
|
48
|
+
ttlMs?: number;
|
|
49
|
+
type?: StoredFactRecord['type'];
|
|
50
|
+
tags?: string[];
|
|
51
|
+
importance?: number;
|
|
52
|
+
provenance?: FactProvenance;
|
|
53
|
+
/**
|
|
54
|
+
* Compare-and-set base: `null` asserts the record does not exist; a number
|
|
55
|
+
* asserts the current revision; undefined is a blind write (legacy).
|
|
56
|
+
*/
|
|
57
|
+
expectedRevision?: number | null;
|
|
58
|
+
/** Explicit op identity (import/replay). Default: generated from the durable op counter. */
|
|
59
|
+
opId?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface WriteOutcome {
|
|
62
|
+
record: StoredFactRecord;
|
|
63
|
+
/** True when an existing tombstone for this key was cleared by the write. */
|
|
64
|
+
resurrected: boolean;
|
|
65
|
+
}
|
|
66
|
+
export type { ImportResult };
|
|
67
|
+
export declare class ScopedFactRepository {
|
|
68
|
+
private readonly _storage;
|
|
69
|
+
private readonly _ctx;
|
|
70
|
+
constructor(storage: FactStoragePort, opts?: ScopedFactRepositoryOptions);
|
|
71
|
+
get storage(): FactStoragePort;
|
|
72
|
+
get contextId(): string;
|
|
73
|
+
get syncEnabled(): boolean;
|
|
74
|
+
/** Current durable store generation (0 on a fresh store). */
|
|
75
|
+
revision(): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* Fetches one record by (scope, key), or undefined. Tombstoned = absent.
|
|
78
|
+
* Uses the `byKey` index when the adapter has it; falls back to the primary
|
|
79
|
+
* key (itself an index on the scoped id) only when that index is missing.
|
|
80
|
+
*/
|
|
81
|
+
get(scope: MemoryScope | undefined, key: string): Promise<StoredFactRecord | undefined>;
|
|
82
|
+
/**
|
|
83
|
+
* Facts in `scope` carrying `tag`. Uses the multi-entry `byTag` index when
|
|
84
|
+
* present. A full scan runs only when the index is missing (legacy schema).
|
|
85
|
+
*/
|
|
86
|
+
getByTag(scope: MemoryScope | undefined, tag: string): Promise<StoredFactRecord[]>;
|
|
87
|
+
/**
|
|
88
|
+
* All records in exactly this scope partition — never a wider or narrower
|
|
89
|
+
* set. Expired records are included; TTL filtering is the caller's policy.
|
|
90
|
+
*/
|
|
91
|
+
getAll(scope: MemoryScope | undefined): Promise<StoredFactRecord[]>;
|
|
92
|
+
/** Tombstones, optionally narrowed to one scope partition. */
|
|
93
|
+
tombstones(scope?: MemoryScope): Promise<TombstoneRecord[]>;
|
|
94
|
+
/** Journal entries not yet acknowledged by sync — the pending operation set. */
|
|
95
|
+
pendingOps(): Promise<JournalEntry[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Stores or overwrites a fact in `scope`. With `expectedRevision` set this
|
|
98
|
+
* is a compare-and-set: a mismatch throws a STORAGE_CONFLICT error carrying
|
|
99
|
+
* the current record, so a concurrent writer's evidence is never silently
|
|
100
|
+
* overwritten (M04/M05).
|
|
101
|
+
*/
|
|
102
|
+
remember(scope: MemoryScope | undefined, key: string, input: RememberRecordInput): Promise<WriteOutcome>;
|
|
103
|
+
/**
|
|
104
|
+
* Deletes a fact. With sync enabled the delete is a durable tombstone +
|
|
105
|
+
* journal entry committed in the same transaction, so the deletion
|
|
106
|
+
* propagates and a stale replica cannot resurrect the fact (M04). Without
|
|
107
|
+
* sync this is the legacy hard delete. Deleting an absent key is a no-op.
|
|
108
|
+
*/
|
|
109
|
+
forget(scope: MemoryScope | undefined, key: string, opts?: {
|
|
110
|
+
expectedRevision?: number;
|
|
111
|
+
}): Promise<boolean>;
|
|
112
|
+
/** Removes every fact in the scope. Tombstones each deletion when sync is enabled. */
|
|
113
|
+
clear(scope: MemoryScope | undefined): Promise<number>;
|
|
114
|
+
/**
|
|
115
|
+
* Imports records INTO `scope`, preserving each record's source timestamp,
|
|
116
|
+
* sequence, tags, importance, TTL and provenance. `importedFrom` is stamped
|
|
117
|
+
* onto the provenance so the snapshot origin survives. 'merge' keeps the
|
|
118
|
+
* record with the newer source timestamp; 'overwrite' always writes.
|
|
119
|
+
* Imported facts are pendingSync when sync is enabled — they are new to
|
|
120
|
+
* THIS replica even if another store already had them.
|
|
121
|
+
*/
|
|
122
|
+
importRecords(scope: MemoryScope | undefined, records: StoredFactRecord[], strategy: 'merge' | 'overwrite', importedFrom?: string): Promise<ImportResult>;
|
|
123
|
+
/** Exports every record in the scope, with scope/revision/provenance intact (M03). */
|
|
124
|
+
exportRecords(scope: MemoryScope | undefined): Promise<StoredFactRecord[]>;
|
|
125
|
+
/**
|
|
126
|
+
* Acknowledges synced operations: their journal entries are pruned and the
|
|
127
|
+
* facts/tombstones they produced clear `pendingSync`, making cap eviction
|
|
128
|
+
* eligible again. Only after this may retention touch those records.
|
|
129
|
+
*/
|
|
130
|
+
markSynced(opIds: string[]): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Local-only cap eviction: removes the lowest-value records past
|
|
133
|
+
* `maxEntries`, EXCLUDING records with unsynced operations — pending
|
|
134
|
+
* knowledge is never evicted to satisfy a cache cap. Eviction of synced
|
|
135
|
+
* records writes no tombstone and no journal entry by design: it is local
|
|
136
|
+
* cache management, not a deletion to propagate. Returns the evicted keys.
|
|
137
|
+
*/
|
|
138
|
+
evictForCap(scope: MemoryScope | undefined, maxEntries: number, isExpired: (record: StoredFactRecord) => boolean): Promise<string[]>;
|
|
139
|
+
/**
|
|
140
|
+
* Intentional retention: removes expired records. With sync enabled each
|
|
141
|
+
* expiry is a scoped deletion — tombstone + journal in one transaction — so
|
|
142
|
+
* the expiry propagates instead of resurrecting from a replica. Returns the
|
|
143
|
+
* number of records purged.
|
|
144
|
+
*/
|
|
145
|
+
purgeExpired(scope: MemoryScope | undefined, isExpired: (record: StoredFactRecord) => boolean): Promise<number>;
|
|
146
|
+
/**
|
|
147
|
+
* Subscribes to change notes committed by OTHER contexts sharing this
|
|
148
|
+
* storage (other tabs, extension windows, MCP processes). In-process
|
|
149
|
+
* serialization alone cannot be the distributed consistency boundary; this
|
|
150
|
+
* is the invalidation half of the seam, and compare-and-set is the
|
|
151
|
+
* write-conflict half.
|
|
152
|
+
*/
|
|
153
|
+
subscribe(listener: (note: ConsistencyNote) => void): () => void;
|
|
154
|
+
private _publish;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=ScopedFactRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScopedFactRepository.d.ts","sourceRoot":"","sources":["../../src/storage/ScopedFactRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storage/ScopedFactRepository.ts – Scoped, journaled, revision-tracked fact
|
|
3
|
+
* repository over a {@link FactStoragePort}.
|
|
4
|
+
*
|
|
5
|
+
* This is the unit-of-work owner the offline-memory scope requires:
|
|
6
|
+
*
|
|
7
|
+
* - Scope partitions (M03): every operation takes an explicit
|
|
8
|
+
* {@link MemoryScope}; records are physically partitioned by scoped record
|
|
9
|
+
* id and logically filtered by partition, so no code path can recall across
|
|
10
|
+
* account/tenant/project/repository/chat boundaries. The unbound scope is
|
|
11
|
+
* an explicit partition for personal, never-connected memory.
|
|
12
|
+
* - Atomic commits (M02): fact mutation + provenance + tombstone + sync
|
|
13
|
+
* journal + durable counters commit in ONE storage transaction.
|
|
14
|
+
* - Durable identity (M04): every record carries a durable `revision` (the
|
|
15
|
+
* store generation that wrote it) and `opId`; deletes write tombstones;
|
|
16
|
+
* writes may be compare-and-set against a base revision, so concurrent
|
|
17
|
+
* evidence conflicts surface as explicit conflicts instead of unconditional
|
|
18
|
+
* last-write-wins.
|
|
19
|
+
* - Sync-aware retention (M06): facts with `pendingSync` are never evicted by
|
|
20
|
+
* caps and are never dropped by import; TTL expiry is an intentional
|
|
21
|
+
* deletion and writes a tombstone when sync is enabled.
|
|
22
|
+
*
|
|
23
|
+
* The shared write rules live in `factMutations.ts`, retention in
|
|
24
|
+
* `factRetention.ts`, import in `factImport.ts`, sync acknowledgement in
|
|
25
|
+
* `syncJournal.ts`.
|
|
26
|
+
*/
|
|
27
|
+
import { factsInScope, indexedFacts } from './factQueries.js';
|
|
28
|
+
import { WRITE_STORES, bumpOp, clearTombstone, deleteWithTombstone, journal, readCounters, writeCounters, } from './factMutations.js';
|
|
29
|
+
import { importInto } from './factImport.js';
|
|
30
|
+
import * as retention from './factRetention.js';
|
|
31
|
+
import * as syncJournal from './syncJournal.js';
|
|
32
|
+
import { FACT_KEY_INDEX, FACT_TAG_INDEX, MemoryStorageError, scopedRecordId, scopePartitionKey, } from './types.js';
|
|
33
|
+
export class ScopedFactRepository {
|
|
34
|
+
_storage;
|
|
35
|
+
_ctx;
|
|
36
|
+
constructor(storage, opts = {}) {
|
|
37
|
+
this._storage = storage;
|
|
38
|
+
this._ctx = {
|
|
39
|
+
syncEnabled: opts.syncEnabled ?? false,
|
|
40
|
+
contextId: opts.contextId ?? 'repo',
|
|
41
|
+
now: opts.now ?? (() => Date.now()),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
get storage() {
|
|
45
|
+
return this._storage;
|
|
46
|
+
}
|
|
47
|
+
get contextId() {
|
|
48
|
+
return this._ctx.contextId;
|
|
49
|
+
}
|
|
50
|
+
get syncEnabled() {
|
|
51
|
+
return this._ctx.syncEnabled;
|
|
52
|
+
}
|
|
53
|
+
// ── Reads ─────────────────────────────────────────────────────────────────
|
|
54
|
+
/** Current durable store generation (0 on a fresh store). */
|
|
55
|
+
async revision() {
|
|
56
|
+
return this._storage.transact(['meta'], 'readonly', async (tx) => (await readCounters(tx)).revision);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fetches one record by (scope, key), or undefined. Tombstoned = absent.
|
|
60
|
+
* Uses the `byKey` index when the adapter has it; falls back to the primary
|
|
61
|
+
* key (itself an index on the scoped id) only when that index is missing.
|
|
62
|
+
*/
|
|
63
|
+
async get(scope, key) {
|
|
64
|
+
const id = scopedRecordId(scope, key);
|
|
65
|
+
return this._storage.transact(['facts'], 'readonly', async (tx) => {
|
|
66
|
+
const indexed = await indexedFacts(tx, FACT_KEY_INDEX, key);
|
|
67
|
+
if (indexed !== undefined)
|
|
68
|
+
return indexed.find((r) => r.id === id);
|
|
69
|
+
return tx.get('facts', id);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Facts in `scope` carrying `tag`. Uses the multi-entry `byTag` index when
|
|
74
|
+
* present. A full scan runs only when the index is missing (legacy schema).
|
|
75
|
+
*/
|
|
76
|
+
async getByTag(scope, tag) {
|
|
77
|
+
const partition = scopePartitionKey(scope);
|
|
78
|
+
return this._storage.transact(['facts'], 'readonly', async (tx) => {
|
|
79
|
+
const indexed = await indexedFacts(tx, FACT_TAG_INDEX, tag);
|
|
80
|
+
if (indexed !== undefined) {
|
|
81
|
+
return indexed.filter((r) => scopePartitionKey(r.scope) === partition);
|
|
82
|
+
}
|
|
83
|
+
return (await factsInScope(tx, scope)).filter((r) => r.tags?.includes(tag) ?? false);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* All records in exactly this scope partition — never a wider or narrower
|
|
88
|
+
* set. Expired records are included; TTL filtering is the caller's policy.
|
|
89
|
+
*/
|
|
90
|
+
async getAll(scope) {
|
|
91
|
+
return this._storage.transact(['facts'], 'readonly', (tx) => factsInScope(tx, scope));
|
|
92
|
+
}
|
|
93
|
+
/** Tombstones, optionally narrowed to one scope partition. */
|
|
94
|
+
async tombstones(scope) {
|
|
95
|
+
return this._storage.transact(['tombstones'], 'readonly', async (tx) => {
|
|
96
|
+
const all = await tx.getAll('tombstones');
|
|
97
|
+
if (scope === undefined)
|
|
98
|
+
return all;
|
|
99
|
+
const partition = scopePartitionKey(scope);
|
|
100
|
+
return all.filter((t) => scopePartitionKey(t.scope) === partition);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/** Journal entries not yet acknowledged by sync — the pending operation set. */
|
|
104
|
+
async pendingOps() {
|
|
105
|
+
return syncJournal.pendingOps(this._storage);
|
|
106
|
+
}
|
|
107
|
+
// ── Writes (unit of work: fact + journal + counters in one transaction) ───
|
|
108
|
+
/**
|
|
109
|
+
* Stores or overwrites a fact in `scope`. With `expectedRevision` set this
|
|
110
|
+
* is a compare-and-set: a mismatch throws a STORAGE_CONFLICT error carrying
|
|
111
|
+
* the current record, so a concurrent writer's evidence is never silently
|
|
112
|
+
* overwritten (M04/M05).
|
|
113
|
+
*/
|
|
114
|
+
async remember(scope, key, input) {
|
|
115
|
+
const id = scopedRecordId(scope, key);
|
|
116
|
+
const outcome = await this._storage.transact(WRITE_STORES, 'readwrite', async (tx) => {
|
|
117
|
+
const counters = await readCounters(tx);
|
|
118
|
+
const existing = await tx.get('facts', id);
|
|
119
|
+
if (input.expectedRevision !== undefined) {
|
|
120
|
+
const current = existing?.revision ?? null;
|
|
121
|
+
if (current !== input.expectedRevision) {
|
|
122
|
+
throw new MemoryStorageError('STORAGE_CONFLICT', `Conflict writing "${key}": expected revision ${String(input.expectedRevision)}, found ${String(current)}. Re-read and reconcile instead of overwriting.`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const bumped = bumpOp(counters, this._ctx);
|
|
126
|
+
const opId = input.opId ?? bumped.opId;
|
|
127
|
+
const next = { ...bumped.counters, seq: Math.max(counters.seq + 1, input.seq ?? 0) };
|
|
128
|
+
const record = {
|
|
129
|
+
id,
|
|
130
|
+
key,
|
|
131
|
+
scope: { ...(scope ?? {}) },
|
|
132
|
+
content: input.content,
|
|
133
|
+
timestamp: input.timestamp ?? this._ctx.now(),
|
|
134
|
+
seq: next.seq,
|
|
135
|
+
revision: next.revision,
|
|
136
|
+
opId,
|
|
137
|
+
ttlMs: input.ttlMs,
|
|
138
|
+
type: input.type,
|
|
139
|
+
tags: input.tags,
|
|
140
|
+
importance: input.importance,
|
|
141
|
+
provenance: input.provenance,
|
|
142
|
+
pendingSync: this._ctx.syncEnabled,
|
|
143
|
+
};
|
|
144
|
+
await tx.put('facts', record);
|
|
145
|
+
const resurrected = await clearTombstone(tx, id);
|
|
146
|
+
await journal(tx, this._ctx, {
|
|
147
|
+
opId,
|
|
148
|
+
kind: 'remember',
|
|
149
|
+
id,
|
|
150
|
+
key,
|
|
151
|
+
scope: record.scope,
|
|
152
|
+
revision: next.revision,
|
|
153
|
+
baseRevision: input.expectedRevision ?? undefined,
|
|
154
|
+
timestamp: record.timestamp,
|
|
155
|
+
});
|
|
156
|
+
await writeCounters(tx, next);
|
|
157
|
+
return { record, resurrected };
|
|
158
|
+
});
|
|
159
|
+
this._publish(scope, outcome.record.revision);
|
|
160
|
+
return outcome;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Deletes a fact. With sync enabled the delete is a durable tombstone +
|
|
164
|
+
* journal entry committed in the same transaction, so the deletion
|
|
165
|
+
* propagates and a stale replica cannot resurrect the fact (M04). Without
|
|
166
|
+
* sync this is the legacy hard delete. Deleting an absent key is a no-op.
|
|
167
|
+
*/
|
|
168
|
+
async forget(scope, key, opts = {}) {
|
|
169
|
+
const id = scopedRecordId(scope, key);
|
|
170
|
+
const revision = await this._storage.transact(WRITE_STORES, 'readwrite', async (tx) => {
|
|
171
|
+
const existing = await tx.get('facts', id);
|
|
172
|
+
if (!existing)
|
|
173
|
+
return 0;
|
|
174
|
+
if (opts.expectedRevision !== undefined && existing.revision !== opts.expectedRevision) {
|
|
175
|
+
throw new MemoryStorageError('STORAGE_CONFLICT', `Conflict deleting "${key}": expected revision ${opts.expectedRevision}, found ${existing.revision}.`);
|
|
176
|
+
}
|
|
177
|
+
const next = await deleteWithTombstone(tx, this._ctx, existing, await readCounters(tx), opts.expectedRevision);
|
|
178
|
+
await writeCounters(tx, next);
|
|
179
|
+
return next.revision;
|
|
180
|
+
});
|
|
181
|
+
if (revision > 0)
|
|
182
|
+
this._publish(scope, revision);
|
|
183
|
+
return revision > 0;
|
|
184
|
+
}
|
|
185
|
+
/** Removes every fact in the scope. Tombstones each deletion when sync is enabled. */
|
|
186
|
+
async clear(scope) {
|
|
187
|
+
const { removed, revision } = await this._storage.transact(WRITE_STORES, 'readwrite', async (tx) => {
|
|
188
|
+
const mine = await factsInScope(tx, scope);
|
|
189
|
+
if (mine.length === 0)
|
|
190
|
+
return { removed: 0, revision: 0 };
|
|
191
|
+
let counters = await readCounters(tx);
|
|
192
|
+
for (const record of mine)
|
|
193
|
+
counters = await deleteWithTombstone(tx, this._ctx, record, counters);
|
|
194
|
+
await writeCounters(tx, counters);
|
|
195
|
+
return { removed: mine.length, revision: counters.revision };
|
|
196
|
+
});
|
|
197
|
+
if (removed > 0)
|
|
198
|
+
this._publish(scope, revision);
|
|
199
|
+
return removed;
|
|
200
|
+
}
|
|
201
|
+
// ── Import / export (M03/M06: scope + source time + provenance preserved) ──
|
|
202
|
+
/**
|
|
203
|
+
* Imports records INTO `scope`, preserving each record's source timestamp,
|
|
204
|
+
* sequence, tags, importance, TTL and provenance. `importedFrom` is stamped
|
|
205
|
+
* onto the provenance so the snapshot origin survives. 'merge' keeps the
|
|
206
|
+
* record with the newer source timestamp; 'overwrite' always writes.
|
|
207
|
+
* Imported facts are pendingSync when sync is enabled — they are new to
|
|
208
|
+
* THIS replica even if another store already had them.
|
|
209
|
+
*/
|
|
210
|
+
async importRecords(scope, records, strategy, importedFrom) {
|
|
211
|
+
const { written, skipped, revision } = await importInto(this._storage, this._ctx, scope, records, strategy, importedFrom);
|
|
212
|
+
if (written > 0)
|
|
213
|
+
this._publish(scope, revision);
|
|
214
|
+
return { written, skipped };
|
|
215
|
+
}
|
|
216
|
+
/** Exports every record in the scope, with scope/revision/provenance intact (M03). */
|
|
217
|
+
async exportRecords(scope) {
|
|
218
|
+
return this.getAll(scope);
|
|
219
|
+
}
|
|
220
|
+
// ── Sync acknowledgement (M04/M06) ────────────────────────────────────────
|
|
221
|
+
/**
|
|
222
|
+
* Acknowledges synced operations: their journal entries are pruned and the
|
|
223
|
+
* facts/tombstones they produced clear `pendingSync`, making cap eviction
|
|
224
|
+
* eligible again. Only after this may retention touch those records.
|
|
225
|
+
*/
|
|
226
|
+
async markSynced(opIds) {
|
|
227
|
+
await syncJournal.markSynced(this._storage, opIds);
|
|
228
|
+
}
|
|
229
|
+
// ── Retention (M06) ───────────────────────────────────────────────────────
|
|
230
|
+
/**
|
|
231
|
+
* Local-only cap eviction: removes the lowest-value records past
|
|
232
|
+
* `maxEntries`, EXCLUDING records with unsynced operations — pending
|
|
233
|
+
* knowledge is never evicted to satisfy a cache cap. Eviction of synced
|
|
234
|
+
* records writes no tombstone and no journal entry by design: it is local
|
|
235
|
+
* cache management, not a deletion to propagate. Returns the evicted keys.
|
|
236
|
+
*/
|
|
237
|
+
async evictForCap(scope, maxEntries, isExpired) {
|
|
238
|
+
return retention.evictForCap(this._storage, scope, maxEntries, isExpired);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Intentional retention: removes expired records. With sync enabled each
|
|
242
|
+
* expiry is a scoped deletion — tombstone + journal in one transaction — so
|
|
243
|
+
* the expiry propagates instead of resurrecting from a replica. Returns the
|
|
244
|
+
* number of records purged.
|
|
245
|
+
*/
|
|
246
|
+
async purgeExpired(scope, isExpired) {
|
|
247
|
+
const { purged, revision } = await retention.purgeExpired(this._storage, this._ctx, scope, isExpired);
|
|
248
|
+
if (purged > 0)
|
|
249
|
+
this._publish(scope, revision);
|
|
250
|
+
return purged;
|
|
251
|
+
}
|
|
252
|
+
// ── Cross-context consistency seam (M05) ──────────────────────────────────
|
|
253
|
+
/**
|
|
254
|
+
* Subscribes to change notes committed by OTHER contexts sharing this
|
|
255
|
+
* storage (other tabs, extension windows, MCP processes). In-process
|
|
256
|
+
* serialization alone cannot be the distributed consistency boundary; this
|
|
257
|
+
* is the invalidation half of the seam, and compare-and-set is the
|
|
258
|
+
* write-conflict half.
|
|
259
|
+
*/
|
|
260
|
+
subscribe(listener) {
|
|
261
|
+
if (!this._storage.subscribe)
|
|
262
|
+
return () => undefined;
|
|
263
|
+
return this._storage.subscribe((note) => {
|
|
264
|
+
if (note.origin === this._ctx.contextId)
|
|
265
|
+
return;
|
|
266
|
+
listener(note);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
_publish(scope, revision) {
|
|
270
|
+
this._storage.publish?.({
|
|
271
|
+
kind: 'facts-changed',
|
|
272
|
+
scope: scope ? { ...scope } : undefined,
|
|
273
|
+
revision,
|
|
274
|
+
origin: this._ctx.contextId,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=ScopedFactRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScopedFactRepository.js","sourceRoot":"","sources":["../../src/storage/ScopedFactRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storage/factImport.ts – Import INTO a scope (M03/M06): source timestamp,
|
|
3
|
+
* sequence, tags, importance, TTL and provenance survive; `importedFrom` is
|
|
4
|
+
* stamped onto the provenance; imported facts are `pendingSync` when sync is
|
|
5
|
+
* enabled (new to THIS replica even if another store already had them).
|
|
6
|
+
*/
|
|
7
|
+
import { type MutationContext } from './factMutations.js';
|
|
8
|
+
import { type FactStoragePort, type MemoryScope, type StoredFactRecord } from './types.js';
|
|
9
|
+
export interface ImportResult {
|
|
10
|
+
written: number;
|
|
11
|
+
skipped: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Writes `records` into `scope` in one transaction. 'merge' keeps the record
|
|
15
|
+
* with the newer source timestamp; 'overwrite' always writes. Returns the
|
|
16
|
+
* counts plus the store revision after the import (0 when nothing was written).
|
|
17
|
+
*/
|
|
18
|
+
export declare function importInto(storage: FactStoragePort, ctx: MutationContext, scope: MemoryScope | undefined, records: StoredFactRecord[], strategy: 'merge' | 'overwrite', importedFrom?: string): Promise<ImportResult & {
|
|
19
|
+
revision: number;
|
|
20
|
+
}>;
|
|
21
|
+
//# sourceMappingURL=factImport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factImport.d.ts","sourceRoot":"","sources":["../../src/storage/factImport.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA8E,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACtI,OAAO,EAAkB,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE3G,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC5B,OAAO,EAAE,eAAe,EACxB,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,WAAW,GAAG,SAAS,EAC9B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,QAAQ,EAAE,OAAO,GAAG,WAAW,EAC/B,YAAY,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,YAAY,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA6C9C"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storage/factImport.ts – Import INTO a scope (M03/M06): source timestamp,
|
|
3
|
+
* sequence, tags, importance, TTL and provenance survive; `importedFrom` is
|
|
4
|
+
* stamped onto the provenance; imported facts are `pendingSync` when sync is
|
|
5
|
+
* enabled (new to THIS replica even if another store already had them).
|
|
6
|
+
*/
|
|
7
|
+
import { WRITE_STORES, bumpOp, clearTombstone, journal, readCounters, writeCounters } from './factMutations.js';
|
|
8
|
+
import { scopedRecordId } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Writes `records` into `scope` in one transaction. 'merge' keeps the record
|
|
11
|
+
* with the newer source timestamp; 'overwrite' always writes. Returns the
|
|
12
|
+
* counts plus the store revision after the import (0 when nothing was written).
|
|
13
|
+
*/
|
|
14
|
+
export async function importInto(storage, ctx, scope, records, strategy, importedFrom) {
|
|
15
|
+
return storage.transact(WRITE_STORES, 'readwrite', async (tx) => {
|
|
16
|
+
let counters = await readCounters(tx);
|
|
17
|
+
let written = 0;
|
|
18
|
+
let skipped = 0;
|
|
19
|
+
for (const incoming of records) {
|
|
20
|
+
const id = scopedRecordId(scope, incoming.key);
|
|
21
|
+
const existing = await tx.get('facts', id);
|
|
22
|
+
if (strategy === 'merge' && existing && existing.timestamp >= incoming.timestamp) {
|
|
23
|
+
skipped++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const bumped = bumpOp(counters, ctx);
|
|
27
|
+
counters = { ...bumped.counters, seq: Math.max(counters.seq + 1, incoming.seq ?? 0) };
|
|
28
|
+
const record = {
|
|
29
|
+
...incoming,
|
|
30
|
+
id,
|
|
31
|
+
key: incoming.key,
|
|
32
|
+
scope: { ...(scope ?? {}) },
|
|
33
|
+
seq: incoming.seq ?? counters.seq,
|
|
34
|
+
revision: counters.revision,
|
|
35
|
+
opId: bumped.opId,
|
|
36
|
+
provenance: {
|
|
37
|
+
...incoming.provenance,
|
|
38
|
+
...(importedFrom ? { importedFrom } : {}),
|
|
39
|
+
originalTimestamp: incoming.provenance?.originalTimestamp ?? incoming.timestamp,
|
|
40
|
+
},
|
|
41
|
+
pendingSync: ctx.syncEnabled,
|
|
42
|
+
};
|
|
43
|
+
await tx.put('facts', record);
|
|
44
|
+
await clearTombstone(tx, id);
|
|
45
|
+
await journal(tx, ctx, {
|
|
46
|
+
opId: bumped.opId,
|
|
47
|
+
kind: 'remember',
|
|
48
|
+
id,
|
|
49
|
+
key: incoming.key,
|
|
50
|
+
scope: record.scope,
|
|
51
|
+
revision: counters.revision,
|
|
52
|
+
timestamp: record.timestamp,
|
|
53
|
+
});
|
|
54
|
+
written++;
|
|
55
|
+
}
|
|
56
|
+
if (written > 0)
|
|
57
|
+
await writeCounters(tx, counters);
|
|
58
|
+
return { written, skipped, revision: written > 0 ? counters.revision : 0 };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=factImport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factImport.js","sourceRoot":"","sources":["../../src/storage/factImport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storage/factMutations.ts – The write primitives every repository mutation
|
|
3
|
+
* shares, so the counter bump / tombstone / journal rules live in ONE place:
|
|
4
|
+
*
|
|
5
|
+
* - {@link bumpOp}: next durable revision + operation id.
|
|
6
|
+
* - {@link journal}: append a sync-journal entry — ONLY when sync is enabled.
|
|
7
|
+
* Without a sync target there is nothing to acknowledge, so writing rows
|
|
8
|
+
* already marked synced just grew the journal forever.
|
|
9
|
+
* - {@link deleteWithTombstone}: delete a fact, tombstone it when sync is on,
|
|
10
|
+
* journal the delete — one call for forget, clear and TTL purge.
|
|
11
|
+
* - {@link clearTombstone}: a write resurrects a key (remember, import).
|
|
12
|
+
*/
|
|
13
|
+
import type { JournalEntry, MemoryScope, MetaCounters, StorageStoreName, StorageTransaction } from './types.js';
|
|
14
|
+
export declare const COUNTERS_ID = "counters";
|
|
15
|
+
/** The stores a mutating unit of work spans. */
|
|
16
|
+
export declare const WRITE_STORES: StorageStoreName[];
|
|
17
|
+
/** Who is writing, and under which retention rules. */
|
|
18
|
+
export interface MutationContext {
|
|
19
|
+
readonly syncEnabled: boolean;
|
|
20
|
+
readonly contextId: string;
|
|
21
|
+
readonly now: () => number;
|
|
22
|
+
}
|
|
23
|
+
type CounterReader = Pick<StorageTransaction, 'get'>;
|
|
24
|
+
export declare function readCounters(tx: CounterReader): Promise<MetaCounters>;
|
|
25
|
+
export declare function writeCounters(tx: StorageTransaction, counters: MetaCounters): Promise<void>;
|
|
26
|
+
/** The next revision + operation identity after `counters`. */
|
|
27
|
+
export declare function bumpOp(counters: MetaCounters, ctx: MutationContext): {
|
|
28
|
+
counters: MetaCounters;
|
|
29
|
+
opId: string;
|
|
30
|
+
};
|
|
31
|
+
/** Appends an unsynced journal entry when sync is enabled; a no-op otherwise. */
|
|
32
|
+
export declare function journal(tx: StorageTransaction, ctx: MutationContext, entry: Omit<JournalEntry, 'synced'>): Promise<void>;
|
|
33
|
+
/** Clears any tombstone for `id`; true when one existed (the write resurrected the key). */
|
|
34
|
+
export declare function clearTombstone(tx: StorageTransaction, id: string): Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Deletes one fact as a durable, propagating deletion: bump the counters,
|
|
37
|
+
* delete, tombstone (sync on), journal (sync on). Returns the bumped counters;
|
|
38
|
+
* the caller persists them once per transaction with {@link writeCounters}.
|
|
39
|
+
*/
|
|
40
|
+
export declare function deleteWithTombstone(tx: StorageTransaction, ctx: MutationContext, record: {
|
|
41
|
+
id: string;
|
|
42
|
+
key: string;
|
|
43
|
+
scope: MemoryScope;
|
|
44
|
+
}, counters: MetaCounters, baseRevision?: number): Promise<MetaCounters>;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=factMutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factMutations.d.ts","sourceRoot":"","sources":["../../src/storage/factMutations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACR,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAErB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,WAAW,aAAa,CAAC;AAEtC,gDAAgD;AAChD,eAAO,MAAM,YAAY,EAAE,gBAAgB,EAA+C,CAAC;AAE3F,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC9B;AAED,KAAK,aAAa,GAAG,IAAI,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;AAErD,wBAAsB,YAAY,CAAC,EAAE,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAG3E;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjG;AAED,+DAA+D;AAC/D,wBAAgB,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,eAAe,GAAG;IAAE,QAAQ,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAG7G;AAED,iFAAiF;AACjF,wBAAsB,OAAO,CACzB,EAAE,EAAE,kBAAkB,EACtB,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,4FAA4F;AAC5F,wBAAsB,cAAc,CAAC,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIzF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACrC,EAAE,EAAE,kBAAkB,EACtB,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,EACvD,QAAQ,EAAE,YAAY,EACtB,YAAY,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,YAAY,CAAC,CA0BvB"}
|