@engramx/client 0.15.0 → 0.16.0-rc.11
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/README.md +262 -26
- package/dist/EngramClient.d.ts +235 -229
- package/dist/EngramClient.js +446 -1247
- package/dist/EngramClient.js.map +1 -1
- package/dist/MarketClient.d.ts +137 -0
- package/dist/MarketClient.js +228 -0
- package/dist/MarketClient.js.map +1 -0
- package/dist/ProfileManager.d.ts +4 -4
- package/dist/SessionManager.d.ts +3 -3
- package/dist/SessionManager.js +1 -1
- package/dist/SessionManager.js.map +1 -1
- package/dist/actionReceipt.d.ts +67 -0
- package/dist/actionReceipt.js +76 -2
- package/dist/actionReceipt.js.map +1 -1
- package/dist/auth.d.ts +16 -0
- package/dist/auth.js +38 -5
- package/dist/auth.js.map +1 -1
- package/dist/certifiedAudit.d.ts +32 -6
- package/dist/certifiedAudit.js +75 -14
- package/dist/certifiedAudit.js.map +1 -1
- package/dist/cli.js +100 -26
- package/dist/cli.js.map +1 -1
- package/dist/encryption.d.ts +30 -12
- package/dist/encryption.js +13 -15
- package/dist/encryption.js.map +1 -1
- package/dist/idl.d.ts +69 -0
- package/dist/idl.js +242 -0
- package/dist/idl.js.map +1 -0
- package/dist/index.d.ts +12 -18
- package/dist/index.js +58 -9
- package/dist/index.js.map +1 -1
- package/dist/ledgerfsHash.js +1 -1
- package/dist/ledgerfsHash.js.map +1 -1
- package/dist/lineage.js +8 -5
- package/dist/lineage.js.map +1 -1
- package/dist/memoryArchive.d.ts +428 -0
- package/dist/memoryArchive.js +504 -0
- package/dist/memoryArchive.js.map +1 -0
- package/dist/migrate.js +31 -4
- package/dist/migrate.js.map +1 -1
- package/dist/openEngram.d.ts +55 -0
- package/dist/openEngram.js +185 -0
- package/dist/openEngram.js.map +1 -0
- package/dist/pair.d.ts +33 -2
- package/dist/pair.js +56 -9
- package/dist/pair.js.map +1 -1
- package/dist/types.d.ts +246 -307
- package/dist/version.d.ts +8 -0
- package/dist/version.js +45 -0
- package/dist/version.js.map +1 -0
- package/dist/wire.d.ts +76 -0
- package/dist/wire.js +79 -0
- package/dist/wire.js.map +1 -0
- package/package.json +11 -19
- package/dist/bootstrap.d.ts +0 -21
- package/dist/bootstrap.js +0 -71
- package/dist/bootstrap.js.map +0 -1
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
// The memory log's archive surface — the client half of the engram's export → verify → condense
|
|
2
|
+
// contract (ledgerfs build stage S4d: "condense refuses without an export receipt").
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS IS MORE THAN FIVE PASSTHROUGHS. The canister's rule is that log entries may only be
|
|
5
|
+
// dropped after a chain-recorded export proved they were served. That makes the SDK, not the
|
|
6
|
+
// canister, the place where an owner actually ends up holding a verifiable archive — and it makes
|
|
7
|
+
// every shortcut here a way to destroy evidence. Three specific ones are closed below:
|
|
8
|
+
//
|
|
9
|
+
// 1. A SHORT ARCHIVE THAT REPORTS ITSELF COMPLETE. `getMemoryLogPage` clamps a page and
|
|
10
|
+
// discloses the clamp through `pageCap`; a client that assumed it got `max` would walk the
|
|
11
|
+
// chain in strides longer than the canister actually served and skip whatever fell between.
|
|
12
|
+
// assembleMemoryArchive advances by the window the canister CONFIRMED, and then asserts the
|
|
13
|
+
// range came back whole. It throws rather than return a gap.
|
|
14
|
+
// ⟨2026-10-07, beside the point above — the owner's decision of that day⟩ The engram serves
|
|
15
|
+
// `listMemoryLog` now, and its page states the window directly: `items.length + withheld`
|
|
16
|
+
// entries scanned, starting at `max(startSeq, firstAvailableSeq)`. The walk advances by that,
|
|
17
|
+
// as the owner's own archive page does. `pageCap` is not served and is not read.
|
|
18
|
+
// 2. A PAGE THE CANISTER CANNOT FIT IN ONE RESPONSE. A `#set`/`#restore` entry carries a full
|
|
19
|
+
// manifest — up to 40 chunk references, since maxFileBytes is 10 MiB at a frozen 256 KiB
|
|
20
|
+
// chunkSize (src/engram/LedgerFsInit.mo) — so a manifest-heavy page can exceed the 2 MiB
|
|
21
|
+
// response limit. The engram already clamps to 256 rows for this (MemoryRouter.LOG_PAGE_MAX,
|
|
22
|
+
// down from the library's 1024) and DISCLOSES the clamp as `pageCap`; the margin it leaves is
|
|
23
|
+
// an estimate, not a proof, so a rejection is retried at HALF the page size, down to a single
|
|
24
|
+
// entry, and is never allowed to end the walk early.
|
|
25
|
+
// 3. AN ARCHIVE NOBODY CHECKED. verifyMemoryArchive recomputes every entry hash and rebuilds
|
|
26
|
+
// the ops root, reusing ./ledgerfsHash.ts — the same file the `ledgerfs-goldens` cross-lock
|
|
27
|
+
// pins byte-for-byte against the Motoko encoder. Nothing here reimplements hashing.
|
|
28
|
+
//
|
|
29
|
+
// It also states a CEILING and refuses past it. Assembling an unbounded chain into one JS array
|
|
30
|
+
// is a real failure mode (see ARCHIVE_MAX_RANGE), and splitting is free: each part gets its own
|
|
31
|
+
// receipt, and receipts compose — condense only needs one that covers the prefix being dropped.
|
|
32
|
+
import { CanisterRefusal } from './wire.js';
|
|
33
|
+
import { eventHash, opsRoot as opsRootOverEventHashes, toHex, } from './ledgerfsHash.js';
|
|
34
|
+
// ⟨DELETED 2026-10-05⟩ `MemoryExportReceiptsPage` stood here: "One page of the
|
|
35
|
+
// receipts-and-checkpoints ledger. `next` is the `before` cursor, null = done", with the fields
|
|
36
|
+
// { receipts, checkpoints, firstAvailableSeq, totalEvents, scannedFromSeq, scanTruncated, next }.
|
|
37
|
+
// The 0.16.0 cut split that reply in two. `listMemoryExportReceipts` answers
|
|
38
|
+
// `ReceiptPage = { firstAvailableSeq; page : { items : vec ExportReceiptRow; total; withheld };
|
|
39
|
+
// scannedThroughSeq }`, paged forward by absolute seq, and `listCondenseCheckpoints` is the other
|
|
40
|
+
// half. Six of the seven fields were therefore served by nothing, and nothing in the workspace read
|
|
41
|
+
// the type. Removing the unserved fields would have left a one-field shell, so the type is gone.
|
|
42
|
+
// The rows survive (`MemoryExportReceiptRow`, `MemoryCheckpointRow` above) because they DO mirror
|
|
43
|
+
// the items both lists serve.
|
|
44
|
+
// ── Ceilings ───────────────────────────────────────────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* How many log entries one assembled archive may span.
|
|
47
|
+
*
|
|
48
|
+
* A `#set` entry carries a full manifest, and maxFileBytes / chunkSize = 10 MiB / 256 KiB caps a
|
|
49
|
+
* manifest at 40 chunk references (src/engram/LedgerFsInit.mo) — several kilobytes of decoded JS
|
|
50
|
+
* objects for a single entry. Ten thousand of those is already past what a browser tab should
|
|
51
|
+
* hold, and an owner who runs out of memory mid-walk gets no archive AND no receipt.
|
|
52
|
+
*
|
|
53
|
+
* The ceiling is on the RANGE WIDTH, not on the entries kept, so an author-filtered archive is
|
|
54
|
+
* bounded by the same number: the range is what drives the round trips, and the range is what a
|
|
55
|
+
* receipt attests. Splitting costs nothing — each part gets its own receipt, and condense needs
|
|
56
|
+
* only one receipt covering the prefix it drops.
|
|
57
|
+
*/
|
|
58
|
+
export const ARCHIVE_MAX_RANGE = 10000n;
|
|
59
|
+
/**
|
|
60
|
+
* Entries requested per page before any clamp.
|
|
61
|
+
*
|
|
62
|
+
* Set to the engram's own cap (`MemoryRouter.LOG_PAGE_MAX`, 256) so a full page is never left
|
|
63
|
+
* half-used — the engram clamps the ask to that number anyway and reports the result as
|
|
64
|
+
* `pageCap`. Asking for the cap is safe rather than optimistic because the fallback below it is
|
|
65
|
+
* real: a page that will not fit is retried at half the size, and a walk that had to shrink says
|
|
66
|
+
* so through `smallestPageUsed`.
|
|
67
|
+
* ⟨2026-10-07, beside "reports the result as `pageCap`"⟩ It reports the window it scanned as
|
|
68
|
+
* `items.length + withheld` now; `pageCap` is not served.
|
|
69
|
+
*/
|
|
70
|
+
export const ARCHIVE_PAGE_EVENTS = 256n;
|
|
71
|
+
// ── Byte/shape normalization ───────────────────────────────────────────────────────────────────
|
|
72
|
+
// The agent decodes `vec nat8` as a Uint8Array, but a hand-built stub (or an older agent) can
|
|
73
|
+
// hand back a plain number[]. Copying once here means every hash input downstream is the shape
|
|
74
|
+
// ledgerfsHash expects, and a wrong shape fails at the boundary instead of producing a digest
|
|
75
|
+
// over the wrong bytes.
|
|
76
|
+
const asBytes = (v) => v instanceof Uint8Array ? v : Uint8Array.from(v);
|
|
77
|
+
const rawPrincipal = (p) => p.toUint8Array();
|
|
78
|
+
function toHashManifest(m) {
|
|
79
|
+
return {
|
|
80
|
+
chunks: m.chunks.map((c) => ({
|
|
81
|
+
hash: asBytes(c.hash),
|
|
82
|
+
size: c.size,
|
|
83
|
+
birthSeq: c.birthSeq,
|
|
84
|
+
})),
|
|
85
|
+
totalSize: m.totalSize,
|
|
86
|
+
contentRoot: asBytes(m.contentRoot),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function toHashImportNote(n) {
|
|
90
|
+
if ('genesis' in n) {
|
|
91
|
+
const b = n.genesis.boundTo;
|
|
92
|
+
return { kind: 'genesis', boundTo: b.length === 0 ? null : asBytes(b[0]) };
|
|
93
|
+
}
|
|
94
|
+
if ('legacyGap' in n)
|
|
95
|
+
return { kind: 'legacyGap', firstSurviving: n.legacyGap.firstSurviving };
|
|
96
|
+
if ('legacyTruncated' in n) {
|
|
97
|
+
return {
|
|
98
|
+
kind: 'legacyTruncated',
|
|
99
|
+
storedBytes: n.legacyTruncated.storedBytes,
|
|
100
|
+
prefixHash: asBytes(n.legacyTruncated.prefixHash),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if ('legacyDelete' in n)
|
|
104
|
+
return { kind: 'legacyDelete' };
|
|
105
|
+
if ('legacyUnreconstructable' in n) {
|
|
106
|
+
return {
|
|
107
|
+
kind: 'legacyUnreconstructable',
|
|
108
|
+
contentHash: asBytes(n.legacyUnreconstructable.contentHash),
|
|
109
|
+
reason: n.legacyUnreconstructable.reason,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Not reachable through Candid: the decoder rejects a tag the IDL does not declare, so an
|
|
113
|
+
// unknown note here means the wire shape was hand-built. Refuse rather than hash a guess.
|
|
114
|
+
throw new Error(`Unrecognized import note in a log entry: ${JSON.stringify(Object.keys(n))}`);
|
|
115
|
+
}
|
|
116
|
+
function toHashOp(op) {
|
|
117
|
+
if ('append' in op) {
|
|
118
|
+
return {
|
|
119
|
+
kind: 'append',
|
|
120
|
+
deltaHash: asBytes(op.append.deltaHash),
|
|
121
|
+
deltaSize: op.append.deltaSize,
|
|
122
|
+
newSize: op.append.newSize,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if ('set' in op)
|
|
126
|
+
return { kind: 'set', manifest: toHashManifest(op.set.manifest) };
|
|
127
|
+
if ('restore' in op) {
|
|
128
|
+
return {
|
|
129
|
+
kind: 'restore',
|
|
130
|
+
toVersion: op.restore.toVersion,
|
|
131
|
+
manifest: toHashManifest(op.restore.manifest),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if ('tombstone' in op)
|
|
135
|
+
return { kind: 'tombstone' };
|
|
136
|
+
if ('evict' in op) {
|
|
137
|
+
return {
|
|
138
|
+
kind: 'evict',
|
|
139
|
+
fromVersion: op.evict.fromVersion,
|
|
140
|
+
toVersion: op.evict.toVersion,
|
|
141
|
+
chunkCount: op.evict.chunkCount,
|
|
142
|
+
byteCount: op.evict.byteCount,
|
|
143
|
+
evictedRoot: asBytes(op.evict.evictedRoot),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if ('condense' in op) {
|
|
147
|
+
return {
|
|
148
|
+
kind: 'condense',
|
|
149
|
+
fromSeq: op.condense.fromSeq,
|
|
150
|
+
toSeq: op.condense.toSeq,
|
|
151
|
+
opCount: op.condense.opCount,
|
|
152
|
+
opsRoot: asBytes(op.condense.opsRoot),
|
|
153
|
+
headAtBoundary: asBytes(op.condense.headAtBoundary),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if ('exported' in op) {
|
|
157
|
+
const f = op.exported.authorFilter;
|
|
158
|
+
return {
|
|
159
|
+
kind: 'exported',
|
|
160
|
+
fromSeq: op.exported.fromSeq,
|
|
161
|
+
toSeq: op.exported.toSeq,
|
|
162
|
+
opsRoot: asBytes(op.exported.opsRoot),
|
|
163
|
+
authorFilterRaw: f.length === 0 ? null : rawPrincipal(f[0]),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if ('imported' in op)
|
|
167
|
+
return { kind: 'imported', note: toHashImportNote(op.imported) };
|
|
168
|
+
throw new Error(`Unrecognized operation in a log entry: ${JSON.stringify(Object.keys(op))}`);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* A decoded log entry in the form the frozen hash contract operates on.
|
|
172
|
+
*
|
|
173
|
+
* The store-global sentinel substitution (`#condense`/`#exported` hash with an empty path, zero
|
|
174
|
+
* version, zero size and a zero content root) is NOT applied here — ledgerfsHash owns it, in the
|
|
175
|
+
* one place the Motoko encoder is mirrored. Doing it twice is how the two drift.
|
|
176
|
+
*/
|
|
177
|
+
export function toLedgerFsEvent(e) {
|
|
178
|
+
return {
|
|
179
|
+
seq: e.seq,
|
|
180
|
+
path: e.path,
|
|
181
|
+
version: e.version,
|
|
182
|
+
op: toHashOp(e.op),
|
|
183
|
+
byRaw: rawPrincipal(e.by),
|
|
184
|
+
at: e.atNs,
|
|
185
|
+
size: e.size,
|
|
186
|
+
contentRoot: asBytes(e.contentRoot),
|
|
187
|
+
prev: asBytes(e.prev),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/** The canonical hash of one log entry, recomputed from its decoded fields. */
|
|
191
|
+
export const memoryLogEventHash = (e) => eventHash(toLedgerFsEvent(e));
|
|
192
|
+
/**
|
|
193
|
+
* Whether a failed page looks like the engram could not fit the response, as opposed to refusing
|
|
194
|
+
* the call.
|
|
195
|
+
*
|
|
196
|
+
* Deliberately STRICT. Halving on every error would spend six round trips turning an
|
|
197
|
+
* authorization refusal into the same refusal, and would report it under a misleading heading;
|
|
198
|
+
* an unmatched error is rethrown with the cursor attached instead. If a new replica wording slips
|
|
199
|
+
* past this, the archive fails LOUDLY — the walk stops and throws — which is the safe direction:
|
|
200
|
+
* the alternative is an archive that is quietly short.
|
|
201
|
+
*/
|
|
202
|
+
export function isResponseTooLarge(err) {
|
|
203
|
+
// A client-side allocation failure is the same class of problem from the owner's side: the
|
|
204
|
+
// page was too big to hold. Halving is the same remedy.
|
|
205
|
+
if (err instanceof RangeError)
|
|
206
|
+
return true;
|
|
207
|
+
const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
208
|
+
return /payload size|response size|message size|response is too large|too large|exceeds[^.]{0,60}\b(size|limit|maximum)\b|IC0504/i.test(text);
|
|
209
|
+
}
|
|
210
|
+
const asBig = (v) => (typeof v === 'bigint' ? v : BigInt(v));
|
|
211
|
+
/**
|
|
212
|
+
* Walk a range of the memory log into one archive, or throw.
|
|
213
|
+
*
|
|
214
|
+
* WHAT IT REFUSES TO DO, in order of how badly each would hurt:
|
|
215
|
+
* - report a short archive as whole (every seq in an unfiltered range is asserted present);
|
|
216
|
+
* - assume it received `max` entries (it advances by the window the engram confirmed via
|
|
217
|
+
* `pageCap`, and by the highest seq actually returned when that is further);
|
|
218
|
+
* - give up on a page the engram could not fit (halves and retries, down to one entry);
|
|
219
|
+
* - start below the retained floor (a condensed prefix is unattestable, so an explicit request
|
|
220
|
+
* for it is refused rather than silently raised);
|
|
221
|
+
* - assemble more than {@link ARCHIVE_MAX_RANGE} entries in one pass.
|
|
222
|
+
* ⟨2026-10-07, beside the second point — the owner's decision of that day⟩ The window the engram
|
|
223
|
+
* confirmed is `page.items.length + page.withheld` now — the entries it scanned for the page,
|
|
224
|
+
* starting at `max(startSeq, firstAvailableSeq)` — the rule the engram's own `listMemoryLog` doc
|
|
225
|
+
* states and the owner's archive page walks by. Two refusals join the list: a page whose window is
|
|
226
|
+
* EMPTY while the range still has entries to serve (the walk would spin, or end short), and a floor
|
|
227
|
+
* that rises above the cursor mid-walk (a condense ran during the walk, and what it dropped can no
|
|
228
|
+
* longer be served or attested).
|
|
229
|
+
*
|
|
230
|
+
* Termination is structural: the cursor advances by at least one seq per successful page, and the
|
|
231
|
+
* page size strictly halves per size failure until it reaches one.
|
|
232
|
+
*/
|
|
233
|
+
export async function assembleMemoryArchive(fetchPage, options = {}) {
|
|
234
|
+
const authorFilter = options.authorFilter;
|
|
235
|
+
const filterArg = authorFilter === undefined ? [] : [authorFilter];
|
|
236
|
+
const explicitFrom = options.fromSeq === undefined ? null : asBig(options.fromSeq);
|
|
237
|
+
const explicitTo = options.toSeq === undefined ? null : asBig(options.toSeq);
|
|
238
|
+
if (explicitFrom !== null && explicitFrom < 1n) {
|
|
239
|
+
throw new Error('assembleMemoryArchive: fromSeq starts at 1 — the log has no entry 0.');
|
|
240
|
+
}
|
|
241
|
+
const requestedStart = explicitFrom ?? 1n;
|
|
242
|
+
let requested = options.pageEvents === undefined ? ARCHIVE_PAGE_EVENTS : asBig(options.pageEvents);
|
|
243
|
+
if (requested < 1n)
|
|
244
|
+
requested = 1n;
|
|
245
|
+
// Only ever decreases: the fallback shrinks the page and never grows it back, so one oversized
|
|
246
|
+
// page slows the rest of the walk rather than re-triggering the same rejection every stride.
|
|
247
|
+
let smallestPageUsed = requested;
|
|
248
|
+
const ceiling = (() => {
|
|
249
|
+
if (options.maxRange === undefined)
|
|
250
|
+
return ARCHIVE_MAX_RANGE;
|
|
251
|
+
const asked = asBig(options.maxRange);
|
|
252
|
+
if (asked < 1n)
|
|
253
|
+
return 1n;
|
|
254
|
+
return asked < ARCHIVE_MAX_RANGE ? asked : ARCHIVE_MAX_RANGE;
|
|
255
|
+
})();
|
|
256
|
+
const bySeq = new Map();
|
|
257
|
+
let cursor = requestedStart;
|
|
258
|
+
let fromSeq = requestedStart;
|
|
259
|
+
let toSeq = explicitTo;
|
|
260
|
+
let pagesFetched = 0;
|
|
261
|
+
// Set from the first page and refreshed by every one after it. No placeholder value: an
|
|
262
|
+
// uninitialized read would be a bug, and a stand-in zero head or zero total is exactly the
|
|
263
|
+
// fabricated reading this whole surface exists to avoid.
|
|
264
|
+
let head;
|
|
265
|
+
let totalEvents;
|
|
266
|
+
let firstAvailableSeq;
|
|
267
|
+
let firstPage = true;
|
|
268
|
+
for (;;) {
|
|
269
|
+
// Where THIS request started. Held separately from `cursor` because the first page can move
|
|
270
|
+
// `fromSeq` up to the retained floor, and the stride must still be measured from where the
|
|
271
|
+
// engram actually began scanning — not from a raised cursor, which would jump a whole window
|
|
272
|
+
// past entries the very page in hand did not reach.
|
|
273
|
+
const pageStart = cursor;
|
|
274
|
+
let reply;
|
|
275
|
+
try {
|
|
276
|
+
reply = await fetchPage(pageStart, requested, filterArg);
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
if (!isResponseTooLarge(err)) {
|
|
280
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
281
|
+
throw new Error(`assembleMemoryArchive: the engram refused the page at seq ${pageStart} (${requested} entries): ${why}. Nothing was assembled.`, { cause: err });
|
|
282
|
+
}
|
|
283
|
+
if (requested <= 1n) {
|
|
284
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
285
|
+
throw new Error(`assembleMemoryArchive: log entry ${pageStart} could not be served even one at a time: ${why}. The archive stops here rather than skipping it — export the ranges either side of ${pageStart} separately and report this entry.`, { cause: err });
|
|
286
|
+
}
|
|
287
|
+
requested = requested / 2n; // bigint division floors; requested > 1 here, so it drops
|
|
288
|
+
if (requested < 1n)
|
|
289
|
+
requested = 1n;
|
|
290
|
+
if (requested < smallestPageUsed)
|
|
291
|
+
smallestPageUsed = requested;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
// A typed refusal is the engram's answer, not a transport failure: it is never halved into
|
|
295
|
+
// (halving an authorization refusal would spend six round trips arriving at the same answer).
|
|
296
|
+
if ('Err' in reply) {
|
|
297
|
+
const refusal = new CanisterRefusal('listMemoryLog', reply.Err);
|
|
298
|
+
throw new Error(`assembleMemoryArchive: the engram refused the page at seq ${pageStart} (${requested} entries): ${refusal.message}. Nothing was assembled.`, { cause: refusal });
|
|
299
|
+
}
|
|
300
|
+
const page = reply.Ok;
|
|
301
|
+
pagesFetched += 1;
|
|
302
|
+
head = asBytes(page.head);
|
|
303
|
+
totalEvents = page.page.total;
|
|
304
|
+
firstAvailableSeq = page.firstAvailableSeq;
|
|
305
|
+
if (firstPage) {
|
|
306
|
+
firstPage = false;
|
|
307
|
+
if (page.firstAvailableSeq > pageStart) {
|
|
308
|
+
if (explicitFrom !== null) {
|
|
309
|
+
throw new Error(`assembleMemoryArchive: entries below seq ${page.firstAvailableSeq} were condensed away and can no longer be served, so a range starting at ${explicitFrom} cannot be attested. Start at ${page.firstAvailableSeq} or later.`);
|
|
310
|
+
}
|
|
311
|
+
// Only `fromSeq` moves. The engram began scanning at the floor, and the stride below is
|
|
312
|
+
// measured from there.
|
|
313
|
+
fromSeq = page.firstAvailableSeq;
|
|
314
|
+
}
|
|
315
|
+
if (explicitTo !== null && explicitTo > page.page.total) {
|
|
316
|
+
throw new Error(`assembleMemoryArchive: the log holds ${page.page.total} entries, so seq ${explicitTo} does not exist yet.`);
|
|
317
|
+
}
|
|
318
|
+
toSeq = explicitTo ?? page.page.total;
|
|
319
|
+
// Guard BEFORE subtracting. An empty log answers total = 0 with a floor of 1, so
|
|
320
|
+
// `toSeq - fromSeq` runs backwards there; in TypeScript that yields a negative BigInt
|
|
321
|
+
// rather than trapping, which would silently size the ceiling check the wrong way round.
|
|
322
|
+
const width = toSeq < fromSeq ? 0n : toSeq - fromSeq + 1n;
|
|
323
|
+
if (width === 0n) {
|
|
324
|
+
return {
|
|
325
|
+
events: [],
|
|
326
|
+
fromSeq,
|
|
327
|
+
toSeq,
|
|
328
|
+
authorFilter: authorFilter ?? null,
|
|
329
|
+
totalEvents,
|
|
330
|
+
firstAvailableSeq,
|
|
331
|
+
head,
|
|
332
|
+
pagesFetched,
|
|
333
|
+
smallestPageUsed,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (width > ceiling) {
|
|
337
|
+
throw new Error(`assembleMemoryArchive: ${width} log entries (seq ${fromSeq}–${toSeq}) is past the ${ceiling}-entry ceiling for one archive. Export it in parts — each part gets its own receipt, and a condense only needs a receipt covering the prefix it drops.`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
else if (page.firstAvailableSeq > pageStart) {
|
|
341
|
+
// The floor rose above the cursor between two pages: a condense ran during the walk, and the
|
|
342
|
+
// engram clamped this page UP past entries the range still needs. They can no longer be
|
|
343
|
+
// served, so the range cannot be attested; a filtered walk would otherwise skip them without
|
|
344
|
+
// noticing. Refused, rather than returned short.
|
|
345
|
+
throw new Error(`assembleMemoryArchive: entries below seq ${page.firstAvailableSeq} were condensed away while the walk was at seq ${pageStart}, so seq ${pageStart}–${page.firstAvailableSeq - 1n} can no longer be served. Nothing is returned — walk the range again from ${page.firstAvailableSeq}.`);
|
|
346
|
+
}
|
|
347
|
+
// THE WINDOW IS THE CURSOR, and the page states it: the entries the engram scanned for this
|
|
348
|
+
// page, `items.length + withheld`, starting at max(pageStart, the floor). Never the rows alone
|
|
349
|
+
// — an author filter makes a page short or empty while the chain continues, and striding by
|
|
350
|
+
// the rows returned would skip every entry the filter removed and stall outright on an empty
|
|
351
|
+
// page; `withheld` is what the filter removed, so the sum is the scan.
|
|
352
|
+
const window = BigInt(page.page.items.length) + page.page.withheld;
|
|
353
|
+
if (window === 0n) {
|
|
354
|
+
// Nothing scanned at a cursor the range still covers (cursor <= toSeq <= total): the engram
|
|
355
|
+
// served nothing for entries that exist. Advancing would spin; stopping would return short.
|
|
356
|
+
throw new Error(`assembleMemoryArchive: the engram scanned no entries at seq ${pageStart} while the log holds ${page.page.total}, so seq ${pageStart}–${toSeq} came back empty. A short archive is not an archive — nothing is returned.`);
|
|
357
|
+
}
|
|
358
|
+
let highestSeen = null;
|
|
359
|
+
for (const e of page.page.items) {
|
|
360
|
+
if (highestSeen === null || e.seq > highestSeen)
|
|
361
|
+
highestSeen = e.seq;
|
|
362
|
+
if (e.seq < fromSeq || e.seq > toSeq)
|
|
363
|
+
continue; // an overshooting window, not an error
|
|
364
|
+
if (!bySeq.has(e.seq))
|
|
365
|
+
bySeq.set(e.seq, e);
|
|
366
|
+
}
|
|
367
|
+
// Advance past the window that was actually covered. A request below the retained floor is
|
|
368
|
+
// clamped UP by the engram, so the window began at max(pageStart, floor). The last clause is
|
|
369
|
+
// belt-and-braces: if a page ever returns an entry beyond the window it declared, the cursor
|
|
370
|
+
// still ends up past it. Every clause only raises the cursor, so the walk always moves.
|
|
371
|
+
const windowStart = pageStart < page.firstAvailableSeq ? page.firstAvailableSeq : pageStart;
|
|
372
|
+
let next = windowStart + window;
|
|
373
|
+
if (highestSeen !== null && highestSeen + 1n > next)
|
|
374
|
+
next = highestSeen + 1n;
|
|
375
|
+
cursor = next;
|
|
376
|
+
if (cursor > toSeq)
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
const events = [...bySeq.values()].sort((a, b) => (a.seq < b.seq ? -1 : a.seq > b.seq ? 1 : 0));
|
|
380
|
+
// The anti-fail-to-empty assertion. Only an UNFILTERED archive can be checked this way: with a
|
|
381
|
+
// filter, the engram is expected to skip entries, and the client has no way to know which. What
|
|
382
|
+
// holds in both cases is that every window covering [fromSeq, toSeq] was requested, which the
|
|
383
|
+
// loop above guarantees by construction — and, since 0.16.0, that every window it covered was
|
|
384
|
+
// counted by the engram itself (`withheld`).
|
|
385
|
+
if (authorFilter === undefined) {
|
|
386
|
+
let expect = fromSeq;
|
|
387
|
+
for (const e of events) {
|
|
388
|
+
if (e.seq !== expect) {
|
|
389
|
+
throw new Error(`assembleMemoryArchive: the walk of seq ${fromSeq}–${toSeq} came back missing entry ${expect} (next entry served was ${e.seq}). A short archive is not an archive — nothing is returned.`);
|
|
390
|
+
}
|
|
391
|
+
expect += 1n;
|
|
392
|
+
}
|
|
393
|
+
if (expect !== toSeq + 1n) {
|
|
394
|
+
throw new Error(`assembleMemoryArchive: the walk of seq ${fromSeq}–${toSeq} stopped after entry ${expect - 1n}. A short archive is not an archive — nothing is returned.`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
events,
|
|
399
|
+
fromSeq,
|
|
400
|
+
toSeq: toSeq,
|
|
401
|
+
authorFilter: authorFilter ?? null,
|
|
402
|
+
totalEvents,
|
|
403
|
+
firstAvailableSeq,
|
|
404
|
+
head,
|
|
405
|
+
pagesFetched,
|
|
406
|
+
smallestPageUsed,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
const bytesEqual = (a, b) => {
|
|
410
|
+
if (a.length !== b.length)
|
|
411
|
+
return false;
|
|
412
|
+
for (let i = 0; i < a.length; i++)
|
|
413
|
+
if (a[i] !== b[i])
|
|
414
|
+
return false;
|
|
415
|
+
return true;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Rebuild the ops root over a set of log entries exactly as the engram does: the entries in
|
|
419
|
+
* `[fromSeq, toSeq]` that pass the author filter, in ascending seq order, each hashed with
|
|
420
|
+
* ledgerfsHash and wrapped as an ops leaf.
|
|
421
|
+
*
|
|
422
|
+
* An empty covered set has a DEFINED root — the honest attestation that this author wrote nothing
|
|
423
|
+
* in the range — so it is computed, never special-cased into a failure.
|
|
424
|
+
*/
|
|
425
|
+
export function recomputeOpsRoot(events, range) {
|
|
426
|
+
const filter = range.authorFilter && range.authorFilter.length === 1 ? range.authorFilter[0] : null;
|
|
427
|
+
const filterText = filter === null ? null : filter.toText();
|
|
428
|
+
const covered = events
|
|
429
|
+
.filter((e) => e.seq >= range.fromSeq && e.seq <= range.toSeq)
|
|
430
|
+
.filter((e) => filterText === null || e.by.toText() === filterText)
|
|
431
|
+
.sort((a, b) => (a.seq < b.seq ? -1 : a.seq > b.seq ? 1 : 0));
|
|
432
|
+
return { root: opsRootOverEventHashes(covered.map(memoryLogEventHash)), covered };
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Check an archive against the receipt (or checkpoint) that attests it.
|
|
436
|
+
*
|
|
437
|
+
* The hashing is ./ledgerfsHash.ts throughout — the file the `ledgerfs-goldens` cross-lock pins
|
|
438
|
+
* against the Motoko encoder. Nothing is reimplemented here, because a second implementation of
|
|
439
|
+
* the hash contract is exactly how an owner ends up being told their evidence is bad when the two
|
|
440
|
+
* copies merely drifted.
|
|
441
|
+
*
|
|
442
|
+
* Never throws on a mismatch: a mismatch is a RESULT to read, and the wording of
|
|
443
|
+
* {@link MemoryArchiveVerification.explanation} deliberately does not accuse the engram.
|
|
444
|
+
*/
|
|
445
|
+
export function verifyMemoryArchive(archive, attestation) {
|
|
446
|
+
const notes = [];
|
|
447
|
+
const filtered = (attestation.authorFilter?.length ?? 0) === 1;
|
|
448
|
+
const { root, covered } = recomputeOpsRoot(archive.events, attestation);
|
|
449
|
+
const attested = asBytes(attestation.opsRoot);
|
|
450
|
+
const matches = bytesEqual(root, attested);
|
|
451
|
+
// Guard before subtracting: an inverted range (toSeq below fromSeq) is a malformed attestation,
|
|
452
|
+
// not a width of minus one.
|
|
453
|
+
const expected = filtered || attestation.toSeq < attestation.fromSeq
|
|
454
|
+
? null
|
|
455
|
+
: Number(attestation.toSeq - attestation.fromSeq + 1n);
|
|
456
|
+
let rangeContiguous = true;
|
|
457
|
+
if (!filtered) {
|
|
458
|
+
let expect = attestation.fromSeq;
|
|
459
|
+
for (const e of covered) {
|
|
460
|
+
if (e.seq !== expect) {
|
|
461
|
+
rangeContiguous = false;
|
|
462
|
+
notes.push(`The archive jumps from seq ${expect - 1n} to ${e.seq}; entries ${expect}–${e.seq - 1n} are not in it.`);
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
expect += 1n;
|
|
466
|
+
}
|
|
467
|
+
if (rangeContiguous && expected !== null && covered.length !== expected) {
|
|
468
|
+
rangeContiguous = false;
|
|
469
|
+
notes.push(`The attestation covers seq ${attestation.fromSeq}–${attestation.toSeq} (${expected} entries) but the archive holds ${covered.length} of them.`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
notes.push('The attestation is author-filtered, so entry count and continuity cannot be checked from the archive alone — only the ops root can be.');
|
|
474
|
+
}
|
|
475
|
+
// Linkage between ADJACENT held entries only. The first entry's `prev` points at something the
|
|
476
|
+
// archive does not hold, and checking it against nothing would be theatre.
|
|
477
|
+
let chainLinked = true;
|
|
478
|
+
for (let i = 1; i < covered.length; i++) {
|
|
479
|
+
const prev = covered[i - 1];
|
|
480
|
+
const here = covered[i];
|
|
481
|
+
if (here.seq !== prev.seq + 1n)
|
|
482
|
+
continue; // not adjacent in the chain; nothing to link
|
|
483
|
+
if (!bytesEqual(asBytes(here.prev), memoryLogEventHash(prev))) {
|
|
484
|
+
chainLinked = false;
|
|
485
|
+
notes.push(`Entry ${here.seq} does not link back to entry ${prev.seq}.`);
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const explanation = matches
|
|
490
|
+
? null
|
|
491
|
+
: "The ops root recomputed from this archive is not the one the receipt records. Read that as a question, not a verdict: the common causes sit on this side of the wire — an archive missing entries, an archive assembled with a different author filter than the receipt used, or a version skew between this SDK's copy of the hash rules and the engram's. Check the counts and notes above first. If they all line up and the roots still differ, keep the archive and report both roots as they stand rather than condensing anything.";
|
|
492
|
+
return {
|
|
493
|
+
matches,
|
|
494
|
+
computedOpsRoot: toHex(root),
|
|
495
|
+
attestedOpsRoot: toHex(attested),
|
|
496
|
+
eventsCovered: covered.length,
|
|
497
|
+
eventsExpected: expected,
|
|
498
|
+
rangeContiguous,
|
|
499
|
+
chainLinked,
|
|
500
|
+
notes,
|
|
501
|
+
explanation,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
//# sourceMappingURL=memoryArchive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryArchive.js","sourceRoot":"","sources":["../src/memoryArchive.ts"],"names":[],"mappings":"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"}
|
package/dist/migrate.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { execFileSync } from 'child_process';
|
|
4
4
|
import { EngramClient } from './EngramClient.js';
|
|
5
|
+
import { NPX_CLIENT } from './version.js';
|
|
5
6
|
function getOpenClawDir() {
|
|
6
7
|
return process.env['OPENCLAW_CONFIG_DIR'] || path.join(process.env['HOME'] || '', '.openclaw');
|
|
7
8
|
}
|
|
@@ -107,8 +108,34 @@ export async function hasValidPairing(options) {
|
|
|
107
108
|
sessionKeyPath: options.sessionKeyPath,
|
|
108
109
|
host: options.host,
|
|
109
110
|
});
|
|
110
|
-
//
|
|
111
|
-
|
|
111
|
+
// Ask the engram directly whether it recognises this session key, rather than inferring it
|
|
112
|
+
// from whether some gated read happened to come back.
|
|
113
|
+
//
|
|
114
|
+
// This probe used to call listBackups() and read any resolved promise as "paired". It reached
|
|
115
|
+
// the right verdict only via a carve-out: every unpaired caller — anonymous, revoked,
|
|
116
|
+
// unrelated — already trapped, while a PAIRED agent that merely lacked canReadBackup
|
|
117
|
+
// was handed `[]` and so looked like a paired agent with no backups. That carve-out closed on
|
|
118
|
+
// 2026-08-16; the same agent now gets a refusal, the SDK throws it, and the old probe
|
|
119
|
+
// would call a genuinely paired agent UNPAIRED and send the user back through pairing.
|
|
120
|
+
//
|
|
121
|
+
// ⟨0.16.0, L2.2 — fixed 2026-08-25⟩ getOwnerConsole(), NOT `status().authorized`.
|
|
122
|
+
//
|
|
123
|
+
// The paragraph this replaces was true of v0.15.0 and is false in BOTH halves at HEAD. The
|
|
124
|
+
// engram's `status()` is gone (it split into public `about()` + gated `getOwnerConsole()`),
|
|
125
|
+
// and `About` carries no `authorized` — `grep authorized src/engram/engram.did` finds only
|
|
126
|
+
// prose and an unrelated `authorizedBy`. So the read DECODED FINE and answered `undefined`,
|
|
127
|
+
// and every caller — owner, agent, guardian alike — was told they were UNPAIRED
|
|
128
|
+
// immediately after pairing successfully. This shipped: dist/migrate.js carried it too.
|
|
129
|
+
//
|
|
130
|
+
// `getOwnerConsole` is the gated half of that split, and its gate is #anyRole DESPITE the name
|
|
131
|
+
// (main.mo: `#anyRole, "getOwnerConsole"`; Gated.mo: "owner | active agent | active
|
|
132
|
+
// guardian") — exactly the audience the old flag answered `true` for. Reaching the record AT
|
|
133
|
+
// ALL is the authorization, so an `Ok` IS the recognition and the SDK throws on `#Err`, which
|
|
134
|
+
// the catch below turns into `false`. No sibling flag survives for a caller to misread.
|
|
135
|
+
//
|
|
136
|
+
// This is still a question put to the CANISTER rather than a string-match on refusal copy,
|
|
137
|
+
// which is what the replaced paragraph was rightly defending.
|
|
138
|
+
await client.getOwnerConsole();
|
|
112
139
|
return true;
|
|
113
140
|
}
|
|
114
141
|
catch {
|
|
@@ -300,7 +327,7 @@ export function listCheckpoints() {
|
|
|
300
327
|
export function rollbackCheckpoint(checkpointId) {
|
|
301
328
|
const checkpoints = listCheckpoints();
|
|
302
329
|
if (checkpoints.length === 0) {
|
|
303
|
-
throw new Error(
|
|
330
|
+
throw new Error(`No checkpoints to roll back. Run "${NPX_CLIENT} pair" first to create a checkpoint.`);
|
|
304
331
|
}
|
|
305
332
|
let target;
|
|
306
333
|
if (checkpointId) {
|
|
@@ -861,7 +888,7 @@ export async function recoverOpenClaw(options) {
|
|
|
861
888
|
const latestByType = new Map();
|
|
862
889
|
for (const backup of finalized) {
|
|
863
890
|
const existing = latestByType.get(backup.dbType);
|
|
864
|
-
if (!existing || backup.
|
|
891
|
+
if (!existing || backup.createdAtNs > existing.createdAtNs) {
|
|
865
892
|
latestByType.set(backup.dbType, backup);
|
|
866
893
|
}
|
|
867
894
|
}
|