@engramx/client 0.15.0 → 0.16.0-rc.4

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.
Files changed (57) hide show
  1. package/README.md +255 -26
  2. package/dist/EngramClient.d.ts +235 -229
  3. package/dist/EngramClient.js +446 -1247
  4. package/dist/EngramClient.js.map +1 -1
  5. package/dist/MarketClient.d.ts +137 -0
  6. package/dist/MarketClient.js +228 -0
  7. package/dist/MarketClient.js.map +1 -0
  8. package/dist/ProfileManager.d.ts +4 -4
  9. package/dist/SessionManager.d.ts +3 -3
  10. package/dist/SessionManager.js +1 -1
  11. package/dist/SessionManager.js.map +1 -1
  12. package/dist/actionReceipt.d.ts +59 -0
  13. package/dist/actionReceipt.js +68 -2
  14. package/dist/actionReceipt.js.map +1 -1
  15. package/dist/auth.d.ts +16 -0
  16. package/dist/auth.js +38 -5
  17. package/dist/auth.js.map +1 -1
  18. package/dist/certifiedAudit.d.ts +32 -6
  19. package/dist/certifiedAudit.js +75 -14
  20. package/dist/certifiedAudit.js.map +1 -1
  21. package/dist/cli.js +100 -26
  22. package/dist/cli.js.map +1 -1
  23. package/dist/encryption.d.ts +30 -12
  24. package/dist/encryption.js +13 -15
  25. package/dist/encryption.js.map +1 -1
  26. package/dist/idl.d.ts +69 -0
  27. package/dist/idl.js +242 -0
  28. package/dist/idl.js.map +1 -0
  29. package/dist/index.d.ts +12 -18
  30. package/dist/index.js +54 -9
  31. package/dist/index.js.map +1 -1
  32. package/dist/ledgerfsHash.js +1 -1
  33. package/dist/ledgerfsHash.js.map +1 -1
  34. package/dist/lineage.js +8 -5
  35. package/dist/lineage.js.map +1 -1
  36. package/dist/memoryArchive.d.ts +384 -0
  37. package/dist/memoryArchive.js +477 -0
  38. package/dist/memoryArchive.js.map +1 -0
  39. package/dist/migrate.js +31 -4
  40. package/dist/migrate.js.map +1 -1
  41. package/dist/openEngram.d.ts +55 -0
  42. package/dist/openEngram.js +185 -0
  43. package/dist/openEngram.js.map +1 -0
  44. package/dist/pair.d.ts +33 -2
  45. package/dist/pair.js +56 -9
  46. package/dist/pair.js.map +1 -1
  47. package/dist/types.d.ts +246 -307
  48. package/dist/version.d.ts +8 -0
  49. package/dist/version.js +45 -0
  50. package/dist/version.js.map +1 -0
  51. package/dist/wire.d.ts +76 -0
  52. package/dist/wire.js +79 -0
  53. package/dist/wire.js.map +1 -0
  54. package/package.json +5 -14
  55. package/dist/bootstrap.d.ts +0 -21
  56. package/dist/bootstrap.js +0 -71
  57. package/dist/bootstrap.js.map +0 -1
package/README.md CHANGED
@@ -4,9 +4,9 @@ TypeScript SDK for EngramX — persistent, decentralized memory for AI agents on
4
4
 
5
5
  ## What is this
6
6
 
7
- EngramX (Engram Externalized) stores your AI agent's memory, identity, session history, and wallet in an ICP canister. If the host machine dies, gets hacked, or moves — the agent resumes on a new machine with full state intact.
7
+ EngramX (Engram Externalized) keeps an engram — its owner's memory, session history, wallet and record — in an ICP canister, and an AI agent paired with it reads and appends there. If the machine the agent runs on dies, gets hacked, or is replaced, the agent resumes on a new machine with the engram intact.
8
8
 
9
- This package is the client SDK that agents use to read and write their engram.
9
+ This package is the SDK an agent uses to read and write the engram it is paired with.
10
10
 
11
11
  ## Install
12
12
 
@@ -14,6 +14,10 @@ This package is the client SDK that agents use to read and write their engram.
14
14
  pnpm add @engramx/client
15
15
  ```
16
16
 
17
+ ⟨2026-10-02⟩ A release candidate is published under npm's `next` tag — `pnpm add @engramx/client@next`
18
+ — and `latest` is the last final release. A release candidate of 0.16.0 pairs only with a 0.16.0
19
+ engram (see "What you can actually call today" below).
20
+
17
21
  ## Quickstart
18
22
 
19
23
  ```typescript
