@engramx/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -0
  3. package/dist/EngramClient.d.ts +204 -0
  4. package/dist/EngramClient.js +1168 -0
  5. package/dist/EngramClient.js.map +1 -0
  6. package/dist/ProfileManager.d.ts +61 -0
  7. package/dist/ProfileManager.js +116 -0
  8. package/dist/ProfileManager.js.map +1 -0
  9. package/dist/SessionManager.d.ts +46 -0
  10. package/dist/SessionManager.js +114 -0
  11. package/dist/SessionManager.js.map +1 -0
  12. package/dist/agent-skill.d.ts +150 -0
  13. package/dist/agent-skill.js +152 -0
  14. package/dist/agent-skill.js.map +1 -0
  15. package/dist/auth.d.ts +17 -0
  16. package/dist/auth.js +65 -0
  17. package/dist/auth.js.map +1 -0
  18. package/dist/bootstrap.d.ts +21 -0
  19. package/dist/bootstrap.js +71 -0
  20. package/dist/bootstrap.js.map +1 -0
  21. package/dist/cache.d.ts +11 -0
  22. package/dist/cache.js +41 -0
  23. package/dist/cache.js.map +1 -0
  24. package/dist/claude-setup.d.ts +29 -0
  25. package/dist/claude-setup.js +57 -0
  26. package/dist/claude-setup.js.map +1 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.js +1078 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/encryption.d.ts +44 -0
  31. package/dist/encryption.js +108 -0
  32. package/dist/encryption.js.map +1 -0
  33. package/dist/index.d.ts +36 -0
  34. package/dist/index.js +23 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/migrate.d.ts +106 -0
  37. package/dist/migrate.js +882 -0
  38. package/dist/migrate.js.map +1 -0
  39. package/dist/offline.d.ts +21 -0
  40. package/dist/offline.js +90 -0
  41. package/dist/offline.js.map +1 -0
  42. package/dist/openclaw-setup.d.ts +24 -0
  43. package/dist/openclaw-setup.js +249 -0
  44. package/dist/openclaw-setup.js.map +1 -0
  45. package/dist/pair.d.ts +20 -0
  46. package/dist/pair.js +68 -0
  47. package/dist/pair.js.map +1 -0
  48. package/dist/path-utils.d.ts +16 -0
  49. package/dist/path-utils.js +29 -0
  50. package/dist/path-utils.js.map +1 -0
  51. package/dist/schema.d.ts +54 -0
  52. package/dist/schema.js +101 -0
  53. package/dist/schema.js.map +1 -0
  54. package/dist/types.d.ts +452 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/package.json +67 -0
