@engramx/idl 0.16.0-rc.1

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.
@@ -0,0 +1,4045 @@
1
+ type X402SignOutcome =
2
+ variant {
3
+ ApprovalPending: ApprovalRequest;
4
+ Signed: SignedAuthorization;
5
+ };
6
+ type WriteReceipt =
7
+ record {
8
+ head: blob;
9
+ seq: nat;
10
+ version: nat;
11
+ };
12
+ type WorldIdRpContext =
13
+ record {
14
+ createdAt: nat;
15
+ expiresAt: nat;
16
+ nonce: text;
17
+ rpId: text;
18
+ signature: text;
19
+ };
20
+ type WorldIdConfig =
21
+ record {
22
+ action: text;
23
+ appId: text;
24
+ configured: bool;
25
+ rpId: text;
26
+ };
27
+ type WalletBalances =
28
+ record {
29
+ ckusdcE6s: nat;
30
+ ckusdtE6s: nat;
31
+ cycles: nat;
32
+ icpE8s: nat;
33
+ };
34
+ type Voucher =
35
+ record {
36
+ cumulativeAmount: nat;
37
+ sequence: nat;
38
+ sessionId: text;
39
+ signature: blob;
40
+ };
41
+ type UnrecordableEngagement =
42
+ record {
43
+ ended: AgentEnding;
44
+ invite: text;
45
+ "principal": principal;
46
+ root: principal;
47
+ };
48
+ type TrackRecordNotice =
49
+ variant {
50
+ NotRecorded: text;
51
+ Recorded;
52
+ };
53
+ type TokenAmount =
54
+ variant {
55
+ Ckusdc: nat;
56
+ Ckusdt: nat;
57
+ Icp: nat;
58
+ };
59
+ type TimerHealth =
60
+ record {
61
+ alertTimerArmed: bool;
62
+ jobExpiryArmed: bool;
63
+ jobExpiryFastMode: bool;
64
+ sessionExpiryArmed: bool;
65
+ };
66
+ type StorageReport =
67
+ record {
68
+ auditLogSize: nat;
69
+ backupCount: nat;
70
+ backupSize: nat;
71
+ fileLimit: nat;
72
+ memoryFilesCount: nat;
73
+ memoryFilesSize: nat;
74
+ sessionFilesCount: nat;
75
+ sessionFilesSize: nat;
76
+ totalContentSize: nat;
77
+ totalFiles: nat;
78
+ versionHistoryCount: nat;
79
+ versionHistorySize: nat;
80
+ };
81
+ type SpendingPolicy =
82
+ record {
83
+ allowedCallers: opt vec principal;
84
+ blockedCallers: opt vec principal;
85
+ maxConcurrentSessions: opt nat;
86
+ maxPerDay: opt nat;
87
+ maxPerTransaction: opt nat;
88
+ maxSessionDeposit: opt nat;
89
+ maxSessionDuration: opt int;
90
+ rateLimitPerMinute: opt nat;
91
+ sessionIdleTimeout: opt int;
92
+ };
93
+ type SpendCeiling =
94
+ record {
95
+ maxPerDay: opt nat;
96
+ spentToday: nat;
97
+ };
98
+ type SigningFloorStatus =
99
+ record {
100
+ balance: nat;
101
+ floor: nat;
102
+ perSignature: nat;
103
+ signaturesAboveFloor: nat;
104
+ };
105
+ type SignedTransaction =
106
+ record {
107
+ rawTx: text;
108
+ txHash: text;
109
+ };
110
+ type SignedAuthorization =
111
+ record {
112
+ authorization:
113
+ record {
114
+ from: text;
115
+ nonce: text;
116
+ signature: text;
117
+ to: text;
118
+ validAfter: nat;
119
+ validBefore: nat;
120
+ value: nat;
121
+ };
122
+ header: text;
123
+ paidAmount: nat;
124
+ };
125
+ type SignatureCounts =
126
+ record {
127
+ byCaller: vec CallerSignatureCount;
128
+ total: nat;
129
+ };
130
+ type SessionIntent =
131
+ record {
132
+ costPerCall: opt nat;
133
+ description: opt text;
134
+ expiryNs: int;
135
+ minDeposit: opt nat;
136
+ network: text;
137
+ recipient: text;
138
+ suggestedDeposit: nat;
139
+ token: text;
140
+ };
141
+ type SessionContext =
142
+ record {
143
+ files: vec MemoryFile;
144
+ includedFiles: nat;
145
+ totalBytes: nat;
146
+ totalFiles: nat;
147
+ truncated: bool;
148
+ };
149
+ type SessionConfig =
150
+ record {
151
+ autoClose: bool;
152
+ idleTimeout: opt int;
153
+ maxDeposit: nat;
154
+ };
155
+ type ServiceEntry =
156
+ record {
157
+ domains: vec text;
158
+ endpoint: text;
159
+ name: text;
160
+ skills: vec text;
161
+ version: text;
162
+ };
163
+ type Seat =
164
+ record {
165
+ firstPaymentDay: opt nat;
166
+ lastPaymentDay: opt nat;
167
+ pairedDay: nat;
168
+ produced: nat;
169
+ weeks: nat64;
170
+ };
171
+ type Role =
172
+ variant {
173
+ Admin;
174
+ Agent;
175
+ Anonymous;
176
+ Buyer;
177
+ Engram;
178
+ Guardian;
179
+ Other;
180
+ Owner;
181
+ Root;
182
+ Seller;
183
+ Viewer;
184
+ WasmAdmin;
185
+ };
186
+ type RetentionClassSpec =
187
+ variant {
188
+ vault;
189
+ working:
190
+ record {
191
+ maxBytes: opt nat;
192
+ maxVersions: opt nat;
193
+ thinning: variant {
194
+ logSpaced: record {denseTail: nat;};
195
+ tail;
196
+ };
197
+ };
198
+ };
199
+ type ResyncReport =
200
+ record {
201
+ cannotBeRecorded: vec UnrecordableEngagement;
202
+ endingsAcknowledged: nat;
203
+ notRecorded:
204
+ vec
205
+ record {
206
+ event: text;
207
+ "principal": principal;
208
+ root: principal;
209
+ why: text;
210
+ };
211
+ startsAcknowledged: nat;
212
+ };
213
+ type Result_9 =
214
+ variant {
215
+ Err: Error;
216
+ Ok: SignedTransaction;
217
+ };
218
+ type Result_8 =
219
+ variant {
220
+ Err: Error;
221
+ Ok: nat;
222
+ };
223
+ type Result_70 =
224
+ variant {
225
+ Err: Error;
226
+ Ok: AgentRecord;
227
+ };
228
+ type Result_7 =
229
+ variant {
230
+ Err: Error;
231
+ Ok: vec Seat;
232
+ };
233
+ type Result_69 =
234
+ variant {
235
+ Err: Error;
236
+ Ok: Eip712Registration;
237
+ };
238
+ type Result_68 =
239
+ variant {
240
+ Err: Error;
241
+ Ok: record {activatesAtNs: int;};
242
+ };
243
+ type Result_67 =
244
+ variant {
245
+ Err: Error;
246
+ Ok: CondenseResult;
247
+ };
248
+ type Result_66 =
249
+ variant {
250
+ Err: Error;
251
+ Ok: MemoryRangeExport;
252
+ };
253
+ type Result_64 =
254
+ variant {
255
+ Err: Error;
256
+ Ok: Page_21;
257
+ };
258
+ type Result_63 =
259
+ variant {
260
+ Err: Error;
261
+ Ok: AgentWriteReview;
262
+ };
263
+ type Result_62 =
264
+ variant {
265
+ Err: Error;
266
+ Ok: BackupMetadata;
267
+ };
268
+ type Result_61 =
269
+ variant {
270
+ Err: Error;
271
+ Ok: ChildAllowanceRow;
272
+ };
273
+ type Result_60 =
274
+ variant {
275
+ Err: Error;
276
+ Ok: ChildOpening;
277
+ };
278
+ type Result_6 =
279
+ variant {
280
+ Err: Error;
281
+ Ok: RailHealth;
282
+ };
283
+ type Result_59 =
284
+ variant {
285
+ Err: Error;
286
+ Ok: blob;
287
+ };
288
+ type Result_58 =
289
+ variant {
290
+ Err: Error;
291
+ Ok: EvmAddresses;
292
+ };
293
+ type Result_57 =
294
+ variant {
295
+ Err: Error;
296
+ Ok: AlertStatus;
297
+ };
298
+ type Result_56 =
299
+ variant {
300
+ Err: Error;
301
+ Ok: MemoryHistory;
302
+ };
303
+ type Result_55 =
304
+ variant {
305
+ Err: Error;
306
+ Ok: MemoryRetentionPolicy;
307
+ };
308
+ type Result_54 =
309
+ variant {
310
+ Err: Error;
311
+ Ok: OwnerConsole;
312
+ };
313
+ type Result_53 =
314
+ variant {
315
+ Err: Error;
316
+ Ok: record {paths: vec text;};
317
+ };
318
+ type Result_52 =
319
+ variant {
320
+ Err: Error;
321
+ Ok: SigningFloorStatus;
322
+ };
323
+ type Result_51 =
324
+ variant {
325
+ Err: Error;
326
+ Ok: SpendCeiling;
327
+ };
328
+ type Result_50 =
329
+ variant {
330
+ Err: Error;
331
+ Ok: SpendingPolicy;
332
+ };
333
+ type Result_5 =
334
+ variant {
335
+ Err: Error;
336
+ Ok: SignatureCounts;
337
+ };
338
+ type Result_49 =
339
+ variant {
340
+ Err: Error;
341
+ Ok: StorageReport;
342
+ };
343
+ type Result_48 =
344
+ variant {
345
+ Err: Error;
346
+ Ok: WalletBalances;
347
+ };
348
+ type Result_47 =
349
+ variant {
350
+ Err: Error;
351
+ Ok: WorldIdConfig;
352
+ };
353
+ type Result_46 =
354
+ variant {
355
+ Err: Error;
356
+ Ok: WorldIdRpContext;
357
+ };
358
+ type Result_45 =
359
+ variant {
360
+ Err: Error;
361
+ Ok: opt text;
362
+ };
363
+ type Result_44 =
364
+ variant {
365
+ Err: Error;
366
+ Ok: bool;
367
+ };
368
+ type Result_43 =
369
+ variant {
370
+ Err: Error;
371
+ Ok: text;
372
+ };
373
+ type Result_42 =
374
+ variant {
375
+ Err: Error;
376
+ Ok: Page_20;
377
+ };
378
+ type Result_41 =
379
+ variant {
380
+ Err: Error;
381
+ Ok: Page_19;
382
+ };
383
+ type Result_40 =
384
+ variant {
385
+ Err: Error;
386
+ Ok: Page_18;
387
+ };
388
+ type Result_4 =
389
+ variant {
390
+ Err: Error;
391
+ Ok: Eip712SignatureOut;
392
+ };
393
+ type Result_39 =
394
+ variant {
395
+ Err: Error;
396
+ Ok: Page_17;
397
+ };
398
+ type Result_38 =
399
+ variant {
400
+ Err: Error;
401
+ Ok: Page_16;
402
+ };
403
+ type Result_37 =
404
+ variant {
405
+ Err: Error;
406
+ Ok: CheckpointPage;
407
+ };
408
+ type Result_36 =
409
+ variant {
410
+ Err: Error;
411
+ Ok: Page_14;
412
+ };
413
+ type Result_35 =
414
+ variant {
415
+ Err: Error;
416
+ Ok: Page_13;
417
+ };
418
+ type Result_34 =
419
+ variant {
420
+ Err: Error;
421
+ Ok: Page_12;
422
+ };
423
+ type Result_33 =
424
+ variant {
425
+ Err: Error;
426
+ Ok: Page_11;
427
+ };
428
+ type Result_32 =
429
+ variant {
430
+ Err: Error;
431
+ Ok: Page_10;
432
+ };
433
+ type Result_31 =
434
+ variant {
435
+ Err: Error;
436
+ Ok: ReceiptPage;
437
+ };
438
+ type Result_30 =
439
+ variant {
440
+ Err: Error;
441
+ Ok: Page_8;
442
+ };
443
+ type Result_3 =
444
+ variant {
445
+ Err: Error;
446
+ Ok: X402SignOutcome;
447
+ };
448
+ type Result_29 =
449
+ variant {
450
+ Err: Error;
451
+ Ok: LogPage;
452
+ };
453
+ type Result_28 =
454
+ variant {
455
+ Err: Error;
456
+ Ok: Page_6;
457
+ };
458
+ type Result_27 =
459
+ variant {
460
+ Err: Error;
461
+ Ok: Page_5;
462
+ };
463
+ type Result_26 =
464
+ variant {
465
+ Err: Error;
466
+ Ok: Page_4;
467
+ };
468
+ type Result_25 =
469
+ variant {
470
+ Err: Error;
471
+ Ok: PaymentSignatureCounts;
472
+ };
473
+ type Result_24 =
474
+ variant {
475
+ Err: Error;
476
+ Ok: Page_2;
477
+ };
478
+ type Result_23 =
479
+ variant {
480
+ Err: Error;
481
+ Ok: Page_1;
482
+ };
483
+ type Result_22 =
484
+ variant {
485
+ Err: Error;
486
+ Ok: Page;
487
+ };
488
+ type Result_21 =
489
+ variant {
490
+ Err: Error;
491
+ Ok: principal;
492
+ };
493
+ type Result_20 =
494
+ variant {
495
+ Err: Error;
496
+ Ok: EngramSessionState;
497
+ };
498
+ type Result_2 =
499
+ variant {
500
+ Err: Error;
501
+ Ok: ControlHandover;
502
+ };
503
+ type Result_19 =
504
+ variant {
505
+ Err: Error;
506
+ Ok: Pairing;
507
+ };
508
+ type Result_18 =
509
+ variant {
510
+ Err: Error;
511
+ Ok: CondensePreview;
512
+ };
513
+ type Result_17 =
514
+ variant {
515
+ Err: Error;
516
+ Ok: vec BatchReadItem;
517
+ };
518
+ type Result_16 =
519
+ variant {
520
+ Err: Error;
521
+ Ok: MemoryByteChunk;
522
+ };
523
+ type Result_15 =
524
+ variant {
525
+ Err: Error;
526
+ Ok: GuardianRecord;
527
+ };
528
+ type Result_14 =
529
+ variant {
530
+ Err: Error;
531
+ Ok: ResetReport;
532
+ };
533
+ type Result_13 =
534
+ variant {
535
+ Err: Error;
536
+ Ok: ResyncReport;
537
+ };
538
+ type Result_12 =
539
+ variant {
540
+ Err: Error;
541
+ Ok: SessionContext;
542
+ };
543
+ type Result_11 =
544
+ variant {
545
+ Err: Error;
546
+ Ok: MemoryFile;
547
+ };
548
+ type Result_10 =
549
+ variant {
550
+ Err: Error;
551
+ Ok: BackupRetentionReport;
552
+ };
553
+ type Result_1 =
554
+ variant {
555
+ Err: Error;
556
+ Ok;
557
+ };
558
+ type Result =
559
+ variant {
560
+ Err: Error;
561
+ Ok: WriteReceipt;
562
+ };
563
+ type ResetReport =
564
+ record {
565
+ agentsPurged: nat;
566
+ agentsRemoved: nat;
567
+ filesCleared: nat;
568
+ guardiansRemoved: nat;
569
+ };
570
+ type Repudiation =
571
+ record {
572
+ atNs: int;
573
+ auditId: nat;
574
+ path: text;
575
+ reason: text;
576
+ version: nat;
577
+ };
578
+ type RegisteredType =
579
+ record {
580
+ ackIrrevocable: bool;
581
+ authorityClass: AuthorityClass;
582
+ chainId: nat;
583
+ cooloffEndsAtNs: int;
584
+ dailySignCap: nat;
585
+ destinationFields: vec text;
586
+ domainName: text;
587
+ domainSeparator: vec nat8;
588
+ domainVersion: text;
589
+ encodeTypeString: text;
590
+ fields: vec record {
591
+ text;
592
+ text;
593
+ };
594
+ primaryType: text;
595
+ registeredAtNs: int;
596
+ typeHash: vec nat8;
597
+ typeId: blob;
598
+ verifyingContract: text;
599
+ };
600
+ type ReceiptPage =
601
+ record {
602
+ firstAvailableSeq: nat;
603
+ page: Page_9;
604
+ scannedThroughSeq: nat;
605
+ };
606
+ type RailHealth =
607
+ record {
608
+ cycles: nat;
609
+ escrowOpen: opt nat;
610
+ freezeReservation: nat;
611
+ memoryBytes: nat;
612
+ paidVouchersConsumed: nat;
613
+ sendHeadroom: nat;
614
+ sessionsClosed: nat;
615
+ sessionsClosing: nat;
616
+ sessionsExpired: nat;
617
+ sessionsOpen: nat;
618
+ unknownSessionRefusals: nat;
619
+ };
620
+ type PurgedAgent =
621
+ record {
622
+ ended: AgentEnding;
623
+ enrolledKey: principal;
624
+ invite: text;
625
+ name: text;
626
+ pairedAtNs: int;
627
+ "principal": principal;
628
+ purgedAtNs: int;
629
+ root: principal;
630
+ };
631
+ type PaymentSignatureCounts =
632
+ record {
633
+ agents: Page_3;
634
+ day: int;
635
+ limit: nat;
636
+ marketplace: opt MarketplacePaymentSignatures;
637
+ };
638
+ type PaymentSignature =
639
+ record {
640
+ asset: opt text;
641
+ authorization: opt Eip3009Authorization;
642
+ network: text;
643
+ nonce: blob;
644
+ publicKey: opt blob;
645
+ scheme: text;
646
+ sender: text;
647
+ signature: blob;
648
+ };
649
+ type Pairing =
650
+ record {
651
+ agent: AgentRecord;
652
+ trackRecord: TrackRecordNotice;
653
+ };
654
+ type Page_9 =
655
+ record {
656
+ items: vec ExportReceiptRow;
657
+ total: nat;
658
+ withheld: nat;
659
+ };
660
+ type Page_8 =
661
+ record {
662
+ items: vec record {
663
+ lastModifiedAtNs: int;
664
+ path: text;
665
+ version: nat;
666
+ };
667
+ total: nat;
668
+ withheld: nat;
669
+ };
670
+ type Page_7 =
671
+ record {
672
+ items: vec MemoryLogEvent;
673
+ total: nat;
674
+ withheld: nat;
675
+ };
676
+ type Page_6 =
677
+ record {
678
+ items: vec ApprovalRequest;
679
+ total: nat;
680
+ withheld: nat;
681
+ };
682
+ type Page_5 =
683
+ record {
684
+ items: vec EngramSessionState;
685
+ total: nat;
686
+ withheld: nat;
687
+ };
688
+ type Page_4 =
689
+ record {
690
+ items: vec OwnershipTransfer;
691
+ total: nat;
692
+ withheld: nat;
693
+ };
694
+ type Page_3 =
695
+ record {
696
+ items: vec AgentPaymentSignatures;
697
+ total: nat;
698
+ withheld: nat;
699
+ };
700
+ type Page_21 =
701
+ record {
702
+ items: vec OwnerRow;
703
+ total: nat;
704
+ withheld: nat;
705
+ };
706
+ type Page_20 =
707
+ record {
708
+ items: vec ActivitySnapshot;
709
+ total: nat;
710
+ withheld: nat;
711
+ };
712
+ type Page_2 =
713
+ record {
714
+ items: vec PurgedAgent;
715
+ total: nat;
716
+ withheld: nat;
717
+ };
718
+ type Page_19 =
719
+ record {
720
+ items: vec AgentRecord;
721
+ total: nat;
722
+ withheld: nat;
723
+ };
724
+ type Page_18 =
725
+ record {
726
+ items: vec record {
727
+ name: text;
728
+ "principal": principal;
729
+ status: AgentStatus;
730
+ };
731
+ total: nat;
732
+ withheld: nat;
733
+ };
734
+ type Page_17 =
735
+ record {
736
+ items: vec BackupMetadata;
737
+ total: nat;
738
+ withheld: nat;
739
+ };
740
+ type Page_16 =
741
+ record {
742
+ items: vec BearerToken;
743
+ total: nat;
744
+ withheld: nat;
745
+ };
746
+ type Page_15 =
747
+ record {
748
+ items: vec CondenseCheckpointRow;
749
+ total: nat;
750
+ withheld: nat;
751
+ };
752
+ type Page_14 =
753
+ record {
754
+ items: vec CycleSnapshot;
755
+ total: nat;
756
+ withheld: nat;
757
+ };
758
+ type Page_13 =
759
+ record {
760
+ items: vec text;
761
+ total: nat;
762
+ withheld: nat;
763
+ };
764
+ type Page_12 =
765
+ record {
766
+ items: vec RegisteredType;
767
+ total: nat;
768
+ withheld: nat;
769
+ };
770
+ type Page_11 =
771
+ record {
772
+ items: vec record {
773
+ activeFromNs: int;
774
+ address: text;
775
+ };
776
+ total: nat;
777
+ withheld: nat;
778
+ };
779
+ type Page_10 =
780
+ record {
781
+ items: vec GuardianRecord;
782
+ total: nat;
783
+ withheld: nat;
784
+ };
785
+ type Page_1 =
786
+ record {
787
+ items: vec AuditEntry;
788
+ total: nat;
789
+ withheld: nat;
790
+ };
791
+ type Page =
792
+ record {
793
+ items: vec Repudiation;
794
+ total: nat;
795
+ withheld: nat;
796
+ };
797
+ type OwnershipTransfer =
798
+ record {
799
+ atNs: int;
800
+ authorizedBy: principal;
801
+ from: opt principal;
802
+ to: principal;
803
+ };
804
+ type OwnerRow =
805
+ record {
806
+ firstPaymentDay: opt nat;
807
+ lastPaymentDay: opt nat;
808
+ pairedDay: nat;
809
+ produced: nat;
810
+ root: principal;
811
+ weeks: nat64;
812
+ };
813
+ type OwnerConsole =
814
+ record {
815
+ agentRevisablePathCount: nat;
816
+ cyclesBalance: nat;
817
+ memoryKeyEpoch: nat;
818
+ owner: opt principal;
819
+ pendingApprovalCount: nat;
820
+ protectedPathCount: nat;
821
+ unknownSessionRefusals: nat;
822
+ };
823
+ type MemoryVersionSummary =
824
+ record {
825
+ atNs: int;
826
+ by: principal;
827
+ contentRoot: blob;
828
+ kind: variant {
829
+ append;
830
+ restore;
831
+ set;
832
+ tombstone;
833
+ };
834
+ seq: nat;
835
+ size: nat;
836
+ version: nat;
837
+ };
838
+ type MemorySource =
839
+ variant {
840
+ agentDerived;
841
+ deterministic;
842
+ fileSync;
843
+ human;
844
+ sessionTranscript;
845
+ };
846
+ type MemoryRetentionPolicy =
847
+ record {
848
+ entries: vec record {
849
+ text;
850
+ RetentionClassSpec;
851
+ };
852
+ guards: vec record {
853
+ notBeforeNs: int;
854
+ prefix: text;
855
+ };
856
+ };
857
+ type MemoryRangeExport =
858
+ record {
859
+ atNs: int;
860
+ authorFilter: opt principal;
861
+ fromSeq: nat;
862
+ head: blob;
863
+ opsRoot: blob;
864
+ seq: nat;
865
+ toSeq: nat;
866
+ };
867
+ type MemoryMaintainStatus =
868
+ record {
869
+ attachDetail: text;
870
+ attachOk: bool;
871
+ lastEvictions: nat;
872
+ lastMaintainAtNs: opt int;
873
+ lastScrubbed: nat;
874
+ more: bool;
875
+ refused: bool;
876
+ refusedQuota: text;
877
+ totalEvictions: nat;
878
+ totalFindings: nat;
879
+ };
880
+ type MemoryLogOp =
881
+ variant {
882
+ append: record {
883
+ deltaHash: blob;
884
+ deltaSize: nat;
885
+ newSize: nat;
886
+ };
887
+ condense:
888
+ record {
889
+ fromSeq: nat;
890
+ headAtBoundary: blob;
891
+ opCount: nat;
892
+ opsRoot: blob;
893
+ toSeq: nat;
894
+ };
895
+ evict:
896
+ record {
897
+ byteCount: nat;
898
+ chunkCount: nat;
899
+ evictedRoot: blob;
900
+ fromVersion: nat;
901
+ toVersion: nat;
902
+ };
903
+ exported:
904
+ record {
905
+ authorFilter: opt principal;
906
+ fromSeq: nat;
907
+ opsRoot: blob;
908
+ toSeq: nat;
909
+ };
910
+ imported: MemoryLogImportNote;
911
+ restore: record {
912
+ manifest: MemoryLogManifest;
913
+ toVersion: nat;
914
+ };
915
+ set: record {manifest: MemoryLogManifest;};
916
+ tombstone;
917
+ };
918
+ type MemoryLogManifest =
919
+ record {
920
+ chunks: vec MemoryLogChunkRef;
921
+ contentRoot: blob;
922
+ totalSize: nat;
923
+ };
924
+ type MemoryLogImportNote =
925
+ variant {
926
+ genesis: record {boundTo: opt blob;};
927
+ legacyDelete;
928
+ legacyGap: record {firstSurviving: nat;};
929
+ legacyTruncated: record {
930
+ prefixHash: blob;
931
+ storedBytes: nat;
932
+ };
933
+ legacyUnreconstructable: record {
934
+ contentHash: blob;
935
+ reason: text;
936
+ };
937
+ };
938
+ type MemoryLogEvent =
939
+ record {
940
+ atNs: int;
941
+ by: principal;
942
+ contentRoot: blob;
943
+ op: MemoryLogOp;
944
+ path: text;
945
+ prev: blob;
946
+ seq: nat;
947
+ size: nat;
948
+ version: nat;
949
+ };
950
+ type MemoryLogChunkRef =
951
+ record {
952
+ birthSeq: nat;
953
+ hash: blob;
954
+ size: nat;
955
+ };
956
+ type MemoryHistory =
957
+ record {
958
+ everCount: nat;
959
+ evictedCount: nat;
960
+ evictions: vec MemoryEviction;
961
+ oldestRetainedVersion: nat;
962
+ retainedCount: nat;
963
+ versions: vec MemoryVersionSummary;
964
+ };
965
+ type MemoryFile =
966
+ record {
967
+ content: blob;
968
+ lastModifiedAtNs: int;
969
+ lastModifiedBy: principal;
970
+ path: text;
971
+ version: nat;
972
+ };
973
+ type MemoryEviction =
974
+ record {
975
+ cause: variant {
976
+ maxBytes;
977
+ maxVersions;
978
+ };
979
+ evictSeq: nat;
980
+ evictedRoot: blob;
981
+ fromVersion: nat;
982
+ toVersion: nat;
983
+ trigger: variant {
984
+ globalCap;
985
+ perFileCap;
986
+ policy;
987
+ thinLogSpaced;
988
+ thinTail;
989
+ };
990
+ };
991
+ type MemoryByteChunk =
992
+ record {
993
+ bytes: blob;
994
+ fileSize: nat;
995
+ more: bool;
996
+ };
997
+ type MarketplacePaymentSignatures =
998
+ record {
999
+ limit: nat;
1000
+ marketplace: principal;
1001
+ sharedSignedToday: nat;
1002
+ };
1003
+ type LogPosition =
1004
+ record {
1005
+ reads: nat;
1006
+ "record": nat;
1007
+ };
1008
+ type LogPage =
1009
+ record {
1010
+ firstAvailableSeq: nat;
1011
+ head: blob;
1012
+ page: Page_7;
1013
+ };
1014
+ type KeyNomination =
1015
+ record {
1016
+ atNs: int;
1017
+ key: principal;
1018
+ };
1019
+ type HttpResponse =
1020
+ record {
1021
+ body: blob;
1022
+ headers: vec record {
1023
+ text;
1024
+ text;
1025
+ };
1026
+ status_code: nat16;
1027
+ upgrade: opt bool;
1028
+ };
1029
+ type HttpRequestResult =
1030
+ record {
1031
+ body: blob;
1032
+ headers: vec HttpHeader;
1033
+ status: nat;
1034
+ };
1035
+ type HttpRequest =
1036
+ record {
1037
+ body: blob;
1038
+ headers: vec record {
1039
+ text;
1040
+ text;
1041
+ };
1042
+ method: text;
1043
+ url: text;
1044
+ };
1045
+ type HttpHeader =
1046
+ record {
1047
+ name: text;
1048
+ value: text;
1049
+ };
1050
+ type HeldKey =
1051
+ record {
1052
+ from: LogPosition;
1053
+ key: principal;
1054
+ until: LogPosition;
1055
+ };
1056
+ type GuardianStatus =
1057
+ variant {
1058
+ Active;
1059
+ Pending;
1060
+ };
1061
+ type GuardianRecord =
1062
+ record {
1063
+ addedAtNs: int;
1064
+ name: text;
1065
+ permissions: GuardianPermissions;
1066
+ "principal": principal;
1067
+ status: GuardianStatus;
1068
+ };
1069
+ type GuardianPermissions =
1070
+ record {
1071
+ canFreezePayments: bool;
1072
+ canPauseWrites: bool;
1073
+ canRevokeAgents: bool;
1074
+ scopes: opt vec text;
1075
+ };
1076
+ type GenesisNote =
1077
+ record {
1078
+ genesisAtNs: int;
1079
+ note: text;
1080
+ predecessorHeadHash: opt blob;
1081
+ predecessorSize: opt nat;
1082
+ version: text;
1083
+ };
1084
+ type ExportReceiptRow =
1085
+ record {
1086
+ atNs: int;
1087
+ authorFilter: opt principal;
1088
+ by: principal;
1089
+ fromSeq: nat;
1090
+ opsRoot: blob;
1091
+ seq: nat;
1092
+ toSeq: nat;
1093
+ };
1094
+ type EvmAddresses =
1095
+ record {
1096
+ engramRegistration: text;
1097
+ pay: text;
1098
+ };
1099
+ type Error =
1100
+ variant {
1101
+ Conflict: text;
1102
+ Invalid: text;
1103
+ NotFound: text;
1104
+ Other: text;
1105
+ Refused: text;
1106
+ Unavailable: text;
1107
+ };
1108
+ type EnrolmentProof =
1109
+ record {
1110
+ enrolledKey: principal;
1111
+ inviteCode: text;
1112
+ rootPubkey: blob;
1113
+ signature: blob;
1114
+ };
1115
+ type Enrolment =
1116
+ record {
1117
+ engram: principal;
1118
+ enrolledKey: principal;
1119
+ rootPubkey: blob;
1120
+ signature: blob;
1121
+ };
1122
+ type EngramSessionStatus =
1123
+ variant {
1124
+ closed;
1125
+ closing;
1126
+ expired;
1127
+ open;
1128
+ other: text;
1129
+ };
1130
+ type EngramSessionState =
1131
+ record {
1132
+ consumed: nat;
1133
+ deposited: nat;
1134
+ id: text;
1135
+ lastActivityAtNs: int;
1136
+ openedAtNs: int;
1137
+ payer: principal;
1138
+ remaining: nat;
1139
+ status: EngramSessionStatus;
1140
+ voucherCount: nat;
1141
+ };
1142
+ type Engram =
1143
+ service {
1144
+ /// ⟨0.16.0⟩ `about()` — the ONE public projection, replacing `status()` and absorbing `metrics()`.
1145
+ ///
1146
+ /// Two public projections of one canister was the L2.2 defect: both answered "what is this engram
1147
+ /// like", disagreed about which counters they carried, and `status()` additionally masked two
1148
+ /// fields behind an `authorized` flag whose own declaration admitted the problem — "a consumer
1149
+ /// that must remember to check a sibling flag is one that will eventually forget."
1150
+ ///
1151
+ /// The fix is not a better flag. The gated fields MOVED (see `getOwnerConsole`), so there is no
1152
+ /// masked field to forget and no flag to check. Reaching the gated record is the authorization.
1153
+ ///
1154
+ /// Ungated by design and marked for the L8 gate-grep.
1155
+ /// PUBLIC: existence, shape, posture and day-floored activity. No principal, no balance, no
1156
+ /// precise timestamp — nothing here identifies a person or reveals when they are at the keyboard.
1157
+ about: () -> (About) query;
1158
+ /// The NEW key accepts the nomination the agent's current key (`agentKey`) made of it, and takes
1159
+ /// over the agent's record: the same lasting identity, enrolment statement, invite and
1160
+ /// permissions, today's spending and signature totals, and any spending limit the owner set for
1161
+ /// that agent. From this message on, `agentKey` is not an agent of this engram and every call it
1162
+ /// makes as one is refused. The registry is not called: it goes on naming the same open
1163
+ /// engagement (`KeyReplacement`'s header says why and what a modified engram could do with it).
1164
+ ///
1165
+ /// ONE MESSAGE, NO AWAIT: every check, the move of the record and of each per-key total, and the
1166
+ /// record entry happen together, so no call can fall between a check and the move, and no total
1167
+ /// is ever held by both keys or by neither. Answers the record as the new key now holds it.
1168
+ ///
1169
+ /// WHO MAY CALL is decided by `KeyReplacement.accepted`: only the key a pending, unexpired
1170
+ /// nomination names. That check compares the caller inside the module, where
1171
+ /// test/key-replacement.test.mo runs it; this body names no roster gate, because the caller holds
1172
+ /// no role here until the move.
1173
+ acceptAgentKeyNomination: (agentKey: principal) -> (Result_70);
1174
+ /// Consensus transform for the alert webhook outcall: collapse the response to status-only so
1175
+ /// replicas agree regardless of response headers/body (we only care that the POST landed 2xx).
1176
+ ///
1177
+ /// PUBLIC: not a choice. The management canister invokes a transform on every replica as part of
1178
+ /// the HTTP-outcall protocol, and that invocation carries no caller identity to gate on — a gate
1179
+ /// here would refuse the only caller there is and break every alert this canister sends. Safe
1180
+ /// because it is total and stateless: it reads its argument and returns, touching nothing.
1181
+ actionAlertTransform: (args:
1182
+ record {
1183
+ context: blob;
1184
+ response: HttpRequestResult;
1185
+ }) -> (HttpRequestResult) query;
1186
+ /// Register a typed-data shape as signable. Owner-only, freeze-gated (a registration is a grant
1187
+ /// of FUTURE authority — a frozen engram must not be able to widen what it can sign), and the
1188
+ /// shape is IMMUTABLE: any change mints a new typeId.
1189
+ addEip712Type: (spec:
1190
+ record {
1191
+ ackIrrevocable: bool;
1192
+ authorityClass: variant {
1193
+ standard;
1194
+ standingAuthority;
1195
+ valueBearing;
1196
+ };
1197
+ canonicalTypeString: text;
1198
+ chainId: nat;
1199
+ dailySignCap: nat;
1200
+ destinationFields: vec text;
1201
+ domainName: text;
1202
+ domainVersion: text;
1203
+ fields: vec record {
1204
+ text;
1205
+ text;
1206
+ };
1207
+ primaryType: text;
1208
+ verifyingContract: text;
1209
+ }) -> (Result_69);
1210
+ /// Owner-managed destination allowlist (D7). Additions LOOSEN and wait 24h; removals TIGHTEN
1211
+ /// and apply instantly — the ratified asymmetry, applied to the one mutable policy surface.
1212
+ addEvmWithdrawDestination: (dest: text) -> (Result_68);
1213
+ /// ⟨0.16.0, L1⟩ THE ROLE-CONFLICT ARM, and why three refusals here are not the same refusal.
1214
+ ///
1215
+ /// This method had five distinct ways to say no through one `text`, and a caller could only tell
1216
+ /// them apart by matching prose. They are now three different arms because they call for three
1217
+ /// different responses: `#Invalid` means fix the argument and retry, `#Refused` means you are not
1218
+ /// the owner and retrying changes nothing, and `#Conflict` means the request was well-formed and
1219
+ /// permitted but this engram's state says no — the principal already holds an incompatible role,
1220
+ /// or the roster is full. Only the last is worth an agent's time to resolve.
1221
+ addGuardian: (guardianPrincipal: principal, name: text, permissions:
1222
+ GuardianPermissions) -> (Result_1);
1223
+ /// Returns the write RECEIPT, not just the version.
1224
+ ///
1225
+ /// The receipt was always computed and then thrown away: MemoryRouter hands back
1226
+ /// {seq; version; contentRoot; head}, the audit detail stringifies `seq=`/`head=` into prose, and
1227
+ /// the caller got a bare Nat. So a writer could be told their write landed but not be given the
1228
+ /// evidence it landed — they had to read the audit log back and parse a string to learn the head
1229
+ /// their own write produced. `head` is the chain head AFTER this write; it transitively commits
1230
+ /// to contentRoot, which is why the root is not repeated here (the same ruling that dropped
1231
+ /// per-entry contentRoot from the audit entries).
1232
+ ///
1233
+ /// This is the half of a consumer request that costs nothing: the commitment ledger was still
1234
+ /// "trust whoever runs the service" one hop from the proof — a third party can now be handed the receipt
1235
+ /// directly by whoever wrote the content. A return-TYPE change cannot be absorbed by the client
1236
+ /// subset rule, so it can only ride an interface break; this release is one.
1237
+ appendMemory: (path: text, content: blob, source: opt MemorySource) ->
1238
+ (Result);
1239
+ /// Replace the retained log prefix through `toSeq` with a single checkpoint.
1240
+ ///
1241
+ /// PREFIX-ONLY: there is no `fromSeq`, because the library made that unrepresentable rather than
1242
+ /// merely checked — a condense always starts at the current floor. It refuses unless an
1243
+ /// UNFILTERED export receipt already covers the whole range; call previewCondense first,
1244
+ /// which reports how far your existing receipts reach.
1245
+ ///
1246
+ /// `historyBytesDelta` is SIGNED, and the sign is the point. A condense subtracts the dropped
1247
+ /// entries' canonical bytes and adds the checkpoint's own, so condensing a short prefix of small
1248
+ /// entries can leave the history meter HIGHER than it found it. The owner is told which happened
1249
+ /// rather than left to assume the number went down.
1250
+ ///
1251
+ /// Gated on requireWritesNotPaused — unlike the export. This one destroys retained entries, which
1252
+ /// is exactly what a guardian pause exists to hold. No action alert is raised: the operation is
1253
+ /// owner-initiated, owner-only, and already chained into the audit log.
1254
+ applyCondense: (toSeq: nat) -> (Result_67);
1255
+ /// Owner-only: activate a guardian who has registered against an invite.
1256
+ ///
1257
+ /// Until this call the guardian is `#Pending` and can do NOTHING — registering is a claim, this
1258
+ /// is the grant. That two-step is what stops a leaked invite code from producing a live guardian
1259
+ /// on its own.
1260
+ ///
1261
+ /// `#Conflict("Guardian is already active")` when there is nothing to do — the refusal says the
1262
+ /// state the caller asked for is the state that holds, rather than pretending it acted.
1263
+ approveGuardian: (guardianPrincipal: principal) -> (Result_1);
1264
+ /// Owner-only: approve one pending over-limit request, letting the agent retry the operation
1265
+ /// its policy refused.
1266
+ ///
1267
+ /// ⚠ THE APPROVAL IS SINGLE-USE AND EXPIRES IN ONE HOUR. Approving does not perform the
1268
+ /// operation and does not raise the policy — it opens a window. The agent must retry within
1269
+ /// the hour, and the retry CONSUMES the approval (`#Used`); a second attempt needs a second
1270
+ /// approval. Past the hour it becomes `#Expired` on first touch. Neither state is recoverable by
1271
+ /// approving again — the owner approves a NEW request.
1272
+ /// ⟨2026-09-29, review finding X-11⟩ One exception, made by the engram and not by the owner: when
1273
+ /// the retry's signer FAILS, nothing was signed, so the approval goes back from `#Used` to
1274
+ /// `#Approved` with its original expiry and an `approval.released` entry says so.
1275
+ ///
1276
+ /// ⚠ IT IS BOUND TO THE EXACT OPERATION THAT WAS REQUESTED, not to the agent. An approval
1277
+ /// carries the operation, the amount and the binding it was raised for, and consuming it checks
1278
+ /// all three — so approving a request does not grant the agent a general allowance for the
1279
+ /// hour, and cannot be redirected at a different recipient or a larger sum.
1280
+ ///
1281
+ /// Refuses `#NotFound` for an unknown id, and `#Conflict` NAMING THE STATUS when the request is
1282
+ /// no longer pending — already approved, denied or expired. The status is in the message because
1283
+ /// "not pending" is the one refusal here an owner acts on differently depending on which it is.
1284
+ approveRequest: (requestId: text) -> (Result_1);
1285
+ /// Discard a backup started by `backupInit` and never finalized. The way out of a failed upload,
1286
+ /// and the reason a stalled one need not occupy space indefinitely. Same gate as the rest of the
1287
+ /// write half: owner, or an agent holding `canAppendMemory`.
1288
+ backupAbort: (backupId: text) -> (Result_1);
1289
+ /// Close a backup started by `backupInit` and mark it usable.
1290
+ ///
1291
+ /// ⚠ ONLY THE CREATOR MAY FINALIZE — `#Refused("Not the creator of this backup")`. An agent
1292
+ /// cannot close an upload someone else began, so a half-finished backup belongs to whoever
1293
+ /// started it.
1294
+ ///
1295
+ /// Refuses with `#Conflict` when the backup is already finalized, when it is marked failed, and
1296
+ /// when it holds ZERO chunks or zero total size — an empty backup is refused rather than recorded
1297
+ /// as a success, which is the difference between "no backup" and "a backup of nothing".
1298
+ ///
1299
+ /// It does NOT verify the `sha256` declared at init; see that method for what the digest is and
1300
+ /// is not.
1301
+ backupFinalize: (backupId: text) -> (Result_1);
1302
+ /// Begin a chunked backup upload. Returns the `backupId` every later call in the sequence needs.
1303
+ ///
1304
+ /// **THE SEQUENCE**, which no individual signature here conveys:
1305
+ /// `backupInit` → `backupPushChunk` × N → `backupFinalize`, or `backupAbort` to discard. A backup
1306
+ /// that is never finalized is not a backup; `backupFinalize` refuses one with zero chunks or zero
1307
+ /// total size rather than recording an empty success.
1308
+ ///
1309
+ /// ⚠ THE `sha256` IS YOUR DECLARATION, NOT THIS CANISTER'S FINDING. It is taken here, before any
1310
+ /// bytes arrive, stored with the metadata and carried through finalize UNCHANGED — verified at
1311
+ /// the site 2026-08-21: `Backup.finalizeBackup` checks existence, creator, state and non-emptiness
1312
+ /// and does NOT recompute or compare a digest. So it is a commitment a restorer can check the
1313
+ /// bytes against LATER, and it is not evidence the bytes match. Treating it as a canister-verified
1314
+ /// hash is the misreading to avoid — and it is an easy one to reach, because the REGISTRY's
1315
+ /// chunked upload (`engramWasmInit`/`PushChunk`/`Finalize`) has the same shape and DOES verify:
1316
+ /// it hashes the assembled bytes and refuses on mismatch. Same protocol, opposite semantics.
1317
+ ///
1318
+ /// **TWO DIFFERENT PERMISSIONS GUARD THE TWO HALVES.** Writing (init, push, finalize, abort) is
1319
+ /// owner-or-agent and an AGENT additionally needs `canAppendMemory`; reading
1320
+ /// (`backupPullChunk`) needs `read_backup`. An agent can therefore be able to upload a backup
1321
+ /// and unable to download one, or the reverse, which is the point of splitting them.
1322
+ ///
1323
+ /// The write half also refuses while writes are PAUSED.
1324
+ backupInit: (dbType: text, backupType: BackupType, parentBackupId:
1325
+ opt text, sha256: text, backupLabel: opt text) -> (Result_43);
1326
+ /// Read one chunk of a finalized backup, by index.
1327
+ ///
1328
+ /// ⚠ GATED ON `read_backup`, which is NOT the permission the upload half uses — that one is
1329
+ /// `canAppendMemory`. Holding one says nothing about the other, deliberately: taking a backup and
1330
+ /// carrying it away are different authorities.
1331
+ backupPullChunk: (backupId: text, chunkIndex: nat) -> (Result_59) query;
1332
+ backupPushChunk: (backupId: text, chunkIndex: nat, data: blob) ->
1333
+ (Result_1);
1334
+ /// The OWNER cancels an agent's pending key nomination; the agent keeps its current key. Refused
1335
+ /// by name when the agent is unknown or has nothing pending — never a silent success.
1336
+ cancelAgentKeyNomination: (agentKey: principal) -> (Result_1);
1337
+ /// Close a session the caller paid for: settle what was consumed, refund the remainder.
1338
+ ///
1339
+ /// PAYER-ONLY, enforced inside the gateway. This is the exit that did not exist — `endSession`
1340
+ /// and `forceCloseSession` were on the MARKET, which is withheld, and whose escrow subaccount is
1341
+ /// under a different principal entirely, so its close could never have reached these funds.
1342
+ closeSession: (sessionId: text) -> (Result_1);
1343
+ /// Convert the engram's OWN earned ckUSDC/ckUSDT into its OWN cycles, via the cashier — the
1344
+ /// engram-self top-up path. Owner-only; per-call cap + cooldown (irreversible); NOT freeze-gated
1345
+ /// (infrastructure funding). Flow: read the engram's deposit address on the cashier, move the
1346
+ /// engram's tokens there, then have the cashier convert — the cashier self-authorizes because the
1347
+ /// caller (this engram) is a registered active engram (registry isRegisteredActiveEngram seam).
1348
+ /// (The owner-funded path lives entirely in the frontend: owner → cashier deposit → topUp.)
1349
+ /// ⟨0.16.0, L4 — `token : Text` KEPT, deliberately, and this note exists so the sweep that finds
1350
+ /// it does not re-litigate it.⟩
1351
+ ///
1352
+ /// `ownerWithdraw` and the market's `recoverEscrow` both took `Wire.TokenAmount` because they
1353
+ /// accept ICP (e8s) BESIDE the stablecoins (e6) — mixed decimals behind one number is a real
1354
+ /// hundred-fold error. This method accepts ckUSDC and ckUSDT only. Both are e6, so there is no
1355
+ /// unit to get wrong, and the census (row 258) rules the pair as `(token, amountE6)`.
1356
+ ///
1357
+ /// `Wire.TokenAmount` would be actively WORSE here: its `#Icp` arm is a token this path cannot
1358
+ /// convert, so the type would make an unacceptable value REPRESENTABLE and hand back a runtime
1359
+ /// rejection anyway — losing the one property the variant is for while gaining a wider type.
1360
+ ///
1361
+ /// ⟨2026-10-02⟩ The second parameter is NAMED `amountE6` now, as the census row above rules the
1362
+ /// pair — e6 base units of the token named first. A name only: Candid passes arguments by
1363
+ /// position, so nothing on the wire moved.
1364
+ convertEarningsToCycles: (token: text, amountE6: nat) -> (Result_8);
1365
+ /// Owner-only: mint a single-use invite code an agent redeems with `pairAgent`.
1366
+ ///
1367
+ /// ⚠ READ THIS BEFORE INVITING SOMEONE BACK. Pairing is ONE-SHOT PER PRINCIPAL on
1368
+ /// this engram: `pairAgent` refuses ANY caller that already holds a record — `#Active`,
1369
+ /// `#Revoked` or `#Resigned` alike — so a principal you have revoked can NEVER redeem another
1370
+ /// invite, however many you mint. That is deliberate, because `revokeAgent` is a soft status
1371
+ /// change and a revoked key talking its way back in would undo it. The remedy is a DIFFERENT
1372
+ /// WALLET; the only thing that clears the record is `resetEngram`, which severs everything else
1373
+ /// too.
1374
+ ///
1375
+ /// Two caps, each refusing with `#Conflict` and naming its own limit: concurrent ACTIVE agents
1376
+ /// (multi-agent owners legitimately need several — a connector host plus a coding agent), and
1377
+ /// concurrent unexpired INVITES. Neither is a rate limit; both are ceilings you clear by revoking
1378
+ /// or by letting invites lapse.
1379
+ ///
1380
+ /// A principal that is already a GUARDIAN cannot become an agent — a guardian is an
1381
+ /// independent check on agents, so one wallet must not hold both.
1382
+ createAgentInvite: (name: text, permissions: AgentPermissions) ->
1383
+ (Result_43);
1384
+ /// Owner-only: mint an invite for a GUARDIAN — an independent check on agents, not a
1385
+ /// co-owner and not a recovery agent.
1386
+ ///
1387
+ /// ⚠ WHAT A GUARDIAN CAN DO IS EXACTLY THREE THINGS, all of them BRAKES: revoke agents,
1388
+ /// freeze payments, pause writes. That is the whole permission set (`GuardianPermissions`), and
1389
+ /// the three flags are FROZEN — any future permission arrives as a scope string instead.
1390
+ ///
1391
+ /// ⚠ WHAT A GUARDIAN CANNOT DO, said plainly because the word invites the opposite reading: they
1392
+ /// cannot read memory, cannot move money, cannot transfer ownership, and **cannot recover a lost
1393
+ /// owner key**. A guardian is not a recovery mechanism. An owner appointing one against the day
1394
+ /// they lose access is appointing the wrong thing.
1395
+ ///
1396
+ /// Two steps, and the invite is only the first: the invitee registers, which leaves them
1397
+ /// `#Pending` and INERT, and `approveGuardian` is what makes them active. A guardian who has
1398
+ /// registered but not been approved can do none of the three.
1399
+ ///
1400
+ /// ⚠ AN AGENT CANNOT BECOME A GUARDIAN, and the refusal lands at REGISTRATION rather than
1401
+ /// here: this call will happily mint an invite for a principal that already holds an agent
1402
+ /// record, and `registerGuardian` then refuses it with "An agent cannot also be a guardian".
1403
+ /// So an invite that fails to redeem for that reason is not a bad code. The rule exists because
1404
+ /// a check must be independent of the thing it checks.
1405
+ createGuardianInvite: (name: text, permissions: GuardianPermissions) ->
1406
+ (Result_43);
1407
+ /// OWNER-ONLY deletion of a stored backup.
1408
+ ///
1409
+ /// ⚠ NOT THE SAME GATE AS THE REST OF THE FAMILY, and the asymmetry is deliberate: `backupInit`,
1410
+ /// `backupPushChunk`, `backupFinalize` and `backupAbort` accept an agent holding
1411
+ /// `canAppendMemory`, and `backupPullChunk` accepts one holding `read_backup`. Only the OWNER may
1412
+ /// delete. So an agent can take backups and carry them away and cannot destroy one —
1413
+ /// creating is delegable, destroying is not.
1414
+ ///
1415
+ /// `backupAbort` is the agent's way out of a failed UPLOAD; it discards something never
1416
+ /// finalized. This removes a finished backup, which is a different act on a different object.
1417
+ deleteBackup: (backupId: text) -> (Result_1);
1418
+ /// ⟨0.16.0, census §10 item 16⟩ RETURNS A REAL RECEIPT, not `R<()>`. The receipt was already
1419
+ /// being computed and written into the audit line — `receiptSuffix(r)` — and then discarded, so
1420
+ /// a caller could not cite the seq its own delete produced and had to go looking for it in a log
1421
+ /// it may not be allowed to read. A delete is an EVENT on the chain; an event that will not tell
1422
+ /// you where it landed is evidence nobody can point at. Same projection as writeMemory.
1423
+ deleteMemoryFile: (path: text) -> (Result);
1424
+ /// Owner-only: deny one pending over-limit request, with a `reason` the agent can read.
1425
+ ///
1426
+ /// The reason is stored on the request rather than discarded, which is the point of taking it —
1427
+ /// an agent told only "denied" retries, and one told why can adapt or stop asking. It is the
1428
+ /// owner's message to whoever raised the request, not an audit-only field.
1429
+ ///
1430
+ /// Terminal: a denied request cannot be approved afterwards. Like `approveRequest` it refuses
1431
+ /// `#NotFound` for an unknown id and `#Conflict` when the request is no longer pending, and it
1432
+ /// does not change the policy — the next request over the same limit will be raised again.
1433
+ denyRequest: (requestId: text, reason: text) -> (Result_1);
1434
+ /// Force the tECDSA derivation and return the address.
1435
+ ///
1436
+ /// It exists because the gateway's own derivation fires from a timer that is lazy on some
1437
+ /// replicas, and until it lands the EVM quote advertises the `0x0` fallback and settle fails
1438
+ /// closed — a live money surface quoting an address that cannot receive.
1439
+ ///
1440
+ /// ⟨0.16.0, B-2⟩ Derivation now comes from `EvmIdentity`, and the return type carries the
1441
+ /// outcome: this used to answer `""` on failure, which is a Text a caller can and did paste into
1442
+ /// a payment flow. An empty string is not an address, and saying so is the difference between a
1443
+ /// failed derivation and an address of nothing.
1444
+ ///
1445
+ /// ⚠ TRANSITIONAL: it also primes the ic402 gate, because the gate still settles commerce on this
1446
+ /// canister until those 47 methods move. That second call disappears with them — it is listed in
1447
+ /// the move order in the spec's B-2 addendum, not left for someone to find.
1448
+ ///
1449
+ /// PUBLIC: it inherits `getEvmAddress`'s gate rather than choosing one, and could not usefully be
1450
+ /// narrower — it exists so a caller can make the derivation happen BEFORE a payment flow, and the
1451
+ /// derivation itself is ungated. Idempotent and bounded to the same one-derivation-per-lifetime
1452
+ /// work, so an anonymous caller can cause it once and no more.
1453
+ /// ⟨L7⟩ UPDATE, NOT QUERY, and this one does not merely await — it WRITES. The body awaits
1454
+ /// `EvmIdentity.deriveAddress` (the management canister's `ecdsa_public_key`) and
1455
+ /// `gate.deriveEvmRecipientAt` ⟨renamed 2026-09-22 with the path⟩, then caches the result in `evmAddressCache`. So a query is
1456
+ /// impossible twice over: it could not await the derivation, and it could not keep the answer.
1457
+ /// Named `ensure*` rather than `get*` for that reason — the write is the point, and a read verb
1458
+ /// would have promised a cheapness this cannot deliver.
1459
+ ///
1460
+ /// ⟨Marker added 2026-08-23; the census row said "L7 mark added per review" and no mark was at
1461
+ /// the site.⟩
1462
+ ensureEvmReady: () -> (Result_43);
1463
+ /// Record that log entries [fromSeq, toSeq] were served, and return the root that proves it.
1464
+ ///
1465
+ /// This is the receipt ledgerfs Q2 requires before any of those entries may be condensed away.
1466
+ /// The canister cannot know the owner KEPT the bytes it served — it can only prove they were
1467
+ /// SERVED — and `opsRoot` is what makes a kept copy verifiable forever afterwards. Keep it with
1468
+ /// the archive; without it the download is just bytes.
1469
+ ///
1470
+ /// NOT gated on requireWritesNotPaused, deliberately. A guardian pause is an emergency brake on
1471
+ /// changing the record; an export changes nothing about the files and only appends its own
1472
+ /// attestation. Trapping the owner's own data behind the brake would make the pause a hostage
1473
+ /// situation rather than a safety measure. It IS gated on requireMemoryAttached: a store that
1474
+ /// failed its integrity check must not attest anything it cannot vouch for.
1475
+ exportMemoryRange: (fromSeq: nat, toSeq: nat, authorFilter:
1476
+ opt principal) -> (Result_66);
1477
+ /// ⟨0.16.0, L2.2 — renamed from `guardianFreezePayments`⟩ The prefix LIED about who may call it.
1478
+ /// The gate is `requireOwnerOrGuardian`, and an owner freezing their own payments is the ordinary
1479
+ /// case — the guardian is the exceptional one. A name that says otherwise sends an owner looking
1480
+ /// for a method that does not exist. `unfreezePayments`, its owner-only inverse, never carried the
1481
+ /// prefix, so the pair read as unrelated.
1482
+ ///
1483
+ /// WHAT THE FREEZE ACTUALLY COVERS ⟨enumerated 2026-08-21 from the eight `requirePaymentsNotFrozen`
1484
+ /// call sites, because it was written down nowhere and had to be re-derived to be trusted⟩:
1485
+ ///
1486
+ /// · EVERY SIGNING PATH, which is the point of the brake. `signX402Payment` and
1487
+ /// `sysSignX402Payment` through `x402SignFor`; `signEip712` and `sysSignEip712` through
1488
+ /// `eip712SignFor`; `signEngramRegistration` directly. ⟨The two helpers are why a grep of an
1489
+ /// individual method's body finds nothing — the check is one level down.⟩
1490
+ /// · `addEip712Type` and `addEvmWithdrawDestination` — pre-staging for a later payout is the
1491
+ /// thing a brake most needs to stop, so the destination list is frozen along with the signing.
1492
+ /// · `sessionReadMemory` and `sessionAppendMemory` — the paid-memory rail, money coming IN.
1493
+ ///
1494
+ /// AND WHAT IT DELIBERATELY DOES NOT, stated so the absence is not read as an oversight:
1495
+ ///
1496
+ /// · `convertEarningsToCycles` — the owner's OWN earnings becoming their OWN engram's fuel. A
1497
+ /// frozen engram still burns cycles, so freezing this would let a brake starve the thing it
1498
+ /// is protecting. Owner-only, capped per call, and it moves nothing outward.
1499
+ /// · `approveRequest` — an approval is not a spend. The spend it authorises goes through
1500
+ /// `x402SignFor`, which IS frozen, so an approval granted during a freeze simply waits.
1501
+ /// · `transferOwnership` — ownership is not a payment, and a guardian's PAYMENTS brake is not
1502
+ /// a veto on who owns the engram.
1503
+ ///
1504
+ /// ⚠ The three exclusions are a reading of intent, not a ruling: no declaration stated the scope
1505
+ /// before this one. If any of them is wrong, this comment is where the argument should happen.
1506
+ freezePayments: () -> (Result_1);
1507
+ /// Delivery status + config readback. Owner-only (the URL is a secret).
1508
+ getActionAlertStatus: () -> (Result_57) query;
1509
+ /// Agents need to know what they may revise, so they can read it too. Whole-value config, same
1510
+ /// carve-out as getProtectedPaths — and kept a SEPARATE method from it deliberately (L2.2): a
1511
+ /// protected path and a revisable path are opposite grants, and one accessor for both would be a
1512
+ /// name that lies about which power it reports.
1513
+ ///
1514
+ /// Refusal reason preserved from the trap: a grant is a power, and its absence and its
1515
+ /// concealment are different facts.
1516
+ getAgentRevisablePaths: () -> (Result_53) query;
1517
+ /// OWNER-ONLY: every engagement's seat weeks on this engram, with the agent's root — the day it
1518
+ /// paired, the days of its first and last payment signature, a 64-week map whose bit 0 is the week
1519
+ /// of the last one, and how many were produced. Never an amount, a recipient or a nonce.
1520
+ ///
1521
+ /// WHY THE OWNER ALWAYS READS IT: these are the rows the metrics reader the registry designated may
1522
+ /// read (`sysGetAgentSeatWeeks`), and an owner deciding whether to leave that read on must be able
1523
+ /// to see exactly what it is handed. The reader's rows are these rows without the root, in the same
1524
+ /// order. Anyone else is refused by name (`#Refused getAgentSeatWeeks: …`), never answered with an
1525
+ /// empty page. Paged; `total` is every engagement ever paired here (a reset keeps the rows).
1526
+ /// Two rows can name one root: an agent paired again after it left is a second engagement.
1527
+ getAgentSeatWeeks: (start: nat, limit: nat) -> (Result_64) query;
1528
+ /// Owner: everything written into this engram by someone other than the owner since the last
1529
+ /// markAgentWritesReviewed() — agent appends/revisions AND paid-session appends (see
1530
+ /// qualifiesForReview for why those two families are recognised differently).
1531
+ /// `capped=true` means exactly 200 rows were returned and MORE unreviewed writes exist beyond
1532
+ /// them — the UI must page (markAgentWritesReviewed(?lastShownId)) and must NOT present 200
1533
+ /// as a total. `earliest` discloses ring eviction — if cursor < earliest, entries between them
1534
+ /// aged out of the ring (the CHAIN still proves total count; this view can't show their bodies).
1535
+ getAgentWriteReview: () -> (Result_63) query;
1536
+ /// Why anchoring is or is not advancing. PUBLIC: ungated on purpose — detecting a stalled
1537
+ /// engram is the point, a gated read would fail-to-empty and hand a third party fabricated zeros,
1538
+ /// and none of these fields is a secret — `total` is already public via getCertifiedAuditHead.
1539
+ /// See the field comments above for how to read the combinations.
1540
+ getAnchorPushStatus: () -> (AnchorPushStatus) query;
1541
+ /// The plain chain head — size and hash, world-readable, no certificate.
1542
+ ///
1543
+ /// ⟨0.16.0, census `keep (NEW)`⟩ It was specced and never built. `Wire.AuditHead`'s own
1544
+ /// declaration says why it matters: heads exist TO BE COMPARED (L8), a head nobody can read
1545
+ /// witnesses nothing, and the registry and cashier already served exactly this shape — so the
1546
+ /// engram, "the canister that IS the evidence product", was the one chain a verifier could not
1547
+ /// ask for a head without also asking for a certificate it may not want.
1548
+ ///
1549
+ /// PUBLIC: bare, and it cannot refuse — a chain head discloses nothing about content or callers,
1550
+ /// and gating it would make the comparison it exists for depend on permission. `getCertifiedAuditHead`
1551
+ /// below serves the same head WITH the subnet certificate for a caller that needs to verify it
1552
+ /// was signed rather than asserted.
1553
+ getAuditHead: () -> (AuditHead) query;
1554
+ getBackupMetadata: (backupId: text) -> (Result_62) query;
1555
+ /// The cashier this engram recognises as its rent biller — `null` until the registry pushes one.
1556
+ ///
1557
+ /// PUBLIC: a payer about to send money needs to know which cashier this engram will accept credit
1558
+ /// from, BEFORE sending it — paying a cashier this engram does not recognise collects funds that
1559
+ /// never become rent. The registry publishes the same fact as `getRentBiller`; publishing it here
1560
+ /// too is what lets the two be COMPARED, which is the only way to catch a mismatch.
1561
+ getBillerId: () -> (opt principal) query;
1562
+ /// ⟨0.16.0, L9/L10 — census row⟩ `Wire.CertifiedAuditHead`, not an anonymous record. The
1563
+ /// registry, market and cashier already served the shared type; the engram — the canister that
1564
+ /// IS the evidence product — was the one chain still answering in a shape of its own, missing
1565
+ /// `witness` entirely. One certified-head shape on every chain is the point: a verifier that can
1566
+ /// read three of them and not the fourth has to special-case the one that matters most.
1567
+ ///
1568
+ /// `witness = null` is the honest answer here and is NOT the same as an empty proof. This engram
1569
+ /// certifies its head via `CertifiedData` but keeps no witness tree, so there is no reveal to
1570
+ /// encode; `?Blob` lets it say "I do not serve a proof" rather than fabricate one.
1571
+ ///
1572
+ /// PUBLIC: the certificate is the point — it lets a stranger verify against the NNS root key
1573
+ /// instead of trusting this replica, and a proof that requires a role to fetch rests on exactly
1574
+ /// the authority it was introduced to remove.
1575
+ getCertifiedAuditHead: () -> (CertifiedAuditHead) query;
1576
+ /// The allowance of the agent holding `agentPrincipal`: its lasting key and the openings left.
1577
+ /// Readable by the owner and by that agent's own key — refused loudly to anyone else, never a zero
1578
+ /// (a zero would be a claim about another owner's grant).
1579
+ ///
1580
+ /// ⟨2026-10-01, review F2⟩ A QUERY, so the refusal is deliberately NOT written to the denial log: a
1581
+ /// query's state writes are discarded when it returns, and the `denied` call first written here
1582
+ /// recorded nothing while reading as if it did. The refusal is loud in the reply instead. WHAT
1583
+ /// WOULD MAKE THIS WRONG: a reason to record who asked — then this becomes an update that says so.
1584
+ getChildAllowance: (agentPrincipal: principal) -> (Result_61) query;
1585
+ /// Owner-only, Billing-class: what opening an engram costs this engram, the balance below which an
1586
+ /// agent's request is refused, the balance now, and every agent's allowance. The console's cost
1587
+ /// sentence reads it. Cycles are owner-class information, as on the owner's console read.
1588
+ getChildOpening: () -> (Result_60) query;
1589
+ /// Derive the shared memory-encryption key, encrypted under the caller's transport
1590
+ /// public key. Gated to owner + read-agents (the same principals allowed to read
1591
+ /// memory). The client decrypts it locally with its transport secret key.
1592
+ /// ⟨0.16.0⟩ THE FOLD: `epoch` is now an optional argument here, and the separate
1593
+ /// `getEncryptedMemoryKeyForEpoch` is gone. Two methods differing only by whether one parameter
1594
+ /// is present is what an optional argument is for, and the split forced a client to decide which
1595
+ /// to call before it knew whether it needed rotation.
1596
+ ///
1597
+ /// `null` means CURRENT — not epoch 0. Those coincide today (nothing increments the epoch yet)
1598
+ /// and will not after the first rotation, which is exactly when a caller that meant "the key that
1599
+ /// seals my memory now" must not silently receive a superseded one. `?0` still names epoch 0
1600
+ /// explicitly for a client rebuilding an old cache entry.
1601
+ ///
1602
+ /// Refusing a FUTURE epoch is not a bounds check, it is the rotation invariant: pre-deriving a
1603
+ /// key for an epoch nobody has reached would make rotating to it meaningless.
1604
+ ///
1605
+ /// Also converted off the lowercase `#ok/#err` dialect (L5) — that spelling arrived from ic402
1606
+ /// and had no business on a vetKD path, which touches nothing of ic402's.
1607
+ ///
1608
+ /// ⟨L7⟩ UPDATE, NOT QUERY, and this is the system call that forces it: the body awaits
1609
+ /// `VetKeys.ManagementCanister.vetKdDeriveKey`. A query cannot await, so the round trip is the
1610
+ /// cost. Stated here because L7 requires the reason be readable from the interface rather than
1611
+ /// inferred by opening the body — which is exactly what had to happen to write this line.
1612
+ getEncryptedMemoryKey: (epoch: opt nat, transportPublicKey: blob) ->
1613
+ (Result_59);
1614
+ /// ⟨0.16.0, owner ruling 2026-08-20 — option (a)⟩ `x402Support` REPORTS THE RAIL, not a constant.
1615
+ ///
1616
+ /// The card's config hardcodes `x402Support = true`, answering a buyer's question with a fact
1617
+ /// about the software rather than about THIS engram. B1 moved the 402 challenge machinery to the
1618
+ /// market; the one x402-payable rail the engram kept is `sessionReadMemory`, gated by
1619
+ /// `paidMemoryReadEnabled`, which DEFAULTS TO FALSE. So a discovering agent was told this engram
1620
+ /// takes x402 payments while every paid read on a default engram is refused with "the owner has
1621
+ /// not opened the monetized read rail". Ruled (a) over keeping `true` to mean "can PARTICIPATE in
1622
+ /// x402, including as a payer" — defensible, but not what a service card says to a buyer.
1623
+ ///
1624
+ /// Overridden HERE rather than at the config because the card is built once as a `public let`
1625
+ /// while the flag is runtime state, and `getCard()` belongs to the VENDORED ic402 module, which
1626
+ /// is not ours to edit.
1627
+ ///
1628
+ /// ⚠ `signEngramRegistration` also reads `getCard()` and is deliberately NOT changed. It signs an
1629
+ /// ERC-8004 registration for on-chain publication — a point-in-time attestation nobody can
1630
+ /// re-sign every time an owner opens or closes the rail. Whether a signed record should carry
1631
+ /// mutable operational state at all is a different decision, it is signing-adjacent, and the
1632
+ /// ruling did not reach it. Filed, not folded in.
1633
+ ///
1634
+ /// PUBLIC: an agent card is a DISCOVERY document — its entire function is to tell a party that
1635
+ /// has never met this engram what it is and how to transact with it. A card behind a gate cannot
1636
+ /// be discovered, and the ERC-8004 ecosystem it participates in assumes an open read.
1637
+ getEngramCard: () -> (AgentCard) query;
1638
+ /// PUBLIC: the engram's permanent spawn ordinal — a provable, non-sensitive fact. It identifies a
1639
+ /// canister, never a person.
1640
+ /// null = spawned before numbering existed; the owner can backfill via syncEngramNumber().
1641
+ getEngramNumber: () -> (opt nat) query;
1642
+ /// Re-pointed at `EvmIdentity` (B-2): custody derivation no longer routes through the ic402
1643
+ /// Gateway, which is leaving for the market canister. Behaviour is unchanged for a caller — same
1644
+ /// address, same tECDSA key — but the engram no longer needs a marketplace to know its own
1645
+ /// address, which is the whole point of the split.
1646
+ ///
1647
+ /// ⟨2026-08-21⟩ DO NOT FOLD THIS WITH `getEvmSignerAddress`. The 0.16.0 census carried the fold
1648
+ /// as an open item, conditional on "if the derivation is identical". It is not, and the condition
1649
+ /// is decidable here rather than by reading either comment:
1650
+ ///
1651
+ /// this method EvmIdentity.deriveAddress -> derivation_path = []
1652
+ /// the signer eip712PublicKey -> derivation_path = ["eip712"]
1653
+ ///
1654
+ /// ⟨CORRECTED 2026-09-22 — the two paths above are what the code passed until KEY-PATHS §4 step 5;
1655
+ /// the table is kept because the ARGUMENT it makes is unchanged and is the reason the fold is
1656
+ /// still refused.⟩ The signer now reads `KeyPaths.EIP712` = `["engramx","eip712","secp256k1","v1"]`
1657
+ /// and this method still derives at the empty path, because `pay` is the one row the genesis could
1658
+ /// not move — ic402 2.15.0 parameterises `EvmSigner` but not `Gateway.deriveEvmRecipient`,
1659
+ /// `EvmSender` or `Identity`, and moving the signer without them would split receiving from
1660
+ /// spending. The paths are still DIFFERENT, which is the whole of the argument below, and they get
1661
+ /// further apart at the genesis rather than closer.
1662
+ ///
1663
+ /// ⟨SHIPPED 2026-09-22, ic402 2.16.0 — the table is now BOTH rows labelled, and the argument is
1664
+ /// unchanged, which is the point of keeping every version of it here:
1665
+ ///
1666
+ /// this method EvmIdentity.deriveAddress -> KeyPaths.PAY = ["engramx","pay","secp256k1","v1"]
1667
+ /// the signer eip712PublicKey -> KeyPaths.EIP712 = ["engramx","eip712","secp256k1","v1"]
1668
+ ///
1669
+ /// Two paths, one master key, therefore two addresses — exactly as when one of them was `[]`.
1670
+ /// The fold stays refused for the reason below, not because one side happened to be a default.
1671
+ /// What changed upstream is that `Gateway`, `EvmSender` and `Identity` all take a path now, so
1672
+ /// the payment identity could move as ONE thing: this derivation, the gateway's inbound
1673
+ /// recipient and outbound sender, and `Payments.evmSigner`. The genesis install derives them.⟩
1674
+ ///
1675
+ /// Same master key and curve, different paths, therefore different public keys and DIFFERENT
1676
+ /// ADDRESSES — that is what a derivation path is for. The difference is also deliberate and
1677
+ /// security-bearing, not incidental: the signer key is declared "outbound-only, N=1, never the
1678
+ /// payment path — this key must never be able to move ic402 pool funds", so merging the two
1679
+ /// would hand the signing surface the payment key. `getEvmSignerAddress` carries the warning
1680
+ /// from its side ("the payment key's address is NOT this one, by design"); this is the other
1681
+ /// side, because a fold is proposed by whoever reads either declaration first.
1682
+ ///
1683
+ /// The gates differ too, which a fold would also have to collapse: this method is UNGATED (any
1684
+ /// caller may cause the derivation) and `getEvmSignerAddress` is owner-only.
1685
+ ///
1686
+ /// PUBLIC: an address is a destination, not a capability — publishing it lets anyone PAY this
1687
+ /// engram and lets nobody spend from it. It has to be readable by a stranger because a stranger
1688
+ /// is exactly who needs it. The write it causes is bounded to one derivation and one
1689
+ /// set-once custody stamp per engram lifetime, which is what keeps "any caller may cause it"
1690
+ /// safe rather than merely permitted.
1691
+ /// ⟨L7⟩ UPDATE, NOT QUERY, and this is the system call that forces it: the body awaits
1692
+ /// `EvmIdentity.deriveAddress`, which reaches the management canister's `ecdsa_public_key`. A
1693
+ /// query cannot await it.
1694
+ ///
1695
+ /// ⟨Marker added 2026-08-23; the census row already said "L7 marked (ecdsa_public_key)" and the
1696
+ /// site did not carry the mark. Same `public func` form as its two neighbours above.⟩
1697
+ ///
1698
+ /// ⟨2026-09-28⟩ THE REPLY IS A RECORD, `Types.EvmAddresses` — the payment address (`pay`, what this
1699
+ /// method returned alone until then) and the address that signs the engram's own registrations
1700
+ /// (`engramRegistration`). The owner decided that date to publish the registration address as a new
1701
+ /// field on an existing address reply, not as a new method; nothing served it before, so an
1702
+ /// agent could not read the address whose nonce `signEngramRegistration` needs and whose ETH
1703
+ /// pays its gas. The registration address is read FROM THE SIGNER ITSELF
1704
+ /// (`engramRegSigner.getEvmAddress`), not re-derived beside it, so the published address is by
1705
+ /// construction the one the signatures come from. It is cached inside that signer object; the
1706
+ /// cache is not stable, so the first call after an upgrade re-derives it once — the same one
1707
+ /// public-key read, bounded, that the payment branch does on a null cache.
1708
+ ///
1709
+ /// It refuses rather than half-answers: if either address cannot be derived the reply is
1710
+ /// `#Err`, never a record carrying an empty string (ic402's `getEvmAddress` answers `""` on a
1711
+ /// failed derivation — that is fail-to-empty on a money surface, and it stops here).
1712
+ getEvmAddress: () -> (Result_58);
1713
+ /// The owner's OWN EVM address, if they registered one — distinct from `getEvmAddress`, which is
1714
+ /// the address this canister derives for itself.
1715
+ ///
1716
+ /// PUBLIC: an address is a public identifier on every chain it exists on; it is a destination,
1717
+ /// not a capability, and knowing it confers nothing. `null` means the owner registered none,
1718
+ /// which a payer needs to be able to tell from an address they failed to read.
1719
+ getEvmOwnerAddress: () -> (opt text) query;
1720
+ /// D1 ships with this: the address of the DEDICATED signing key. Without it the owner funds the
1721
+ /// wrong address — the payment key's address is NOT this one, by design.
1722
+ ///
1723
+ /// ⟨L7⟩ UPDATE, NOT QUERY, and this is the system call that forces it: `eip712PublicKey()` awaits
1724
+ /// the management canister's `ecdsa_public_key`. A query cannot await, so the round-trip is the
1725
+ /// cost — stated here because L7 requires the reason be readable from the interface rather than
1726
+ /// inferred by opening the body.
1727
+ getEvmSignerAddress: () -> (Result_43);
1728
+ /// PUBLIC: where this chain begins, for the stranger deciding whether to trust it.
1729
+ ///
1730
+ /// The fourth of the four evidence methods (`getAuditHead`, `getCertifiedAuditHead`,
1731
+ /// `listAuditEntries`, this) that every chain canister now serves in the same shape. Ungated for
1732
+ /// the same reason the head is: a verifier who must ask permission to learn where a chain starts
1733
+ /// cannot verify it, and nothing here identifies anybody.
1734
+ getGenesisNote: () -> (GenesisNote) query;
1735
+ /// A guardian's own channel status (their URL is their secret — each guardian sees only theirs).
1736
+ getGuardianAlertStatus: () -> (Result_57) query;
1737
+ /// The market this engram will accept signing requests from — `null` until the registry pushes
1738
+ /// one at spawn, which is why `requireMarket` refuses fleet-wide today.
1739
+ ///
1740
+ /// PUBLIC: this is the whole of what stands between a stranger and this engram's signing key. A
1741
+ /// counterparty evaluating an engram's market claim has to be able to read which market it is
1742
+ /// pinned to, and `null` — "pinned to nobody, so it signs for nobody" — is the most important
1743
+ /// answer it can give.
1744
+ getMarketId: () -> (opt principal) query;
1745
+ /// FD, the one coordinated interface break: history no longer carries per-version CONTENT.
1746
+ ///
1747
+ /// The legacy shape returned a full snapshot of every version, which is what made history cost
1748
+ /// O(total bytes ever written) and forced a per-version truncation cap that silently mangled
1749
+ /// large files. What a reader gets now is the COMMITMENT — size and content root per version —
1750
+ /// and content is read per version through readMemoryBytes, verified as it is served.
1751
+ ///
1752
+ /// Two deliberate shape changes beyond dropping the bytes:
1753
+ ///
1754
+ /// * it is a RESULT. The old surface returned `[]` for "no such file", "you may not read
1755
+ /// this", and "this file genuinely has no history" alike — a gated empty indistinguishable
1756
+ /// from a real one, which is the exact failure class this store exists to end. A refusal
1757
+ /// now says so.
1758
+ /// * the counts ride along. `everCount` vs `retainedCount` makes byte eviction impossible to
1759
+ /// hide: a reader who sees them differ knows something was reclaimed, and `evictions`
1760
+ /// carries the disclosure — chain sequence and evicted root — for each range.
1761
+ ///
1762
+ /// Paging is by version number, which works as its own cursor because versions are dense and
1763
+ /// never renumbered: the next page starts at the last version returned plus one. Defaults
1764
+ /// return the whole history of any file a pre-FD engram could have held.
1765
+ getMemoryHistory: (path: text, start: opt nat, max: opt nat) ->
1766
+ (Result_56) query;
1767
+ /// PUBLIC: the epoch alone, and deliberately nothing else (rotation study D4). A `rotatedAt[]`
1768
+ /// history would be a public, fleet-enumerable feed of when each owner terminated a trust
1769
+ /// relationship — readable by, among others, the party just terminated.
1770
+ getMemoryKeyEpoch: () -> (nat) query;
1771
+ /// BLS verification key for the shared memory-encryption key. Used by the
1772
+ /// client to verify the derived key. Static per (context, key_id) — cacheable.
1773
+ ///
1774
+ /// PUBLIC: intentionally unauthenticated — a vetKD public key reveals nothing secret and
1775
+ /// cannot decrypt anything — the sensitive derivation is getEncryptedMemoryKey
1776
+ /// below, which IS gated (read_memory) and returns key material encrypted to the
1777
+ /// caller's transport key. Do not add an auth guard here (breaks client caching).
1778
+ /// ⟨L7⟩ UPDATE, NOT QUERY, and this is the system call that forces it: the body awaits
1779
+ /// `VetKeys.ManagementCanister.vetKdPublicKey`. A query cannot await, so the round trip is the
1780
+ /// cost of getting a key nobody has to trust us for — which is the point of publishing it.
1781
+ ///
1782
+ /// ⟨Marker added 2026-08-23; the census row already said "L7 (vetKD pubkey call)" and the site did
1783
+ /// not carry it. Declared `public func` rather than `public shared`, which is why the naming rule
1784
+ /// in check-update-for-read.mjs did not see it — the census population did.⟩
1785
+ getMemoryKeyVerificationKey: () -> (blob);
1786
+ /// Memory maintenance liveness. PUBLIC: ungated on purpose, like the anchor-push status — a stalled
1787
+ /// sweep or a failed storage check is exactly what a third party must be able to observe, and
1788
+ /// a gated read that failed to empty would report a healthy zero to anyone without a role.
1789
+ /// Counts and flags only — the paths involved are in the chained audit entries, where the
1790
+ /// access rules that govern them already apply.
1791
+ getMemoryMaintainStatus: () -> (MemoryMaintainStatus) query;
1792
+ /// The DECLARED policy. Read it together with getMemoryRetentionGuards — on its own it is not
1793
+ /// what governs eviction.
1794
+ /// ⟨0.16.0⟩ ONE PROJECTION, absorbing what were two methods — and the fold is a correctness fix,
1795
+ /// not tidying.
1796
+ ///
1797
+ /// Declared policy and the guards holding it back were separate reads, and a console rendering
1798
+ /// only the first told an owner their policy was in force while a guard still held it. The old
1799
+ /// `.did` warned in prose that the two had to be read TOGETHER; a comment is not a mechanism, and
1800
+ /// the guard arms on the FIRST policy an owner ever sets — so the surface misled every owner who
1801
+ /// ever touched retention, from first use. Making it one record makes reading half of it
1802
+ /// impossible: the trap becomes structure.
1803
+ ///
1804
+ /// Refusal reasons of both merge: an empty policy reads as "nothing is capped, every version kept
1805
+ /// forever" — a claim about what this engram will DESTROY — and an empty guard list reads as
1806
+ /// "nothing is held back".
1807
+ getMemoryRetentionPolicy: () -> (Result_55) query;
1808
+ /// The metrics reader this engram answers — `null` until the spawning registry names one.
1809
+ ///
1810
+ /// PUBLIC: an owner deciding whether to switch the read off needs to know who it is, and anyone
1811
+ /// comparing this engram's copy with the reader the registry publishes (`getMetricsReader` there)
1812
+ /// needs the same value. A canister id is not a secret.
1813
+ getMetricsReaderId: () -> (opt principal) query;
1814
+ /// A guardian asking about their OWN standing.
1815
+ ///
1816
+ /// #NotFound and not #Refused, deliberately, even though this reads a roster no stranger may
1817
+ /// see: the only row it can ever return is the caller's own, so "you are not a guardian here" is
1818
+ /// a fact about the caller, told to the caller. There is nothing to leak by omission — which is
1819
+ /// the test the #NotFound arm sets.
1820
+ getMyGuardianStatus: () -> (Result_15) query;
1821
+ /// ⟨0.16.0, NEW⟩ The gated half of the status() split.
1822
+ ///
1823
+ /// Every field is bare — no masking, no `authorized` discriminator — because reaching this record
1824
+ /// AT ALL is the authorization. An unauthorized caller gets `#Err(#Refused …)` and no record.
1825
+ ///
1826
+ /// `cyclesBalance` is a bare `Nat`, and that retires an argument this repo carried for weeks. It
1827
+ /// was `nat`, then widened to `opt nat` so a refusal could be `null`, which broke every published
1828
+ /// client (rule 4) and bought a masked answer nobody had asked for — the census could not even
1829
+ /// find a consumer that read the masked `0`. Behind a gate there is nobody to mask it from.
1830
+ getOwnerConsole: () -> (Result_54) query;
1831
+ /// Owner + agents only (agents need it to avoid writing protected paths).
1832
+ ///
1833
+ /// A NAMED RECORD, not a Page: the setter replaces this list wholesale, so there is no window to
1834
+ /// page over and no `total` that could differ from `items.size()`. Forcing the Page shape here
1835
+ /// would be uniformity for its own sake — the census ratified the whole-value-config carve-out
1836
+ /// precisely so L3 does not become a rule applied past its meaning.
1837
+ ///
1838
+ /// The trap's `whyNotEmpty` clause is preserved as the `#Refused` reason: `[]` would read as "no
1839
+ /// path on this engram is protected", and an agent consults this list precisely to avoid
1840
+ /// writing one — so a fabricated empty here does not merely misinform, it invites the write the
1841
+ /// list exists to prevent.
1842
+ getProtectedPaths: () -> (Result_53) query;
1843
+ /// Whether the metrics reader's read is answered — `true` from birth.
1844
+ ///
1845
+ /// PUBLIC: the promise that an owner can switch this read off can be checked only if the switch
1846
+ /// can be read — the reason the reader pointer is public too. It discloses one setting, never a row.
1847
+ getSeatWeeksReadEnabled: () -> (bool) query;
1848
+ /// Owner-only, Billing-class: the signing floor, the balance, one signature's price, and how many
1849
+ /// payment signatures the balance carries above the floor before `signX402Payment` is refused by
1850
+ /// name. The same numbers the check uses, so Billing can say only what the engram enforces.
1851
+ getSigningFloor: () -> (Result_52) query;
1852
+ /// The registry that spawned this engram, if it recorded one.
1853
+ ///
1854
+ /// PUBLIC: it is the provenance claim. An engram asserting it is part of a fleet, under that
1855
+ /// fleet's attested template and rent rail, is asserting something a stranger must be able to
1856
+ /// check against the registry named here — and a provenance you need permission to read is not
1857
+ /// provenance. `null` says this engram makes no such claim, which is equally worth serving.
1858
+ getSpawnedBy: () -> (opt principal) query;
1859
+ /// Read the engram-wide daily ceiling and what has been spent against it today, across every
1860
+ /// caller. ⟨2026-09-28, the owner's decision to add the ceiling⟩
1861
+ /// ⟨CORRECTED 2026-09-29, review finding X-8: "spent" means signed in x402 payments; typed-data
1862
+ /// and the engram's registration signatures are not counted here — see `setSpendCeiling`.⟩
1863
+ ///
1864
+ /// OWNER-ONLY, for the reason `getSpendPolicy` is: the limits are the owner's custody
1865
+ /// configuration, and the engram-wide total would also tell one agent how much the others spent.
1866
+ /// A refusal names this method (`Gated.gatedResult`) rather than answering null/0 — a ceiling of
1867
+ /// "none" and a spend of 0 are both real answers, so a fabricated one would be read as fact.
1868
+ getSpendCeiling: () -> (Result_51) query;
1869
+ /// Read the caps in force for a caller.
1870
+ ///
1871
+ /// OWNER-ONLY, and this is where it diverges from the market's inbound twin, which is public.
1872
+ /// There the limits ride inside every 402 challenge, so withholding them would hide nothing.
1873
+ /// Here the engram serves no 402s at all: these caps are the owner's private custody
1874
+ /// configuration, and publishing them would tell any caller exactly how large a signing request
1875
+ /// can be before it needs an approval nobody is watching for.
1876
+ getSpendPolicy: (caller: opt principal) -> (Result_50) query;
1877
+ getStorageStats: () -> (Result_49) query;
1878
+ /// Timer arm-state. PUBLIC and deliberately so: these timers are self-arming to save cycles,
1879
+ /// and an optimisation nobody can verify is an optimisation nobody should trust. Cheap to read,
1880
+ /// leaks nothing (booleans about our own scheduling).
1881
+ ///
1882
+ /// IDLE STEADY STATE (no webhook, no x402 sessions, no service jobs):
1883
+ /// alertTimerArmed=false sessionExpiryArmed=false jobExpiryArmed=false jobExpiryFastMode=false
1884
+ ///
1885
+ /// ⚠ CORRECTED 2026-08-19. This said `jobExpiryArmed=TRUE` and that it "stays true by design —
1886
+ /// ic402 keeps an hourly idle POLL because createJobFromReceipt is synchronous and cannot disarm a
1887
+ /// timer itself". Both halves are stale, and an ops reader comparing a live engram against this
1888
+ /// paragraph would have seen `false` and gone looking for a fault that is not there.
1889
+ ///
1890
+ /// The engram sets `setExpiryIdlePollSeconds(0)` in its actor body, and ic402's ServiceRegistry
1891
+ /// CANCELS the timer outright at 0 rather than re-arming it — so the hourly tick this paragraph
1892
+ /// promised has not existed since that line landed. Independently, B1 moved job creation to the
1893
+ /// market: the engram now serves no job method at all, so nothing can arm the timer in the first
1894
+ /// place. `jobExpiryArmed` reads false on an idle engram, and false is CORRECT.
1895
+ ///
1896
+ /// EXPECTED FALSE POSITIVE: for up to one interval after any install or upgrade,
1897
+ /// sessionExpiryArmed reads true with zero sessions. ic402's startTimers arms unconditionally
1898
+ /// because a persistent actor re-runs its init body BEFORE postupgrade restores stable sessions,
1899
+ /// so a predicate-gated arm would skip exactly the canisters that have live ones. The first tick
1900
+ /// disarms it. Still true minutes after an upgrade is a real bug — report it upstream.
1901
+ /// NOT the whole timer story. The hourly CYCLE timer — which also carries audit anchoring — is
1902
+ /// deliberately absent: it is a `transient let` armed unconditionally at init, so an "armed"
1903
+ /// boolean for it would read true forever and tell you nothing, exactly the false-positive the
1904
+ /// paragraph above describes for sessionExpiryArmed. Use getAnchorPushStatus() instead: last-tick
1905
+ /// TIMESTAMPS are evidence where an arm flag is only an intention.
1906
+ ///
1907
+ /// PUBLIC: the same standing as the two liveness reads above. Booleans and timestamps about this
1908
+ /// canister's own timers — nothing about content, callers or money — and a liveness signal only
1909
+ /// its owner can read cannot tell a third party that the engram is still running.
1910
+ getTimerHealth: () -> (TimerHealth) query;
1911
+ /// PUBLIC: a cap identifies no user, and a caller deserves to know the rule before it stops them.
1912
+ ///
1913
+ /// ⟨0.16.0, L8⟩ Was owner-gated behind a trap. The disclosure law asks what the value reveals, and
1914
+ /// the answer here is nothing about anybody — it is a policy ceiling, not a balance or a history.
1915
+ /// Publishing it removes the trap AND the envelope: an ungated read has no refusal to carry, so
1916
+ /// wrapping it in `Result` would be ceremony that implies a gate exists.
1917
+ ///
1918
+ /// Predictability is itself the honesty posture: a limit that only reveals itself by refusing a
1919
+ /// top-up teaches the caller by failure. `/// PUBLIC:` is the marker the L8 gate greps for.
1920
+ getTopUpLimits: () ->
1921
+ (record {
1922
+ cooldownSeconds: nat;
1923
+ maxCkusdcE6s: nat;
1924
+ }) query;
1925
+ /// ⟨0.16.0⟩ `walletBalance()` DELETED — a ONE-LETTER trap beside `walletBalances()`, and a strict
1926
+ /// projection of it: it returned `ExperimentalCycles.balance()`, which is `WalletBalances.cycles`.
1927
+ ///
1928
+ /// Two methods whose names differ by a single character, with the same gate and one a subset of
1929
+ /// the other, is a call site waiting to be written wrong — and wrong in the direction that
1930
+ /// silently reads cycles when it meant to read money. Doc callers (CLI-REFERENCE, Docs.vue) sweep
1931
+ /// to the survivor.
1932
+ /// RENAMED from `walletBalances` (L2.1: `get*` for a single value).
1933
+ ///
1934
+ /// Stays an UPDATE, marked per L7: it awaits three ICRC-1 ledgers. The name cannot say that, so
1935
+ /// the declaration does — a caller budgeting round-trips needs to know this is not a query.
1936
+ ///
1937
+ /// The ledger failure is `#Unavailable`, not `#Invalid`: a dependency refused or timed out, the
1938
+ /// request was fine, and retry is the correct response. That is the one arm in the whole set that
1939
+ /// means "try again", and it is the reason `Error` is a closed variant rather than a sentence.
1940
+ ///
1941
+ /// ⟨L7⟩ MARKER ADDED 2026-08-23, reasoning above UNCHANGED — this block already said "Stays an
1942
+ /// UPDATE, marked per L7: it awaits three ICRC-1 ledgers". It was marked in prose and invisible
1943
+ /// to any gate; the glyph is what `scripts/check-update-for-read.mjs` matches.
1944
+ getWalletBalances: () -> (Result_48);
1945
+ /// Refusal reason preserved, and it is the clearest case of why a masked read is worse than no
1946
+ /// read: three empty strings with `configured = false` is a VALID-LOOKING answer meaning "this
1947
+ /// engram has no World ID configuration" — handed to a caller who merely lacks a grant. The
1948
+ /// fabrication is indistinguishable from the truth by construction.
1949
+ getWorldIdConfig: () -> (Result_47) query;
1950
+ /// ⟨L7⟩ UPDATE, NOT QUERY: this awaits THREE system calls — `raw_rand()` for the nonce,
1951
+ /// `Call.signWithEcdsa` for the signature, and `worldIdRpPublicKey()` for the derived key. A
1952
+ /// query can do none of them.
1953
+ ///
1954
+ /// Owner-only: produce a World ID 4.0 relying-party context for the frontend's IDKit request.
1955
+ /// Valid iff the derived signer address (getWorldIdRpSignerAddress) is registered for this rp_id in
1956
+ /// the World Developer Portal. Short-lived (300s TTL, idkit-server's default) and single-purpose —
1957
+ /// it authorizes opening one verification request, nothing else.
1958
+ getWorldIdRpContext: () -> (Result_46);
1959
+ /// Owner-only: the EVM address derived from the engram's dedicated World ID RP signing key (the
1960
+ /// "worldid-rp" tECDSA path). Register THIS address as the rp_id's signer in the World Developer
1961
+ /// Portal — the engram never holds or exports a private key (only threshold shares exist, on the
1962
+ /// subnet). Distinct from the payment/EVM address by construction.
1963
+ ///
1964
+ /// ⟨L7⟩ UPDATE, NOT QUERY, for TWO reasons and the second is the one a reader would miss. It
1965
+ /// awaits `worldIdRpPublicKey()` (management `ecdsa_public_key`), and it calls `denied()` on
1966
+ /// refusal — a state write, which a query cannot perform. So even if the key were cached this
1967
+ /// could not become a query without silently retiring its denial record (ADR-0002's class).
1968
+ getWorldIdRpSignerAddress: () -> (Result_43);
1969
+ /// The verified IDKitResult, verbatim, as World's /api/v4/verify accepted it — the re-checkable
1970
+ /// artifact behind verifiedHuman. Role-gated because it carries the NULLIFIER.
1971
+ /// `Result<opt Text>` — and the double wrapper is the entire point, not redundancy.
1972
+ ///
1973
+ /// `#Ok(null)` means HONEST ABSENCE: no verification is on record, and you were entitled to ask.
1974
+ /// `#Err(#Refused …)` means you may not ask. Under the old bare `?Text` those two collapsed into
1975
+ /// one `null`, so a caller lacking a grant was told an unverified human stands behind this engram
1976
+ /// — a false claim about a person, produced by an authorization failure.
1977
+ ///
1978
+ /// The artifact stays role-gated because it carries the NULLIFIER. The public, anonymous answer to
1979
+ /// "is a human behind this engram" is `about().verifiedHuman`, which is a boolean and leaks
1980
+ /// nothing.
1981
+ ///
1982
+ /// ⟨CORRECTED 2026-08-21 — this said `metrics().verifiedHuman`, and `metrics()` no longer exists:
1983
+ /// L2.2 folded it and `status()` into `about()`, which carries the field. The sentence is a
1984
+ /// `///`, so the dead name was PUBLISHED in engram.did and pointed every client at a method the
1985
+ /// canister does not serve. The tombstones below and at `about()` are historical and correct as
1986
+ /// written; this one was present tense, which is the difference that matters.⟩
1987
+ getWorldIdVerification: () -> (Result_45) query;
1988
+ /// ⟨0.16.0, L1⟩ THE EMERGENCY FAMILY. Every refusal below is `#Refused` or `#NotFound`, and the
1989
+ /// absence of a `#Conflict` here is the point: these are the calls someone makes while something
1990
+ /// is going wrong, and the only two answers that help are "you may not do this" and "there is
1991
+ /// nothing by that name". A missing PERMISSION is `#Refused` rather than `#Conflict` — the
1992
+ /// guardian's standing is what fell short, and no change to engram state would let them through.
1993
+ guardianRevokeAgent: (agentPrincipal: principal) -> (Result_1);
1994
+ /// The HTTP surface: canister-info JSON, OAuth discovery documents, and the MCP route's query
1995
+ /// half. See the `http_request_update` twin below for what upgrades and why.
1996
+ ///
1997
+ /// PUBLIC: not a choice. The boundary node invokes this for every inbound HTTP request with no
1998
+ /// caller identity to gate on, so a gate here would refuse the only caller there is. Authorization
1999
+ /// lives INSIDE, per route — `/mcp` demands a bearer token, the discovery documents are meant to
2000
+ /// be world-readable by their own specs, and the canister-info JSON serves only facts already
2001
+ /// published by the queries above it.
2002
+ http_request: (request: HttpRequest) -> (HttpResponse) query;
2003
+ /// The update twin of the router above — the routes the query half UPGRADES so their responses
2004
+ /// are consensus-certified and served on the certified hostname.
2005
+ ///
2006
+ /// PUBLIC: for the same reason as its twin, and it must match. A route the query half upgrades
2007
+ /// but this half does not handle falls through to the 404 at the end, turning a serving fault
2008
+ /// into what reads as a wrong URL — half a fix being worse than none, as the /oauth/authorize
2009
+ /// arm inside records.
2010
+ http_request_update: (request: HttpRequest) -> (HttpResponse);
2011
+ /// ⟨0.16.0⟩ `isPaidMemoryReadEnabled()` DELETED — folded to `about().paidMemoryReadEnabled`
2012
+ /// (L2.6: one idiom for a boolean feature — a setter plus the flag on its family's projection).
2013
+ /// Its callers are a buyer's pre-check and the market canister, both ANONYMOUS, so the public
2014
+ /// projection is exactly where it belongs.
2015
+ /// PUBLIC: a wallet calls this BEFORE its user has any standing on this engram — that is what the
2016
+ /// call is for — so gating it would make the message unreadable at exactly the moment it is
2017
+ /// needed, and a signature given without it is a signature given blind.
2018
+ icrc21_canister_call_consent_message: (request:
2019
+ record {
2020
+ arg: blob;
2021
+ method: text;
2022
+ user_preferences: record {};
2023
+ }) ->
2024
+ (variant {
2025
+ Err: record {
2026
+ description: text;
2027
+ error_code: nat;
2028
+ };
2029
+ Ok:
2030
+ record {
2031
+ consent_message: variant {GenericDisplayMessage: text;};
2032
+ metadata: record {
2033
+ language: text;
2034
+ utc_offset_minutes: opt int16;
2035
+ };
2036
+ };
2037
+ }) query;
2038
+ /// Owner-only: is the derived EVM address CACHED right now, without deriving it to find out.
2039
+ ///
2040
+ /// WHY A SEPARATE METHOD AND NOT A FIELD ON `getEvmAddress`. That method cannot answer this
2041
+ /// question by construction: when the cache is null it DERIVES, caches the result and returns it,
2042
+ /// so by the time a caller sees the reply the cache is populated either way. Asking it whether the
2043
+ /// cache had been populated changes the answer.
2044
+ ///
2045
+ /// WHY IT EXISTS AT ALL, since a public surface is never free on a canister meant to go public.
2046
+ /// `evmAddressCache` is money-classed and its annotation says why: derivation is deterministic, so
2047
+ /// a lost cache LOOKS harmless, but until something re-derives, the EVM payment quote advertises
2048
+ /// the fallback — an address that cannot receive. A window in which a live money surface publishes
2049
+ /// an unreceivable address is a money defect, not a cache miss. Nothing else in this canister
2050
+ /// reads that var — one reader and two writers, all in this file — so before this method there was
2051
+ /// no observation anywhere that could tell a survived cache from a rebuilt one, and the upgrade
2052
+ /// coverage gate carried a money-classed var nothing could prove.
2053
+ ///
2054
+ /// ⚠ IT MUST NEVER DERIVE. A version that fell back to deriving would answer `true` always and
2055
+ /// prove nothing — the same trap `getEvmAddress` sets for anyone trying to use it as a witness.
2056
+ /// It reads the var and nothing else, and it is a `query` precisely so that it CANNOT make the
2057
+ /// inter-canister call a derivation requires: the restriction is enforced by the system, not by
2058
+ /// this comment.
2059
+ ///
2060
+ /// Owner-gated because it reports on custody posture, and answered as a Result so that a
2061
+ /// wrong-identity caller gets a refusal rather than a bare `false` that reads as "no address yet".
2062
+ isEvmAddressCached: () -> (Result_44) query;
2063
+ /// Issue an OAuth authorization code. Called by an OAuth client's sign-in bridge
2064
+ /// (an `/auth/connector`-style route) after II authentication. The client then
2065
+ /// redirects the user's browser to `redirectUri` with `?code=<this>` so the
2066
+ /// MCP client (e.g. claude.ai) can exchange it for a bearer token at /oauth/token.
2067
+ ///
2068
+ /// Owner-only: only the engram owner can authorize a Connector install.
2069
+ issueAuthCode: (redirectUri: text, scope: text, codeChallenge: text,
2070
+ clientId: opt text) -> (Result_43);
2071
+ /// ⟨0.16.0⟩ `getCycleSnapshotSource` DELETED — folded into `CycleSnapshot.source`, one label per
2072
+ /// ROW. A single label for a whole ring misattributes every row recorded under the other source,
2073
+ /// and the source flips by design (self-call first, balance on fallback). Its documentation of
2074
+ /// what the two values mean lives on the type.
2075
+ /// Hourly (timestamp, totalMemoryReads, totalMemoryWrites) snapshots, ~30 days.
2076
+ /// Same guard + fail-to-empty shape as getCycleHistory. Diff consecutive rows for a
2077
+ /// per-engram activity time-series (retention instrumentation).
2078
+ /// RENAMED from `getActivityHistory` (L2.1); named fields replace a bare 3-tuple whose two Nats
2079
+ /// were positionally indistinguishable.
2080
+ ///
2081
+ /// Refusal reason preserved, and it is the sharpest of this family: a masked `[]` reads as a
2082
+ /// DORMANT engram and corrupts retention math — a fabricated empty here does not just misinform a
2083
+ /// reader, it feeds a wrong number into a decision about destroying data.
2084
+ listActivitySnapshots: (start: nat, limit: nat) -> (Result_42) query;
2085
+ /// ⟨0.16.0⟩ THE PAGE EXEMPLAR. Trap → `#Refused`, bare array → `Result<Page<T>>` (L1 + L3).
2086
+ ///
2087
+ /// The refusal prose that used to be `gatedTrap`'s `whyNotEmpty` argument is not lost — it is why
2088
+ /// this method could never fail soft, and it now travels ON THE WIRE inside `#Refused` where a
2089
+ /// caller can actually read it, instead of inside a trap message only a log sees.
2090
+ ///
2091
+ /// `total` is the ROSTER SIZE, not `items.size()`. That distinction is the whole reason this
2092
+ /// shape exists here: `MAX_AGENTS` clamps the returned rows, and the old code returned the
2093
+ /// clamped array with nothing saying it had clamped — a caller reading 100 agents could not
2094
+ /// tell whether that was all of them. Now a clamped answer is self-describing, and the previous
2095
+ /// behaviour (silently truncate, look complete) is unrepresentable.
2096
+ ///
2097
+ /// `withheld` is 0: this roster is owner-only in whole, so there is no partial-visibility case.
2098
+ /// A method that filters rows by caller sets it — see L1's row-level honesty rule.
2099
+ listAgents: (start: nat, limit: nat) -> (Result_41) query;
2100
+ /// The GUARDIAN-audience projection of the agent roster — three fields, not the full record.
2101
+ ///
2102
+ /// Two audiences, two methods (L1/L8), rather than one method masking fields by caller: a
2103
+ /// guardian needs enough to revoke (who, what they are called, whether they are active) and no
2104
+ /// more. Field-masking would make the same call return different shapes to different callers,
2105
+ /// which is the design L1 abolished — and it would put the decision about what a guardian may see
2106
+ /// inside a build closure where no gate can read it.
2107
+ ///
2108
+ /// Refusal reason preserved: `[]` reads as "this engram has no agents", and this is the list a
2109
+ /// guardian consults BEFORE REVOKING ONE. A fabricated empty here does not merely misinform —
2110
+ /// it tells someone acting under emergency powers that there is nothing to act on.
2111
+ listAgentsForGuardian: (start: nat, limit: nat) -> (Result_40) query;
2112
+ /// Owner-only: rows carry caller, amount, operation and free-text rationale. The SDK omits it.
2113
+ ///
2114
+ /// The optional leading filter SURVIVES the L3 reshape — ratified (census tier-2 #8) rather than
2115
+ /// assumed, because Billing.vue calls the filtered form live and pushing the filter client-side
2116
+ /// would make the console fetch a queue to discard most of it.
2117
+ ///
2118
+ /// `total` counts the FILTERED set, not the whole queue: with a filter applied, "how many match"
2119
+ /// is the number a pager needs, and reporting the unfiltered count would make every filtered view
2120
+ /// claim more pages than it has.
2121
+ ///
2122
+ /// Refusal reason preserved: `[]` reads as "nothing is awaiting the owner's decision".
2123
+ listApprovalRequests: (statusFilter: opt ApprovalStatus, start: nat,
2124
+ limit: nat) -> (Result_28) query;
2125
+ /// ⟨0.16.0⟩ `total` IS THE HEAD COUNTER, and on this method that is the entire point.
2126
+ ///
2127
+ /// The audit log is a bounded RING: it trims its tail, so "rows you can read" and "entries that
2128
+ /// have ever existed" are different numbers. The old bare array published only the first, which
2129
+ /// made a trimmed log indistinguishable from a complete one — and this is the evidence surface,
2130
+ /// where that difference IS the product. `totalAppended()` is monotonic across trims and is the
2131
+ /// same number `getCertifiedAuditHead` publishes, so a caller comparing page against head gets
2132
+ /// agreement by construction rather than by luck.
2133
+ ///
2134
+ /// Refusal reason preserved: for an unauthenticated integrity check the caller wants
2135
+ /// `getCertifiedAuditHead()`, which is public and subnet-certified. The BODIES are role-gated.
2136
+ ///
2137
+ /// ⟨2026-09-29, owner ruling follow-up 16 (B)⟩ AN AGENT READS ONLY THE ENTRIES IT CAUSED; the
2138
+ /// owner and guardians read everything (`RecordView`). So `limit` is a SPAN — how many record ids
2139
+ /// to examine from `start` — and an agent's page holds the entries in that span whose caller is
2140
+ /// its own key, with `withheld` counting the rest: `items.size() + withheld` is the span examined.
2141
+ /// For the owner and a guardian `withheld` is 0 and the page is the span. WHAT WOULD MAKE THIS
2142
+ /// WRONG: a reader who must see the whole record without being the owner or a guardian — the
2143
+ /// public certified head is the answer for integrity, and nothing else is.
2144
+ listAuditEntries: (start: nat, limit: nat) -> (Result_23) query;
2145
+ /// ⟨0.16.0, L1 + L3 — census-ruled⟩ THE TWO-MECHANISM EXCEPTION DISSOLVES.
2146
+ ///
2147
+ /// This method used to answer THREE ways to one question: a list, an `#Err` text, and a trap,
2148
+ /// picked by which kind of caller asked. That split existed only because the return type could
2149
+ /// not carry a refusal — with `Result<Page<T>>` it can, so every caller gets one shape and the
2150
+ /// arm says which no it is. A paired agent lacking `canReadBackup` is `#Refused`; so is
2151
+ /// everyone else who may not ask. The trap is gone: `Debug.trap` is for invariant violations,
2152
+ /// never authorization (L1), and a refusal a caller cannot catch is not a better refusal.
2153
+ ///
2154
+ /// ⚠ THE RATIFIED LOCKSTEP EXCEPTION THIS CARRIED IS SUPERSEDED, not quietly dropped.
2155
+ /// `ADR-0002-listBackups-refusal-lockstep/listBackups` existed because published
2156
+ /// @engramx/client 0.15.0 declares `IDL.Vec(BackupMetadata)` here and cannot decode a variant at
2157
+ /// all — it throws — and @engramx/openclaw's dedup probe catches that throw and degrades to
2158
+ /// "skipped", so every database silently stops being backed up while the CLI reports success.
2159
+ /// That analysis was correct and is still correct. What changed is its PREMISE: plan v2 measured
2160
+ /// the installed base at zero external users (prod = two engrams, both ours; npm published
2161
+ /// 2026-08-14), so the population the ceremony protected does not exist. The SDK is republished
2162
+ /// at 0.16.0 in this same campaign. If an outside caller ever appears before that ships,
2163
+ /// this reasoning is void and the lockstep is owed again.
2164
+ ///
2165
+ /// `dbTypeFilter` DIES on the wire (census). The capability does not: `EngramClient.listBackups`
2166
+ /// keeps its optional filter and applies it to the page, so no caller loses the ability to ask
2167
+ /// "backups of this database" — it stops being a thing the canister has to page around.
2168
+ listBackups: (start: nat, limit: nat) -> (Result_39) query;
2169
+ /// List all bearer tokens — Active, Revoked, and Expired. Owner uses this in
2170
+ /// the dashboard to show installed Connectors (claude.ai, Claude Desktop, etc.).
2171
+ /// Refusal reason preserved from the trap: `[]` reads as "no connector is installed on this
2172
+ /// engram" — a claim about what can REACH it. An owner auditing access cannot tell that from
2173
+ /// "you may not see the list", and the two are opposite facts.
2174
+ ///
2175
+ /// `bearerTokenStore.list()` already resolves each token's status at read time (a token that
2176
+ /// merely stopped being used is reported `#Expired`, not a stale `#Active`), so the page carries
2177
+ /// the same honesty the store gained on 2026-08-17.
2178
+ listBearerTokens: (start: nat, limit: nat) -> (Result_38) query;
2179
+ /// The condense checkpoints in the retained log at or above `startSeq`, oldest first.
2180
+ ///
2181
+ /// ⟨0.16.0, NEW⟩ The other half of the split above. A checkpoint is the only surviving record of a
2182
+ /// stretch of chain that no longer exists, so this is the list that accounts for the distance
2183
+ /// between `firstAvailableSeq` and the entries an owner remembers writing — without it, a
2184
+ /// condensed prefix is a gap the owner has to take on trust.
2185
+ ///
2186
+ /// Owner-only like every other archive surface, and for the same structural reason: these rows
2187
+ /// carry the chain positions and roots of entries whose paths were inside the hash preimage.
2188
+ listCondenseCheckpoints: (startSeq: nat, max: nat) -> (Result_37) query;
2189
+ /// ⟨0.16.0⟩ `metrics()` DELETED — absorbed by `about()`.
2190
+ ///
2191
+ /// Two public projections of one canister (L2.2): `metrics()` and `status()` both answered "what
2192
+ /// is this engram like" and disagreed on which counters they carried, so a consumer had to fetch
2193
+ /// both and know which one owned each field. Every field here survives on `about()`, day-flooring
2194
+ /// included.
2195
+ listCycleSnapshots: (start: nat, limit: nat) -> (Result_36) query;
2196
+ /// Same `withheld` treatment as listMemoryFiles — the tombstone listing filtered reserved paths
2197
+ /// for non-owners just as silently, and a caller could not tell a filtered tombstone list from a
2198
+ /// complete one. Counted before slicing, so policy-hidden rows never blur into paging.
2199
+ listDeletedMemoryFiles: (start: nat, limit: nat) -> (Result_35) query;
2200
+ /// The registered shapes — E3: encodeTypeString is the display artifact, verbatim.
2201
+ /// Stays on the engram under B1 (hybrid): EIP-712 signing is custody, and custody does not leave.
2202
+ ///
2203
+ /// Refusal reason preserved: `[]` reads as "this engram can sign nothing" — a claim about SIGNING
2204
+ /// AUTHORITY, which is not a thing to hand an unauthorized reader. The public trail of every
2205
+ /// registration remains the audit chain under getCertifiedAuditHead.
2206
+ listEip712Types: (start: nat, limit: nat) -> (Result_34) query;
2207
+ /// Stays on the engram under B1 (hybrid): withdrawal custody does not leave.
2208
+ ///
2209
+ /// NAMED item, not a `(Text, Int)` tuple. The Int is an activation time, and a bare Int beside a
2210
+ /// bare Text told a reader nothing about which was which — the same defect fixed on the purge log.
2211
+ ///
2212
+ /// Refusal reason preserved, and it has two halves that matter separately: `[]` reads as "this
2213
+ /// engram may withdraw nowhere", OR — worse — as "no allowlist is in force", which is the
2214
+ /// opposite security posture. It also erases any pending 24h activation from view.
2215
+ listEvmWithdrawDestinations: (start: nat, limit: nat) -> (Result_33) query;
2216
+ /// Accessible by owner and guardians.
2217
+ ///
2218
+ /// Refusal reason carried over from the trap, because it is the sharpest example of why this
2219
+ /// surface may not fail soft: `[]` reads as "this engram has nobody who can freeze payments or
2220
+ /// pause writes" — a claim about who can STOP it. A caller who cannot tell that from "you may not
2221
+ /// see the roster" has been told the engram is unguarded when it may be fully guarded.
2222
+ listGuardians: (start: nat, limit: nat) -> (Result_32) query;
2223
+ /// The export receipts in the retained log at or above `startSeq`, oldest first.
2224
+ ///
2225
+ /// ⟨0.16.0, L3⟩ ONE LIST, ONE QUESTION. This method used to return receipts AND checkpoints
2226
+ /// together behind a backward `before`/`max` cursor. They answer different questions — what may be
2227
+ /// condensed, and what already was — so a caller wanting one paid to decode the other, and the
2228
+ /// merged shape had to carry two arrays, a truncation flag and an opaque cursor to stay honest
2229
+ /// about a walk that served both. `listCondenseCheckpoints` is the other half.
2230
+ ///
2231
+ /// The cursor dies with it (census R6): paging is by ABSOLUTE SEQ now, forward, so a caller resumes
2232
+ /// at `scannedThroughSeq + 1` and is finished when that passes `page.total`. `scanTruncated` is
2233
+ /// gone because it has nothing left to say — "I stopped at entry N" is the same claim it made, and
2234
+ /// says it as a position rather than a flag a reader has to pair with a second field.
2235
+ ///
2236
+ /// Read `MemoryRouter.ReceiptPage` before reading `page.total` as a receipt count. It is not one;
2237
+ /// it is the chain's length, and the declaration says why.
2238
+ listMemoryExportReceipts: (startSeq: nat, max: nat) -> (Result_31) query;
2239
+ /// ⟨0.16.0⟩ THE `withheld` EXEMPLAR — L1 names this exact method as the defect it fixes.
2240
+ ///
2241
+ /// A non-owner's answer silently dropped every `config/` and `secrets/` row. Not "refused the
2242
+ /// call" and not "returned a marker" — the rows simply were not there, with nothing in band
2243
+ /// saying so, and the filtering happened INSIDE a combinator closure where neither honesty gate
2244
+ /// could see it. Meanwhile `readMemory` returned an explicit refusal for the very same paths.
2245
+ /// Same policy, same caller, two answers, and the quiet one was the lie.
2246
+ ///
2247
+ /// `withheld` is now the count of rows policy removed. It leaks no names — which is the point:
2248
+ /// the caller learns THAT something is hidden without learning WHAT, so an agent can tell a
2249
+ /// short listing from a complete one and stop inferring the engram is emptier than it is.
2250
+ ///
2251
+ /// Note the count is computed from the unfiltered list before slicing, so it reports rows hidden
2252
+ /// by POLICY, never rows merely off the end of this page — those are `total`'s job. Conflating
2253
+ /// the two would rebuild the ambiguity in a new place.
2254
+ listMemoryFiles: (start: nat, limit: nat) -> (Result_30) query;
2255
+ /// One page of the memory log, seq-ordered, optionally narrowed to one author.
2256
+ ///
2257
+ /// THE WINDOW IS THE CURSOR, and the page now states it. The window is
2258
+ /// `items.size() + withheld` entries starting at `max(startSeq, firstAvailableSeq)`; advance by
2259
+ /// that, never by "last entry returned + 1", which stalls forever on the first empty page. Stop
2260
+ /// when the window passes `total`.
2261
+ ///
2262
+ /// ⟨0.16.0, L3 — CENSUS CONFLICT, RECORDED⟩ The census row says `→R<P<MemoryLogEvent>>` and
2263
+ /// "authorFilter dropped (kills the filtered-window cursor stall)". The stall is real and the
2264
+ /// diagnosis is right; amputating the filter is not the only cure, and it is the expensive one —
2265
+ /// `Archive.vue` ships a working author-filter UI, and "what did this agent write" is a
2266
+ /// question an owner asks of an evidence log. The filter STAYS. What kills the stall instead is
2267
+ /// `withheld`: it carries the count this filter removed from the window, so a short or empty
2268
+ /// filtered page is no longer ambiguous between "nobody wrote here" and "the window was small",
2269
+ /// and the correct cursor advance is computable from the page itself. `pageCap` therefore dies
2270
+ /// with nothing lost — it existed only to disclose a window that `items.size() + withheld` now
2271
+ /// states directly.
2272
+ ///
2273
+ /// `firstAvailableSeq` and `head` sit BESIDE the page rather than in it. They are facts about the
2274
+ /// chain, not this slice: the condense floor tells a caller reading an archived range where the
2275
+ /// log now begins instead of letting them read it as empty, and the head is what a third party
2276
+ /// checks the page against. Flattening to a bare `Page` to satisfy the shape would buy uniformity
2277
+ /// with the disclosures that make the page verifiable — the hidden-bound defect the census itself
2278
+ /// names on previewCondense's row.
2279
+ listMemoryLog: (startSeq: nat, max: nat, authorFilter: opt principal) ->
2280
+ (Result_29) query;
2281
+ /// The caller's OWN spend-approval requests. ⟨0.16.0, L1 + L3 — fixed 2026-08-18⟩
2282
+ ///
2283
+ /// Two defects in seven lines, and the first is the one this release exists to remove:
2284
+ ///
2285
+ /// · `if (Principal.isAnonymous(msg.caller)) return []` is a FABRICATED EMPTY. An anonymous
2286
+ /// caller was told "you have no approval requests", which is a claim about their queue rather
2287
+ /// than about their identity, and is indistinguishable from the honest answer. It refuses now.
2288
+ /// · The reply was uncapped over `pendingApprovals`, which NEVER SHRINKS — the hourly expiry
2289
+ /// timer flips a row's status to `#Expired` and deletes nothing. So the array grew for the
2290
+ /// life of the engram, and a caller with enough history would eventually get a reply too
2291
+ /// large to decode: not a truncated list, no list at all.
2292
+ ///
2293
+ /// The caller-scoping is not `withheld`: rows belonging to someone else were never this caller's
2294
+ /// to see, so `total` over their own rows is the honest count and nothing is hidden FROM them.
2295
+ /// (The unbounded MAP is filed separately — pruning an approval record destroys evidence of what
2296
+ /// was asked, which is a retention ruling rather than a shape fix.)
2297
+ listMyApprovalRequests: (start: nat, limit: nat) -> (Result_28) query;
2298
+ /// The payer's own sessions, live ones first. PAYER-SCOPED: a caller sees only their own, which
2299
+ /// is why this needs no role — a session id is not a secret from the person who paid for it.
2300
+ ///
2301
+ /// ⟨2026-10-02, paged⟩ Takes `(start, limit)` like every other list here, capped at
2302
+ /// `MAX_SESSIONS_PAGE` rows per call; `total` is how many of the caller's sessions still exist.
2303
+ /// It took no arguments, and a cursor added after the freeze is an argument-list change every
2304
+ /// client would break on — the market paged its own pending withdrawals before its cut for the
2305
+ /// same reason. The order is the order sessions were opened.
2306
+ listMySessions: (start: nat, limit: nat) -> (Result_27) query;
2307
+ ///
2308
+ /// ⟨0.16.0, L3⟩ Paged and enveloped like every other `list*`, and the envelope here is uniformity
2309
+ /// rather than gating: this method cannot refuse anybody, so `#Err` is unreachable on it. That is
2310
+ /// deliberate — the off-chain verifier that reads this chain reads several other lists beside it,
2311
+ /// and one shape for all of them is worth more than saving a variant tag on the one that is
2312
+ /// public. `total` is the FULL chain length, so a verifier holding a short page can tell it has
2313
+ /// not seen the whole custody history — the distinction a bare array could not express.
2314
+ ///
2315
+ /// PUBLIC: custody history is the answer to "is this engram what it claims to be", and it is
2316
+ /// asked by strangers. `verifyEngramActiveOwner` on the registry answers only about the owner
2317
+ /// IT recorded, so this chain is what a caller composes with when currency matters — which it
2318
+ /// cannot do if it needs a role here.
2319
+ listOwnershipTransfers: (start: nat, limit: nat) -> (Result_26) query;
2320
+ /// ⟨2026-09-29, the platform's question from the build review⟩ OWNER-ONLY: how many payment
2321
+ /// signatures each agent has obtained today, and the daily limit (200, `PaymentSignCap`). The
2322
+ /// owner's console stated the rule and showed no number, because nothing served one; a number it
2323
+ /// made up would be a fabricated zero. Anyone else is refused by name (`#Refused
2324
+ /// listPaymentSignatureCounts: …`), never answered with zeros. Paged like `listAgents`, in the same
2325
+ /// order, one row per agent in the roster; each count is the agent's, taken by its root as the
2326
+ /// signer takes it, so every key the agent holds here shows the same number. The owner is not
2327
+ /// counted and has no row; the marketplace's count is not an agent's and is not here. WHAT WOULD
2328
+ /// MAKE THIS WRONG: a count kept by something other than the agent's root (`PaymentSignCap.rowFor`
2329
+ /// says so beside it).
2330
+ ///
2331
+ /// ⟨2026-09-30, following the owner's answer 4 of that date⟩ "is not here" no longer holds: the
2332
+ /// marketplace's one shared count today is in the reply's `marketplace` field, with the limit this
2333
+ /// engram enforces for it — beside the agents, never a row among them
2334
+ /// (`PaymentSignCap.marketplaceRow`). Null when no marketplace is set.
2335
+ listPaymentSignatureCounts: (start: nat, limit: nat) -> (Result_25) query;
2336
+ /// Agent records removed by a reset. Each row is a NAMED record with the fields `principal`,
2337
+ /// `name`, `registeredAt` and `purgedAt` — read them BY NAME, never by position.
2338
+ /// ⟨2026-09-29, the vocabulary ruling: pairing is the one verb that admits an agent⟩ The field
2339
+ /// this prose calls `registeredAt` is now `pairedAt`, on the served row and on the agent record
2340
+ /// it is copied from. What is said below about the two timestamps holds for `pairedAt`.
2341
+ ///
2342
+ /// The names are the whole point, not decoration. Both timestamps are a bare Int, so they were
2343
+ /// positionally indistinguishable at the client: a decoder that swapped registeredAt and
2344
+ /// purgedAt would type-check everywhere and show up only as nonsense in a UI. This prose used to
2345
+ /// describe the row as a positional 4-tuple and readers duly destructured it as one — the same
2346
+ /// bug, twice, in two different repos (corrected 2026-08-16). `agentPurgeLog` really is a
2347
+ /// 4-tuple in stable state; the SERVED shape is not, and that seam is where the mistake starts.
2348
+ ///
2349
+ /// resetEngram HARD-DELETES agent records so a purged principal can be invited back
2350
+ /// (owner ruling 2026-08-15). That makes the reset a genuine clear rather than a permanent burn —
2351
+ /// but it also means the `agents` map is no longer evidence that a principal ever held access,
2352
+ /// and the audit chain cannot stand in: it is a bounded ring that trims its oldest entries, so
2353
+ /// agent.pair rows are evicted over the life the rent model assumes. This is that
2354
+ /// evidence, and it is why the tombstone is written rather than the roster simply dropped.
2355
+ ///
2356
+ /// Owner-gated and TRAPS on refusal: an empty list must mean "no reset has removed an agent",
2357
+ /// never "you may not ask".
2358
+ /// ⟨CORRECTED 2026-10-02: it no longer traps. It is gated through `gatedResult`, so a refusal
2359
+ /// comes back as an `Err` VALUE that names this method — still never as an empty page, which is
2360
+ /// the property the sentence above was protecting.⟩
2361
+ /// RENAMED from `getOperatorPurgeLog` (L2.1: `get*` returns one value, `list*` returns a Page —
2362
+ /// the old name promised a scalar and delivered an array).
2363
+ ///
2364
+ /// Refusal reason preserved: an empty list must mean "no reset has removed an agent", never
2365
+ /// "you may not ask". On an evidence trail those are not close.
2366
+ ///
2367
+ /// Named record, not a 4-tuple. Both Ints are timestamps and were positionally indistinguishable
2368
+ /// at the client — swapping registeredAt and purgedAt would have type-checked everywhere and been
2369
+ /// visible only as nonsense in a UI.
2370
+ ///
2371
+ /// ⟨2026-09-29, review finding LIFE-2⟩ Each row now also carries the agent's `root`, the
2372
+ /// `invite` its enrolment named, and `ended` — why and on what day its engagement ended (its own
2373
+ /// earlier ending, or the reset) — the facts `resyncAgentEdges` re-sends. The stored rows are
2374
+ /// served as they are.
2375
+ listPurgedAgents: (start: nat, limit: nat) -> (Result_24) query;
2376
+ /// ⟨0.16.0⟩ ONE ACCESSOR, folding `readReadLog` and `getDenialLog` — and the fold only became
2377
+ /// safe because of a change made earlier in this same leg.
2378
+ ///
2379
+ /// They were two methods over the SAME log behind the SAME gate. `getDenialLog`'s own declaration
2380
+ /// explained why the split existed: nothing published this log's head, so a client could only
2381
+ /// reach the OLDEST retained entries, and a "recent refusals" band built on `readReadLog` would
2382
+ /// have rendered the oldest slice of a 10,000-entry ring under those words. That rationale died
2383
+ /// when the Page conversion started publishing `total = totalAppended()` — the head is now a fact
2384
+ /// a caller can read, so paging backward from it is ordinary arithmetic.
2385
+ ///
2386
+ /// The census proposed deleting `readReadLog` outright as a "duplicate accessor". Checked at the
2387
+ /// site, that premise was false: `getDenialLog` filtered `not e.success`, so deleting the other
2388
+ /// would have removed the only way to read SUCCESSFUL entries — and this ring is overwhelmingly
2389
+ /// successful MCP reads. Folded instead, which keeps both capabilities and still ends the pair.
2390
+ ///
2391
+ /// ⚠ `limit` IS A SPAN, NOT A ROW COUNT, and the same in both modes: it is how many log ids to
2392
+ /// EXAMINE starting at `start`. Unfiltered, items == the entries in that span. Filtered, items is
2393
+ /// however many of them matched — fewer, and that is not truncation. This is the one honest
2394
+ /// reading, because the alternative (keep scanning until `limit` rows match) hides how far it
2395
+ /// looked, which is precisely what `getDenialLog` had to disclose with `scannedFrom`/`more`.
2396
+ /// A caller knows exactly which ids were examined: `[start, start + limit)`.
2397
+ ///
2398
+ /// `earliest` is gone as a field and lost nothing: entries carry their own `id`, so a caller
2399
+ /// paging from 0 reads the oldest retained id off the first item. That is L3's stated design —
2400
+ /// items self-identify — rather than a second number to keep in sync.
2401
+ ///
2402
+ /// `withheld` is 0 even when filtering. The filter is CALLER-REQUESTED; `withheld` means rows
2403
+ /// POLICY removed. Overloading it would rebuild the ambiguity this campaign exists to remove.
2404
+ ///
2405
+ /// ⚠ **CORRECTED 2026-08-21, BESIDE the paragraph above because it is what the caller contract
2406
+ /// USED to be: THAT RULE WAS REVERSED ON 2026-08-18 AND THIS COMMENT DID NOT FOLLOW.** The body
2407
+ /// below computes `withheld = examined.size() - kept.size()`, and its own dated note explains
2408
+ /// why: `withheld = 0` beside a filter is the FABRICATED-COMPLETE PAGE, and this is the worst
2409
+ /// method in the file to have it on — `successFilter = ?false` is how the denial band asks "who
2410
+ /// was refused here", and a short page claiming nothing was withheld says the engram examined
2411
+ /// this window and found no refusals, when it examined `span` rows of which most were successes.
2412
+ ///
2413
+ /// So `withheld` now means BOTH policy-removed and filter-removed rows, and a caller can recover
2414
+ /// the examined span as `items.size() + withheld` — which is the successor to the retired
2415
+ /// `scannedFrom`/`head` window, and the number a UI needs to say what it actually looked at
2416
+ /// rather than implying it looked at everything.
2417
+ ///
2418
+ /// This mattered beyond the file: doc comments are emitted into `engram.did`, so every SDK and
2419
+ /// hand-written client has been reading the pre-fix rule off the interface for three days.
2420
+ ///
2421
+ /// Named `listReadLog`, not the census's `listDenialEntries`: that name assumed only the denial
2422
+ /// half survived. A method serving every row must not be named for one of them.
2423
+ ///
2424
+ /// Refusal reason preserved from both: this log has NO public counterpart — no certified head to
2425
+ /// fall back to — so an empty answer would falsely assert nobody has read this record, and an
2426
+ /// empty denial band would assert nobody has ever been refused here.
2427
+ ///
2428
+ /// WHAT "success = false" MEANS, PRECISELY, kept from getDenialLog because it is easy to misread:
2429
+ /// REFUSED-OR-FAILED, not authorization-denied alone. `denied()` writes authz refusals here, and
2430
+ /// so do settle failures, webhook auto-disables, expiry sweeps and MCP rate limits. Do not narrow
2431
+ /// this to a name it does not carry.
2432
+ ///
2433
+ /// ⟨2026-09-29, owner ruling follow-up 16 (B)⟩ An agent reads only the entries it caused, here as
2434
+ /// on the chained record — the same `RecordView` filter, and `withheld` counts what it removed
2435
+ /// together with what `successFilter` removed. WHAT WOULD MAKE THIS WRONG: see `listAuditEntries`.
2436
+ listReadLog: (successFilter: opt bool, start: nat, limit: nat) ->
2437
+ (Result_23) query;
2438
+ /// Same visibility as listAuditEntries: the owner, this engram's agents, and ACTIVE guardians
2439
+ /// see disputes. An unauthorized read REFUSES — it does not return an empty list, because an
2440
+ /// empty list reads as "no disputes have been filed", which is a claim, not an absence of one.
2441
+ /// RENAMED from `getRepudiations` (L2.1). Refusal reason preserved: an empty list would read as
2442
+ /// "no disputes have been filed" — and a dispute record that can be mistaken for an absence of
2443
+ /// disputes is worse than no record at all. Not public by design.
2444
+ ///
2445
+ /// ⟨CORRECTED 2026-09-29, owner ruling follow-up 16 (B)⟩ "Same visibility as listAuditEntries"
2446
+ /// still holds, and so the first sentence no longer does: an agent reads only the record entries it
2447
+ /// caused, and every dispute here is the owner's own `memory.repudiate` entry. So the owner and
2448
+ /// guardians get the rows; an agent gets an empty page whose `withheld` counts the rows in its
2449
+ /// window — which says disputes exist without showing them, never "none have been filed". WHAT
2450
+ /// WOULD MAKE THIS WRONG: a ruling that an agent may see the disputes of its own writes; this would
2451
+ /// then have to look up each disputed version's writer, which it does not do today.
2452
+ listRepudiations: (start: nat, limit: nat) -> (Result_22) query;
2453
+ /// Owner: advance the review cursor — "I have seen everything up to and including uptoAuditId"
2454
+ /// (null = everything at the current head). Bounded so the attestation can never cover entries
2455
+ /// the console did not render; clamped monotonic (the cursor never moves backward) and to the
2456
+ /// current head (never attests the future). Logged, so the review itself is part of the record.
2457
+ ///
2458
+ /// Deliberately filter-agnostic: the cursor is a plain audit id in the CHAINED log's one id space
2459
+ /// (every family qualifiesForReview admits is written to auditLogger, so they share that space),
2460
+ /// and this never inspects an operation or a role. Widening what the review shows therefore
2461
+ /// changes only which ids the console can pass here — it cannot desynchronise the cursor.
2462
+ markAgentWritesReviewed: (uptoAuditId: opt nat) -> (Result_8);
2463
+ /// The AGENT's current key nominates the key that will replace it. Nothing moves yet: the new
2464
+ /// key must accept, by its own call, within 24 hours (`acceptAgentKeyNomination`). One pending
2465
+ /// nomination per agent — a new one replaces the earlier one, and the record says which. Refused,
2466
+ /// with nothing changed, for a caller that is not an active agent here and for a key that could
2467
+ /// not hold the record (`KeyReplacement.nomineeRefusal`).
2468
+ nominateAgentKey: (newKey: principal) -> (Result_1);
2469
+ /// An AGENT paired here asks this engram to open an engram of its own. This engram attaches 1.3 T of
2470
+ /// its own cycles to the registry's `sysSpawnChild`; the agent's lasting key owns what is opened, and
2471
+ /// the engram is recorded as opened through this one. Answers the new engram's canister id.
2472
+ ///
2473
+ /// APPROVING IS GIVING: the owner allowed this with `setChildAllowance`, and what is attached is the
2474
+ /// agent's from the moment that engram exists. Nothing is ever taken back, and the registry never
2475
+ /// funds that engram — its owner (the agent) tops it up.
2476
+ ///
2477
+ /// ORDER, with no await before the allowance is taken: the caller is an active agent; there is a
2478
+ /// registry; payments are not frozen (opening sends this engram's cycles out of it, and a freeze is
2479
+ /// the brake a guardian holds over resources leaving); the balance carries the attachment above this
2480
+ /// engram's own reserve; then one opening is TAKEN from the agent's allowance in this message, and only
2481
+ /// then is the registry called. If the registry refuses or cannot be reached, the opening is GIVEN
2482
+ /// BACK (one added to the current count) and the cycles the call did not use come back by the
2483
+ /// network's refund. Every outcome is written to the record under `agent.openChild` in the message
2484
+ /// that hears the registry's answer, before the reply — in the alerts' money class.
2485
+ ///
2486
+ /// ⟨CORRECTED 2026-10-01, review F1 and F4 — the sentence "If the registry refuses or cannot be
2487
+ /// reached, the opening is GIVEN BACK" above is what was first built.⟩ The opening is given back only
2488
+ /// when the WHOLE attachment came back as the call's refund (the registry accepted none of it) and
2489
+ /// the owner has not set this agent's count since the take (`ChildAllowance.settle`). A refusal after
2490
+ /// the registry accepted the cycles (the template would not install, or the key came to own an
2491
+ /// engram meanwhile) leaves the 1.3 T in a canister the agent's key controls; giving the opening back
2492
+ /// then let an allowance of 1 drain this engram 1.3 T per call. Now that opening is spent and the
2493
+ /// reply says so.
2494
+ ///
2495
+ /// WHAT WOULD MAKE THIS WRONG: an await added between `ChildAllowance.take` and the registry call; the
2496
+ /// registry's `CHILD_MIN_ATTACHED_CYCLES` moving without `CHILD_ATTACH_CYCLES`.
2497
+ openChildEngram: () -> (Result_21);
2498
+ openSession: (config: SessionConfig, sig: PaymentSignature) -> (Result_20);
2499
+ /// Withdraw tokens to the owner's principal. This is the ONLY way to
2500
+ /// move tokens out of the engram to an external address, and the
2501
+ /// destination is always hardcoded to the owner.
2502
+ /// NOT freeze-gated: the owner always controls their own funds.
2503
+ /// Guardian freeze only blocks agent/public operations.
2504
+ ownerWithdraw: (amount: TokenAmount, ownerAuth: opt ActionAuth) ->
2505
+ (Result_8);
2506
+ /// ⟨0.16.0⟩ `payRentFromEarnings` DELETED — ruled TWICE (B1 and B2), and it was the single
2507
+ /// clearest violation of the OSS/platform split in this repo.
2508
+ ///
2509
+ /// It hand-declared four cashier methods inline, read `getFeeSchedule`, computed
2510
+ /// `priceE6 = rentUsdE6PerEngramYear * months / 12`, and deposited `priceE6 + 3 * CKUSDC_FEE`
2511
+ /// under a comment explaining that three is "the three token fees the cashier's rail spends" —
2512
+ /// a PUBLIC canister hardcoding how many ledger fees a PRIVATE one consumes internally. Any
2513
+ /// change to the cashier's rail silently mispriced this call.
2514
+ ///
2515
+ /// It also carried the only RE-ENTRANT cross-canister chain in the system: engram →
2516
+ /// cashier.payRentWithStablecoin → (registry, ledger, CMC …) → back into the SAME engram's
2517
+ /// sysRecordFeeEvent while the originating message was still suspended, both writing the same audit
2518
+ /// ring, with no lock.
2519
+ ///
2520
+ /// Rent is the cashier's entirely (B2). Owners pay through the cashier's rails; the engram keeps
2521
+ /// append-only receipts and no fee math.
2522
+ /// ⟨0.16.0, L1 — census-ruled⟩ THE ONE-SHOT ARM. Re-redemption is `#Err(#Conflict)`, named in
2523
+ /// the census row for this method, and it is the arm the whole guard exists to express: this
2524
+ /// principal HAS held agent access, and no code will give it back. A caller reading
2525
+ /// `#Conflict` knows to pair a fresh key; a caller reading `#Refused` would keep presenting the
2526
+ /// same identity, which is exactly the loop the one-shot rule was written to stop.
2527
+ /// Redeem an invite code minted by `createAgentInvite`. The CALLER becomes the agent, so
2528
+ /// redeem from the wallet that will do the work.
2529
+ ///
2530
+ /// ⚠ IT REFUSES ANY CALLER THAT ALREADY HOLDS A RECORD — `#Active`, `#Revoked` or `#Resigned`.
2531
+ /// The `#Revoked` case is the one that matters: revocation is a soft status change, so allowing
2532
+ /// a revoked principal to redeem a fresh invite would be a self-un-revoke. To CHANGE an existing
2533
+ /// agent's permissions the owner uses `setAgentPermissions`; this method is only ever the
2534
+ /// first admission of a principal.
2535
+ ///
2536
+ /// A GUARDIAN cannot redeem one either. A guardian is an independent check on agents, so one
2537
+ /// wallet must not hold both roles — use a separate wallet.
2538
+ ///
2539
+ /// Anonymous callers are refused, and every refusal above is recorded in the denial log.
2540
+ ///
2541
+ /// ⟨RESHAPED 2026-09-28, owner decision: one lasting identity per agent — one root key per agent,
2542
+ /// with an enrolled key per client⟩ The argument is one record: the invite `code` and an
2543
+ /// `enrolment` statement signed by the agent's ROOT key naming this engram, the calling key and
2544
+ /// this code (`Enrolment.verify`). The root is stored on the record and is what the registry keeps
2545
+ /// the track record under. REQUIRED, not optional: every engram launches on this code (the
2546
+ /// building-phase rule, 2026-09-27), so there is no key without a root and no second path for one.
2547
+ /// Two more refusals, each leaving the invite unconsumed: a statement that does not verify, and a
2548
+ /// root that already holds an ACTIVE key here (`Enrolment.activeKeyOf` — one active key per agent
2549
+ /// per engram). The one-shot rule and the five-active cap are unchanged and still read the KEY.
2550
+ /// ⟨CORRECTED 2026-09-29, the cycle-drain hole measured that day⟩ "each leaving the invite
2551
+ /// unconsumed" holds only until the invite's fifth failed statement check: every failed check
2552
+ /// counts against the invite whoever sends it, the fifth voids it (the refusal says so, and the
2553
+ /// record gets `agent.invite.void`), and after that every attempt with it is refused
2554
+ /// `#Conflict` before any check is made (`InviteFailures`).
2555
+ ///
2556
+ /// ⟨RESHAPED 2026-09-29, review finding LIFE-3⟩ The reply is `Types.Pairing`: the agent
2557
+ /// record AND `trackRecord`, which says plainly when the registry refused or failed to record the
2558
+ /// engagement on the agent's track record, and what to do. Before, the reply was `#Ok` with the
2559
+ /// record alone even when the registry had refused the start, so the agent was never told.
2560
+ pairAgent: (arg: record {
2561
+ code: text;
2562
+ enrolment: Enrolment;
2563
+ }) -> (Result_19);
2564
+ /// ⟨0.16.0, L2.2 — renamed from `guardianPauseWrites`⟩ Same correction as freezePayments above:
2565
+ /// owner-or-guardian, and `resumeWrites` never carried the prefix either.
2566
+ pauseWrites: () -> (Result_1);
2567
+ /// What condensing through `toSeq` would do, and whether it is unlocked. COMMITS NOTHING — it is
2568
+ /// a query, so it structurally cannot; a preview that wrote would be its own trap.
2569
+ ///
2570
+ /// It does not ask the library, because the library's refusal cannot answer the owner's question.
2571
+ /// `Chain.prepareCondense` reports `lastExported = null` whenever the only receipts present are
2572
+ /// author-FILTERED, since a filtered receipt never satisfies the gate and is never recorded as a
2573
+ /// near-miss — and an owner who exported minutes ago would read that as "you have never
2574
+ /// exported". So `filteredReceiptsSeen` is counted separately, and `maxUnlockableToSeq` says how
2575
+ /// far the receipts already on file reach, so a console can offer that instead of only "no".
2576
+ ///
2577
+ /// `maxUnlockableToSeq` is a maximum over every retained unfiltered receipt, and a NEWER receipt
2578
+ /// may cover a SHORTER range than an older one — so the scan cannot stop at the first hit. It is
2579
+ /// bounded by entry count instead, and `scannedFromSeq` / `scanTruncated` say where it stopped
2580
+ /// and whether the bound was the reason. A bounded scan that hid its bound would report "no
2581
+ /// receipts" for "I did not look far enough", which is this codebase's own defect class.
2582
+ ///
2583
+ /// `unlocked` is the export-receipt gate ONLY. It is not a promise the condense will land: the
2584
+ /// history-budget delta is checked at commit time and can still refuse a short prefix.
2585
+ previewCondense: (toSeq: nat) -> (Result_18) query;
2586
+ /// A quote for a session this caller has not opened and is not committed to — the intent a
2587
+ /// buyer signs against. Pure dry run, no state, no funds.
2588
+ ///
2589
+ /// PUBLIC: a price list nobody can read is not a price list. A buyer has to see the deposit
2590
+ /// minimum and per-call cost BEFORE signing anything, and gating the quote behind a role would
2591
+ /// mean the only way to learn this engram's price is to already be permitted to pay it.
2592
+ ///
2593
+ /// ⟨CORRECTED 2026-08-24 — this said "⟨L7⟩ UPDATE, NOT QUERY" and cited a market ruling that
2594
+ /// DEC-107 had already closed.⟩ QUERY, per the ruled quote convention: FAST READS. Both
2595
+ /// `quotePayment` and `previewOpenSession` become queries, and L7's open-ruling exception
2596
+ /// retires with them — so the argument written here for keeping it an update was arguing the
2597
+ /// losing side of a decision already made, and was added the same day by not reading forward
2598
+ /// to the ruling. `gate.offerSession` is pure — it validates the intent and returns it — so
2599
+ /// nothing forced the update in the first place.
2600
+ ///
2601
+ /// ONE-REPLICA CAVEAT, stated here because the ruling requires it at both sites: a query is one
2602
+ /// replica's word and is NOT a settleable challenge. Nothing quoted here can be redeemed. The
2603
+ /// commitment happens in `openSession`, which IS a certified update call, and a buyer who needs
2604
+ /// the price attested rather than quoted takes it from that reply.
2605
+ ///
2606
+ /// ⟨RESTORED 2026-08-24, owner ruling R-A1⟩ Named to match the market's, because it is the
2607
+ /// same act against a different seller and a reader comparing the two should not have to learn
2608
+ /// two verbs for it.
2609
+ ///
2610
+ /// ⟨2026-10-02⟩ Answers the house `Types.SessionIntent` — ic402's intent with `expiry` spelled
2611
+ /// `expiryNs`. Same value, same meaning; only the name carries the unit now.
2612
+ previewOpenSession: () -> (SessionIntent) query;
2613
+ /// Read one memory file at its current version.
2614
+ ///
2615
+ /// Gated on the `read_memory` permission, so an agent holding that grant reaches it — unlike
2616
+ /// `writeMemory`, which is owner-only.
2617
+ ///
2618
+ /// ⚠ `config/` AND `secrets/` ARE OWNER-ONLY whatever the grant says, and the refusal is
2619
+ /// `#Refused` rather than `#Invalid` deliberately: the path is well-formed and the file may well
2620
+ /// exist — the caller simply may not have it. Under L1 that distinction is the caller's to branch
2621
+ /// on, which is the whole reason `Error` is a closed variant rather than a sentence.
2622
+ ///
2623
+ /// ⚠ A QUERY, so it does NOT count as a read. The IC discards query-path state changes, so a
2624
+ /// counter here could never persist; read counting lives on the update path
2625
+ /// (`sessionReadContext` and the MCP reads). A caller measuring engram usage from this method
2626
+ /// will measure nothing.
2627
+ readMemory: (path: text) -> (Result_11) query;
2628
+ /// Read many paths in one call. Per-path Results are KEPT — partial success is the point, and
2629
+ /// one unreadable path must not fail the other nine.
2630
+ ///
2631
+ /// ⟨0.16.0⟩ The OUTER envelope is new, and it fixes two ways this used to answer dishonestly:
2632
+ ///
2633
+ /// · Over the path cap it returned `[("", #Err(#Invalid …))]` — ONE row, for a path the caller
2634
+ /// never asked about. A consumer looping rows saw a result for the empty path and no result
2635
+ /// for any real one. The call as a whole failed; dressing that as a per-path outcome is the
2636
+ /// fabrication ADR-0002 is about.
2637
+ /// · An AUTHORIZATION refusal was fanned out across every requested path, so one "no" arrived
2638
+ /// as N per-path noes and read like N independent permission facts about N files.
2639
+ ///
2640
+ /// Both are now what they are: a single `#Err` for the whole call. `#Ok` still carries a row per
2641
+ /// path, each with its own Result — a refused RESERVED path sits beside a readable one, which is
2642
+ /// a genuine per-path fact.
2643
+ readMemoryBatch: (paths: vec text) -> (Result_17) query;
2644
+ /// ⟨0.16.0⟩ `getMemoryRetentionGuards` DELETED — folded into `getMemoryRetentionPolicy` above.
2645
+ /// It was the second L2.1 array-returning `get*`, and separating it from the policy it modifies
2646
+ /// was the defect described there. Its own docs are preserved at the fold site.
2647
+ /// One page of a file's content — the paged read the whole-file surfaces point at.
2648
+ ///
2649
+ /// `version = null` reads the live file; a version reads that version as it was recorded.
2650
+ /// The store clamps a page to one chunk, and `more` DISCLOSES the clamp: a caller that cannot
2651
+ /// see it cannot know whether it has the whole range, which is how silent truncation gets
2652
+ /// reintroduced. Loop while `more` is true, advancing by the bytes actually returned.
2653
+ readMemoryBytes: (path: text, offset: nat, len: nat, version: opt nat) ->
2654
+ (Result_16) query;
2655
+ /// The escrow exit for a session whose settle cannot pay.
2656
+ ///
2657
+ /// PAYER-ONLY, enforced inside the gateway, and deliberately not owner-triggered: the payer's own
2658
+ /// deposit is the only thing here, so making them wait for anyone else would be a worse rail than
2659
+ /// the one this replaces. (The market's twin is admin-triggered because a marketplace session has
2660
+ /// a counterparty; a memory session does not.)
2661
+ ///
2662
+ /// WHY IT IS NEEDED AT ALL: `closeSessionInternal` settles only when
2663
+ /// `deposited >= consumed + fee`, and vouchers may consume up to the full deposit — so a payer
2664
+ /// who spends their whole budget makes their own session unsettleable, with no adversary
2665
+ /// involved. Before this, the refusal text told them to call `recoverEscrow`, which existed on no
2666
+ /// canister they could reach. RB2 caps consumable spend so ordinary use cannot reach that state;
2667
+ /// this is the way back for a session that already has.
2668
+ ///
2669
+ /// ⟨CORRECTION 2026-09-30, beside the paragraph above and the first line⟩ No longer true. ic402
2670
+ /// 2.17.0's `closeSessionInternal` settles `min(consumed, deposited − fee)` and skips a refund of
2671
+ /// zero, so a session spent to its whole budget still closes, and under the one-fee reserve a clean
2672
+ /// close leaves the escrow holding nothing or less than one fee, which no transfer can move. The
2673
+ /// way back is for a REFUND LEG THAT FAILED (a transient ledger error): the settle went through,
2674
+ /// ic402 marks the session closed, and the remainder stays in escrow until the payer calls this.
2675
+ /// A failed settle is not this method's case: ic402 puts that session back to open, where this
2676
+ /// method refuses, and the close is retried instead. WHAT WOULD MAKE THIS WRONG: an ic402 whose
2677
+ /// close stops capping the settle, or a live ckUSDC fee above `Env.CKUSDC_FEE`.
2678
+ ///
2679
+ /// ckUSDC only — an engram session's intent names `Env.CKUSDC_LEDGER` and nothing else, so there
2680
+ /// is no token to choose and no wrong one to pass.
2681
+ ///
2682
+ /// ⟨2026-10-02, decided — the twin divergence is deliberate⟩ The market's `recoverEscrow` takes
2683
+ /// `Wire.TokenAmount`; this one takes a bare `nat`, and the two stay different on purpose. The
2684
+ /// market escrows more than one token, so the amount has to say which; this engram's sessions
2685
+ /// escrow ckUSDC only, and a `TokenAmount` here would make an `#Icp` amount representable and then
2686
+ /// refuse it at runtime — the argument `convertEarningsToCycles` makes. What changed is the NAME:
2687
+ /// `amountE6`, ckUSDC base units. Candid passes arguments by position, so nothing on the wire moved.
2688
+ ///
2689
+ /// ⚠ `#Conflict` HERE ALSO COVERS "NOT THE SESSION'S PAYER". ⟨2026-10-02, audited at this site⟩
2690
+ /// The payer check is ic402's (`recoverEscrow` authorizes `caller == session.payer`), and the
2691
+ /// library answers every refusal down one text channel, so "Not authorized: only session payer can
2692
+ /// recover escrow" arrives as `#Conflict`, beside the state refusals (an open session, nothing to
2693
+ /// recover, no such session). Read the MESSAGE, not the arm. This is the 0.16.0 contract as
2694
+ /// ratified on 2026-10-02; giving the library one arm per kind of refusal is post-cut dependency
2695
+ /// work. (The payments-frozen refusal in the body is this engram's own, and is state.)
2696
+ ///
2697
+ /// ⟨2026-10-05, ic402 2.17.3 — beside the list above⟩ A fourth state refusal: a session that reads
2698
+ /// `closing` or `expired`, whose close has not finished. Escrow comes back only from a session that
2699
+ /// reads `closed`, and the refusal moves nothing. A close that stopped part-way (its refund call
2700
+ /// rejected, or a trap) leaves the session reading `closing`, and no method on this engram moves it.
2701
+ recoverEscrow: (sessionId: text, amountE6: nat) -> (Result_8);
2702
+ /// ⟨0.16.0, L1⟩ A REDEMPTION PATH, where the arm choice is a disclosure decision.
2703
+ ///
2704
+ /// Four of these refusals are about the CODE and one is about the caller. The code arms restate
2705
+ /// exactly what the text already said — an unknown code is `#NotFound`, a used or expired one is
2706
+ /// `#Conflict` — so no fact is newly disclosed by the arms; the anti-enumeration control here is
2707
+ /// the attempt throttle above, not vagueness in the reply. Throttling is itself `#Conflict` (at
2708
+ /// capacity) rather than `#Unavailable`: nothing failed, and the caller's own recent behaviour is
2709
+ /// what closed the door, so "try again" is true only after the stated wait.
2710
+ ///
2711
+ /// ⟨2026-10-02⟩ Answers the `GuardianRecord` it created — `#Pending` until the owner approves —
2712
+ /// rather than `()`. The new guardian learns, in the same reply, the name and permissions the
2713
+ /// owner's invite gave them and that they cannot act until approved; before, that took a second
2714
+ /// read they had no reason to know to make.
2715
+ registerGuardian: (code: text) -> (Result_15);
2716
+ /// Remove a per-caller override so that caller reverts to — and keeps TRACKING — the global caps.
2717
+ ///
2718
+ /// It travels with its pair and is never dropped alone: without it the only way "back" from an
2719
+ /// override is to overwrite it with a frozen copy of the global policy, which then stops
2720
+ /// following later changes. That makes every override permanent in effect.
2721
+ removeCallerSpendPolicy: (caller: principal) -> (Result_1);
2722
+ /// De-register a type that has NEVER SIGNED. Exists because of a hazard the first external
2723
+ /// validation found within hours (consumer report, 2026-08-06): a bare-name #standard registration —
2724
+ /// cheap, no watcher preconditions — permanently blocked the guarded namespaced registration
2725
+ /// via the collision rule, with no way out. An owner typo could brick a venue integration for
2726
+ /// the engram's lifetime. Never-signed is the whole gate: a type that signed even once is part
2727
+ /// of what the audit chain's eip712.sign entries mean, and stays resolvable forever — this
2728
+ /// removes only authority that was never exercised, which is removing nothing but the mistake.
2729
+ /// NOT freeze-gated, same reasoning as removeEvmWithdrawDestination: this is TIGHTENING, and a
2730
+ /// frozen engram must still be able to shrink what it could sign.
2731
+ removeEip712Type: (typeId: blob) -> (Result_1);
2732
+ removeEvmWithdrawDestination: (dest: text) -> (Result_1);
2733
+ /// Owner-only: remove a guardian from the roster.
2734
+ ///
2735
+ /// Unlike revoking an agent this is NOT terminal for the principal — the guardian roster does
2736
+ /// not carry the one-shot rule that blocks a revoked agent from ever pairing again, so the
2737
+ /// same wallet can be invited again.
2738
+ ///
2739
+ /// `#NotFound` rather than `#Refused` when there is no such guardian: only the owner reaches this
2740
+ /// line, so the roster is theirs and its contents are not a secret being withheld from them.
2741
+ removeGuardian: (guardianPrincipal: principal) -> (Result_1);
2742
+ /// Owner disavows a specific memory write, in the open. Marks — never deletes: the content and
2743
+ /// the original audit entry stay exactly where they were; this appends a chained
2744
+ /// `memory.repudiate` entry and lists the (path, version) so readers can see the dispute.
2745
+ repudiateMemoryWrite: (path: text, version: nat, reason: text) ->
2746
+ (Result_8);
2747
+ /// ⚠ OWNER-ONLY BULK RESET. Destructive, and wider than its name suggests.
2748
+ ///
2749
+ /// It severs EVERY grant surface that can outlive a role — agents, guardians, invites and
2750
+ /// connector tokens — clears memory, and returns the operational flags (encryption, writes-paused,
2751
+ /// payments-frozen) to a fresh baseline. Anyone holding access through any of those loses it.
2752
+ ///
2753
+ /// What SURVIVES, deliberately:
2754
+ /// · the cumulative counters, which never reset — an engram cannot make itself look new;
2755
+ /// · memory DELETES are soft, so version history is retained and paths stay enumerable via
2756
+ /// `listDeletedMemoryFiles`;
2757
+ /// · `agentPurgeLog`, which is the durable record of who was severed;
2758
+ /// · ⟨2026-09-29⟩ today's spending totals — each caller's and the engram-wide one — and the
2759
+ /// owner's engram-wide ceiling. A reset severs access; it never gives back a day's allowance
2760
+ /// already spent, so a key re-admitted after a reset keeps what it spent today (the totals are
2761
+ /// kept per calling key).
2762
+ /// · ⟨2026-10-02⟩ the seat weeks of every engagement (`agentSeatWeeks`), with the signature count
2763
+ /// they sit beside, and the owner's switch over the metrics reader's read and the reader it
2764
+ /// names — a reset severs access, it does not change who may read what was kept.
2765
+ ///
2766
+ /// ⟨2026-10-02⟩ It tells the spawning registry that it happened (`sysNoteReset`), on every reset.
2767
+ ///
2768
+ /// It is recorded as ONE chained `engram.reset` entry that also names the flag transitions.
2769
+ ///
2770
+ /// ⚠ AND THE LIMIT OF THAT RECORD, because a caller relying on it should know: the audit chain is
2771
+ /// NOT unbounded. It caps at MAX_ENTRIES and trims its oldest tenth on overflow, so on any engram
2772
+ /// long-lived enough for the rent model to apply, the reset entry is not guaranteed to still be
2773
+ /// there. `agentPurgeLog` is the evidence that does not age out — which is why it exists.
2774
+ resetEngram: () -> (Result_14);
2775
+ /// ⟨2026-09-28, owner decision: an agent can resign from an engram, and the resignation is
2776
+ /// recorded⟩ The AGENT ends its own engagement with this engram.
2777
+ ///
2778
+ /// WHY IT EXISTS: before this, every ending was something done TO an agent — revoked by the
2779
+ /// owner, by a guardian, by the kill switch, or by a reset — so an agent that wanted to leave
2780
+ /// cleanly had to wait to be fired, and its track record across engrams could not tell a clean exit
2781
+ /// from a firing. This is the one ending the agent itself chooses, and the registry records it
2782
+ /// under its own reason (`#Resigned`), never folded into a revocation.
2783
+ ///
2784
+ /// TERMINAL for this principal on this engram, exactly like a revocation: the record stays (status
2785
+ /// `#Resigned`, auditable), and `pairAgent` refuses any principal that already holds a
2786
+ /// record, so returning means a different principal and a fresh invite from the owner.
2787
+ ///
2788
+ /// SELF-SERVICE, so the refusals are about the record's state, not the caller's standing:
2789
+ /// `#NotFound` for a caller that is not an agent here, `#Conflict` for one already ended.
2790
+ /// The roster change and its chained audit line commit BEFORE the registry is told, so a registry
2791
+ /// that is down cannot leave the agent half-resigned; a missed notice is logged by
2792
+ /// `notifyRegistryAgentEdge`. WHAT WOULD MAKE THIS WRONG: a second path that sets `#Resigned`.
2793
+ resignAgent: () -> (Result_1);
2794
+ /// OWNER-ONLY release of the write pause, with the same one-way shape as `unfreezePayments`: a
2795
+ /// guardian holding `canPauseWrites` can stop writes without the owner and cannot restart them,
2796
+ /// and the owner can always restart them. A tripwire rather than a lock.
2797
+ resumeWrites: () -> (Result_1);
2798
+ /// Self-heal: re-send every ACTIVE agent's start to the spawning registry (idempotent — the
2799
+ /// registry mints no duplicate for an open engagement), for when a notice was missed. Owner-only.
2800
+ /// Returns how many the registry ACKNOWLEDGED. ⟨2026-09-28: it used to count sends that did not
2801
+ /// throw, against a method that could not say no; it now counts `#Ok` answers.⟩ It re-sends
2802
+ /// starts only: an ending carries a reason this roster does not keep per principal.
2803
+ ///
2804
+ /// ⟨CORRECTED 2026-09-29, review finding LIFE-2 — owner's option A⟩ It now re-sends ENDINGS as
2805
+ /// well as starts, endings first (`AgentEdges.resyncPlan` says why): every ended record on the
2806
+ /// roster and every agent a reset removed keeps its ending's reason and day
2807
+ /// (`Types.AgentEnding`), so an ending the registry never received — the one loss that used to
2808
+ /// show an engram's engagement as live on the agent's record forever and block the agent's later starts here —
2809
+ /// is recorded by one call of this. The registry answers an ending it already holds `#Ok`.
2810
+ /// RESHAPED REPLY: `Types.ResyncReport` in place of a bare count — the endings and starts the
2811
+ /// registry acknowledged, and every notice it did not, with its own words, so the owner can tell
2812
+ /// an expected answer (an ending for a start that was never recorded says so) from a real one.
2813
+ /// The calls leave in batches of `RESYNC_BATCH`, each batch in order and wholly answered before the
2814
+ /// next leaves, so the order above holds at the registry.
2815
+ /// ⟨2026-09-29⟩ WHAT A RESYNC CANNOT DO: record an engagement whose start the registry never
2816
+ /// recorded and which has since ENDED. A start is re-sent only for an agent still active here, so
2817
+ /// all that is left to send is the ending, and the registry has nothing to end. The report lists
2818
+ /// each such engagement under `cannotBeRecorded` (key, root, invite, and how and when it ended),
2819
+ /// apart from `notRecorded`, so the owner sees them rather than silence.
2820
+ resyncAgentEdges: () -> (Result_13);
2821
+ /// Return to managed custody: the engram adds the registry back as a controller ALONGSIDE the
2822
+ /// owner — it does not remove the owner. The inverse of `claimSovereignty`, and the reason
2823
+ /// sovereignty is not a one-way door.
2824
+ returnToManaged: () -> (Result_1);
2825
+ /// Replace the whole content of an EXISTING file.
2826
+ ///
2827
+ /// This is the one thing agents could never do before: every agent write was an append,
2828
+ /// so a file could only ever grow. The ruling that opened it is narrow, and every part of the
2829
+ /// narrowness is enforced below — an agent may replace content only on a path the owner has
2830
+ /// granted, only where retention is not #vault, and only where the file already exists. Vault
2831
+ /// paths stay append-only forever, which is the claim the product makes about them.
2832
+ ///
2833
+ /// Replacing is safe here in a way it was not in the legacy store: the prior version is not
2834
+ /// overwritten, it is superseded. It stays readable, its hash stays committed, and the agent
2835
+ /// floor keeps its bytes for the window a counterparty needs to extract them.
2836
+ /// ⟨0.16.0, L1⟩ FIVE AGENT REFUSALS, ALL `#Refused`, AND THE UNIFORMITY IS THE FINDING.
2837
+ ///
2838
+ /// Reserved path, protected path, missing permission, ungranted revise path, `#vault` class — a
2839
+ /// caller could previously tell these apart only by reading five different sentences, and every
2840
+ /// one of them ends the same way: this agent may not make this write, and no retry by this
2841
+ /// agent changes that. The prose already said "Not operator-retryable" four times over,
2842
+ /// because a text field was the only place to put it. It is an arm now.
2843
+ ///
2844
+ /// The one that could tempt a different arm is `#vault`, which looks like a state fact. It is
2845
+ /// not: the class is an AUTHORIZATION statement about who may replace content there, and it
2846
+ /// resolves to the same "you may not", forever, for everyone but the owner.
2847
+ reviseMemory: (path: text, content: blob) -> (Result);
2848
+ /// Owner-only: revoke one agent.
2849
+ ///
2850
+ /// ⚠ SOFT AND TERMINAL, which sounds contradictory and is not. The record is not deleted — the
2851
+ /// status becomes `#Revoked` and the row survives, which is what makes the revocation auditable.
2852
+ /// And because `pairAgent` refuses any principal that already holds a record of ANY
2853
+ /// status, this is permanent FOR THAT PRINCIPAL: they cannot be re-invited. Re-admitting the same
2854
+ /// human means a different wallet.
2855
+ ///
2856
+ /// To narrow what an agent may do rather than end its access, use `setAgentPermissions`.
2857
+ revokeAgent: (agentPrincipal: principal) -> (Result_1);
2858
+ /// Owner-only panic button: revoke EVERY active agent at once. Returns how many were revoked.
2859
+ ///
2860
+ /// ⚠ IT REVOKES ONLY AGENTS. Invites and connector bearer tokens are separate surfaces with
2861
+ /// their own buttons (`revokeAllInvites`, `revokeAllBearerTokens`), and an owner reaching for one
2862
+ /// of these in an emergency almost certainly wants all three. Nothing here cascades.
2863
+ ///
2864
+ /// ⚠ AND IT IS IRREVERSIBLE PER PRINCIPAL, which a "revoke all" reads as temporary and is not.
2865
+ /// Each agent's record is set to `#Revoked` rather than deleted, and `pairAgent`
2866
+ /// refuses any principal holding a record of ANY status — so every wallet this touches can never
2867
+ /// be an agent on this engram again. Re-admitting the same agents means new keys. Only
2868
+ /// `resetEngram` clears the records, and it severs everything else with them.
2869
+ ///
2870
+ /// The revocation is committed BEFORE the registry is told: state is cleared, the audit line is
2871
+ /// written, and only then does it await one notification per revoked agent. So a notification
2872
+ /// that fails leaves the agents revoked here regardless — a partial notify is not a partial
2873
+ /// revocation, which is the direction that matters for a panic button.
2874
+ revokeAllAgents: () -> (Result_8);
2875
+ /// Owner-only panic button: revoke every connector bearer token at once. Returns how many.
2876
+ ///
2877
+ /// The THIRD independent surface — hosted connectors authenticate with bearer tokens, not with
2878
+ /// agent records or invites, so neither of the buttons above touches them. An owner cutting
2879
+ /// off access completely needs all three, and this is the one that reaches an AI host already
2880
+ /// holding a live session.
2881
+ ///
2882
+ /// Tokens are re-issuable: revoking them is not permanent the way revoking an agent is.
2883
+ revokeAllBearerTokens: () -> (Result_8);
2884
+ /// ⟨0.16.0⟩ `removeAllGuardians` DELETED — test-only (L6), and verified NOT part of KILL-SWITCH's
2885
+ /// mass-revoke set. The destructive phase-07 suite iterates `removeGuardian` instead, so nothing
2886
+ /// loses a capability: what dies is a bulk spelling no runbook ever invoked.
2887
+ /// Owner-only panic button: void every unredeemed invite. Returns how many were voided.
2888
+ ///
2889
+ /// Separate from `revokeAllAgents`: this stops invites that have NOT been redeemed yet and
2890
+ /// touches no existing agent. An invite already redeemed is an agent, and only the other
2891
+ /// button reaches it.
2892
+ ///
2893
+ /// Unlike revoking an agent, this costs nothing permanently — an invite is a one-time code,
2894
+ /// so voiding it and minting a fresh one is the ordinary path.
2895
+ revokeAllInvites: () -> (Result_8);
2896
+ /// Revoke a bearer token by its public tokenId. Idempotent.
2897
+ /// Subsequent /mcp requests using this token will be rejected.
2898
+ revokeBearerToken: (tokenId: text) -> (Result_1);
2899
+ /// ⟨0.16.0, census §10 item 16⟩ RETURNS A REAL RECEIPT, not `R<()>` — same reason as the delete
2900
+ /// above, and sharper here: a rollback WRITES A NEW VERSION, so the caller's next question is
2901
+ /// always "which version am I on now". `r.version` was already read for the audit line.
2902
+ rollbackMemory: (path: text, toVersion: nat) -> (Result);
2903
+ /// SELF-SERVICE, and that is the surprising part: the AGENT calls this, not the owner. An
2904
+ /// agent rotates its own CallerKey binding without needing the owner to act.
2905
+ ///
2906
+ /// ⚠ IT ROTATES THE KEY, NOT THE PRINCIPAL. Only `publicKeyHash` changes; the agent record
2907
+ /// stays keyed on the same principal, keeps its permissions, and keeps its place against the
2908
+ /// one-shot registration rule. So this is the answer to "my key is compromised", and it is NOT a
2909
+ /// way to move an agent seat to a different wallet — that is a new invite, and only if the
2910
+ /// old principal was never revoked.
2911
+ /// ⟨CORRECTED 2026-09-29, the owner's ruling of that date (follow-up 10, option A)⟩ Two sentences
2912
+ /// above are no longer true. An agent's record CAN now move to a different key, in one step and
2913
+ /// with no new invite: `nominateAgentKey` from the current key, then `acceptAgentKeyNomination`
2914
+ /// from the new one. And a compromised key is answered by that move — this method only changes a
2915
+ /// recorded hash, and the compromised principal keeps calling as the agent. This method still
2916
+ /// does what it says; the move is the one that changes which key the engram answers.
2917
+ ///
2918
+ /// `#Conflict` rather than `#Refused` for a `#Revoked` or `#Resigned` record: the caller IS the
2919
+ /// agent on that record, so its standing is not the problem — the record's state is. An
2920
+ /// agent reading `#Refused` would go looking for a permission that was never missing.
2921
+ rotateAgentKeyHash: (newPublicKeyHash: text) -> (Result_1);
2922
+ /// One-time onboarding seed: writes a starter file so the first read-back is non-empty.
2923
+ ///
2924
+ /// Owner-only, and it REFUSES once any memory exists — so it can only ever add the initial
2925
+ /// placeholder and can never overwrite real content. Deliberately NOT counted as a write (no
2926
+ /// `totalMemoryWrites`, no `firstWriteAt`): it is a system seed rather than activity, and
2927
+ /// counting it would corrupt both the write metrics and time-to-first-write for every engram.
2928
+ seedMemory: (path: text, content: blob) -> (Result);
2929
+ /// Append to a memory file against a PAID x402 session voucher — the write half of the paid rail,
2930
+ /// authorized by the voucher rather than by a role.
2931
+ /// ⟨2026-09-30, ic402 2.17.0⟩ Authorized by the payer's own call with the voucher: only the
2932
+ /// principal that opened the session, calling as that key, can spend it (see `sessionReadMemory`).
2933
+ /// ⟨CORRECTED 2026-09-30, the owner's answer "2 fresh key"⟩ Authorized by the session KEY's own
2934
+ /// call: the fresh key the payer named at open. The write is attributed to the payer, who paid;
2935
+ /// the record row also names the key that made it.
2936
+ ///
2937
+ /// ⚠ AS WITH `sessionReadMemory`, EVERY REFUSAL FIRES BEFORE THE VOUCHER IS CONSUMED — nothing is
2938
+ /// written and no funds move. It refuses when payments are frozen, when writes are PAUSED, and
2939
+ /// when the engram is client-side encrypted: a paid session appending plaintext to an encrypted
2940
+ /// store would CORRUPT it, so that refusal is protecting the owner's data rather than gating
2941
+ /// access. Append through the EngramX client/SDK instead.
2942
+ /// ⟨API-101/DEC-101, 2026-08-24 — v3 phase X3⟩ RETURNS THE SAME RECEIPT THE FREE WRITER DOES.
2943
+ /// It answered `Wire.Result<Nat>`: the version, and nothing else. The free `writeMemory` returns
2944
+ /// `{ version; seq; head }`, so a PAYING writer got strictly LESS evidence than a caller who paid
2945
+ /// nothing — on the one rail a stranger can buy, where the receipt is the whole product. The head
2946
+ /// is what makes an append verifiable by someone who was not there; a bare version number is not
2947
+ /// checkable against anything.
2948
+ ///
2949
+ /// It cost nothing to fix: `r` was already in scope and already being read — the old body did
2950
+ /// `let v = r.version` and passed `r` to `receiptSuffix(r)` for the audit line, then discarded
2951
+ /// everything but the version on the way out. Free until the cut, breaking forever after, which
2952
+ /// is why it lands now rather than after the test migration (ruling R-A2).
2953
+ sessionAppendMemory: (voucher: Voucher, path: text, content: blob) ->
2954
+ (Result);
2955
+ /// The whole engram in one call — newest-first, byte-capped — for an AI host to inject at session
2956
+ /// start. The "read me back" that continuity is built on. `clientLabel` names the reading host
2957
+ /// (e.g. "openclaw", "claude.ai") for the distinct-reader set.
2958
+ ///
2959
+ /// ⚠ AN UPDATE, NOT A QUERY, AND ON PURPOSE (L7). `readMemory`/`readMemoryBatch` are queries and
2960
+ /// therefore CANNOT persist a read count; this runs on the update path and bumps the reuse
2961
+ /// counters, so a builder whose SDK calls `startSession()` registers a weekly-reused engram as a
2962
+ /// byproduct of normal use rather than through an elective, separate read. Callers should expect
2963
+ /// update latency and consensus cost here, and get a recorded read in exchange.
2964
+ ///
2965
+ /// ⚠ IT DOES NOT RETURN EVERYTHING, and the omission is deliberate: `config/` and `secrets/` are
2966
+ /// SKIPPED. Those are operational, not agent memory, and must never be blindly spilled into a
2967
+ /// model's context window — so a caller must not treat this as a complete listing of the engram.
2968
+ ///
2969
+ /// ⚠ RAW STORED BYTES, like `readMemoryBatch`. On a Private (encrypted) engram the content is
2970
+ /// CIPHERTEXT; the key-holding SDK decrypts it. A caller without the key gets bytes it cannot
2971
+ /// read, not an error.
2972
+ sessionReadContext: (clientLabel: text) -> (Result_12);
2973
+ /// Read one memory file against a PAID x402 session voucher. No role, no invite — the voucher is
2974
+ /// the authorization, which is what makes this the engram's one rail a stranger can pay to use.
2975
+ ///
2976
+ /// ⟨2026-09-30, ic402 2.17.0⟩ WHO MAY SPEND A SESSION: only its payer, calling as the same raw
2977
+ /// Ed25519 key that opened it. The voucher still carries the amount and the sequence; the
2978
+ /// authorization is now the call itself, and the canister no longer checks a voucher signature.
2979
+ /// A stranger can still pay to use this rail — by opening a session of their own.
2980
+ /// ⟨CORRECTED 2026-09-30, the owner's answer "2 fresh key"⟩ Not the payer: the session's own key
2981
+ /// — the fresh Ed25519 key the payer named at open — calling as itself. The payer, who opened
2982
+ /// the session and closes it, is refused here like anyone else who is not that key.
2983
+ /// ⟨CORRECTED 2026-09-30, review⟩ …unless the payer named their own identity key at open, which
2984
+ /// nothing refuses: then the payer IS that key and spends.
2985
+ ///
2986
+ /// ⚠ EVERY REFUSAL BELOW HAPPENS BEFORE THE VOUCHER IS CONSUMED, so **no funds move on any of
2987
+ /// them**. That is the property a payer most needs and the one a signature cannot express: a
2988
+ /// refusal here costs nothing, and retrying after the owner changes something costs nothing extra.
2989
+ ///
2990
+ /// It refuses when:
2991
+ /// · **Paid reads are not enabled — and they are OFF BY DEFAULT.** Selling the owner's memory to
2992
+ /// any voucher-bearer is a deliberate choice, not a default posture, so on a fresh engram this
2993
+ /// is the refusal you will get. `getEngramCard`'s `x402Support` reports this same flag, so a
2994
+ /// buyer can check before paying rather than discover it here.
2995
+ /// · **The engram is Private (client-side encrypted).** A paid session holds no key and the
2996
+ /// canister never decrypts, so it could only return ciphertext. Read through the EngramX
2997
+ /// client/SDK instead — this is not a permission problem and no setting fixes it.
2998
+ /// · **The path is `config/` or `secrets/`.** Owner-only on every other surface, and a paid
2999
+ /// session must not be the side door: without this guard a stranger holding a fraction-of-a-
3000
+ /// cent voucher could read `secrets/*`.
3001
+ /// · Payments are frozen on this engram.
3002
+ sessionReadMemory: (voucher: Voucher, path: text) -> (Result_11);
3003
+ /// Configure action alerts: an https webhook that receives new chained-audit entries matching the
3004
+ /// enabled event classes (money / approvals / guardian / agentWrites), batched, at most one
3005
+ /// POST per minute. Owner-only. Payloads carry audit METADATA (operation, caller, role, bounded
3006
+ /// details) — never memory file contents. Setting a URL (re)arms delivery FROM NOW: the cursor
3007
+ /// jumps to the current end of the log so history is not replayed. Passing url = null disables.
3008
+ /// The URL itself is never written to the audit log (webhook URLs often embed secret tokens).
3009
+ setActionAlertConfig: (cfg: AlertConfig) -> (Result_1);
3010
+ /// Owner-only: replace an existing agent's permission set. The way to NARROW what an agent
3011
+ /// may do without ending their access — `revokeAgent` is terminal for that principal, this is
3012
+ /// not.
3013
+ ///
3014
+ /// ⚠ ONE GRANT IS REFUSED OUTRIGHT: memory-read to the EngramX PLATFORM principal while this
3015
+ /// engram is Private (encrypted), because that principal could then derive the decryption key and
3016
+ /// Private mode would be a claim rather than a property. It is the mirror of the guard on
3017
+ /// `setEncryptionEnabled`, which refuses to ENABLE Private while that grant already exists —
3018
+ /// between them the invariant holds whichever order an owner works in.
3019
+ ///
3020
+ /// That refusal is `#Refused` ABOUT THE SUBJECT, not about the caller: the owner is authorized
3021
+ /// and the grantee is not eligible. It is standing rather than a conflict to resolve, so the way
3022
+ /// forward is to grant read to your own agents instead, or to switch to Connected mode first —
3023
+ /// both of which the message says.
3024
+ setAgentPermissions: (agentPrincipal: principal, permissions:
3025
+ AgentPermissions) -> (Result_1);
3026
+ /// Paths an agent holding a grant may REVISE rather than only append to.
3027
+ ///
3028
+ /// Matching is EXACT, except that an entry ending in "/" grants everything beneath it. A grant
3029
+ /// is a power, so the default reading is the narrow one; the directory form exists because the
3030
+ /// real use case is rotating dated files under a prefix, and per-file grants would be unusable
3031
+ /// for that. What a bare prefix would do — "MEMORY.md" also matching "MEMORY.md.bak" — is the
3032
+ /// kind of accident a permission surface must not have.
3033
+ setAgentRevisablePaths: (paths: vec text) -> (Result_1);
3034
+ /// ⟨0.16.0 — census row⟩ Returns the APPLIED config, which is how this closes the
3035
+ /// setter-without-a-getter gap without adding a method: the write is the read.
3036
+ ///
3037
+ /// `prunedBackups` is the part that matters. Enforcing a retention cap DELETES finalized backups
3038
+ /// beyond it, and this method used to answer `#Ok(())` over that — the module's own comment
3039
+ /// called it an "ops floor: retention enforcement deletes owner backup data silently", mitigated
3040
+ /// by a `Debug.print` nobody reads. An owner lowering the cap is destroying their own backups;
3041
+ /// they are told how many, in the reply and in the audit line.
3042
+ setBackupRetention: (dbType: text, maxBackups: nat) -> (Result_10);
3043
+ /// Override the caps for ONE caller — an agent this owner trusts more, or less, than the rest.
3044
+ setCallerSpendPolicy: (caller: principal, policy: SpendingPolicy) ->
3045
+ (Result_1);
3046
+ /// Owner-only: set how many engrams `agentPrincipal`'s agent may open of its own through this
3047
+ /// engram. Kept by the agent's lasting key, so it carries over a key replacement. Any count is
3048
+ /// accepted; the registry lets a lasting key open one engram through a parent, ever, so 1 or more
3049
+ /// behaves as 1. 0 withdraws it.
3050
+ ///
3051
+ /// APPROVING IS GIVING: what this engram attaches to open an engram (1.3 T cycles) is the agent's
3052
+ /// from the moment that engram exists — the agent's lasting key owns it, and nothing is ever taken
3053
+ /// back. The change is written to the record under `agent.childAllowance.set`, which is in the
3054
+ /// alerts' money class (`ActionAlerts.classify`), so an owner or guardian with an alert channel
3055
+ /// hears of it.
3056
+ setChildAllowance: (agentPrincipal: principal, allowed: nat) -> (Result_1);
3057
+ /// ⟨0.16.0⟩ `getEncryptedMemoryKeyForEpoch` DELETED — folded into `getEncryptedMemoryKey`'s epoch
3058
+ /// argument — see that method for the fold and the `null`-means-CURRENT rule.
3059
+ /// Owner-only: turn client-side memory encryption (Private mode) on or off for FUTURE writes.
3060
+ ///
3061
+ /// ⚠ IT DOES NOT TOUCH EXISTING FILES, and this is the fact that decides whether calling it alone
3062
+ /// is safe. Verified at the site 2026-08-21: the body sets the flag and writes an audit line —
3063
+ /// there is no migration pass. So
3064
+ /// · turning it ON leaves everything already stored as PLAINTEXT at rest, and
3065
+ /// · turning it OFF leaves everything already stored as CIPHERTEXT, which the hosted connector
3066
+ /// cannot read.
3067
+ /// Either direction leaves a MIXED store that the flag alone misdescribes. The owner console
3068
+ /// pairs this call with a migration (`useMemoryEncryption`'s `migrate` / `migrateToPlaintext`)
3069
+ /// for exactly that reason; a caller reaching this method directly gets the flag and nothing else.
3070
+ ///
3071
+ /// ⚠ ENABLING IS REFUSED while the EngramX platform principal holds memory-read access as an
3072
+ /// agent — it could derive the decryption key, which would make Private mode a claim rather
3073
+ /// than a property. Revoke that agent's read access first. The refusal says so, and it is a
3074
+ /// `#Conflict` rather than a `#Refused` because the caller IS the owner and the problem is the
3075
+ /// engram's state, not their standing.
3076
+ ///
3077
+ /// Private mode also closes the paid read rail by construction: a paid session holds no key, so
3078
+ /// `sessionReadMemory` refuses on an encrypted engram whatever `setPaidMemoryReadEnabled` says.
3079
+ setEncryptionEnabled: (enabled: bool) -> (Result_1);
3080
+ /// Register the owner's EVM address for withdrawals.
3081
+ ///
3082
+ /// First set: immediate — no guardian needed.
3083
+ /// Change: only allowed if an active guardian exists (proving the owner
3084
+ /// has a safety net). Without a guardian, the address is locked after
3085
+ /// first set — this prevents a compromised II from silently redirecting
3086
+ /// EVM withdrawals when no one is watching.
3087
+ ///
3088
+ /// The guardian doesn't approve the change — the owner is sovereign.
3089
+ /// The guardian's existence is the gate, not their active consent.
3090
+ setEvmOwnerAddress: (evmAddress: text) -> (Result_1);
3091
+ /// A guardian arms their OWN webhook over the guarded engram's audit stream (same event
3092
+ /// classes and payload as the owner channel — audit metadata, never memory file contents;
3093
+ /// the guardian honesty rail: integrity, not content). Active guardians only. Arming starts
3094
+ /// from NOW (no history replay); url = null disarms and frees the slot.
3095
+ setGuardianAlertConfig: (cfg: AlertConfig) -> (Result_1);
3096
+ /// Per-prefix retention policy. Longest prefix wins; anything unmatched keeps everything.
3097
+ ///
3098
+ /// A CHANGE does not take effect immediately, in EITHER direction: every prefix whose EFFECTIVE
3099
+ /// class moves is guarded for the same window as the agent floor, so history cannot be
3100
+ /// thinned in the same sweep as the decision to thin it. Loosening waits too — distinguishing
3101
+ /// "tightened" from "loosened" precisely would mean comparing two cap lattices, so armGuards
3102
+ /// (RetentionPolicy.mo) guards any change it sees. That is the conservative reading, and its
3103
+ /// only cost is a delay on keeping MORE, which destroys nothing.
3104
+ setMemoryRetentionPolicy: (entries:
3105
+ vec record {
3106
+ text;
3107
+ RetentionClassSpec;
3108
+ }) -> (Result_1);
3109
+ /// Owner-only: open or close the monetized read rail. When disabled (the default), sessionReadMemory
3110
+ /// fails closed for every voucher-bearer — selling the owner's memory is opt-in, not the default.
3111
+ ///
3112
+ /// ⚠ ENABLING IT IS NOT SUFFICIENT. On a Private (encrypted) engram `sessionReadMemory` still
3113
+ /// refuses, because a paid session holds no key and the canister never decrypts — see
3114
+ /// `setEncryptionEnabled`. An owner who turns this on and still sees refusals should check that
3115
+ /// flag before looking anywhere else.
3116
+ ///
3117
+ /// `getEngramCard`'s `x402Support` reports THIS flag, so a buyer can read the rail's state before
3118
+ /// paying rather than discovering it in a refusal.
3119
+ setPaidMemoryReadEnabled: (enabled: bool) -> (Result_1);
3120
+ /// Open or close the paid-memory WRITE rail (RB4). Owner-only, like its read twin.
3121
+ ///
3122
+ /// Kept as a SEPARATE flag rather than widening `setPaidMemoryReadEnabled` into one switch: an
3123
+ /// owner selling read access has not thereby agreed to sell write access, and a single flag makes
3124
+ /// the larger permission ride in on the smaller one. Two flags is the shape that lets an owner
3125
+ /// say yes to one and no to the other, which is the common case.
3126
+ setPaidMemoryWriteEnabled: (enabled: bool) -> (Result_1);
3127
+ /// ⟨0.16.0, L1⟩ Every validation refusal here is `#Invalid` and the cap is `#Conflict`, and the
3128
+ /// split is worth stating once for the whole whole-value-config family below: a malformed path is
3129
+ /// the CALLER'S to fix and retrying with a corrected list works, while "max 20" is a property of
3130
+ /// this engram that no argument gets around. A caller that cannot tell those apart either edits
3131
+ /// forever or gives up on a request that was one character from valid.
3132
+ setProtectedPaths: (paths: vec text) -> (Result_1);
3133
+ /// OWNER-ONLY: switch the metrics reader's read of the seat weeks off (`false`) or back on (`true`).
3134
+ /// On from birth, by the owner's ruling of 2026-10-02. Off, `sysGetAgentSeatWeeks` refuses the
3135
+ /// reader by name, naming this switch; the rows are still kept and the owner still reads them. Each
3136
+ /// call is a chained record entry (`metrics.setSeatWeeksReadEnabled`, the new and the old value), so
3137
+ /// when the read was on or off is part of the record. Anyone else is refused by name.
3138
+ setSeatWeeksReadEnabled: (enabled: bool) -> (Result_1);
3139
+ /// Set the most this engram may spend per day ACROSS EVERY CALLER — five agents, the owner and the
3140
+ /// marketplace together. `null` removes the ceiling. ⟨2026-09-28⟩
3141
+ /// ⟨CORRECTED 2026-09-29, review finding X-8: "spend" was too wide. The ceiling covers the x402
3142
+ /// payments this engram signs (`signX402Payment` and the marketplace's `sysSignX402Payment`) and
3143
+ /// nothing else: it is the most this engram may sign in x402 payments per day. Two other signing
3144
+ /// paths are NOT counted against it — typed-data signatures (`signEip712` / `sysSignEip712`),
3145
+ /// bounded instead by each registered type's daily count and destination list; and agent-
3146
+ /// registration transactions (`signEngramRegistration`), bounded by three per caller per day and
3147
+ /// the 500-gwei gas ceiling. Whether the ceiling should cover them is a separate owner question.⟩
3148
+ ///
3149
+ /// WHY: the owner decided on 2026-09-28 to add one daily spending ceiling across all callers on an
3150
+ /// engram and keep the limit of five agents. Every other spending limit is per caller, so without
3151
+ /// this the engram's daily exposure was the SUM of up to seven allowances ($35.00 at the defaults)
3152
+ /// and nobody could see it. The amount is in the same minor units the per-caller caps compare
3153
+ /// (USDC's six decimals: 5_000_000 = $5.00), and like them it does not distinguish tokens.
3154
+ ///
3155
+ /// A payment that would take today's engram-wide total past this number is not signed: the caller
3156
+ /// gives a reason and it becomes a row in the approval queue, exactly as over a per-caller cap.
3157
+ /// `0` is allowed and means every payment needs the owner's approval.
3158
+ /// ⟨CORRECTED 2026-09-29, review finding X-2: not every payment — a payment of amount 0 crosses no
3159
+ /// limit, so it is signed under a ceiling of 0 too. Whether amount-0 payments should be refused is
3160
+ /// an open owner question.⟩ ⟨CORRECTED 2026-09-29, later — the owner ruled (follow-up 15, option A):
3161
+ /// a payment of amount 0 is now refused by name before any limit is read (`X402Input.refusal`), so
3162
+ /// under a ceiling of 0 every payment that can be signed at all does need the owner's approval.
3163
+ /// And a new engram no longer starts with no ceiling: it starts at `SpendCeiling.DEFAULT_ENGRAM_DAY`
3164
+ /// ($10.00 a day; follow-up 9, option A).⟩ Takes effect on the next
3165
+ /// payment, counted against what was ALREADY spent today (the total is kept whether or not a
3166
+ /// ceiling is set), so setting a ceiling at noon does not grant a fresh day.
3167
+ ///
3168
+ /// Unlike `setSpendPolicy`, this does NOT auto-approve pending requests it now covers — a request
3169
+ /// held by the ceiling waits for `approveRequest`, or the caller retries against the new ceiling.
3170
+ /// The auto-approval exists for per-caller raises and is kept away from the ceiling on purpose
3171
+ /// (see `resolvePendingOnPolicyRaise`).
3172
+ ///
3173
+ /// The record entry `spend.setCeiling` carries the new AND the previous value, so a widening is
3174
+ /// distinguishable from a tightening afterwards — the reason `spend.setPolicy` records its limits.
3175
+ /// Not blocked by a payments freeze, like `setSpendPolicy`: setting a limit moves no money.
3176
+ setSpendCeiling: (maxPerDay: opt nat) -> (Result_1);
3177
+ /// Set this engram's spending caps.
3178
+ ///
3179
+ /// The audit row records the LIMITS, not merely that a change happened, so a WIDENING is
3180
+ /// distinguishable from a tightening afterwards. A quiet policy raise is the shape most custody
3181
+ /// incidents take, and "policy changed" alone cannot tell an investigator which way it moved.
3182
+ ///
3183
+ /// Raising a cap RESOLVES pending approval requests it now covers — that is the second half of
3184
+ /// the escalation an agent starts when it hits a limit, and without it the queue is a place
3185
+ /// requests go to expire.
3186
+ setSpendPolicy: (policy: SpendingPolicy) -> (Result_1);
3187
+ /// Owner-only: set YOUR OWN safety rails on the funding path — a per-top-up ckUSDC ceiling and a
3188
+ /// minimum gap between top-ups. Read them back with `getTopUpLimits`.
3189
+ ///
3190
+ /// ⚠ THESE ARE THE OWNER'S LIMITS, NOT THE PLATFORM'S. "Limits" reads like something imposed;
3191
+ /// nothing outside this engram sets them, and an owner may raise them at will. They exist so a
3192
+ /// compromised agent key or a runaway agent cannot drain a wallet in one call or in a fast loop,
3193
+ /// which is a bound on YOUR OWN blast radius rather than on what you are permitted to spend.
3194
+ ///
3195
+ /// The cooldown defaults to five minutes. `cooldownSeconds = 0` removes the gap entirely, which
3196
+ /// is a real choice and not an error.
3197
+ /// ⟨API-105, 2026-08-24 — v3 phase X3⟩ ONE RECORD, NOT TWO ADJACENT NATS. This took
3198
+ /// `(newMaxCkusdcE6s : Nat, cooldownSeconds : Nat)` — the documented swap class, and a live one:
3199
+ /// the suite calls `setTopUpLimits(10_000_000n, 300n)`, and a caller who transposes those gets a
3200
+ /// 300-e6 cap with a 116-DAY cooldown. Candid matches positionally and both are `nat`, so the
3201
+ /// wire accepts it, the types accept it, and the engram is quietly unable to top itself up.
3202
+ ///
3203
+ /// The field names are NOT a new vocabulary — they are `getTopUpLimits`'s, which already answers
3204
+ /// `{ cooldownSeconds; maxCkusdcE6s }`. Setter and getter now share one shape, so read-modify-
3205
+ /// write is expressible and neither side can drift from the other without the compiler saying so.
3206
+ setTopUpLimits: (limits:
3207
+ record {
3208
+ cooldownSeconds: nat;
3209
+ maxCkusdcE6s: nat;
3210
+ }) -> (Result_1);
3211
+ /// Owner sets the World ID app_id + action + rp_id from their World Developer Portal (rp_id appears
3212
+ /// after "Enable World ID 4.0" on a production app). The action MUST be configured there with
3213
+ /// max_verifications = Unlimited (idempotent /verify → survives IC consensus). After setting this,
3214
+ /// register getWorldIdRpSignerAddress() as the rp_id's signer address in the Portal.
3215
+ setWorldIdConfig: (appId: text, action: text, rpId: text) -> (Result_1);
3216
+ /// Submit a verified-human proof: the IDKitResult from the frontend's World ID flow, forwarded
3217
+ /// VERBATIM (compact JSON, as JSON.stringify emits) to World's /api/v4/verify/{rp_id} over an HTTPS
3218
+ /// outcall (never trust the client). The v4 endpoint verifies both 4.0 proofs and legacy 3.0 proofs,
3219
+ /// so the OrbLegacy migration path and the 4.0 endgame ride the same method. Owner-only. `nullifier`
3220
+ /// is the claimed unique-human pseudonym; it must appear verbatim inside resultJson, binding what we
3221
+ /// store to the payload the verifier actually accepted. Only proof_of_human credentials are accepted
3222
+ /// (selfie is farmable, passport/mnc are document identity — a different tier). Sets worldIdVerified
3223
+ /// and stores the result ONLY on a real verification success — never self-asserted.
3224
+ setWorldIdProof: (resultJson: text, nullifier: text) -> (Result_1);
3225
+ /// Sign a registered type. ONE call, owner-only, freeze-gated — the delay lives on registration
3226
+ /// (D2), never here: an emergency withdrawal must not need two owner round-trips.
3227
+ ///
3228
+ /// ⟨0.16.0⟩ PERSISTS beside the market's `sysSignEip712` (census row 177, ruled). The owner does
3229
+ /// not lose the ability to sign because a marketplace gained it, and the two share every control
3230
+ /// below the caller gate.
3231
+ signEip712: (typeId: blob, values: vec record {
3232
+ text;
3233
+ Eip712Value;
3234
+ }) -> (Result_4);
3235
+ /// Sign the engram's own ERC-8004 registration tx for client-side broadcast: the engram signs, the client
3236
+ /// broadcasts and keeps the resulting registration id.
3237
+ ///
3238
+ /// Agent-accessible. The comment here claimed "saves ~55-85M cycles vs registerAgent" until
3239
+ /// 2026-08-17; `registerAgent` — the engram method that broadcast the registration itself — left
3240
+ /// the candid surface in commit d25b53b, so the comparison had outlived its other term. The
3241
+ /// saving was real and is now simply the shape of this method: no RPC outcall, no receipt poll.
3242
+ /// ⟨API-105, 2026-08-24 — v3 phase X3⟩ ONE REQUEST RECORD, not three adjacent nats.
3243
+ ///
3244
+ /// ⟨Ruling R-B3⟩ This method KEEPS. Census row 309 dispositions it `die` ("test-only; folds into
3245
+ /// signEip712 + registered type") and row 311 records the owner's answer: RULED 2026-08-20 —
3246
+ /// KEEP, ERC-8004 stays. Both halves of the `die` rationale fail at the site: it is not
3247
+ /// test-only (a downstream web client's billing page calls it), and it is not a shipped customer
3248
+ /// capability either — that panel sits behind `evmBilling: { mode: 'off' }` in both the flag
3249
+ /// defaults and the shipped config, and its own dialog says the signature is dormant and not
3250
+ /// broadcastable. It is a real surface in a disabled state, which is neither thing the census
3251
+ /// row claimed.
3252
+ ///
3253
+ /// THE SWAP CLASS, and here it is three-deep: `nonce`, `maxFeePerGas` and `maxPriorityFeePerGas`
3254
+ /// were adjacent and all `Nat`. Transposing the two gas fields type-checks, decodes, and signs a
3255
+ /// transaction whose priority fee exceeds its max fee — invalid on chain, and the 500-gwei
3256
+ /// ceiling below cannot see it because both values are individually under the cap. Named fields
3257
+ /// remove the class.
3258
+ ///
3259
+ /// ⟨2026-09-28⟩ LIMITED PER CALLER PER DAY. The owner decided to cap this signature rather than
3260
+ /// delete it: each caller (the owner, or an active agent) may obtain
3261
+ /// `EngramRegCap.MAX_PER_CALLER_PER_DAY` registration signatures per UTC day; the next is refused
3262
+ /// with `#Conflict` naming this method. A signature that fails does not count. The `nonce` must
3263
+ /// be the REGISTRATION address's nonce — `getEvmAddress().engramRegistration` — below 2^64 − 1.
3264
+ signEngramRegistration: (req:
3265
+ record {
3266
+ maxFeePerGas: nat;
3267
+ maxPriorityFeePerGas: nat;
3268
+ nonce: nat;
3269
+ }) -> (Result_9);
3270
+ /// Sign an EIP-3009 authorization for x402 payment. Agent-accessible.
3271
+ /// Client probes the URL, extracts chain/token/recipient/amount from the 402, and calls this.
3272
+ /// Canister signs → returns header → client retries with header.
3273
+ ///
3274
+ /// ⟨0.16.0⟩ PERSISTS beside the market's `sysSignX402Payment` (census row 177, ruled). An agent
3275
+ /// with an x402 rail does not lose it because a marketplace gained one, and both run the same body.
3276
+ /// ⟨API-105, 2026-08-24 — v3 phase X3⟩ ONE REQUEST RECORD, not eight positional arguments.
3277
+ ///
3278
+ /// THE HAZARD IS FUNDS MISDIRECTION, and it is the sharpest case in this batch. `tokenAddress`
3279
+ /// and `recipient` were ADJACENT and both `Text`. Candid matches positionally, so transposing
3280
+ /// them type-checks, decodes, and signs a payment whose payee is the TOKEN CONTRACT rather than
3281
+ /// the intended one — an irreversible outbound signature spending the engram's own key, with
3282
+ /// nothing between the mistake and the money. `tokenName`/`tokenVersion` are a second adjacent
3283
+ /// Text pair; that one only corrupts the EIP-712 domain, so it fails loudly instead.
3284
+ ///
3285
+ /// Named fields remove the class outright: a transposition is now a decode error at the edge.
3286
+ ///
3287
+ /// The body is UNCHANGED. Each parameter was referenced exactly once below, so the record is
3288
+ /// destructured on entry rather than threaded through — the diff is the signature and this line,
3289
+ /// which is what makes a reshape of a money path reviewable.
3290
+ signX402Payment: (req:
3291
+ record {
3292
+ amount: nat;
3293
+ approvalId: opt text;
3294
+ chainId: nat;
3295
+ rationale: opt text;
3296
+ recipient: text;
3297
+ tokenAddress: text;
3298
+ tokenName: text;
3299
+ tokenVersion: text;
3300
+ }) -> (Result_3);
3301
+ /// Self-heal: if the registry's spawn-time push was missed, the owner pulls the number from
3302
+ /// the spawning registry. Idempotent; fails closed if the registry has no record.
3303
+ syncEngramNumber: () -> (Result_8);
3304
+ /// Genesis seed — the atomic "spawn only when there's real content" primitive. The spawning
3305
+ /// REGISTRY pushes the owner's first real memory immediately after install, so a canister is
3306
+ /// never spawned holding nothing (see the registry's `provisionEngram` optional genesis).
3307
+ ///
3308
+ /// Gated `spawnedBy`-only and ONE-SHOT: it refuses once any file exists, so it can seed and never
3309
+ /// overwrite. Unlike the owner-only `seedMemory` placeholder this is REAL owner content, so it
3310
+ /// IS counted as an owner write (`firstWriteAt`, `totalMemoryWrites`) and is authored by the
3311
+ /// OWNER rather than by the registry principal that called it. It never touches the
3312
+ /// agent metrics.
3313
+ ///
3314
+ /// ⚠ THE PAYLOAD IS `Nat` AND MUST STAY `Nat`, which is a constraint on any client of this
3315
+ /// method rather than an internal detail. Its only caller is the registry, over an
3316
+ /// inter-canister boundary, through a hand-declared actor type — and the registry is ROUTINELY a
3317
+ /// different version from the engram template it is seeding: it upgrades before the fleet does,
3318
+ /// and during a transition it can serve a template older than itself. A return-type change here
3319
+ /// breaks genesis provisioning across that skew. That is measured, not predicted: the registry
3320
+ /// trapped with "IDL error: unexpected IDL type when parsing Nat" while seeding an engram built
3321
+ /// at the staging tag. A receipt would buy nothing anyway — the caller does `ignore await`.
3322
+ ///
3323
+ /// ⚠ ORDERING: upload the engram TEMPLATE first, then upgrade the registry. The reverse leaves a
3324
+ /// window where a spawn seeds an old-shape engram with a new-shape expectation; the reply fails
3325
+ /// to decode and `provisionEngram` dies on a trap its own `try/catch` cannot reach (ADR-0001),
3326
+ /// failing the WHOLE SPAWN rather than just the seed.
3327
+ sysApplyGenesisSeed: (path: text, content: blob) -> (Result_8);
3328
+ /// OWNER force-close, for a session the payer abandoned.
3329
+ ///
3330
+ /// ⟨R1b, ruled 2026-08-26: owner-gated, NO guardian.⟩ It moves someone else's money, and only ever
3331
+ /// BACK TO THEM — the destination is the session's own payer and is not caller-influenced, so the
3332
+ /// guardian ceremony that guards destination changes buys nothing here and would strand payers on
3333
+ /// engrams that have no guardian.
3334
+ ///
3335
+ /// Calls the gateway's NO-AUTH variant deliberately. `gate.closeSession` is payer-only, which is
3336
+ /// why the market's admin-gated `forceCloseSession` could never work: it authorised an admin and
3337
+ /// then delegated to a method that rejects everyone but the payer.
3338
+ ///
3339
+ /// ⟨NOTE 2026-10-02 — NOT RENAMED, deliberately, and this is the one `sys` method that still has a
3340
+ /// human caller.⟩ The sys-tier retier (ruled 2026-08-24, executed 2026-10-02 on the registry and
3341
+ /// the market) named FOUR admin-called methods; this one became owner-gated two days after that
3342
+ /// list was made, so the ruling never named it. Its caller is the OWNER, a person, which is the
3343
+ /// exact thing the retier removes from the `sys` tier — so "a `sys` method has no human caller"
3344
+ /// stays false here until it is renamed too. Left for the owner: the rename is free until the cut
3345
+ /// and a break after it, and it also moves `packages/client/README.md` and
3346
+ /// `docs/AGENT-ONBOARDING.md`, which name it.
3347
+ sysForceCloseSession: (sessionId: text) -> (Result_1);
3348
+ /// The metrics reader the registry designated reads every engagement's seat weeks, in one call.
3349
+ ///
3350
+ /// CALLER: the metrics reader the spawning registry named on this engram (`metricsReaderId`), and
3351
+ /// nobody else — not the owner (who reads `getAgentSeatWeeks`), an agent, a guardian, the registry,
3352
+ /// the biller or the market (ADR-0020: a `sys` method names its caller). Answered only while the
3353
+ /// owner leaves it on (`seatWeeksReadEnabled`, on from birth by the owner's ruling of 2026-10-02).
3354
+ ///
3355
+ /// WHAT LEAVES: per engagement, the pairing day, the first and last payment-signature days, the
3356
+ /// 64-week map and the count — NO root, NO invite, NO amount, recipient or nonce. The reader matches
3357
+ /// rows to the registry's engagements by day, so no agent's identity leaves this engram here.
3358
+ ///
3359
+ /// ⟨L7⟩ UPDATE, NOT QUERY, so a refusal leaves its `sysGetAgentSeatWeeks.denied` row naming the
3360
+ /// caller's class, and an answered read leaves a `sysGetAgentSeatWeeks` row in the read log — the
3361
+ /// owner can see every time the reader read. A refusal is `#Refused` naming this method and the
3362
+ /// class (`SeatWeeks.readRefusal`), never an empty list: an empty list is a real answer ("no agent
3363
+ /// has paired here"), and a fabricated one would be counted as one.
3364
+ sysGetAgentSeatWeeks: () -> (Result_7);
3365
+ /// The metrics reader the registry designated reads this engram's paid-rail health, in one call:
3366
+ /// the balance and the line it must stay above, the memory that line is computed from, the sessions
3367
+ /// by state, the escrow open sessions hold, the vouchers spent and the refused ones
3368
+ /// (`RailHealth.RailHealth` says what each is, and what is never in it).
3369
+ ///
3370
+ /// CALLER: the metrics reader the spawning registry named on this engram (`metricsReaderId`), and
3371
+ /// nobody else — the owner reads its balance through `getOwnerConsole`. Answered only while the
3372
+ /// owner leaves that reader's read on (`seatWeeksReadEnabled`): the SAME gate and the SAME switch
3373
+ /// as `sysGetAgentSeatWeeks`, because it is the same reader and the owner has one switch over it.
3374
+ ///
3375
+ /// A QUERY, AND A DEPARTURE FROM THE SYS METHODS BESIDE IT, ON PURPOSE ⟨2026-10-02⟩. It is read
3376
+ /// every hour, and as an update it would leave a row in this engram's read log every hour — about
3377
+ /// 720 a month in a record whose rows are what its owner reads. What a query cannot do is write the
3378
+ /// denial row of a refusal (a query's writes are discarded) — the trade `getOwnerConsole` makes. A
3379
+ /// refusal is still `#Err(#Refused …)` naming this method and the caller's class
3380
+ /// (`RailHealth.readRefusal`), never a zero balance: a made-up zero would read as an engram about
3381
+ /// to freeze. Called by another canister, a query runs replicated and this engram pays for it —
3382
+ /// a few million cycles an hour, the cost of being watched.
3383
+ sysGetRailHealth: () -> (Result_6) query;
3384
+ /// The cashier reads how many signatures this engram has produced, per caller and in total.
3385
+ /// ⟨2026-09-28⟩ The owner's decision of that date: a running counter on the engram, exported to
3386
+ /// the cashier — and to nobody else (`AccessControl.requireCashierForSignatureCounts`).
3387
+ ///
3388
+ /// ONE NEW METHOD ON THE ENGRAM, not a field on the registry's `sysMeterEngram` reply. The study
3389
+ /// ruled out widening that reply to protect deployed clients, and that reason is gone (nothing is
3390
+ /// launched; the next release reinstalls every canister). The deciding fact is a different one,
3391
+ /// read at the site: `sysMeterEngram` is the REGISTRY's method and answers from the management
3392
+ /// canister's `canister_status` — the registry holds no signature count. Carrying the count in its
3393
+ /// reply would add a registry-to-engram call inside the meter and a second trust hop between the
3394
+ /// counter and the biller. The cashier is already the engram's recognised biller (`cashierId`) and
3395
+ /// already calls it directly (`sysRecordFeeEvent`), so it reads the count at the source.
3396
+ ///
3397
+ /// UPDATE, NOT QUERY, so that a refusal leaves its `sysGetSignatureCounts.denied` row: a refused
3398
+ /// query cannot write one (see `Gated.mo`'s header). A refusal is `#Refused` naming this method,
3399
+ /// never an empty list or a zero — a zero is a real answer, and a fabricated one would be billed.
3400
+ ///
3401
+ /// ⟨2026-09-29⟩ Each row names the key, the agent it was enrolled under (none for the owner and
3402
+ /// the marketplace) and the role, so one agent's keys can be added up. And a limit for whoever
3403
+ /// bills from this: the number comes from a canister its owner controls and can reinstall, so bill
3404
+ /// only engrams the registry confirms run published code, and read a lower count than last time as
3405
+ /// a reinstall — the reasoning is at `signaturesByCaller`.
3406
+ sysGetSignatureCounts: () -> (Result_5);
3407
+ /// Rent lifecycle notice — how the OWNER (not the agent) learns their subscription lapsed. D6
3408
+ /// shipped the handoff but every alarm it added talks to the operating service's watchdog; the
3409
+ /// doc named the debt plainly: "an owner learns of their own lapse only by looking." This closes
3410
+ /// it through the channel the owner already has: the entry lands in the audit log under `rent.*`,
3411
+ /// ActionAlerts classifies that as #money (default ON), and the drain delivers it to the owner's
3412
+ /// webhook. No new channel, no new config — an owner who armed alerts gets told.
3413
+ ///
3414
+ /// Gated exactly like sysRecordFeeEvent (spawnedBy or cashierId): the sweep is the only thing that
3415
+ /// OBSERVES the transition, so it is the only thing that can report it. The state whitelist is
3416
+ /// closed — this is a lifecycle feed, not a free-text push surface into the owner's audit chain.
3417
+ sysNotifyRentLifecycle: (state: text, detail: text) -> (Result_1);
3418
+ /// ⟨0.16.0⟩ `getWalletActivation()` DELETED — folded to `about().walletActivatedAtNs`, with its
3419
+ /// custody-posture rationale carried onto the field. "Can this thing hold money" is a question a
3420
+ /// counterparty is entitled to ask before dealing with an engram, which is why it stays public.
3421
+ /// ⟨0.16.0, sys tier⟩ THE MARKET TELLING CUSTODY, which is the half of the wallet-activation
3422
+ /// story the engram cannot see for itself.
3423
+ ///
3424
+ /// B1 moved the storefront to the market, so listing an endpoint — a seller offering to be paid —
3425
+ /// happens on a canister this one does not observe. `walletActivatedAt` exists to raise a custody
3426
+ /// decision (a wallet-bearing engram is sovereign-by-default and its owner has a claimSovereignty
3427
+ /// still to complete), and a seller who has never derived an EVM address would have gone
3428
+ /// unstamped forever. That is the under-reporting direction, which is the harmful one: a missing
3429
+ /// flag retires a decision that is still pending.
3430
+ ///
3431
+ /// ONE METHOD, NOT A FAMILY (L6). The other surface the old comment named — opening a payment
3432
+ /// session — is NOT here and is not an omission: measured at the market 2026-08-20, `openSession`'s
3433
+ /// caller is a BUYER, and a buying engram signs with a key it derived through `getEvmAddress`,
3434
+ /// which stamps. It is already marked before it can pay. Adding a case for it would be surface
3435
+ /// bought for a gap that does not exist.
3436
+ ///
3437
+ /// THE SURFACE WHITELIST IS CLOSED, exactly as `sysNotifyRentLifecycle`'s state whitelist is, and
3438
+ /// for the same reason: this is a lifecycle feed, not a free-text push into the owner's audit
3439
+ /// chain. The market is trusted to REPORT, never to compose what the owner reads.
3440
+ ///
3441
+ /// IDEMPOTENT AND SILENT WHEN ALREADY STAMPED. `markWalletActivated` returns early once the var is
3442
+ /// set, so a second notice writes nothing and still answers `#Ok` — a retry learns it succeeded
3443
+ /// rather than that it failed. The stamp keeps the FIRST wallet-bearing moment, which is the one a
3444
+ /// custody audit wants; a later notice must not overwrite it with its own time.
3445
+ ///
3446
+ /// The audit line records this engram as the subject, and `surface=endpoint-listed` is what names
3447
+ /// the origin: only the configured market can pass that value, so the line cannot be produced by
3448
+ /// any other caller.
3449
+ sysNotifyWalletActivity: (surface: text) -> (Result_1);
3450
+ /// The rent RECEIPT seam (B2): the cashier tells this engram that money moved on its behalf, and
3451
+ /// the engram appends a `fee.recorded` line to its own audit chain. It computes nothing.
3452
+ ///
3453
+ /// ⚠ GATED TO TWO PRINCIPALS AND NEITHER IS THE OWNER — the SPAWNING REGISTRY or the CONFIGURED
3454
+ /// CASHIER. An owner cannot write their own fee receipts, which is the point: a receipt an owner
3455
+ /// could forge is not evidence. `getBillerId` publishes which cashier this engram accepts, so the
3456
+ /// same fact is checkable from outside.
3457
+ ///
3458
+ /// This is the whole of what B2 left on the engram. Entitlement, custody and fee math live on the
3459
+ /// cashier; here there is an append-only record and no arithmetic — so a disagreement between the
3460
+ /// two is visible rather than reconciled silently.
3461
+ /// ⟨API-105/ARCH-108, 2026-08-24 — v3 phase X3⟩ ONE FEE-EVENT RECORD, not three positional
3462
+ /// arguments. ARCH-108 records that the census prescribed this fold and it "was refused for a
3463
+ /// wire-break reason the ruling voids", with the instruction to execute it or ratify
3464
+ /// positional-forever with a dated note — "silence spends the window". Executed.
3465
+ ///
3466
+ /// The argument for a record here is EVOLUTION, not transposition: unlike `setTopUpLimits`'s two
3467
+ /// adjacent nats, these three types differ, so a swap would not type-check. What positional
3468
+ /// arguments cost is the next field. A fee event is exactly the shape that grows one — token,
3469
+ /// amount, rate — and ADR-0018's rule is that an INBOUND record gains optional fields forever
3470
+ /// while an argument LIST cannot gain anything without breaking every caller at once.
3471
+ ///
3472
+ /// ⚠ ALL FOUR CALLERS MOVE IN THE SAME CHANGE, and this is ADR-0001's fatal direction: three
3473
+ /// inline hand-IDLs in the cashier and one in the registry declare this method's argument shape
3474
+ /// structurally. Every one of them sits inside `try { … } catch (_e) {}`, which reads as
3475
+ /// containment and is not: a reply-decode mismatch calls `ic0.trap` INSIDE the caller's own
3476
+ /// callback, and Motoko's `catch` sees rejects, never traps. That is the failure that took
3477
+ /// production monitoring dark for ~33h. The ENGRAM WASM MUST SHIP BEFORE its callers, and
3478
+ /// `check-didc-compat` is suspended for the cut, so nothing mechanical will catch a wrong order.
3479
+ sysRecordFeeEvent: (fee:
3480
+ record {
3481
+ cycles: nat;
3482
+ paidThroughNs: opt int;
3483
+ scheduleVersion: opt text;
3484
+ }) -> (Result_1);
3485
+ /// Point this engram at the biller that converts its tokens → cycles. Settable, not hardcoded:
3486
+ /// coupling is by principal id, so the same wasm runs against any deployment's biller.
3487
+ ///
3488
+ /// ⟨0.16.0, B2⟩ Renamed from `setCashierId`, and NARROWED to the spawning registry (census row
3489
+ /// 266, ruled). Two changes, for two reasons:
3490
+ /// · "cashier" is a PLATFORM word and this is the OSS wire. `biller` names the role, not the
3491
+ /// product that fills it.
3492
+ /// · The write is now sys-tier. It previously accepted the OWNER as well, and that second
3493
+ /// writer is what the ruling removed — three writers for one field (owner, spawner, and the
3494
+ /// frontend acting for the owner) is a last-write-wins race over a money-routing target.
3495
+ ///
3496
+ /// ⚠ THE COST, RULED AND ACCEPTED: an engram whose registry never set this has NO owner remedy.
3497
+ /// `convertEarningsToCycles` fails closed against a null biller, and with the owner leg gone
3498
+ /// nobody but the spawning registry can fix it. That refusal therefore names the real remedy
3499
+ /// rather than telling an owner to run a call they can no longer make.
3500
+ sysSetBillerId: (id: principal) -> (Result_1);
3501
+ /// One-shot assignment of the spawn ordinal — callable ONLY by the spawning registry, ONLY
3502
+ /// while unset. Neither the owner nor any later registry can change an assigned number.
3503
+ sysSetEngramNumber: (n: nat) -> (Result_1);
3504
+ /// Point this engram at the commerce satellite allowed to ask it for signatures. ⟨0.16.0, B1⟩
3505
+ ///
3506
+ /// SAME AUTHORITY AS `sysSetBillerId`, and for a stronger reason. That one routes money; this one
3507
+ /// confers the capability to ask this canister to sign with its own tECDSA key. Three writers for
3508
+ /// one field would be a last-write-wins race over a SIGNING target — so the spawning registry is
3509
+ /// the only writer, and an engram with no registry has no market to be pointed at.
3510
+ ///
3511
+ /// The same accepted cost applies, one notch sharper: an engram whose registry never set this can
3512
+ /// never be sold through the market, and no owner action fixes it. Both `sysSign*` methods refuse
3513
+ /// against a null pointer and their refusals name the registry as the remedy rather than telling
3514
+ /// an owner to run a call they cannot make.
3515
+ sysSetMarketId: (id: principal) -> (Result_1);
3516
+ /// Name the metrics reader allowed to read this engram's seat weeks (`sysGetAgentSeatWeeks`).
3517
+ /// ⟨2026-10-02, the owner's ruling of that day⟩
3518
+ ///
3519
+ /// CALLER: the registry that spawned this engram, and nobody else (ADR-0020) — the same authority
3520
+ /// as `sysSetBillerId` and `sysSetMarketId`. The registry calls it once, at spawn, last of its
3521
+ /// pushes, with exactly this interface: `sysSetMetricsReaderId : (Principal) -> async
3522
+ /// Wire.Result<()>`. ⚠ THAT CLIENT DECLARATION IS HAND-WRITTEN AT THE REGISTRY: a different NAME
3523
+ /// here leaves every newborn with no reader, silently (the push is best-effort and its reject is
3524
+ /// caught), and a different REPLY type traps inside the registry's callback. Rename neither without
3525
+ /// the registry; test/seat-weeks-wiring.test.ts compares the two.
3526
+ ///
3527
+ /// A CANISTER ONLY: a person's principal, the anonymous principal or the management canister is
3528
+ /// refused `#Invalid` and nothing changes — a person named here would be handed this engram's seat
3529
+ /// counts. Set or replaced, never cleared: the owner's control is the switch
3530
+ /// (`setSeatWeeksReadEnabled`), not the pointer. The chained record entry
3531
+ /// (`metrics.sysSetMetricsReaderId`) is the evidence of when the read was granted, and to whom.
3532
+ sysSetMetricsReaderId: (reader: principal) -> (Result_1);
3533
+ /// The market asks this engram to sign a registered type — B1's seam, settlement leg. ⟨0.16.0⟩
3534
+ ///
3535
+ /// THE ENGRAM DOES NOT TRUST THE MARKET WITH ANYTHING BEYOND "ask". Every control that bounds
3536
+ /// what may be signed stays here and is unchanged: the type must already be OWNER-REGISTERED
3537
+ /// (the market cannot introduce a shape), its cool-off must have elapsed, its per-type daily cap
3538
+ /// still binds, the destination allowlist still fails closed, and a payments freeze still stops
3539
+ /// it. What the market supplies is the request; what the engram supplies is the whole policy.
3540
+ ///
3541
+ /// That is what "signatures only" means in code, and it is why this method needs no new authority
3542
+ /// model: an unregistered typeId is refused for the market exactly as it is for the owner.
3543
+ sysSignEip712: (typeId: blob, values: vec record {
3544
+ text;
3545
+ Eip712Value;
3546
+ }) -> (Result_4);
3547
+ /// The market asks this engram to sign an x402 payment — B1's seam, the paid-request leg. ⟨0.16.0⟩
3548
+ ///
3549
+ /// THE MARKET SUPPLIES THE REQUEST; THE ENGRAM SUPPLIES THE POLICY. Every control below the caller
3550
+ /// gate is unchanged and still binds: a payments freeze stops it, the recipient must be on the
3551
+ /// owner's EVM allowlist if the owner has set one, the per-transaction and per-day caps hold, and
3552
+ /// an over-policy request goes to the OWNER'S approval queue rather than through.
3553
+ ///
3554
+ /// `#ApprovalPending` is a real answer here, not an error, exactly as it is for an agent: the
3555
+ /// market is told the spend needs the owner and can retry with the `approvalId` once granted. A
3556
+ /// caller that treats it as a failure will simply never complete a spend the owner would have
3557
+ /// allowed — which is the safe direction, and the reason it rides inside `Ok` (L5).
3558
+ ///
3559
+ /// READ `x402SignFor`'s declaration before setting a policy for this caller. The cap is ONE bucket
3560
+ /// for ALL market traffic; per-seller limits are not expressible in the store it reads.
3561
+ sysSignX402Payment: (chainId: nat, tokenAddress: text, recipient:
3562
+ text, amount: nat, tokenName: text, tokenVersion: text, rationale:
3563
+ opt text, approvalId: opt text) -> (Result_3);
3564
+ /// Write an `alerts.test` entry into the audit log; the drain timer delivers it to the webhook
3565
+ /// within ~60s. Owner-only; requires a configured URL so the test can actually go somewhere.
3566
+ testActionAlert: () -> (Result_1);
3567
+ /// Guardian test alert — an audited guardian action (`alerts.test`, role Guardian) that every
3568
+ /// armed channel (owner + guardians) receives within ~60s. Requires the caller's own channel
3569
+ /// to be armed so the test has somewhere to land for them.
3570
+ testGuardianAlert: () -> (Result_1);
3571
+ /// RENAMED from `init` (L2.4). This is the OWNERSHIP-TRANSFER method — the most consequential
3572
+ /// owner-facing operation on the canister — and it was named for first-time setup, colliding with
3573
+ /// the actor's own `service : (initArgs)`. A reader scanning for "how does ownership move" found
3574
+ /// nothing; a reader scanning for `init` found a constructor.
3575
+ ///
3576
+ /// Its read counterpart is `listOwnershipTransfers` (was `getOwnershipHistory`), which now shares
3577
+ /// the stem — the pair was previously unfindable from either half.
3578
+ /// ⟨0.16.0, L1⟩ TWO OF THESE REFUSALS ARE THE FIRST-CLAIM PATH, and they are not authorization.
3579
+ ///
3580
+ /// On an unowned engram there is no owner to refuse anyone, so "caller must set themselves as
3581
+ /// owner" is a rule about the ARGUMENT — pass your own principal and it succeeds. `#Invalid` says
3582
+ /// retrying with a different argument can work; `#Refused` would tell a first claimant their
3583
+ /// standing was the problem, on the one call where nobody has standing yet.
3584
+ ///
3585
+ /// ⟨2026-09-29, owner ruling follow-up 8 (option A)⟩ A CHANGE OF OWNER HANDS OVER CONTROL OF THE
3586
+ /// CODE. The new owner becomes a controller of this canister and the former owner stops being one;
3587
+ /// the answer says who moved it (`ControlHandover`). Managed: the registry moves it while it
3588
+ /// accepts the transfer. Sovereign: the registry answers `#EngramMustMove` and this engram sets
3589
+ /// its own controllers. THE OWNER CHANGES HERE ONLY AFTER CONTROL HAS MOVED — if it cannot move,
3590
+ /// the registry's record is put back and nothing is transferred; if even that fails, the refusal
3591
+ /// says who the registry now records and that the same call again finishes it. A standalone
3592
+ /// engram (no registry) cannot know who controls it and changes the owner only, answering
3593
+ /// `#NotHeldHere`. The rules are `src/shared/ControlHandover.mo`'s.
3594
+ ///
3595
+ /// ⟨2026-09-29, review PR-3: a half-finished change of owner⟩ The registry now MARKS a change of
3596
+ /// owner as unfinished from the moment it moves its record until it sees control move. A sovereign
3597
+ /// engram therefore reports the move (`sysConfirmOwnershipTransfer`) after setting its own
3598
+ /// controllers; until the registry hears it, the registry refuses the new owner's
3599
+ /// `claimSovereignty`, `returnToManaged`, `abandonMyEngram` and `upgradeMyEngram` for this engram.
3600
+ /// While a transfer is marked unfinished, a transfer to anyone but its would-be owner is refused by
3601
+ /// the registry, by name; calling this again with that owner finishes it.
3602
+ ///
3603
+ /// ⟨2026-09-29, review finding RV-4⟩ THE OWNER NAMING THEMSELVES IS A REPORT, NOT A HAND-ON. It is
3604
+ /// the way out when the registry did not hear that a sovereign engram moved control: it moves
3605
+ /// control to the same owner again and reports it, and it writes NO entry to
3606
+ /// `listOwnershipTransfers` (until this day it wrote one from the owner to themselves, a hand-on
3607
+ /// that did not happen). The record entry says it was a report. `ControlHandover.isReport`.
3608
+ transferOwnership: (newOwner: principal) -> (Result_2);
3609
+ /// OWNER-ONLY release of the payment freeze — and the asymmetry with `freezePayments` is the
3610
+ /// governance design, not an oversight.
3611
+ ///
3612
+ /// ⚠ A GUARDIAN CAN PULL THIS BRAKE AND CANNOT RELEASE IT. `freezePayments` accepts the owner OR
3613
+ /// a guardian holding `canFreezePayments`; only the owner reaches this method. Two consequences,
3614
+ /// and both parties should know them before the role is accepted:
3615
+ /// · a guardian can stop payments in an emergency WITHOUT the owner, which is the point;
3616
+ /// · a guardian cannot hold the engram hostage, because the owner releases it immediately and
3617
+ /// unilaterally.
3618
+ /// So a guardian's freeze is a TRIPWIRE, not a lock — it buys the time it takes the owner to
3619
+ /// look, and no more.
3620
+ unfreezePayments: () -> (Result_1);
3621
+ /// Consensus transform for the World ID /verify outcall. Replicas each call independently, so the raw
3622
+ /// response (timestamps, `uses`, request-id headers) is non-deterministic. Collapse it to a single
3623
+ /// canonical byte — "1" iff HTTP 200 AND the body asserts success — with no headers, so every replica
3624
+ /// agrees. The caller keys off this byte, never the raw body.
3625
+ ///
3626
+ /// PUBLIC: not a choice, exactly as `actionAlertTransform` above — the management canister calls
3627
+ /// it on every replica with no caller identity attached. Stateless and total, so being callable
3628
+ /// by anyone grants nothing: an outsider invoking it learns only what they already passed in.
3629
+ worldIdTransform: (args:
3630
+ record {
3631
+ context: blob;
3632
+ response: HttpRequestResult;
3633
+ }) -> (HttpRequestResult) query;
3634
+ /// Write a memory file, creating it or adding a version to it. Returns a write receipt — the new
3635
+ /// `version`, the audit `seq`, and the chain `head` after the write — so a caller can prove what
3636
+ /// it wrote without a second read.
3637
+ ///
3638
+ /// ⚠ OWNER-ONLY, and this is the one most likely to surprise: an agent holding `write_memory`
3639
+ /// does NOT reach this method. Agent writes go through the agent surface, which applies the
3640
+ /// grant and the protected-path rules on top. `#Refused` here means the caller is not the owner,
3641
+ /// not that a permission is missing.
3642
+ ///
3643
+ /// It also refuses while writes are PAUSED and while memory is not attached — both are states an
3644
+ /// owner can be in without knowing, so they answer as themselves rather than as a generic failure.
3645
+ ///
3646
+ /// ⚠ THE FILE-COUNT CAP BLOCKS NEW FILES ONLY. At the cap, creating a new path is refused and
3647
+ /// APPENDING TO AN EXISTING FILE STILL WORKS — the refusal says so, because "you are full" and
3648
+ /// "you cannot write at all" lead to different next actions. A TOMBSTONED path counts as absent,
3649
+ /// so writing to a deleted path is a new file and is capped like one; the delete record survives
3650
+ /// either way.
3651
+ ///
3652
+ /// Path and content are validated before the caller is even checked, so a malformed request gets
3653
+ /// `#Invalid` rather than a refusal it would waste time investigating.
3654
+ writeMemory: (path: text, content: blob) -> (Result);
3655
+ };
3656
+ type Eip712Value =
3657
+ variant {
3658
+ address: text;
3659
+ "bool": bool;
3660
+ bytes: vec nat8;
3661
+ fixedBytes: vec nat8;
3662
+ string: text;
3663
+ uint: nat;
3664
+ };
3665
+ type Eip712SignatureOut =
3666
+ record {
3667
+ digest: blob;
3668
+ r: blob;
3669
+ rendered: text;
3670
+ s: blob;
3671
+ v: nat8;
3672
+ };
3673
+ type Eip712Registration =
3674
+ record {
3675
+ cooloffEndsAtNs: int;
3676
+ encodeTypeString: text;
3677
+ typeId: blob;
3678
+ };
3679
+ type Eip3009Authorization =
3680
+ record {
3681
+ from: text;
3682
+ nonce: blob;
3683
+ r: blob;
3684
+ s: blob;
3685
+ to: text;
3686
+ v: nat8;
3687
+ validAfter: nat;
3688
+ validBefore: nat;
3689
+ value: nat;
3690
+ };
3691
+ type CycleSnapshot =
3692
+ record {
3693
+ atNs: int;
3694
+ cycles: nat;
3695
+ seq: nat;
3696
+ source: variant {
3697
+ balance;
3698
+ self;
3699
+ };
3700
+ };
3701
+ type ControlHandover =
3702
+ variant {
3703
+ MovedByEngram;
3704
+ MovedByRegistry;
3705
+ NotHeldHere;
3706
+ };
3707
+ type CondenseResult =
3708
+ record {
3709
+ fromSeq: nat;
3710
+ head: blob;
3711
+ headAtBoundary: blob;
3712
+ historyBytesDelta: int;
3713
+ newFirstAvailableSeq: nat;
3714
+ opCount: nat;
3715
+ opsRoot: blob;
3716
+ seq: nat;
3717
+ toSeq: nat;
3718
+ };
3719
+ type CondensePreview =
3720
+ record {
3721
+ bestUnfilteredReceipt: opt record {
3722
+ fromSeq: nat;
3723
+ seq: nat;
3724
+ toSeq: nat;
3725
+ };
3726
+ coveringReceipt: opt record {
3727
+ fromSeq: nat;
3728
+ seq: nat;
3729
+ toSeq: nat;
3730
+ };
3731
+ filteredReceiptsSeen: nat;
3732
+ firstAvailableSeq: nat;
3733
+ maxUnlockableToSeq: nat;
3734
+ opCountInRange: nat;
3735
+ proposedToSeq: nat;
3736
+ scanTruncated: bool;
3737
+ scannedFromSeq: nat;
3738
+ totalEvents: nat;
3739
+ unlocked: bool;
3740
+ };
3741
+ type CondenseCheckpointRow =
3742
+ record {
3743
+ atNs: int;
3744
+ by: principal;
3745
+ fromSeq: nat;
3746
+ headAtBoundary: blob;
3747
+ opCount: nat;
3748
+ opsRoot: blob;
3749
+ seq: nat;
3750
+ toSeq: nat;
3751
+ };
3752
+ type ChildOpening =
3753
+ record {
3754
+ allowances: vec ChildAllowanceRow;
3755
+ attachCycles: nat;
3756
+ balance: nat;
3757
+ floorCycles: nat;
3758
+ };
3759
+ type ChildAllowanceRow =
3760
+ record {
3761
+ remaining: nat;
3762
+ root: principal;
3763
+ };
3764
+ type CheckpointPage =
3765
+ record {
3766
+ firstAvailableSeq: nat;
3767
+ page: Page_15;
3768
+ scannedThroughSeq: nat;
3769
+ };
3770
+ type CertifiedAuditHead =
3771
+ record {
3772
+ certificate: opt blob;
3773
+ headHash: blob;
3774
+ size: nat;
3775
+ total: nat;
3776
+ witness: opt blob;
3777
+ };
3778
+ type CallerSignatureCount =
3779
+ record {
3780
+ caller: principal;
3781
+ count: nat;
3782
+ role: Role;
3783
+ root: opt principal;
3784
+ };
3785
+ type BearerTokenStatus =
3786
+ variant {
3787
+ Active;
3788
+ Expired;
3789
+ Revoked;
3790
+ };
3791
+ type BearerToken =
3792
+ record {
3793
+ clientLabel: text;
3794
+ expiresAtNs: int;
3795
+ issuedAtNs: int;
3796
+ issuedBy: principal;
3797
+ lastUsedNs: int;
3798
+ permissions: AgentPermissions;
3799
+ status: BearerTokenStatus;
3800
+ tokenHash: text;
3801
+ tokenId: text;
3802
+ };
3803
+ type BatchReadItem =
3804
+ record {
3805
+ path: text;
3806
+ result: variant {
3807
+ Err: Error;
3808
+ Ok: MemoryFile;
3809
+ };
3810
+ };
3811
+ type BackupType =
3812
+ variant {
3813
+ FullSnapshot;
3814
+ IncrementalDiff;
3815
+ PartialSnapshot;
3816
+ };
3817
+ type BackupStatus =
3818
+ variant {
3819
+ Failed;
3820
+ Finalized;
3821
+ Uploading;
3822
+ };
3823
+ type BackupRetentionReport =
3824
+ record {
3825
+ dbType: text;
3826
+ maxBackups: nat;
3827
+ prunedBackups: nat;
3828
+ };
3829
+ type BackupMetadata =
3830
+ record {
3831
+ backupId: text;
3832
+ backupLabel: opt text;
3833
+ backupType: BackupType;
3834
+ chunkCount: nat;
3835
+ createdAtNs: int;
3836
+ createdBy: principal;
3837
+ dbType: text;
3838
+ parentBackupId: opt text;
3839
+ sha256: text;
3840
+ status: BackupStatus;
3841
+ totalSize: nat;
3842
+ };
3843
+ type AuthorityClass =
3844
+ variant {
3845
+ standard;
3846
+ standingAuthority;
3847
+ valueBearing;
3848
+ };
3849
+ type AuditHead =
3850
+ record {
3851
+ headHash: blob;
3852
+ size: nat;
3853
+ };
3854
+ type AuditEntry =
3855
+ record {
3856
+ caller: principal;
3857
+ callerRole: Role;
3858
+ details: text;
3859
+ entryHash: blob;
3860
+ id: nat;
3861
+ operation: text;
3862
+ success: bool;
3863
+ timestampNs: int;
3864
+ };
3865
+ type ApprovalStatus =
3866
+ variant {
3867
+ Approved;
3868
+ Denied;
3869
+ Expired;
3870
+ Pending;
3871
+ Used;
3872
+ };
3873
+ type ApprovalRequest =
3874
+ record {
3875
+ amount: nat;
3876
+ approvedAtNs: opt int;
3877
+ approvedBy: opt principal;
3878
+ binding: text;
3879
+ caller: principal;
3880
+ createdAtNs: int;
3881
+ denialReason: opt text;
3882
+ expiresAtNs: int;
3883
+ id: text;
3884
+ operation: text;
3885
+ policyField: text;
3886
+ policyLimit: nat;
3887
+ rationale: text;
3888
+ status: ApprovalStatus;
3889
+ };
3890
+ type AnchorPushStatus =
3891
+ record {
3892
+ anchorTickAtNs: opt int;
3893
+ attemptAtNs: opt int;
3894
+ attemptTotal: nat;
3895
+ cadenceSeconds: nat;
3896
+ carrierArmedAtNs: int;
3897
+ currentTotal: nat;
3898
+ lastError: text;
3899
+ okAtNs: opt int;
3900
+ okTotal: nat;
3901
+ skipCursor: nat;
3902
+ timerTickAtNs: opt int;
3903
+ };
3904
+ type AlertStatus =
3905
+ record {
3906
+ config: AlertConfig;
3907
+ cursor: nat;
3908
+ disabled: bool;
3909
+ failures: nat;
3910
+ lastDeliveredAtNs: opt int;
3911
+ totalAudit: nat;
3912
+ };
3913
+ type AlertConfig =
3914
+ record {
3915
+ agentWrites: bool;
3916
+ approvals: bool;
3917
+ classes: opt vec record {
3918
+ text;
3919
+ bool;
3920
+ };
3921
+ guardian: bool;
3922
+ money: bool;
3923
+ url: opt text;
3924
+ };
3925
+ type AgentWriteRow =
3926
+ record {
3927
+ connector: opt text;
3928
+ entry: AuditEntry;
3929
+ path: opt text;
3930
+ source: opt text;
3931
+ version: opt nat;
3932
+ };
3933
+ type AgentWriteReview =
3934
+ record {
3935
+ capped: bool;
3936
+ cursor: nat;
3937
+ earliest: nat;
3938
+ head: nat;
3939
+ rows: vec AgentWriteRow;
3940
+ };
3941
+ type AgentStatus =
3942
+ variant {
3943
+ Active;
3944
+ Resigned;
3945
+ Revoked;
3946
+ };
3947
+ type AgentRecord =
3948
+ record {
3949
+ earlierKeys: vec HeldKey;
3950
+ ended: opt AgentEnding;
3951
+ enrolment: EnrolmentProof;
3952
+ keyHeldFrom: LogPosition;
3953
+ keyNomination: opt KeyNomination;
3954
+ keyRotatedAtNs: int;
3955
+ keyRotationCount: nat;
3956
+ lastSeenNs: int;
3957
+ name: text;
3958
+ pairedAtNs: int;
3959
+ permissions: AgentPermissions;
3960
+ "principal": principal;
3961
+ publicKeyHash: opt text;
3962
+ root: principal;
3963
+ status: AgentStatus;
3964
+ };
3965
+ type AgentPermissions =
3966
+ record {
3967
+ callsPerMinute: nat;
3968
+ canAppendMemory: bool;
3969
+ canReadBackup: opt bool;
3970
+ canReadMemory: bool;
3971
+ canReadWallet: bool;
3972
+ scopes: opt vec text;
3973
+ };
3974
+ type AgentPaymentSignatures =
3975
+ record {
3976
+ agent: principal;
3977
+ root: principal;
3978
+ signedToday: nat;
3979
+ };
3980
+ type AgentEnding =
3981
+ record {
3982
+ day: int;
3983
+ reason: AgentEndReason;
3984
+ };
3985
+ type AgentEndReason =
3986
+ variant {
3987
+ GuardianRevoked;
3988
+ KillSwitch;
3989
+ OwnerRevoked;
3990
+ Reset;
3991
+ Resigned;
3992
+ };
3993
+ type AgentCard =
3994
+ record {
3995
+ description: text;
3996
+ name: text;
3997
+ services: vec ServiceEntry;
3998
+ x402Support: bool;
3999
+ };
4000
+ type ActivitySnapshot =
4001
+ record {
4002
+ atNs: int;
4003
+ memoryReads: nat;
4004
+ memoryWrites: nat;
4005
+ };
4006
+ type ActionAuth =
4007
+ record {
4008
+ nonce: nat;
4009
+ pubkey: blob;
4010
+ signature: blob;
4011
+ };
4012
+ type About =
4013
+ record {
4014
+ agentCount: nat;
4015
+ agentMemoryReads: nat;
4016
+ agentMemoryWrites: nat;
4017
+ auditLogSize: nat;
4018
+ backupCount: nat;
4019
+ distinctReaderClients: nat;
4020
+ distinctReaderPrincipals: nat;
4021
+ encryptionEnabled: bool;
4022
+ firstAgentWriteAtNs: opt int;
4023
+ firstWriteAtNs: opt int;
4024
+ guardianCount: nat;
4025
+ lastAgentReadAtNs: opt int;
4026
+ lastAgentWriteAtNs: opt int;
4027
+ lastReadAtNs: opt int;
4028
+ lastWriteAtNs: opt int;
4029
+ memoryFileCount: nat;
4030
+ paidMemoryReadEnabled: bool;
4031
+ paidMemoryWriteEnabled: bool;
4032
+ paymentsFrozen: bool;
4033
+ totalBackupSize: nat;
4034
+ totalMemoryReads: nat;
4035
+ totalMemoryWrites: nat;
4036
+ verifiedHuman: bool;
4037
+ version: text;
4038
+ walletActivatedAtNs: opt int;
4039
+ writesPaused: bool;
4040
+ };
4041
+ service : (initArgs: opt
4042
+ record {
4043
+ owner: opt principal;
4044
+ registry: opt principal;
4045
+ }) -> Engram