@@ -22,7 +26,7 @@ import { EngramClient } from '@engramx/client';
22
26
  const engram = new EngramClient({
23
27
  canisterId: 'xxxxx-xxxxx-xxxxx-xxxxx-xxx',
24
28
  sessionKeyPath: '~/.engramx/session.key',
25
- clientLabel: 'my-agent', // identifies this host in the engram's cross-model reuse set
29
+ clientLabel: 'my-agent', // identifies this agent in the engram's cross-model reuse set
26
30
  });
27
31
 
28
32
  // At the START of a session: load everything the agent remembers, ready to inject as a
@@ -35,31 +39,99 @@ const ctx = await engram.startSession();
35
39
  console.log(`loaded ${ctx.includedFiles}/${ctx.totalFiles} files`);
36
40
 
37
41
  // AS THE AGENT WORKS: append (append-only; the canister concatenates the delta)
38
- const version = await engram.appendMemory('memory/daily.md', '## Notes\nSomething happened.');
42
+ const receipt = await engram.appendMemory('memory/daily.md', '## Notes\nSomething happened.');
43
+ // receipt = { version, seq, head }. `head` is the audit-chain head this write advanced to, hex —
44
+ // the evidence the write landed, verifiable by a third party against the chain and its anchor.
45
+ // Carry it forward: reading the file back later returns content, not proof.
39
46
 
40
47
  // Point reads: one file, or several at once
41
48
  const file = await engram.readMemory('MEMORY.md');
42
49
  const files = await engram.readMemoryBatch(['MEMORY.md', 'memory/daily.md']);
43
50
 
44
- // Wallet reads need canReadWallet, which the DEFAULT operator grant does NOT include — the
51
+ // Wallet reads need canReadWallet, which the DEFAULT agent grant does NOT include — the
45
52
  // default is read + append memory, no wallet, deliberately. Ask the owner for a grant with
46
53
  // wallet access before uncommenting:
47
54
  // const balance = await engram.walletBalance();
48
55
  ```
49
56
 
57
+ ### What you can actually call today (2026-08-17)
58
+
59
+ Two things in the snippet above are worth stating plainly rather than letting you discover them.
60
+
61
+ **`sessionSystemPrompt()` / `startSession()` work, and nothing in this repository calls them.**
62
+ They shipped in `0.15.0` and are in this release ⟨corrected 2026-10-02: this said 0.15.0 "is the
63
+ current `latest` on npm", which stops being true the moment 0.16.0 is promoted, and this README
64
+ ships inside the package⟩, so a new agent can call them today — notes elsewhere saying the
65
+ published package predates this surface are stale. But every
66
+ reference to them in this repo and in the dashboard is *teaching copy*: the connect guide, the
67
+ landing page snippet, the docs page. Every shipped consumer — the CLI, the stdio MCP server's
68
+ profile tools, the OpenClaw plugin — still reads memory the older way, with `readMemory`,
69
+ `readMemoryBatch` and `listMemoryFiles`. So: the API is real and supported, the on-chain reuse
70
+ accounting behind it is real, and you would be its first production caller. Treat the absence of an
71
+ in-repo consumer as a reason to report what breaks, not as a warning that it does not work.
72
+
73
+ The 2026-08-17 charter diet (below) deleted a lot of caller-less methods and **deliberately kept
74
+ these two.** Zero in-repo callers was the reason to look at them; it was not the reason to decide.
75
+ Three facts decided it. They are what `npx @engramx/mcp connect` *instructs* whoever builds a new
76
+ SDK-lane agent to call, verbatim, in the guide the dashboard's connect page renders — deleting a method
77
+ while still telling people to call it is the failure this whole exercise exists to prevent.
78
+ `startSession()` is the only read on the **update** path, so it is the only one the canister can
79
+ count toward the engram's cross-model reuse metric; a query read leaves no trace, because the IC
80
+ discards query-path state changes. And they are the difference between "load everything this agent
81
+ remembers" being one call or being a loop written by hand and subtly wrong.
82
+ An entry point with no in-repo caller is unexercised, not dead.
83
+
84
+ **`appendMemory` returns a receipt in this package, and a bare version number in `0.15.0`.** The
85
+ receipt is part of the 0.16.0 interface break this package carries ⟨corrected 2026-10-02 for the
86
+ 0.16.0 release candidate's publish: this paragraph called the receipt a thing of "THIS tree" and the
87
+ break "unreleased", which the publish makes false; on 2026-09-30 it had pointed at a changelog in
88
+ the source, which is private during the beta⟩. Against `@engramx/client@0.15.0` the same call
89
+ answers `Promise<bigint>`, and `listBackups` answers a bare array instead of unwrapping a refusal.
90
+ An SDK from either side of that break does not interoperate with an engram from the other.
91
+ ⟨corrected 2026-10-02: this ended "so upgrade the canister first and the SDK second" — there is no
92
+ upgrade across this break; 0.16.0 canisters are installed fresh, so pair this package only with a
93
+ 0.16.0 engram, and keep the SDK and the canisters on the same side.⟩
94
+
50
95
  ## Pairing
51
96
 
52
- Register your machine as an operator:
97
+ Pair your agent with an engram:
53
98
 
54
99
  ```bash