@@ -0,0 +1,1168 @@
1
+ import { Actor, HttpAgent } from '@icp-sdk/core/agent';
2
+ import { Principal } from '@icp-sdk/core/principal';
3
+ import { Ic402Client } from '@ic402/client';
4
+ import { LRUCache } from './cache';
5
+ import { OfflineQueue } from './offline';
6
+ import { loadSessionKey, identityFromSeed } from './auth';
7
+ import { EncryptionManager } from './encryption';
8
+ // Only operator-safe, append-only methods may be queued offline. writeMemory is
9
+ // owner-only and intentionally not on the SDK surface, so it must never be
10
+ // replayable from a (tamperable) on-disk queue.
11
+ const ALLOWED_OFFLINE_METHODS = new Set(['appendMemory']);
12
+ function validateMemoryPath(p) {
13
+ if (p.includes('\0'))
14
+ throw new Error('Path contains null byte');
15
+ if (p.includes('..'))
16
+ throw new Error('Path contains traversal sequence');
17
+ }
18
+ // Minimal Candid IDL factory — in production, use generated declarations
19
+ const idlFactory = ({ IDL }) => {
20
+ const OperatorPermissions = IDL.Record({
21
+ canReadMemory: IDL.Bool,
22
+ canAppendMemory: IDL.Bool,
23
+ canReadWallet: IDL.Bool,
24
+ callsPerMinute: IDL.Nat,
25
+ canReadBackup: IDL.Opt(IDL.Bool),
26
+ });
27
+ const OperatorRecord = IDL.Record({
28
+ principal: IDL.Principal,
29
+ name: IDL.Text,
30
+ registeredAt: IDL.Int,
31
+ lastSeen: IDL.Int,
32
+ permissions: OperatorPermissions,
33
+ status: IDL.Variant({
34
+ Active: IDL.Null,
35
+ Revoked: IDL.Null,
36
+ Expired: IDL.Null,
37
+ }),
38
+ publicKeyHash: IDL.Opt(IDL.Text),
39
+ keyRotatedAt: IDL.Int,
40
+ keyRotationCount: IDL.Nat,
41
+ });
42
+ const MemoryFile = IDL.Record({
43
+ path: IDL.Text,
44
+ content: IDL.Vec(IDL.Nat8),
45
+ version: IDL.Nat,
46
+ lastModifiedBy: IDL.Principal,
47
+ lastModifiedAt: IDL.Int,
48
+ });
49
+ const MemoryVersion = IDL.Record({
50
+ version: IDL.Nat,
51
+ modifiedBy: IDL.Principal,
52
+ modifiedAt: IDL.Int,
53
+ operation: IDL.Variant({
54
+ Set: IDL.Null,
55
+ Append: IDL.Null,
56
+ Rollback: IDL.Null,
57
+ }),
58
+ contentSnapshot: IDL.Vec(IDL.Nat8),
59
+ });
60
+ // ic402 types
61
+ const Eip3009Authorization = IDL.Record({
62
+ from: IDL.Text,
63
+ to: IDL.Text,
64
+ value: IDL.Nat,
65
+ validAfter: IDL.Nat,
66
+ validBefore: IDL.Nat,
67
+ nonce: IDL.Vec(IDL.Nat8),
68
+ v: IDL.Nat8,
69
+ r: IDL.Vec(IDL.Nat8),
70
+ s: IDL.Vec(IDL.Nat8),
71
+ });
72
+ const PaymentSignature = IDL.Record({
73
+ scheme: IDL.Text,
74
+ network: IDL.Text,
75
+ signature: IDL.Vec(IDL.Nat8),
76
+ asset: IDL.Opt(IDL.Text), // ic402 2.5.3: optional asset (token) hint
77
+ publicKey: IDL.Opt(IDL.Vec(IDL.Nat8)),
78
+ sender: IDL.Text,
79
+ nonce: IDL.Vec(IDL.Nat8),
80
+ authorization: IDL.Opt(Eip3009Authorization),
81
+ });
82
+ const PaymentRequirement = IDL.Record({
83
+ scheme: IDL.Text,
84
+ network: IDL.Text,
85
+ token: IDL.Text,
86
+ amount: IDL.Nat,
87
+ recipient: IDL.Text,
88
+ nonce: IDL.Vec(IDL.Nat8),
89
+ expiry: IDL.Int,
90
+ tokenName: IDL.Opt(IDL.Text),
91
+ tokenVersion: IDL.Opt(IDL.Text),
92
+ });
93
+ const SessionStatus = IDL.Variant({
94
+ open: IDL.Null,
95
+ closing: IDL.Null,
96
+ closed: IDL.Null,
97
+ expired: IDL.Null,
98
+ });
99
+ const SessionIntent = IDL.Record({
100
+ network: IDL.Text,
101
+ token: IDL.Text,
102
+ recipient: IDL.Text,
103
+ suggestedDeposit: IDL.Nat,
104
+ minDeposit: IDL.Opt(IDL.Nat),
105
+ expiry: IDL.Int,
106
+ costPerCall: IDL.Opt(IDL.Nat),
107
+ description: IDL.Opt(IDL.Text),
108
+ });
109
+ const SessionConfig = IDL.Record({
110
+ maxDeposit: IDL.Nat,
111
+ autoClose: IDL.Bool,
112
+ idleTimeout: IDL.Opt(IDL.Int),
113
+ });
114
+ const SessionState = IDL.Record({
115
+ id: IDL.Text,
116
+ payer: IDL.Principal,
117
+ deposited: IDL.Nat,
118
+ consumed: IDL.Nat,
119
+ remaining: IDL.Nat,
120
+ voucherCount: IDL.Nat,
121
+ status: SessionStatus,
122
+ openedAt: IDL.Int,
123
+ lastActivityAt: IDL.Int,
124
+ });
125
+ const Voucher = IDL.Record({
126
+ sessionId: IDL.Text,
127
+ cumulativeAmount: IDL.Nat,
128
+ sequence: IDL.Nat,
129
+ signature: IDL.Vec(IDL.Nat8),
130
+ });
131
+ const SpendingPolicy = IDL.Record({
132
+ maxPerTransaction: IDL.Opt(IDL.Nat),
133
+ maxPerDay: IDL.Opt(IDL.Nat),
134
+ rateLimitPerMinute: IDL.Opt(IDL.Nat),
135
+ maxSessionDeposit: IDL.Opt(IDL.Nat),
136
+ maxConcurrentSessions: IDL.Opt(IDL.Nat),
137
+ maxSessionDuration: IDL.Opt(IDL.Int),
138
+ sessionIdleTimeout: IDL.Opt(IDL.Int),
139
+ allowedCallers: IDL.Opt(IDL.Vec(IDL.Principal)),
140
+ blockedCallers: IDL.Opt(IDL.Vec(IDL.Principal)),
141
+ });
142
+ const ContentEntry = IDL.Record({
143
+ id: IDL.Text,
144
+ mimeType: IDL.Text,
145
+ totalSize: IDL.Nat,
146
+ chunkCount: IDL.Nat,
147
+ createdAt: IDL.Int,
148
+ });
149
+ const ContentRef = IDL.Record({
150
+ id: IDL.Text,
151
+ mimeType: IDL.Opt(IDL.Text),
152
+ sizeBytes: IDL.Opt(IDL.Nat),
153
+ metadata: IDL.Opt(IDL.Vec(IDL.Tuple(IDL.Text, IDL.Text))),
154
+ });
155
+ const AccessGrant = IDL.Record({
156
+ grantId: IDL.Text,
157
+ contentRef: ContentRef,
158
+ grantee: IDL.Principal,
159
+ receiptId: IDL.Text,
160
+ issuedAt: IDL.Int,
161
+ expiresAt: IDL.Int,
162
+ hmac: IDL.Vec(IDL.Nat8),
163
+ });
164
+ const DeliveryMethod = IDL.Variant({
165
+ inline: IDL.Vec(IDL.Nat8),
166
+ canisterQuery: IDL.Record({ method: IDL.Text, chunkCount: IDL.Nat }),
167
+ httpUrl: IDL.Text,
168
+ assetCanister: IDL.Record({ canisterId: IDL.Principal, path: IDL.Text }),
169
+ });
170
+ const ContentDelivery = IDL.Record({
171
+ grant: AccessGrant,
172
+ delivery: DeliveryMethod,
173
+ });
174
+ const PaymentReceipt = IDL.Record({
175
+ id: IDL.Text,
176
+ amount: IDL.Nat,
177
+ token: IDL.Text,
178
+ sender: IDL.Text,
179
+ recipient: IDL.Text,
180
+ network: IDL.Text,
181
+ timestamp: IDL.Int,
182
+ txHash: IDL.Opt(IDL.Text),
183
+ sessionId: IDL.Opt(IDL.Text),
184
+ refunded: IDL.Opt(IDL.Nat),
185
+ });
186
+ const PaymentResult = IDL.Variant({
187
+ ok: PaymentReceipt,
188
+ insufficientFunds: IDL.Text,
189
+ invalidSignature: IDL.Text,
190
+ expired: IDL.Text,
191
+ policyDenied: IDL.Text,
192
+ tokenNotAccepted: IDL.Text,
193
+ networkNotSupported: IDL.Text,
194
+ settlementFailed: IDL.Text,
195
+ reputationTooLow: IDL.Nat,
196
+ depositBelowMinimum: IDL.Nat,
197
+ });
198
+ const AgentCard = IDL.Record({
199
+ name: IDL.Text,
200
+ description: IDL.Text,
201
+ services: IDL.Vec(IDL.Record({
202
+ name: IDL.Text,
203
+ endpoint: IDL.Text,
204
+ version: IDL.Text,
205
+ skills: IDL.Vec(IDL.Text),
206
+ domains: IDL.Vec(IDL.Text),
207
+ })),
208
+ x402Support: IDL.Bool,
209
+ });
210
+ const AccessGrantResult = IDL.Variant({
211
+ ok: IDL.Null,
212
+ expired: IDL.Text,
213
+ revoked: IDL.Text,
214
+ invalidGrant: IDL.Text,
215
+ });
216
+ const AuditEntry = IDL.Record({
217
+ id: IDL.Nat,
218
+ timestamp: IDL.Int,
219
+ caller: IDL.Principal,
220
+ callerRole: IDL.Variant({ Owner: IDL.Null, Operator: IDL.Null, Guardian: IDL.Null }),
221
+ operation: IDL.Text,
222
+ details: IDL.Text,
223
+ success: IDL.Bool,
224
+ });
225
+ const GuardianPermissions = IDL.Record({
226
+ canRevokeOperators: IDL.Bool,
227
+ canFreezePayments: IDL.Bool,
228
+ canPauseWrites: IDL.Bool,
229
+ });
230
+ const GuardianRecord = IDL.Record({
231
+ principal: IDL.Principal,
232
+ name: IDL.Text,
233
+ addedAt: IDL.Int,
234
+ permissions: GuardianPermissions,
235
+ });
236
+ const VerificationLevel = IDL.Variant({
237
+ Orb: IDL.Null,
238
+ Device: IDL.Null,
239
+ Document: IDL.Null,
240
+ });
241
+ const WorldIdProof = IDL.Record({
242
+ nullifierHash: IDL.Text,
243
+ merkleRoot: IDL.Text,
244
+ proof: IDL.Text,
245
+ verificationLevel: VerificationLevel,
246
+ verifiedAt: IDL.Int,
247
+ });
248
+ const BackupType = IDL.Variant({
249
+ FullSnapshot: IDL.Null,
250
+ PartialSnapshot: IDL.Null,
251
+ IncrementalDiff: IDL.Null,
252
+ });
253
+ const BackupStatus = IDL.Variant({
254
+ Uploading: IDL.Null,
255
+ Finalized: IDL.Null,
256
+ Failed: IDL.Null,
257
+ });
258
+ const BackupMetadata = IDL.Record({
259
+ backupId: IDL.Text,
260
+ dbType: IDL.Text,
261
+ backupType: BackupType,
262
+ parentBackupId: IDL.Opt(IDL.Text),
263
+ totalSize: IDL.Nat,
264
+ chunkCount: IDL.Nat,
265
+ sha256: IDL.Text,
266
+ createdAt: IDL.Int,
267
+ createdBy: IDL.Principal,
268
+ backupLabel: IDL.Opt(IDL.Text),
269
+ status: BackupStatus,
270
+ });
271
+ const ApprovalStatus = IDL.Variant({
272
+ Pending: IDL.Null,
273
+ Approved: IDL.Null,
274
+ Denied: IDL.Null,
275
+ Used: IDL.Null,
276
+ Expired: IDL.Null,
277
+ });
278
+ const ApprovalRequest = IDL.Record({
279
+ id: IDL.Text,
280
+ caller: IDL.Principal,
281
+ operation: IDL.Text,
282
+ amount: IDL.Nat,
283
+ policyLimit: IDL.Nat,
284
+ policyField: IDL.Text,
285
+ rationale: IDL.Text,
286
+ createdAt: IDL.Int,
287
+ expiresAt: IDL.Int,
288
+ status: ApprovalStatus,
289
+ approvedBy: IDL.Opt(IDL.Principal),
290
+ approvedAt: IDL.Opt(IDL.Int),
291
+ denialReason: IDL.Opt(IDL.Text),
292
+ });
293
+ // ic402 service-marketplace types (minimal — Candid record decoding tolerates the
294
+ // canister's extra fields, so these read enough for the SDK's job/service flows).
295
+ const ServiceType = IDL.Variant({ Sync: IDL.Null, Async: IDL.Null });
296
+ const PricingScheme = IDL.Variant({ Exact: IDL.Nat, Upto: IDL.Nat, Session: IDL.Null });
297
+ const ServiceDeliveryMethod = IDL.Variant({ Both: IDL.Null, Poll: IDL.Null, Callback: IDL.Null });
298
+ const JobStatus = IDL.Variant({
299
+ Disputed: IDL.Null,
300
+ Refunded: IDL.Null,
301
+ Computing: IDL.Null,
302
+ Settling: IDL.Null,
303
+ Submitted: IDL.Null,
304
+ Assigned: IDL.Null,
305
+ Verified: IDL.Null,
306
+ Expired: IDL.Null,
307
+ Settled: IDL.Null,
308
+ Pending: IDL.Null,
309
+ });
310
+ const Job = IDL.Record({
311
+ id: IDL.Text,
312
+ status: JobStatus,
313
+ serviceId: IDL.Text,
314
+ amount: IDL.Nat,
315
+ result: IDL.Opt(IDL.Vec(IDL.Nat8)),
316
+ });
317
+ return IDL.Service({
318
+ init: IDL.Func([IDL.Principal], [], []),
319
+ createOperatorInvite: IDL.Func([IDL.Text, OperatorPermissions], [IDL.Variant({ Ok: IDL.Text, Err: IDL.Text })], []),
320
+ revokeOperator: IDL.Func([IDL.Principal], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
321
+ listOperators: IDL.Func([], [IDL.Vec(OperatorRecord)], ['query']),
322
+ updateOperatorPermissions: IDL.Func([IDL.Principal, OperatorPermissions], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
323
+ setProtectedPaths: IDL.Func([IDL.Vec(IDL.Text)], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
324
+ getProtectedPaths: IDL.Func([], [IDL.Vec(IDL.Text)], ['query']),
325
+ writeMemory: IDL.Func([IDL.Text, IDL.Vec(IDL.Nat8)], [IDL.Variant({ Ok: IDL.Nat, Err: IDL.Text })], []),
326
+ deleteMemoryFile: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
327
+ rollbackMemory: IDL.Func([IDL.Text, IDL.Nat], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
328
+ registerOperator: IDL.Func([IDL.Text], [IDL.Variant({ Ok: OperatorRecord, Err: IDL.Text })], []),
329
+ readMemory: IDL.Func([IDL.Text], [IDL.Variant({ Ok: MemoryFile, Err: IDL.Text })], ['query']),
330
+ listMemoryFiles: IDL.Func([], [IDL.Vec(IDL.Record({ path: IDL.Text, version: IDL.Nat, lastModifiedAt: IDL.Int }))], ['query']),
331
+ appendMemory: IDL.Func([IDL.Text, IDL.Vec(IDL.Nat8)], [IDL.Variant({ Ok: IDL.Nat, Err: IDL.Text })], []),
332
+ readMemoryBatch: IDL.Func([IDL.Vec(IDL.Text)], [IDL.Vec(IDL.Tuple(IDL.Text, IDL.Variant({ Ok: MemoryFile, Err: IDL.Text })))], ['query']),
333
+ getMemoryHistory: IDL.Func([IDL.Text], [IDL.Vec(MemoryVersion)], ['query']),
334
+ walletBalance: IDL.Func([], [IDL.Variant({ Ok: IDL.Nat, Err: IDL.Text })], []),
335
+ readAuditLog: IDL.Func([IDL.Nat, IDL.Nat], [IDL.Vec(AuditEntry)], ['query']),
336
+ auditLogSize: IDL.Func([], [IDL.Nat], ['query']),
337
+ status: IDL.Func([], [
338
+ IDL.Record({
339
+ owner: IDL.Opt(IDL.Principal),
340
+ memoryFileCount: IDL.Nat,
341
+ auditLogSize: IDL.Nat,
342
+ operatorCount: IDL.Nat,
343
+ cyclesBalance: IDL.Nat,
344
+ version: IDL.Text,
345
+ guardianCount: IDL.Nat,
346
+ paymentsFrozen: IDL.Bool,
347
+ writesPaused: IDL.Bool,
348
+ backupCount: IDL.Nat,
349
+ totalBackupSize: IDL.Nat,
350
+ }),
351
+ ], ['query']),
352
+ wasmHash: IDL.Func([], [IDL.Text], ['query']),
353
+ addGuardian: IDL.Func([IDL.Principal, IDL.Text, GuardianPermissions], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
354
+ removeGuardian: IDL.Func([IDL.Principal], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
355
+ listGuardians: IDL.Func([], [IDL.Vec(GuardianRecord)], ['query']),
356
+ guardianRevokeOperator: IDL.Func([IDL.Principal], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
357
+ guardianFreezePayments: IDL.Func([], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
358
+ guardianPauseWrites: IDL.Func([], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
359
+ unfreezePayments: IDL.Func([], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
360
+ resumeWrites: IDL.Func([], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
361
+ rotateOperatorKey: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
362
+ // ic402: Payment Gateway
363
+ getPaymentRequirements: IDL.Func([IDL.Nat], [IDL.Vec(PaymentRequirement)], ['query']),
364
+ // ic402: Service Marketplace (minimal — full ServiceDefinition has nested
365
+ // types ServiceType/PricingScheme/etc. The decoder tolerates extra record
366
+ // fields, so a minimal {id, name, enabled} shape lets us read enough for
367
+ // most uses. The full IDL lives in src/generated/engram.idl.js for the
368
+ // dashboard's raw-IDL access.)
369
+ listServices: IDL.Func([], [IDL.Vec(IDL.Record({ id: IDL.Text, name: IDL.Text, enabled: IDL.Bool }))], ['query']),
370
+ // Owner/operator view: includes disabled service drafts.
371
+ listAllServices: IDL.Func([], [IDL.Vec(IDL.Record({ id: IDL.Text, name: IDL.Text, enabled: IDL.Bool }))], ['query']),
372
+ registerService: IDL.Func([
373
+ IDL.Text,
374
+ IDL.Text,
375
+ ServiceType,
376
+ PricingScheme,
377
+ IDL.Text,
378
+ IDL.Opt(IDL.Text),
379
+ IDL.Opt(IDL.Vec(IDL.Nat8)),
380
+ ServiceDeliveryMethod,
381
+ IDL.Nat,
382
+ ], [IDL.Variant({ ok: IDL.Text, err: IDL.Text })], []),
383
+ submitServiceRequest: IDL.Func([
384
+ IDL.Text,
385
+ IDL.Vec(IDL.Nat8),
386
+ IDL.Opt(PaymentSignature),
387
+ IDL.Opt(IDL.Text),
388
+ IDL.Opt(IDL.Text),
389
+ ], [
390
+ IDL.Variant({
391
+ ok: IDL.Record({ jobId: IDL.Text }),
392
+ error: IDL.Text,
393
+ paymentRequired: IDL.Vec(PaymentRequirement),
394
+ approvalPending: IDL.Record({ requestId: IDL.Text, reason: IDL.Text }),
395
+ }),
396
+ ], []),
397
+ claimJob: IDL.Func([IDL.Text], [IDL.Variant({ ok: IDL.Null, err: IDL.Text })], []),
398
+ submitJobResult: IDL.Func([IDL.Text, IDL.Vec(IDL.Nat8), IDL.Opt(IDL.Vec(IDL.Nat8)), IDL.Opt(IDL.Nat)], [IDL.Variant({ ok: IDL.Null, err: IDL.Text })], []),
399
+ confirmJob: IDL.Func([IDL.Text], [IDL.Variant({ ok: IDL.Null, err: IDL.Text })], []),
400
+ disputeJob: IDL.Func([IDL.Text, IDL.Text], [IDL.Variant({ ok: IDL.Null, err: IDL.Text })], []),
401
+ getJob: IDL.Func([IDL.Text], [IDL.Opt(Job)], ['query']),
402
+ getJobStatus: IDL.Func([IDL.Text], [IDL.Opt(JobStatus)], ['query']),
403
+ getJobResult: IDL.Func([IDL.Text], [IDL.Opt(IDL.Vec(IDL.Nat8))], ['query']),
404
+ keccak256: IDL.Func([IDL.Vec(IDL.Nat8)], [IDL.Vec(IDL.Nat8)], ['query']),
405
+ // ic402: Sessions
406
+ requestSession: IDL.Func([], [SessionIntent], []),
407
+ openSession: IDL.Func([SessionConfig, PaymentSignature, IDL.Opt(IDL.Text), IDL.Opt(IDL.Text)], [IDL.Variant({ ok: SessionState, err: IDL.Text, approvalPending: ApprovalRequest })], []),
408
+ sessionReadMemory: IDL.Func([Voucher, IDL.Text], [IDL.Variant({ ok: MemoryFile, error: IDL.Text })], []),
409
+ sessionAppendMemory: IDL.Func([Voucher, IDL.Text, IDL.Vec(IDL.Nat8)], [IDL.Variant({ ok: IDL.Nat, error: IDL.Text })], []),
410
+ endSession: IDL.Func([IDL.Text], [PaymentResult], []),
411
+ forceCloseSession: IDL.Func([IDL.Text], [PaymentResult], []),
412
+ // ic402: Content Store
413
+ uploadContent: IDL.Func([IDL.Text, IDL.Text, IDL.Vec(IDL.Nat8)], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
414
+ uploadContentInit: IDL.Func([IDL.Text, IDL.Text, IDL.Nat, IDL.Nat], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
415
+ uploadContentChunk: IDL.Func([IDL.Text, IDL.Nat, IDL.Vec(IDL.Nat8)], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
416
+ deleteContent: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
417
+ listContent: IDL.Func([], [IDL.Vec(ContentEntry)], ['query']),
418
+ getContent: IDL.Func([IDL.Text, IDL.Opt(PaymentSignature), IDL.Opt(IDL.Text), IDL.Opt(IDL.Text)], [
419
+ IDL.Variant({
420
+ paymentRequired: IDL.Vec(PaymentRequirement),
421
+ ok: ContentDelivery,
422
+ error: IDL.Text,
423
+ approvalPending: ApprovalRequest,
424
+ }),
425
+ ], []),
426
+ getChunk: IDL.Func([AccessGrant, IDL.Nat], [IDL.Opt(IDL.Vec(IDL.Nat8))], ['query']),
427
+ // EVM wallet management
428
+ setOwnerEvmAddress: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
429
+ getOwnerEvmAddress: IDL.Func([], [IDL.Opt(IDL.Text)], ['query']),
430
+ // ic402: ERC-8004 Identity
431
+ getAgentCard: IDL.Func([], [AgentCard], ['query']),
432
+ getAgentRegistrationId: IDL.Func([], [IDL.Opt(IDL.Nat)], ['query']),
433
+ getEvmAddress: IDL.Func([], [IDL.Text], []),
434
+ // ic402: Policy
435
+ setPaymentPolicy: IDL.Func([SpendingPolicy], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
436
+ setCallerPolicy: IDL.Func([IDL.Principal, SpendingPolicy], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
437
+ getPaymentPolicy: IDL.Func([], [SpendingPolicy], ['query']),
438
+ // ic402: Grants
439
+ verifyGrant: IDL.Func([AccessGrant], [AccessGrantResult], ['query']),
440
+ // ic402: Approval Management
441
+ listApprovalRequests: IDL.Func([IDL.Opt(ApprovalStatus)], [IDL.Vec(ApprovalRequest)], ['query']),
442
+ approveRequest: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
443
+ denyRequest: IDL.Func([IDL.Text, IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
444
+ setWorldIdProof: IDL.Func([WorldIdProof], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
445
+ getWorldIdProof: IDL.Func([], [IDL.Opt(WorldIdProof)], ['query']),
446
+ // ic402: Endpoint Registry
447
+ createEndpoint: IDL.Func([IDL.Text, IDL.Text, IDL.Variant({ Content: IDL.Null }), IDL.Opt(IDL.Text), IDL.Nat], [
448
+ IDL.Variant({
449
+ Ok: IDL.Record({
450
+ id: IDL.Text,
451
+ name: IDL.Text,
452
+ description: IDL.Text,
453
+ endpointType: IDL.Variant({ Content: IDL.Null }),
454
+ contentId: IDL.Opt(IDL.Text),
455
+ suggestedPrice: IDL.Nat,
456
+ price: IDL.Nat,
457
+ createdBy: IDL.Principal,
458
+ createdAt: IDL.Int,
459
+ status: IDL.Variant({ Disabled: IDL.Null, Enabled: IDL.Null, Deleted: IDL.Null }),
460
+ enabledBy: IDL.Opt(IDL.Principal),
461
+ enabledAt: IDL.Opt(IDL.Int),
462
+ totalRevenue: IDL.Nat,
463
+ totalPurchases: IDL.Nat,
464
+ }),
465
+ Err: IDL.Text,
466
+ }),
467
+ ], []),
468
+ enableEndpoint: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
469
+ disableEndpoint: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
470
+ deleteEndpoint: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
471
+ setEndpointPrice: IDL.Func([IDL.Text, IDL.Nat], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
472
+ listEndpoints: IDL.Func([IDL.Opt(IDL.Variant({ Disabled: IDL.Null, Enabled: IDL.Null, Deleted: IDL.Null }))], [
473
+ IDL.Vec(IDL.Record({
474
+ id: IDL.Text,
475
+ name: IDL.Text,
476
+ description: IDL.Text,
477
+ endpointType: IDL.Variant({ Content: IDL.Null }),
478
+ contentId: IDL.Opt(IDL.Text),
479
+ suggestedPrice: IDL.Nat,
480
+ price: IDL.Nat,
481
+ createdBy: IDL.Principal,
482
+ createdAt: IDL.Int,
483
+ status: IDL.Variant({ Disabled: IDL.Null, Enabled: IDL.Null, Deleted: IDL.Null }),
484
+ enabledBy: IDL.Opt(IDL.Principal),
485
+ enabledAt: IDL.Opt(IDL.Int),
486
+ totalRevenue: IDL.Nat,
487
+ totalPurchases: IDL.Nat,
488
+ })),
489
+ ], ['query']),
490
+ getEndpointPurchases: IDL.Func([IDL.Text, IDL.Nat, IDL.Nat], [
491
+ IDL.Vec(IDL.Record({
492
+ id: IDL.Text,
493
+ endpointId: IDL.Text,
494
+ buyer: IDL.Text,
495
+ amount: IDL.Nat,
496
+ network: IDL.Text,
497
+ timestamp: IDL.Int,
498
+ receiptId: IDL.Text,
499
+ txHash: IDL.Opt(IDL.Text),
500
+ })),
501
+ ], ['query']),
502
+ getRevenueSummary: IDL.Func([], [
503
+ IDL.Record({
504
+ totalRevenue: IDL.Nat,
505
+ totalPurchases: IDL.Nat,
506
+ endpointBreakdown: IDL.Vec(IDL.Record({
507
+ endpointId: IDL.Text,
508
+ name: IDL.Text,
509
+ revenue: IDL.Nat,
510
+ purchases: IDL.Nat,
511
+ })),
512
+ }),
513
+ ], ['query']),
514
+ // Backup
515
+ initBackup: IDL.Func([IDL.Text, BackupType, IDL.Opt(IDL.Text), IDL.Text, IDL.Opt(IDL.Text)], [IDL.Variant({ Ok: IDL.Text, Err: IDL.Text })], []),
516
+ pushBackupChunk: IDL.Func([IDL.Text, IDL.Nat, IDL.Vec(IDL.Nat8)], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
517
+ finalizeBackup: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
518
+ abortBackup: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
519
+ listBackups: IDL.Func([IDL.Opt(IDL.Text)], [IDL.Vec(BackupMetadata)], ['query']),
520
+ pullBackupChunk: IDL.Func([IDL.Text, IDL.Nat], [IDL.Variant({ Ok: IDL.Vec(IDL.Nat8), Err: IDL.Text })], ['query']),
521
+ getBackupMetadata: IDL.Func([IDL.Text], [IDL.Variant({ Ok: BackupMetadata, Err: IDL.Text })], ['query']),
522
+ deleteBackup: IDL.Func([IDL.Text], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
523
+ setBackupRetention: IDL.Func([IDL.Text, IDL.Nat], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
524
+ storageStats: IDL.Func([], [
525
+ IDL.Record({
526
+ totalFiles: IDL.Nat,
527
+ totalContentSize: IDL.Nat,
528
+ memoryFilesSize: IDL.Nat,
529
+ memoryFilesCount: IDL.Nat,
530
+ sessionFilesSize: IDL.Nat,
531
+ sessionFilesCount: IDL.Nat,
532
+ versionHistorySize: IDL.Nat,
533
+ versionHistoryCount: IDL.Nat,
534
+ backupSize: IDL.Nat,
535
+ backupCount: IDL.Nat,
536
+ fileLimit: IDL.Nat,
537
+ auditLogSize: IDL.Nat,
538
+ }),
539
+ ], ['query']),
540
+ // Encryption (vetKeys): derive the shared memory-encryption key.
541
+ getEncryptedMemoryKey: IDL.Func([IDL.Vec(IDL.Nat8)], [IDL.Variant({ ok: IDL.Vec(IDL.Nat8), err: IDL.Text })], []),
542
+ getMemoryKeyVerificationKey: IDL.Func([], [IDL.Vec(IDL.Nat8)], []),
543
+ isEncryptionEnabled: IDL.Func([], [IDL.Bool], ['query']),
544
+ setEncryptionEnabled: IDL.Func([IDL.Bool], [IDL.Variant({ Ok: IDL.Null, Err: IDL.Text })], []),
545
+ });
546
+ };
547
+ // Minimal ICRC-2 ledger IDL for auto-payment approval
548
+ const icrc2LedgerIdlFactory = ({ IDL }) => {
549
+ const Account = IDL.Record({ owner: IDL.Principal, subaccount: IDL.Opt(IDL.Vec(IDL.Nat8)) });
550
+ return IDL.Service({
551
+ icrc1_balance_of: IDL.Func([Account], [IDL.Nat], ['query']),
552
+ icrc2_approve: IDL.Func([
553
+ IDL.Record({
554
+ spender: Account,
555
+ amount: IDL.Nat,
556
+ fee: IDL.Opt(IDL.Nat),
557
+ memo: IDL.Opt(IDL.Vec(IDL.Nat8)),
558
+ from_subaccount: IDL.Opt(IDL.Vec(IDL.Nat8)),
559
+ created_at_time: IDL.Opt(IDL.Nat64),
560
+ expected_allowance: IDL.Opt(IDL.Nat),
561
+ expires_at: IDL.Opt(IDL.Nat64),
562
+ }),
563
+ ], [IDL.Variant({ Ok: IDL.Nat, Err: IDL.Record({ message: IDL.Text }) })], []),
564
+ });
565
+ };
566
+ export class EngramClient {
567
+ unwrap(result) {
568
+ if ('Err' in result)
569
+ throw new Error(result.Err);
570
+ if (!('Ok' in result))
571
+ throw new Error('Invalid canister response: missing Ok and Err');
572
+ return result.Ok;
573
+ }
574
+ constructor(config) {
575
+ this.encryptionModeSynced = false;
576
+ if (config.sessionKeyPath) {
577
+ this.identity = loadSessionKey(config.sessionKeyPath);
578
+ }
579
+ else if (config.sessionKeySeed) {
580
+ this.identity = identityFromSeed(config.sessionKeySeed);
581
+ }
582
+ else {
583
+ throw new Error('Either sessionKeyPath or sessionKeySeed must be provided');
584
+ }
585
+ this.canisterId = config.canisterId;
586
+ const host = config.host || 'https://ic0.app';
587
+ const isLocal = (() => {
588
+ try {
589
+ const hostname = new URL(host).hostname;
590
+ return hostname === 'localhost' || hostname === '127.0.0.1';
591
+ }
592
+ catch {
593
+ return false;
594
+ }
595
+ })();
596
+ this.agent = HttpAgent.createSync({
597
+ identity: this.identity,
598
+ host,
599
+ shouldFetchRootKey: isLocal,
600
+ });
601
+ this.actor = Actor.createActor(idlFactory, {
602
+ agent: this.agent,
603
+ canisterId: config.canisterId,
604
+ });
605
+ this.cache = new LRUCache(config.cacheMaxSize ?? 100, config.cacheMaxAge ?? 60000);
606
+ this.encryption = new EncryptionManager(config.encryptMemory ?? false);
607
+ // If the caller didn't force a mode, sync it from the canister's flag lazily.
608
+ this.encryptionModeSynced = config.encryptMemory !== undefined;
609
+ if (config.offlineQueuePath) {
610
+ this.offlineQueue = new OfflineQueue(config.offlineQueuePath);
611
+ }
612
+ }
613
+ async readMemory(path) {
614
+ validateMemoryPath(path);
615
+ const cached = this.cache.get(`mem:${path}`);
616
+ if (cached)
617
+ return cached;
618
+ const raw = this.unwrap(await this.actor.readMemory(path));
619
+ const decrypted = await this.encryption.decrypt(this.actor, new Uint8Array(raw.content));
620
+ const file = {
621
+ path: raw.path,
622
+ content: decrypted,
623
+ version: raw.version,
624
+ lastModifiedBy: raw.lastModifiedBy,
625
+ lastModifiedAt: raw.lastModifiedAt,
626
+ };
627
+ this.cache.set(`mem:${path}`, file);
628
+ return file;
629
+ }
630
+ async appendMemory(path, content) {
631
+ validateMemoryPath(path);
632
+ try {
633
+ // Client-side read-modify-write: read → decrypt → append → encrypt → write
634
+ let existing = '';
635
+ try {
636
+ const file = await this.readMemory(path);
637
+ existing = file.content;
638
+ }
639
+ catch {
640
+ // File doesn't exist yet — start fresh
641
+ }
642
+ const newContent = existing ? `${existing}\n${content}` : content;
643
+ await this.ensureEncryptionMode();
644
+ const ciphertext = await this.encryption.encrypt(this.actor, newContent);
645
+ const version = this.unwrap(await this.actor.appendMemory(path, ciphertext));
646
+ this.cache.invalidate(`mem:${path}`);
647
+ return version;
648
+ }
649
+ catch (err) {
650
+ if (this.offlineQueue) {
651
+ this.offlineQueue.enqueue('appendMemory', [path, content]);
652
+ return BigInt(0);
653
+ }
654
+ throw err;
655
+ }
656
+ }
657
+ async listMemoryFiles() {
658
+ return await this.actor.listMemoryFiles();
659
+ }
660
+ async readMemoryBatch(paths) {
661
+ for (const p of paths)
662
+ validateMemoryPath(p);
663
+ const result = await this.actor.readMemoryBatch(paths);
664
+ return await Promise.all(result.map(async ([p, r]) => {
665
+ if ('Err' in r)
666
+ return [p, new Error(r.Err)];
667
+ const raw = r.Ok;
668
+ try {
669
+ const decrypted = await this.encryption.decrypt(this.actor, new Uint8Array(raw.content));
670
+ const file = {
671
+ path: raw.path,
672
+ content: decrypted,
673
+ version: raw.version,
674
+ lastModifiedBy: raw.lastModifiedBy,
675
+ lastModifiedAt: raw.lastModifiedAt,
676
+ };
677
+ return [p, file];
678
+ }
679
+ catch (err) {
680
+ return [p, err instanceof Error ? err : new Error(String(err))];
681
+ }
682
+ }));
683
+ }
684
+ async getMemoryHistory(path) {
685
+ const rawVersions = await this.actor.getMemoryHistory(path);
686
+ return await Promise.all(rawVersions.map(async (raw) => ({
687
+ version: raw.version,
688
+ modifiedBy: raw.modifiedBy,
689
+ modifiedAt: raw.modifiedAt,
690
+ operation: raw.operation,
691
+ contentSnapshot: await this.encryption.decrypt(this.actor, new Uint8Array(raw.contentSnapshot)),
692
+ })));
693
+ }
694
+ // writeMemory removed: owner-only by design. The @engramx/client SDK runs on
695
+ // operator-credentialed agent hosts where this would always fail. Owner uses
696
+ // the dashboard at engramx.ai or the canister IDL via Internet Identity.
697
+ async walletBalance() {
698
+ return this.unwrap(await this.actor.walletBalance());
699
+ }
700
+ async readAuditLog(fromIndex = 0, limit = 50) {
701
+ return await this.actor.readAuditLog(BigInt(fromIndex), BigInt(limit));
702
+ }
703
+ async auditLogSize() {
704
+ return await this.actor.auditLogSize();
705
+ }
706
+ async status() {
707
+ // Canister returns a record directly; do not destructure as array.
708
+ return await this.actor.status();
709
+ }
710
+ // addGuardian, removeGuardian removed: owner-only.
711
+ // setWorldIdProof removed: owner-only.
712
+ async listGuardians() {
713
+ return await this.actor.listGuardians();
714
+ }
715
+ async rotateOperatorKey(newPublicKeyHash) {
716
+ this.unwrap(await this.actor.rotateOperatorKey(newPublicKeyHash));
717
+ }
718
+ async getWorldIdProof() {
719
+ const result = await this.actor.getWorldIdProof();
720
+ return result.length > 0 ? result[0] : null;
721
+ }
722
+ // === ic402 Payment Gateway ===
723
+ async getPaymentRequirements(amount) {
724
+ return await this.actor.getPaymentRequirements(amount);
725
+ }
726
+ // === ic402 Sessions ===
727
+ async requestSession() {
728
+ return await this.actor.requestSession();
729
+ }
730
+ async openSession(config, sig, rationale, approvalId) {
731
+ const result = await this.actor.openSession(config, sig, rationale ? [rationale] : [], approvalId ? [approvalId] : []);
732
+ if ('err' in result)
733
+ throw new Error(result.err);
734
+ if ('approvalPending' in result)
735
+ return { approvalPending: result.approvalPending };
736
+ return result.ok;
737
+ }
738
+ async sessionReadMemory(voucher, path) {
739
+ const result = await this.actor.sessionReadMemory(voucher, path);
740
+ if ('error' in result)
741
+ throw new Error(result.error);
742
+ return result.ok;
743
+ }
744
+ async sessionAppendMemory(voucher, path, content) {
745
+ const result = await this.actor.sessionAppendMemory(voucher, path, content);
746
+ if ('error' in result)
747
+ throw new Error(result.error);
748
+ return result.ok;
749
+ }
750
+ async endSession(sessionId) {
751
+ return await this.actor.endSession(sessionId);
752
+ }
753
+ async forceCloseSession(sessionId) {
754
+ return await this.actor.forceCloseSession(sessionId);
755
+ }
756
+ // === ic402 Content Store ===
757
+ async uploadContent(id, mimeType, data) {
758
+ this.unwrap(await this.actor.uploadContent(id, mimeType, data));
759
+ }
760
+ async listContent() {
761
+ return await this.actor.listContent();
762
+ }
763
+ async getContent(contentId, paymentSig, rationale, approvalId) {
764
+ const result = await this.actor.getContent(contentId, paymentSig ? [paymentSig] : [], rationale ? [rationale] : [], approvalId ? [approvalId] : []);
765
+ if ('paymentRequired' in result)
766
+ return { paymentRequired: result.paymentRequired };
767
+ if ('ok' in result)
768
+ return { delivery: result.ok };
769
+ if ('approvalPending' in result)
770
+ return { approvalPending: result.approvalPending };
771
+ return { error: result.error };
772
+ }
773
+ async deleteContent(id) {
774
+ this.unwrap(await this.actor.deleteContent(id));
775
+ }
776
+ // === ic402 Endpoint Registry ===
777
+ async createEndpoint(name, description, endpointType, contentId, suggestedPrice) {
778
+ const result = await this.actor.createEndpoint(name, description, endpointType, contentId ? [contentId] : [], suggestedPrice);
779
+ if ('Err' in result)
780
+ throw new Error(result.Err);
781
+ return result.Ok;
782
+ }
783
+ // enableEndpoint, disableEndpoint, deleteEndpoint, setEndpointPrice removed:
784
+ // owner-only. Operators create endpoints (disabled); the owner enables and
785
+ // adjusts pricing via the dashboard.
786
+ async listEndpoints(statusFilter) {
787
+ return this.actor.listEndpoints(statusFilter ? [statusFilter] : []);
788
+ }
789
+ async getEndpointPurchases(endpointId, fromIndex, limit) {
790
+ return this.actor.getEndpointPurchases(endpointId, fromIndex, limit);
791
+ }
792
+ async getRevenueSummary() {
793
+ return this.actor.getRevenueSummary();
794
+ }
795
+ // === ic402 Policy (read-only on operator hosts) ===
796
+ // setPaymentPolicy, setCallerPolicy removed: owner-only.
797
+ async getPaymentPolicy() {
798
+ return await this.actor.getPaymentPolicy();
799
+ }
800
+ // listApprovalRequests, approveRequest, denyRequest removed: owner-only.
801
+ // The dashboard surfaces the approval queue for the owner.
802
+ // === ic402 ERC-8004 Agent Identity ===
803
+ async getAgentCard() {
804
+ return await this.actor.getAgentCard();
805
+ }
806
+ async getEvmAddress() {
807
+ return await this.actor.getEvmAddress();
808
+ }
809
+ // === EVM Wallet Management (read-only on operator hosts) ===
810
+ // setOwnerEvmAddress removed: owner-only.
811
+ async getOwnerEvmAddress() {
812
+ const result = await this.actor.getOwnerEvmAddress();
813
+ return result.length > 0 ? result[0] : null;
814
+ }
815
+ async initBackup(dbType, backupType, parentBackupId, sha256, backupLabel) {
816
+ return this.unwrap(await this.actor.initBackup(dbType, backupType, parentBackupId ? [parentBackupId] : [], sha256, backupLabel ? [backupLabel] : []));
817
+ }
818
+ async pushBackupChunk(backupId, chunkIndex, data) {
819
+ this.unwrap(await this.actor.pushBackupChunk(backupId, BigInt(chunkIndex), data));
820
+ }
821
+ async finalizeBackup(backupId) {
822
+ this.unwrap(await this.actor.finalizeBackup(backupId));
823
+ }
824
+ async abortBackup(backupId) {
825
+ this.unwrap(await this.actor.abortBackup(backupId));
826
+ }
827
+ async listBackups(dbTypeFilter) {
828
+ return await this.actor.listBackups(dbTypeFilter ? [dbTypeFilter] : []);
829
+ }
830
+ async pullBackupChunk(backupId, chunkIndex) {
831
+ return this.unwrap(await this.actor.pullBackupChunk(backupId, BigInt(chunkIndex)));
832
+ }
833
+ async getBackupMetadata(backupId) {
834
+ return this.unwrap(await this.actor.getBackupMetadata(backupId));
835
+ }
836
+ async deleteBackup(backupId) {
837
+ this.unwrap(await this.actor.deleteBackup(backupId));
838
+ }
839
+ // setBackupRetention removed: owner-only.
840
+ async latestBackupMatchesHash(dbType, sha256) {
841
+ const backups = await this.listBackups(dbType);
842
+ const finalized = backups
843
+ .filter((b) => 'Finalized' in b.status)
844
+ .sort((a, b) => Number(b.createdAt) - Number(a.createdAt));
845
+ const latest = finalized[0];
846
+ if (!latest)
847
+ return null;
848
+ return latest.sha256 === sha256 ? latest : null;
849
+ }
850
+ async storageStats() {
851
+ return await this.actor.storageStats();
852
+ }
853
+ /** Warm the vetKeys memory-encryption key cache (one vetKD derivation). Optional —
854
+ * read/append derive lazily on first use. */
855
+ async initEncryption() {
856
+ await this.encryption.prepare(this.actor);
857
+ }
858
+ clearEncryptionCache() {
859
+ this.encryption.clearCache();
860
+ }
861
+ /** Sync the write-time encryption mode from the canister flag (once, cached),
862
+ * unless an explicit `encryptMemory` was given at construction. */
863
+ async ensureEncryptionMode() {
864
+ if (this.encryptionModeSynced)
865
+ return;
866
+ const enabled = await this.actor.isEncryptionEnabled();
867
+ this.encryption.setEnabled(enabled);
868
+ this.encryptionModeSynced = true;
869
+ }
870
+ /** Whether this engram has memory encryption enabled (reads the canister flag). */
871
+ async isMemoryEncryptionEnabled() {
872
+ return await this.actor.isEncryptionEnabled();
873
+ }
874
+ /** Owner-only: turn memory encryption on/off on the canister. Updates the local
875
+ * write mode immediately. Turning ON does not re-encrypt existing files — call
876
+ * migrateMemoryToEncrypted() for that. */
877
+ async setMemoryEncryption(enabled) {
878
+ this.unwrap(await this.actor.setEncryptionEnabled(enabled));
879
+ this.encryption.setEnabled(enabled);
880
+ this.encryptionModeSynced = true;
881
+ }
882
+ /** Owner-only: re-encrypt all existing memory files under the current key. Reads
883
+ * each file (decrypting any that are already encrypted), then rewrites it via
884
+ * writeMemory so it is stored as ciphertext. Idempotent and resumable — files
885
+ * already encrypted are simply re-written identically. Requires encryption to be
886
+ * enabled (canister flag) and an owner identity (writeMemory is owner-only). */
887
+ async migrateMemoryToEncrypted() {
888
+ await this.ensureEncryptionMode();
889
+ if (!this.encryption.isEnabled()) {
890
+ throw new Error('Enable encryption first (setMemoryEncryption(true)) before migrating');
891
+ }
892
+ const files = await this.listMemoryFiles();
893
+ const failed = [];
894
+ let migrated = 0;
895
+ for (const info of files) {
896
+ try {
897
+ // Byte-exact: read the RAW stored bytes (not the string read API) so binary /
898
+ // non-UTF-8 content is never corrupted by a TextDecoder round-trip.
899
+ const raw = this.unwrap(await this.actor.readMemory(info.path));
900
+ const bytes = new Uint8Array(raw.content);
901
+ if (this.encryption.isEncrypted(bytes)) {
902
+ migrated += 1; // already encrypted — idempotent skip
903
+ continue;
904
+ }
905
+ const ciphertext = await this.encryption.encryptBytes(this.actor, bytes);
906
+ this.unwrap(await this.actor.writeMemory(info.path, ciphertext));
907
+ this.cache.invalidate(`mem:${info.path}`);
908
+ migrated += 1;
909
+ }
910
+ catch (err) {
911
+ failed.push(`${info.path}: ${err instanceof Error ? err.message : String(err)}`);
912
+ }
913
+ }
914
+ return { migrated, total: files.length, failed };
915
+ }
916
+ async syncOfflineQueue() {
917
+ if (!this.offlineQueue)
918
+ return { synced: 0, failed: 0 };
919
+ return this.offlineQueue.flush(async (method, args) => {
920
+ if (!ALLOWED_OFFLINE_METHODS.has(method)) {
921
+ console.error(`engramx: offline queue contains unknown method "${method}" — operation dropped`);
922
+ return;
923
+ }
924
+ const fn = this.actor[method];
925
+ if (!fn)
926
+ throw new Error(`Unknown method: ${method}`);
927
+ const result = await fn(...args);
928
+ if (result && typeof result === 'object' && 'Err' in result) {
929
+ throw new Error(result.Err);
930
+ }
931
+ });
932
+ }
933
+ async registerOperator(inviteCode) {
934
+ return this.unwrap(await this.actor.registerOperator(inviteCode));
935
+ }
936
+ // setProtectedPaths removed: owner-only.
937
+ async getProtectedPaths() {
938
+ return await this.actor.getProtectedPaths();
939
+ }
940
+ // === ic402 Payment Client Integration ===
941
+ /**
942
+ * Create a pre-configured Ic402Client for auto-payment operations.
943
+ * The client handles 402 → ICRC-2 approve → retry automatically.
944
+ */
945
+ createPaymentClient(options) {
946
+ return new Ic402Client({
947
+ canisterId: this.canisterId,
948
+ actorFactory: (id) => Actor.createActor(idlFactory, { agent: this.agent, canisterId: id }),
949
+ identity: this.identity,
950
+ network: 'icp:1',
951
+ autoPayment: options?.autoPayment ?? true,
952
+ ledger: options?.ledger || 'xevnm-gaaaa-aaaar-qafnq-cai', // ckUSDC mainnet
953
+ ledgerActorFactory: (ledgerId) => Actor.createActor(icrc2LedgerIdlFactory, { agent: this.agent, canisterId: ledgerId }),
954
+ });
955
+ }
956
+ /**
957
+ * Auto-pay bridge for the engramx x402 methods. The stock ic402 client injects the
958
+ * payment signature into the LAST argument, but the engramx canister appends
959
+ * `rationale` + `approvalId` AFTER `paymentSig` (for the owner approval workflow), so the
960
+ * signature must land at a FIXED index, not last. Flow: call with no sig → on
961
+ * #paymentRequired, icrc2_approve the amount (+ fee buffer) → build the ICP payment
962
+ * signature (a nonce-carrier; the real authorization is the ICRC-2 allowance the canister
963
+ * spends via transfer_from) → retry with the sig at `sigIndex`. Returns the raw canister
964
+ * result; the caller unwraps ok/error/approvalPending.
965
+ */
966
+ async payAndCall(method, baseArgs, sigIndex, options) {
967
+ const actor = this.actor;
968
+ const first = (await actor[method](...baseArgs));
969
+ if (!first || typeof first !== 'object' || !('paymentRequired' in first)) {
970
+ return first;
971
+ }
972
+ const reqs = first['paymentRequired'];
973
+ const requirement = Array.isArray(reqs)
974
+ ? reqs[0]
975
+ : reqs;
976
+ if (!requirement)
977
+ throw new Error('Malformed paymentRequired response');
978
+ const ledger = options?.ledger || 'xevnm-gaaaa-aaaar-qafnq-cai'; // ckUSDC mainnet
979
+ const ledgerActor = Actor.createActor(icrc2LedgerIdlFactory, {
980
+ agent: this.agent,
981
+ canisterId: ledger,
982
+ });
983
+ const buffer = options?.approvalFeeBuffer ?? 100000n;
984
+ const approve = await ledgerActor.icrc2_approve({
985
+ spender: { owner: Principal.fromText(this.canisterId), subaccount: [] },
986
+ amount: BigInt(requirement['amount']) + buffer,
987
+ fee: [],
988
+ memo: [],
989
+ from_subaccount: [],
990
+ created_at_time: [],
991
+ expected_allowance: [],
992
+ expires_at: [],
993
+ });
994
+ if (approve && typeof approve === 'object' && 'Err' in approve) {
995
+ throw new Error(`ICRC-2 approve failed: ${JSON.stringify(approve.Err, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))}`);
996
+ }
997
+ // Candid may decode `vec nat8` as Uint8Array | number[] | indexed object — normalize.
998
+ const rawNonce = (requirement['nonce'] ?? new Uint8Array(32));
999
+ const nonce = rawNonce instanceof Uint8Array
1000
+ ? Array.from(rawNonce)
1001
+ : Array.isArray(rawNonce)
1002
+ ? rawNonce
1003
+ : Object.values(rawNonce);
1004
+ const sig = {
1005
+ scheme: 'exact',
1006
+ network: 'icp:1',
1007
+ signature: nonce,
1008
+ asset: [], // ic402 2.5.3: optional asset hint (unused on the ICP rail)
1009
+ publicKey: [],
1010
+ sender: this.identity.getPrincipal().toText(),
1011
+ nonce,
1012
+ authorization: [],
1013
+ };
1014
+ const retryArgs = [...baseArgs];
1015
+ retryArgs[sigIndex] = [sig];
1016
+ return (await actor[method](...retryArgs));
1017
+ }
1018
+ /**
1019
+ * Get paid content with auto-payment. Handles #paymentRequired → approve → retry.
1020
+ * @param contentId Content identifier
1021
+ * @param options Optional ledger override
1022
+ */
1023
+ async getContentWithPayment(contentId, options) {
1024
+ const result = await this.payAndCall('getContent', [contentId, [], [], []], 1, options);
1025
+ if ('ok' in result)
1026
+ return result['ok'];
1027
+ if ('error' in result)
1028
+ throw new Error(String(result['error']));
1029
+ if ('approvalPending' in result) {
1030
+ const ap = result['approvalPending'];
1031
+ throw new Error(`Owner approval required: ${ap.reason ?? ''}`);
1032
+ }
1033
+ throw new Error('Payment required (auto-payment did not settle)');
1034
+ }
1035
+ /**
1036
+ * Open a streaming payment session with auto-escrow and voucher signing.
1037
+ * Returns a SessionHandle that auto-signs vouchers on each call.
1038
+ *
1039
+ * Usage:
1040
+ * const session = await engram.openPaymentSession({ maxDeposit: 50_000n });
1041
+ * const file = await session.call('sessionReadMemory', ['SOUL.md']);
1042
+ * const receipt = await session.close();
1043
+ */
1044
+ async openPaymentSession(options) {
1045
+ const client = this.createPaymentClient({
1046
+ ledger: options?.ledger,
1047
+ });
1048
+ return await client.openSession({
1049
+ maxDeposit: options?.maxDeposit,
1050
+ autoClose: options?.autoClose ?? true,
1051
+ }, {
1052
+ sign: (payload) => this.identity.sign(payload),
1053
+ getPublicKey: async () => new Uint8Array(this.identity.getPublicKey().toRaw()),
1054
+ });
1055
+ }
1056
+ // === ic402 Service Marketplace ===
1057
+ async registerService(name, description, serviceType, pricing, verificationMethod, delivery, timeout, verifierCanisterId, verificationKey) {
1058
+ const result = await this.actor.registerService(name, description, serviceType, pricing, verificationMethod, verifierCanisterId ? [verifierCanisterId] : [], verificationKey ? [verificationKey] : [], delivery, timeout);
1059
+ if ('err' in result)
1060
+ throw new Error(result.err);
1061
+ return result.ok;
1062
+ }
1063
+ // enableService, disableService removed: owner-only. Operators register
1064
+ // services (disabled); owner enables them via the dashboard.
1065
+ async listServices() {
1066
+ return await this.actor.listServices();
1067
+ }
1068
+ /** Owner/operator view — includes disabled service drafts (others get enabled-only). */
1069
+ async listAllServices() {
1070
+ return await this.actor.listAllServices();
1071
+ }
1072
+ async submitServiceRequest(serviceId, params, paymentSig, rationale, approvalId) {
1073
+ const result = await this.actor.submitServiceRequest(serviceId, params, paymentSig ? [paymentSig] : [], rationale ? [rationale] : [], approvalId ? [approvalId] : []);
1074
+ if ('error' in result)
1075
+ throw new Error(result.error);
1076
+ if ('paymentRequired' in result)
1077
+ throw new Error('Payment required');
1078
+ if ('approvalPending' in result)
1079
+ return { approvalPending: result.approvalPending };
1080
+ return { jobId: result.ok.jobId };
1081
+ }
1082
+ /**
1083
+ * Submit a paid service request with auto-payment (the marketplace counterpart of
1084
+ * getContentWithPayment). Handles #paymentRequired → approve → retry, injecting the
1085
+ * signature at the correct index for the engramx submitServiceRequest signature
1086
+ * (serviceId, params, paymentSig, rationale, approvalId). Returns the jobId, or an
1087
+ * approval-pending handle when the charge exceeds policy and the owner must approve.
1088
+ */
1089
+ async submitServiceRequestWithPayment(serviceId, params, options) {
1090
+ const rationale = options?.rationale ? [options.rationale] : [];
1091
+ const result = await this.payAndCall('submitServiceRequest', [serviceId, params, [], rationale, []], 2, options);
1092
+ if ('ok' in result)
1093
+ return { jobId: result['ok'].jobId };
1094
+ if ('approvalPending' in result)
1095
+ return { approvalPending: result['approvalPending'] };
1096
+ if ('error' in result)
1097
+ throw new Error(String(result['error']));
1098
+ if ('paymentRequired' in result) {
1099
+ throw new Error('Payment required (auto-payment did not settle)');
1100
+ }
1101
+ throw new Error('Unexpected submitServiceRequest result');
1102
+ }
1103
+ async claimJob(jobId) {
1104
+ const result = await this.actor.claimJob(jobId);
1105
+ if ('err' in result)
1106
+ throw new Error(result.err);
1107
+ }
1108
+ async submitJobResult(jobId, result, proof, actualCost) {
1109
+ const r = await this.actor.submitJobResult(jobId, result, proof ? [proof] : [], actualCost ? [actualCost] : []);
1110
+ if ('err' in r)
1111
+ throw new Error(r.err);
1112
+ }
1113
+ async confirmJob(jobId) {
1114
+ const result = await this.actor.confirmJob(jobId);
1115
+ if ('err' in result)
1116
+ throw new Error(result.err);
1117
+ }
1118
+ async disputeJob(jobId, reason) {
1119
+ const result = await this.actor.disputeJob(jobId, reason);
1120
+ if ('err' in result)
1121
+ throw new Error(result.err);
1122
+ }
1123
+ async getJobStatus(jobId) {
1124
+ const result = await this.actor.getJobStatus(jobId);
1125
+ return result.length > 0 ? result[0] : null;
1126
+ }
1127
+ async getJob(jobId) {
1128
+ const result = await this.actor.getJob(jobId);
1129
+ return result.length > 0 ? result[0] : null;
1130
+ }
1131
+ async getJobResult(jobId) {
1132
+ const result = await this.actor.getJobResult(jobId);
1133
+ return result.length > 0 ? new Uint8Array(result[0]) : null;
1134
+ }
1135
+ async pollJobResult(jobId, maxAttempts = 30, intervalMs = 2000) {
1136
+ for (let i = 0; i < maxAttempts; i++) {
1137
+ const job = await this.getJob(jobId);
1138
+ if (!job)
1139
+ throw new Error('Job not found');
1140
+ const jobObj = job;
1141
+ const status = jobObj['status'];
1142
+ if (status &&
1143
+ typeof status === 'object' &&
1144
+ ('Settled' in status ||
1145
+ 'Verified' in status ||
1146
+ 'Refunded' in status ||
1147
+ 'Expired' in status ||
1148
+ 'Disputed' in status)) {
1149
+ return job;
1150
+ }
1151
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
1152
+ }
1153
+ throw new Error('Job did not reach terminal status');
1154
+ }
1155
+ // === ic402 EIP-712 Signing ===
1156
+ // signTypedData removed: owner-only. Cryptographic agent-identity signing is
1157
+ // a value-bearing operation that operators must not perform.
1158
+ async keccak256(data) {
1159
+ return await this.actor.keccak256(data);
1160
+ }
1161
+ clearCache() {
1162
+ this.cache.clear();
1163
+ }
1164
+ getPrincipal() {
1165
+ return this.identity.getPrincipal();
1166
+ }
1167
+ }
1168
+ //# sourceMappingURL=EngramClient.js.map