55
- # Pair this host — register as operator and install the OpenClaw plugin
56
- pnpm dlx @engramx/client pair <invite-code> --engram <canister-id> --target openclaw
100
+ # Pair the agent on this machine with the engram and install the OpenClaw plugin
101
+ pnpm dlx @engramx/client@0.16.0-rc.4 pair <invite-code> --engram <canister-id> --target openclaw
57
102
 
58
- # Replacement host — after pairing, restore memory from the engram
103
+ # Replacement machine — after pairing, restore memory from the engram
59
104
  pnpm dlx @engramx/client migrate --engram <canister-id> --recover
60
105
  ```
61
106
 
62
- `pair` generates `~/.engramx/session.key`, registers the operator, and installs the OpenClaw plugin. `migrate` then moves memory, saving a local checkpoint first — run `npx @engramx/client rollback` to undo. Its default direction pushes local memory files to the engram; `--recover` instead restores database backups, and memory files sync automatically on next OpenClaw startup.
107
+ `pair` generates the key for this engram at `~/.engramx/session.key` (and, on the first pairing, the agent's lasting key at `~/.engramx/root.key`), pairs the agent — the CLI prints `Paired as an agent` — and installs the OpenClaw plugin. `migrate` then moves memory, saving a local checkpoint first — run `npx @engramx/client rollback` to undo. Its default direction pushes local memory files to the engram; `--recover` instead restores database backups, and memory files sync automatically on next OpenClaw startup.
108
+
109
+ ⟨2026-09-29⟩ From code, pairing is one call: `EngramClient.pairAgent(inviteCode, root)`, or sign the
110
+ statement with `signEnrolment(root, engramId, key, inviteCode)` and call the engram's `pairAgent`
111
+ yourself. The statement no longer carries a `nonce`, and `pairEnrolled` — which re-signed when the
112
+ engram's signature check wrongly refused a valid statement — is no longer exported: ic402 2.16.1
113
+ fixed that check, so a refusal is final. An invite stops working after three failed checks.
114
+
115
+ ### An engram of the agent's own ⟨2026-10-01⟩
116
+
117
+ If the owner of the wallet allows it (a standing allowance they set for this agent on the Access
118
+ page), the agent can open an engram of its own through the engram it works in. The owner may be a program. Whoever runs that program is the owner for every purpose on these pages.
119
+
120
+ ```bash
121
+ npx @engramx/client open-engram --engram <canister-id>
122
+ ```
123
+
124
+ The engram it works in pays to open it, from its own balance, and the agent's lasting key
125
+ (`~/.engramx/root.key`, or `--root-key <path>`) owns what is opened. Approving is giving: what was
126
+ paid in is the agent's from the moment the new engram exists, and nothing is taken back. The command
127
+ reads the allowance first and stops, with nothing spent, when there is none; it never creates a
128
+ lasting key; and it prints the engram's own sentence when the engram refuses. It then prints the
129
+ owner kit's command that acts as the new engram's owner with the same file —
130
+ `npx @engramx/owner --connect --key-file <that file> --registry <id>`, with both filled in — which
131
+ mints an invite for an agent to pair into it. The agent's lasting key is an owner key for the engram
132
+ it opened only; on the engram it works in it stays an agent. The agent's work in an engram its own
133
+ key owns is never counted on its track record. From code: `EngramClient.getChildAllowance(key)`,
134
+ `EngramClient.openChildEngram()`, or `openEngram(options, { io })`, which is the whole command.
63
135
 
64
136
  ## Guardian API
65
137
 
@@ -72,10 +144,145 @@ Internet Identity authentication. The SDK exposes the read-only side:
72
144
  const guardians = await engram.listGuardians();
73
145
  ```
74
146
 
75
- The SDK runs on operator-credentialed agent hosts and intentionally does NOT
76
- expose owner-only methods (writeMemory, addGuardian, setProtectedPaths,
77
- setPaymentPolicy, resetMyEngram, etc.). Owner workflows belong in the
78
- dashboard at engramx.ai.
147
+ ## The charter — who this SDK is for
148
+
149
+ `EngramClient` runs **on the machine an agent runs on, with that agent's key**. An agent is an AI
150
+ agent: a software program, not a person acting on someone's behalf. It is the append-only lane: read
151
+ memory, append to it, run backups, pay for services. The owner lane is a separate package
152
+ (`@engramx/owner`) so that an agent's machine never has owner powers to leak — that split is the
153
+ product, not packaging taste. ⟨2026-10-01⟩ An engram the agent opened with `open-engram` is
154
+ the one exception, and it is the agent's own: its lasting key owns that engram. Over the engram it
155
+ works in, it is still an agent.
156
+
157
+ The lines that hold, unconditionally:
158
+
159
+ - The most dangerous owner primitives are **not here and will not be**: `writeMemory` (whole-file
160
+ overwrite), `addGuardian`, `setProtectedPaths`, `setPaymentPolicy`, `resetMyEngram`. Those live in
161
+ the dashboard at engramx.ai and in `@engramx/owner`.
162
+ - Nothing on this class can widen its own grant. Every method is still gated in the canister by the
163
+ agent record the owner minted; the SDK is a convenience over a wire, never an authority.
164
+
165
+ **The class matched its charter on 2026-08-17.** An audit on 2026-08-16 counted 94 public methods,
166
+ of which 23 had a real (non-test) caller anywhere. The rest had accumulated: roughly two dozen
167
+ owner-console methods and a nearly complete ic402 marketplace library, unused by any shipped surface
168
+ — including the dashboard they deferred to. They were gated in the canister, so their presence
169
+ widened nobody's authority; what it did was make this README's claim about the SDK false. The owner
170
+ ratified the diet and it has landed: **94 public methods → 49**, and the Candid interface this SDK
171
+ declares to the canister went from 108 method names to 39.
172
+
173
+ **What left, so you can find it if you were calling it.**
174
+
175
+ > **⟨CORRECTED 2026-08-26.⟩** This paragraph used to say the methods behind *every* name below were
176
+ > "LIVE and unchanged — this removed the SDK's wrapper, not the surface", with `keccak256` as the
177
+ > single exception. Measured against the served `.did` files at 0.16.0, that was wrong for **29 of
178
+ > the 45 names**: 15 moved to the market canister, 14 are gone from both, and of the 16 that are
179
+ > still on the engram some were renamed. A reader following the old sentence would have built a raw
180
+ > Candid actor against methods that reject with `IC0536 method not found`, from a README that told
181
+ > them the surface was untouched. The four groups below are measured, not asserted.
182
+
183
+ **1. Still on the engram — a raw Candid actor or `@engramx/owner` reaches these.** Only the SDK
184
+ wrapper went. *(16 names)*
185
+
186
+ `listMemoryLog`, `exportMemoryRange`, `previewCondense`, `applyCondense`, `listMemoryExportReceipts`,
187
+ `listCondenseCheckpoints`, `repudiateMemoryWrite`, `getAgentWriteReview`,
188
+ `markAgentWritesReviewed`, `ownerWithdraw`, `deleteBackup`, `openSession`, `sessionReadMemory`,
189
+ `sessionAppendMemory`, `getEngramCard`, `getEvmAddress`
190
+
191
+ ⟨2026-09-29⟩ Three of these are served under new names since the vocabulary change: the SDK's
192
+ `getOperatorWriteReview`, `markOperatorWritesReviewed` and `getAgentCard` are the engram's
193
+ `getAgentWriteReview`, `markAgentWritesReviewed` and `getEngramCard` (the vocabulary change: owner,
194
+ agent, guardian).
195
+
196
+ **2. Renamed on the engram — the capability survives under a new name.** *(4 names)*
197
+
198
+ | you called | call instead |
199
+ |---|---|
200
+ | `getRepudiations` | `listRepudiations` |
201
+ | `storageStats` | `getStorageStats` |
202
+ | `getPaymentRequirements` | `previewOpenSession` |
203
+ | `requestSession` | `previewOpenSession` |
204
+
205
+ **3. Moved to the MARKET canister — which is NOT DEPLOYED at 0.16.0.** The commerce surface was
206
+ extracted from the engram into a separate canister, and that canister is deliberately withheld from
207
+ this release. **These names are unreachable today at any address**, and two were renamed in the move
208
+ as well, so they will not answer even once a market exists. *(15 names)*
209
+
210
+ `endSession`, `forceCloseSession`, `uploadContent`, `listContent`, `getContent`, `deleteContent`,
211
+ `createEndpoint`, `listEndpoints`, `getRevenueSummary`, `registerService`, `claimJob`,
212
+ `submitJobResult`, `listJobs`, `getJobStatus`, `getPaymentPolicy`
213
+ — plus `listAllServices` → `listServices` and `getEndpointPurchases` → `listEndpointPurchases`,
214
+ and `uploadContentChunked` / `getContentWithPayment`, whose jobs are now done by `uploadContent`
215
+ and `getContent` there.
216
+
217
+ **4. Gone entirely — no canister serves these and none is planned.** *(the remainder)*
218
+
219
+ `assembleMemoryArchive`, `exportMemoryArchive`, `openPaymentSession`, `createPaymentClient`,
220
+ `submitServiceRequest`, `keccak256`. `getOwnerEvmAddress` folded into `getEvmAddress`.
221
+
222
+ ⚠ **`openSession` is on the engram and `endSession` is on the withheld market.** If you open a paid
223
+ session today there is no served method to close it. That asymmetry is a known open issue, not a
224
+ documentation error — do not build a paid flow on it yet.
225
+
226
+ ⟨CORRECTED 2026-09-30⟩ "There is no served method to close it" is untrue: the engram serves
227
+ `closeSession`, called by whoever paid for the session (it settles what was spent and refunds the
228
+ rest), and `sysForceCloseSession`, the owner's close for a session whoever paid for it abandoned. The market's
229
+ `endSession` was never the way to close an engram's session.
230
+
231
+ | family | removed from this class |
232
+ |---|---|
233
+ | Memory archive (owner-only on the canister) | `listMemoryLog`, `exportMemoryRange`, `previewCondense`, `applyCondense`, `listMemoryExportReceipts`, `listCondenseCheckpoints`, `assembleMemoryArchive`, `exportMemoryArchive` |
234
+ | Dispute + review | `repudiateMemoryWrite`, `getRepudiations`, `getOperatorWriteReview`, `markOperatorWritesReviewed` |
235
+ | Owner money + storage | `ownerWithdraw`, `storageStats`, `deleteBackup` |
236
+ | ic402 sessions | `getPaymentRequirements`, `requestSession`, `openSession`, `sessionReadMemory`, `sessionAppendMemory`, `endSession`, `forceCloseSession`, `openPaymentSession`, `createPaymentClient` |
237
+ | ic402 content store | `uploadContent`, `uploadContentChunked`, `listContent`, `getContent`, `getContentWithPayment`, `deleteContent` |
238
+ | x402 endpoint registry | `createEndpoint`, `listEndpoints`, `getEndpointPurchases`, `getRevenueSummary` |
239
+ | Marketplace seller side | `registerService`, `listAllServices`, `submitServiceRequest`, `claimJob`, `submitJobResult`, `listJobs`, `getJobStatus` |
240
+ | Misc reads | `getPaymentPolicy`, `getAgentCard`, `getEvmAddress`, `getOwnerEvmAddress` |
241
+ | Module exports | `loadBootstrapConfig` / `saveBootstrapConfig` (no importer in either repo; `saveBootstrapConfig` reached `writeMemory` through an `as unknown as` cast around the very surface this class withholds); the `Vault*` deprecation aliases; the blind `@ic402/client` re-export block — depend on `@ic402/client` directly if you want it, and pin its version yourself |
242
+
243
+ **`keccak256` is the exception: there is no canister method to fall back to** *(corrected
244
+ 2026-08-17 — this row sat in the table above under the claim that its canister method was live,
245
+ which was false)*. The engram removed the public `keccak256` query on 2026-08-17 in the same diet.
246
+ It was a consensus round trip, paid for and replicated, to hash bytes the caller already holds, so
247
+ **compute it locally instead** — `import { keccak256 } from 'viem'` (what the EVM e2e suite already
248
+ does), or any keccak library in your language. The Motoko library function
249
+ (`Ic402.EvmAddress.keccak256`) is untouched and still backs the in-canister EIP-191/EIP-712 digests;
250
+ only the public door is gone. Nothing you can compute locally changed value.
251
+
252
+ One thing was **added**, and it is verification-side: `verifyActionReceipt` (with `actionMessage`,
253
+ `actionFields` and the `ActionAuth` type) is now exported from the package entry point. The party
254
+ who needs to check an owner-action receipt is whoever checks the record, reading the audit log, not
255
+ the party that signed it — and until now the only way to reach that verifier was through `ownerWithdraw`, a
256
+ method for signing one. *⟨2026-10-01⟩* `verifyActionReceipt` checks a receipt exactly as the engram
257
+ does — strict RFC 8032, refusing a small-order key, a non-canonical point and a signature with S ≥ L
258
+ — so a receipt verifies here if and only if the engram would have accepted it.
259
+
260
+ One thing was **fixed** on the way past. The SDK declares a subset of the engram's Candid interface,
261
+ and `getEncryptedMemoryKeyForEpoch` was missing from it. `encryption.ts` feature-detects that method
262
+ and, not finding it, refused post-key-rotation content with *"this engram predates epoch-aware
263
+ derivation"* — a sentence that was false for every engram, because the absence was in this package,
264
+ not on the canister. Declaring the name makes the detect able to succeed.
265
+
266
+ **One family that should have gone did not: the encryption owner controls.** `setMemoryEncryption`,
267
+ `migrateMemoryToEncrypted` and `migrateMemoryToPlaintext` are owner-shaped by every test above — the
268
+ toggle is owner-gated and the migrations rewrite whole files through `writeMemory`, which this class
269
+ does not expose. They stayed because the encryption runbook instructs whoever migrates an engram to run
270
+ `EngramClient.migrateMemoryToEncrypted()` **by name**, under a section of cautions worth keeping.
271
+ Deleting a method a runbook tells people to call, while the runbook still tells them to call it, is
272
+ worse than the blemish. Moving the family to `@engramx/owner` is a two-repo change: the doc rider
273
+ first, then the methods.
274
+
275
+ **The removals are a break.** A method this class no longer has fails at the call site, loudly,
276
+ with a `TypeError`; a method the SDK no longer *declares* to the canister fails as a catchable
277
+ `IC0536` reject rather than a decode trap. Neither corrupts data, and both ride the next interface
278
+ cut rather than a patch release. `@engramx/client@0.15.0` on npm still has all of them.
279
+
280
+ **What survives, and the rule that keeps it that way.** Memory core, session start, history and
281
+ ranged/versioned reads, `reviseMemory`, the audit and certified-head reads, guardian and
282
+ approval-request reads, an agent's key-hash rotation, the whole backup lane, and the BUYER half of the
283
+ marketplace: `listServices` → `submitServiceRequestWithPayment` → `pollJobResult`/`getJob` →
284
+ `getJobResult`, with `confirmJob`/`disputeJob` to settle. From here, no method returns to this class
285
+ without a named caller. "A seller might want it" is how the last two dozen got here.
79
286
 
80
287
  ## Backup API
81
288
 
@@ -104,6 +311,13 @@ const backups = await engram.listBackups('sqlite');
104
311
  const data = await engram.pullBackupChunk(backupId, 0);
105
312
  ```
106
313
 
314
+ `listBackups` **throws on refusal rather than returning `[]`.** An empty array is a claim about the
315
+ engram ("you have no backups"); a refusal is a claim about the caller ("you may not read this"), and
316
+ conflating them is how a destructive restore gets invited. Note that listing needs `canReadBackup`,
317
+ which the default connect-an-agent grant leaves unset — `initBackup`/`pushBackupChunk` need only
318
+ `canAppendMemory`, so an agent can *write* backups it cannot *list* until the owner widens the grant.
319
+ The refusal names the missing permission.
320
+
107
321
  ## Verify canonical lineage
108
322
 
109
323
  An engram's own `getSpawnedBy()` is a self-report — any fork can claim any spawner.
@@ -128,24 +342,39 @@ const verdict = await verifyCanonicalLineage(engramId, agent);
128
342
  // false on a managed engram usually just means one release behind, not suspect.
129
343
  ```
130
344
 
131
- The canonical registry id ships in the SDK as `CANONICAL_REGISTRY_CANISTER_ID`
132
- (empty until the production fleet is live — while empty, calls without an explicit
133
- registry report `'indeterminate'` by design). Pass `{ registryId }` to ask a
134
- different fleet (e.g. staging); the answer is then _that fleet's_ testimony, not
135
- canonicity.
345
+ The canonical registry id ships in the SDK as `CANONICAL_REGISTRY_CANISTER_ID`; it is set to the
346
+ live production registry, so a call with no explicit registry asks the canonical fleet. (If it were
347
+ ever empty again, such a call would report `'indeterminate'` by design — the SDK never guesses at
348
+ canonicity.) Pass `{ registryId }` to ask a different fleet (e.g. staging); the answer is then
349
+ _that fleet's_ testimony, not canonicity.
136
350
 
137
351
  ## OpenClaw Integration
138
352
 
139
- The `--target openclaw` option on `pair` handles plugin installation and `openclaw.json` configuration automatically. For manual setup or customization, see [integrations/openclaw/README.md](../../integrations/openclaw/README.md).
353
+ The `--target openclaw` option on `pair` handles plugin installation and `openclaw.json` configuration automatically. For manual setup or customization, see the README of the `@engramx/openclaw` package on npm.
354
+
355
+ ## Maturity of the payment and identity surfaces
356
+
357
+ Verified against the canister interface on 2026-08-16. "Stubbed" was doing too much work here, so
358
+ each surface now says what it actually does.
140
359
 
141
- ## Roadmap
360
+ | Surface | State |
361
+ |---|---|
362
+ | **x402 payments — Internet Computer rail** | **Not reachable at 0.16.0.** ICRC-2 approve-and-transfer in ckUSDC. `MarketClient.createJob` settles at submit time and is what the stdio MCP money tools call; it addresses the market canister, which is not in this release (section 3 above). |
363
+ | **x402 payments — EVM rail** | **Implemented, unfunded on mainnet.** EIP-3009 USDC across the five chains in `Env.mo`. Signing and settlement paths exist; there is no funded production traffic, so treat it as untested in anger. |
364
+ | **World ID** | **Live**, as shipped: proof submission, RP-context and verification reads, and the HTTP-outcall transform are all on the interface, with a wired dashboard. |
365
+ | **ERC-8004 listing of the engram** | **Sign-only.** The canister signs its own registration transaction (`signEngramRegistration`) for you to broadcast; it lists the engram, never an agent. It does not broadcast, and it does not track registration state. |
366
+ | **Autonomous outbound spending (tECDSA)** | **Served, inside the owner's limits.** The engram signs stablecoin payment authorisations on EVM chains on an agent's request (`signX402Payment`), within the per-agent limits, the engram-wide daily ceiling and the daily signature count; the signing key is a threshold key derived to the canister, so no single party holds it. ⟨Corrected 2026-09-30. This row said "Dormant by choice. Single-key custody is a total-loss risk; not funded on mainnet." — untrue of the code: the path is live and the key is threshold-derived. What is still yours to do is fund the address you mean to pay from.⟩ |
142
367
 
143
- These features are stubbed and available in the canister interface but not yet fully implemented:
368
+ ⟨CORRECTED 2026-09-30⟩ The first row said "Live" and listed two tokens besides ckUSDC. The buyer path
369
+ it names moved to `MarketClient`, which pays in ckUSDC (its default ledger is the ckUSDC ledger), and
370
+ the market it pays is enrolled in the local environment only (`icp.yaml`, `environments`).
144
371
 
145
- - **ERC-8004 Agent Identity** — threshold ECDSA-derived Ethereum address
146
- - **x402 Payment Protocol** — autonomous HTTP-based payments
147
- - **World ID** — proof-of-personhood for engram owners
372
+ ⟨CORRECTED 2026-09-30, same day⟩ The first row then named `MarketClient.submitServiceRequestWithPayment`.
373
+ `MarketClient` has no method of that name: its paying call is `createJob`, and that is what the stdio
374
+ MCP money tools call. `submitServiceRequestWithPayment` and `pollJobResult`, in the charter section
375
+ above, are the buyer path's names from before it moved to `MarketClient`; there they name the family,
376
+ not methods any class serves today.
148
377
 
149
378
  ## License
150
379
 
151
- MIT
380
+ [Apache-2.0](LICENSE) — the same as every EngramX package.