@reinconsole/erc8004 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,651 @@
1
+ import { Address, PublicClient, WalletClient, Transport, Chain, Account } from 'viem';
2
+ import { ReputationScore, ReputationSubject } from '@reinconsole/core';
3
+
4
+ /**
5
+ * The slice of the ERC-8004 Identity Registry ABI Rein touches, verified
6
+ * against the published ABIs (github.com/erc-8004/erc-8004-contracts, abis/)
7
+ * in session 17. The registry is an upgradeable ERC-721 (URIStorage): tokenId
8
+ * IS the spec's `agentId`.
9
+ *
10
+ * Only the `register(string)` overload is declared — the contract also has
11
+ * `register()` and `register(string, (string,bytes)[])`, but a single overload
12
+ * keeps viem call sites unambiguous and we always pass an agentURI.
13
+ *
14
+ * `getAgentWallet` returns `address(bytes20(metadata["agentWallet"]))` — the
15
+ * ZERO address when unset/cleared, never a revert (even for a nonexistent
16
+ * agent). `ownerOf` is standard OZ ERC-721 and DOES revert on nonexistent ids.
17
+ */
18
+ declare const identityRegistryAbi: readonly [{
19
+ readonly name: "register";
20
+ readonly type: "function";
21
+ readonly stateMutability: "nonpayable";
22
+ readonly inputs: readonly [{
23
+ readonly type: "string";
24
+ readonly name: "agentURI";
25
+ }];
26
+ readonly outputs: readonly [{
27
+ readonly type: "uint256";
28
+ readonly name: "agentId";
29
+ }];
30
+ }, {
31
+ readonly name: "ownerOf";
32
+ readonly type: "function";
33
+ readonly stateMutability: "view";
34
+ readonly inputs: readonly [{
35
+ readonly type: "uint256";
36
+ readonly name: "tokenId";
37
+ }];
38
+ readonly outputs: readonly [{
39
+ readonly type: "address";
40
+ }];
41
+ }, {
42
+ readonly name: "tokenURI";
43
+ readonly type: "function";
44
+ readonly stateMutability: "view";
45
+ readonly inputs: readonly [{
46
+ readonly type: "uint256";
47
+ readonly name: "tokenId";
48
+ }];
49
+ readonly outputs: readonly [{
50
+ readonly type: "string";
51
+ }];
52
+ }, {
53
+ readonly name: "getAgentWallet";
54
+ readonly type: "function";
55
+ readonly stateMutability: "view";
56
+ readonly inputs: readonly [{
57
+ readonly type: "uint256";
58
+ readonly name: "agentId";
59
+ }];
60
+ readonly outputs: readonly [{
61
+ readonly type: "address";
62
+ }];
63
+ }, {
64
+ readonly name: "Registered";
65
+ readonly type: "event";
66
+ readonly inputs: readonly [{
67
+ readonly type: "uint256";
68
+ readonly name: "agentId";
69
+ readonly indexed: true;
70
+ }, {
71
+ readonly type: "string";
72
+ readonly name: "agentURI";
73
+ }, {
74
+ readonly type: "address";
75
+ readonly name: "owner";
76
+ readonly indexed: true;
77
+ }];
78
+ }];
79
+ /**
80
+ * The Reputation Registry slice Rein touches, verified against the published
81
+ * ABI + contract source (ReputationRegistryUpgradeable.sol) in session 19:
82
+ *
83
+ * - `giveFeedback` is open to any clientAddress EXCEPT the agent's owner or
84
+ * an approved operator ("Self-feedback not allowed"); a nonexistent agentId
85
+ * reverts ERC721NonexistentToken via the Identity Registry.
86
+ * - `feedbackIndex` is a 1-BASED counter per (agentId, clientAddress).
87
+ * - `feedbackHash` is the KECCAK-256 of the content behind `feedbackURI`
88
+ * (optional for content-addressed URIs); bytes32(0) is accepted.
89
+ * - `getSummary` returns the WAD-normalized AVERAGE of matching non-revoked
90
+ * feedback, scaled to the mode of the matched valueDecimals. LIVE-VERIFIED
91
+ * DIVERGENCE from the repo's main-branch source (S19): the DEPLOYED Base
92
+ * Sepolia contract REVERTS "clientAddresses required" on an empty client
93
+ * list — callers must resolve `getClients(agentId)` first (readSummary
94
+ * does). `readFeedback` reverts on an out-of-bounds index.
95
+ */
96
+ declare const reputationRegistryAbi: readonly [{
97
+ readonly name: "getIdentityRegistry";
98
+ readonly type: "function";
99
+ readonly stateMutability: "view";
100
+ readonly inputs: readonly [];
101
+ readonly outputs: readonly [{
102
+ readonly type: "address";
103
+ }];
104
+ }, {
105
+ readonly name: "giveFeedback";
106
+ readonly type: "function";
107
+ readonly stateMutability: "nonpayable";
108
+ readonly inputs: readonly [{
109
+ readonly type: "uint256";
110
+ readonly name: "agentId";
111
+ }, {
112
+ readonly type: "int128";
113
+ readonly name: "value";
114
+ }, {
115
+ readonly type: "uint8";
116
+ readonly name: "valueDecimals";
117
+ }, {
118
+ readonly type: "string";
119
+ readonly name: "tag1";
120
+ }, {
121
+ readonly type: "string";
122
+ readonly name: "tag2";
123
+ }, {
124
+ readonly type: "string";
125
+ readonly name: "endpoint";
126
+ }, {
127
+ readonly type: "string";
128
+ readonly name: "feedbackURI";
129
+ }, {
130
+ readonly type: "bytes32";
131
+ readonly name: "feedbackHash";
132
+ }];
133
+ readonly outputs: readonly [];
134
+ }, {
135
+ readonly name: "readFeedback";
136
+ readonly type: "function";
137
+ readonly stateMutability: "view";
138
+ readonly inputs: readonly [{
139
+ readonly type: "uint256";
140
+ readonly name: "agentId";
141
+ }, {
142
+ readonly type: "address";
143
+ readonly name: "clientAddress";
144
+ }, {
145
+ readonly type: "uint64";
146
+ readonly name: "feedbackIndex";
147
+ }];
148
+ readonly outputs: readonly [{
149
+ readonly type: "int128";
150
+ readonly name: "value";
151
+ }, {
152
+ readonly type: "uint8";
153
+ readonly name: "valueDecimals";
154
+ }, {
155
+ readonly type: "string";
156
+ readonly name: "tag1";
157
+ }, {
158
+ readonly type: "string";
159
+ readonly name: "tag2";
160
+ }, {
161
+ readonly type: "bool";
162
+ readonly name: "isRevoked";
163
+ }];
164
+ }, {
165
+ readonly name: "getSummary";
166
+ readonly type: "function";
167
+ readonly stateMutability: "view";
168
+ readonly inputs: readonly [{
169
+ readonly type: "uint256";
170
+ readonly name: "agentId";
171
+ }, {
172
+ readonly type: "address[]";
173
+ readonly name: "clientAddresses";
174
+ }, {
175
+ readonly type: "string";
176
+ readonly name: "tag1";
177
+ }, {
178
+ readonly type: "string";
179
+ readonly name: "tag2";
180
+ }];
181
+ readonly outputs: readonly [{
182
+ readonly type: "uint64";
183
+ readonly name: "count";
184
+ }, {
185
+ readonly type: "int128";
186
+ readonly name: "summaryValue";
187
+ }, {
188
+ readonly type: "uint8";
189
+ readonly name: "summaryValueDecimals";
190
+ }];
191
+ }, {
192
+ readonly name: "getClients";
193
+ readonly type: "function";
194
+ readonly stateMutability: "view";
195
+ readonly inputs: readonly [{
196
+ readonly type: "uint256";
197
+ readonly name: "agentId";
198
+ }];
199
+ readonly outputs: readonly [{
200
+ readonly type: "address[]";
201
+ }];
202
+ }, {
203
+ readonly name: "getLastIndex";
204
+ readonly type: "function";
205
+ readonly stateMutability: "view";
206
+ readonly inputs: readonly [{
207
+ readonly type: "uint256";
208
+ readonly name: "agentId";
209
+ }, {
210
+ readonly type: "address";
211
+ readonly name: "clientAddress";
212
+ }];
213
+ readonly outputs: readonly [{
214
+ readonly type: "uint64";
215
+ }];
216
+ }, {
217
+ readonly name: "NewFeedback";
218
+ readonly type: "event";
219
+ readonly inputs: readonly [{
220
+ readonly type: "uint256";
221
+ readonly name: "agentId";
222
+ readonly indexed: true;
223
+ }, {
224
+ readonly type: "address";
225
+ readonly name: "clientAddress";
226
+ readonly indexed: true;
227
+ }, {
228
+ readonly type: "uint64";
229
+ readonly name: "feedbackIndex";
230
+ }, {
231
+ readonly type: "int128";
232
+ readonly name: "value";
233
+ }, {
234
+ readonly type: "uint8";
235
+ readonly name: "valueDecimals";
236
+ }, {
237
+ readonly type: "string";
238
+ readonly name: "indexedTag1";
239
+ readonly indexed: true;
240
+ }, {
241
+ readonly type: "string";
242
+ readonly name: "tag1";
243
+ }, {
244
+ readonly type: "string";
245
+ readonly name: "tag2";
246
+ }, {
247
+ readonly type: "string";
248
+ readonly name: "endpoint";
249
+ }, {
250
+ readonly type: "string";
251
+ readonly name: "feedbackURI";
252
+ }, {
253
+ readonly type: "bytes32";
254
+ readonly name: "feedbackHash";
255
+ }];
256
+ }];
257
+
258
+ type Erc8004ErrorCode = 'unknown_agent' | 'registration_failed' | 'bad_id' | 'feedback_failed';
259
+ /** A registry lookup or write that failed, with a machine-readable code. */
260
+ declare class Erc8004Error extends Error {
261
+ readonly code: Erc8004ErrorCode;
262
+ constructor(code: Erc8004ErrorCode, message: string);
263
+ }
264
+
265
+ /**
266
+ * ERC-8004 singleton deployments (verified live, session 17). The same
267
+ * address serves Ethereum + Base per environment.
268
+ */
269
+ declare const IDENTITY_REGISTRY_MAINNET: Address;
270
+ declare const IDENTITY_REGISTRY_TESTNET: Address;
271
+ declare const REPUTATION_REGISTRY_TESTNET: Address;
272
+ declare const BASE_SEPOLIA_CHAIN_ID = 84532;
273
+ /** Which registry contract (on which chain) a set of identity facts came from. */
274
+ interface RegistryRef {
275
+ chainId: number;
276
+ address: string;
277
+ }
278
+ declare const BASE_SEPOLIA_REGISTRY: RegistryRef;
279
+ /**
280
+ * The slice of viem's PublicClient the reader needs. Offline code fakes the
281
+ * domain-level `IdentityRegistryReader` port instead (MockIdentityRegistry) —
282
+ * nothing in the repo ever fakes raw readContract calls.
283
+ */
284
+ type RegistryChainReader = Pick<PublicClient, 'readContract'>;
285
+ /**
286
+ * The domain-level port the linking layer depends on: resolve on-chain
287
+ * identity facts for one agentId. viem-backed against the real registry
288
+ * (`identityRegistryReader`), in-memory for every offline path
289
+ * (`MockIdentityRegistry`).
290
+ */
291
+ interface IdentityRegistryReader {
292
+ readonly ref: RegistryRef;
293
+ /** ERC-721 owner. Throws Erc8004Error('unknown_agent') for a nonexistent id. */
294
+ ownerOf(tokenId: bigint): Promise<string>;
295
+ /**
296
+ * The EIP-712-verified payment wallet (`agentWallet` reserved metadata key;
297
+ * auto-set to the owner at registration, cleared on transfer). `undefined`
298
+ * when unset — the real contract returns the zero address and never reverts,
299
+ * even for nonexistent agents.
300
+ */
301
+ agentWallet(tokenId: bigint): Promise<string | undefined>;
302
+ /** The registration file URI (ERC-721 tokenURI). Throws 'unknown_agent' like ownerOf. */
303
+ agentURI(tokenId: bigint): Promise<string>;
304
+ }
305
+ /** The real reader, over any viem PublicClient (or structural equivalent). */
306
+ declare function identityRegistryReader(client: RegistryChainReader, ref?: RegistryRef): IdentityRegistryReader;
307
+ /** Cross-check: the Reputation Registry names its Identity Registry on-chain. */
308
+ declare function getIdentityRegistryAddress(client: RegistryChainReader, reputationRegistry?: Address): Promise<string>;
309
+ interface RegisteredAgent {
310
+ tokenId: bigint;
311
+ /** Canonical id string — what goes on the Agent/Vendor doc. */
312
+ erc8004Id: string;
313
+ txHash: string;
314
+ owner: string;
315
+ }
316
+ /**
317
+ * The write path: mint an agent on the real registry. simulate -> write ->
318
+ * wait for the receipt -> decode the `Registered` event (the EVENT is
319
+ * authoritative — a simulate return value can go stale between simulation and
320
+ * inclusion). The client params are the exact method slices used — naming the
321
+ * full viem client types here trips chain-formatter variance (a baseSepolia-
322
+ * typed client is not assignable to PublicClient<Transport, Chain>).
323
+ *
324
+ * RETRY WARNING: a 'registration_failed' can be a receipt-wait TIMEOUT — the
325
+ * tx may still land later. Blindly retrying on that code can mint a SECOND
326
+ * identity; callers persisting the id (the demo's .env) should check the
327
+ * chain before re-registering.
328
+ */
329
+ declare function registerAgent(options: {
330
+ publicClient: Pick<PublicClient, 'simulateContract' | 'waitForTransactionReceipt'>;
331
+ walletClient: Pick<WalletClient<Transport, Chain, Account>, 'writeContract' | 'account'>;
332
+ ref?: RegistryRef;
333
+ agentURI: string;
334
+ }): Promise<RegisteredAgent>;
335
+
336
+ /**
337
+ * The ERC-8004 Reputation Registry write side: publish Rein's graph-derived
338
+ * scores on-chain as feedback about an agent's on-chain identity.
339
+ *
340
+ * Contract facts this module is built on (verified against source, S19):
341
+ * `giveFeedback` may be called by ANY address except the agent's owner or an
342
+ * approved operator — so the publisher is a counterparty (a Rein gate/graph
343
+ * operator scoring the agents it transacted with), never the agent itself.
344
+ * Feedback indexes are 1-based per (agentId, clientAddress). `getSummary`
345
+ * averages (WAD-normalized) non-revoked matching entries.
346
+ */
347
+ /** The singleton Reputation Registry on Base Sepolia (same 0x8004B… everywhere). */
348
+ declare const BASE_SEPOLIA_REPUTATION: RegistryRef;
349
+ /** Rein's tag1 convention: filters getSummary down to Rein-published scores. */
350
+ declare const REIN_SCORE_TAG = "rein-score";
351
+ interface FeedbackInput {
352
+ /** The identity's tokenId (the spec's agentId). */
353
+ agentId: bigint;
354
+ /** Fixed-point value at `valueDecimals` (Rein scores: 0-100, decimals 0). */
355
+ value: bigint;
356
+ valueDecimals?: number;
357
+ tag1?: string;
358
+ tag2?: string;
359
+ /** The evaluated service endpoint; empty = the agent overall. */
360
+ endpoint?: string;
361
+ /** Off-chain evidence JSON; data: URIs keep the demo infra-free. */
362
+ feedbackURI?: string;
363
+ /** keccak256 of the feedbackURI content (optional for content-addressed URIs). */
364
+ feedbackHash?: `0x${string}`;
365
+ }
366
+ interface PublishedFeedback {
367
+ agentId: bigint;
368
+ clientAddress: string;
369
+ /** 1-based per (agentId, clientAddress). */
370
+ feedbackIndex: bigint;
371
+ txHash: string;
372
+ }
373
+ interface FeedbackSummary {
374
+ count: bigint;
375
+ /** WAD-normalized average, scaled to `valueDecimals`. */
376
+ value: bigint;
377
+ valueDecimals: number;
378
+ }
379
+ interface FeedbackEntry {
380
+ value: bigint;
381
+ valueDecimals: number;
382
+ tag1: string;
383
+ tag2: string;
384
+ revoked: boolean;
385
+ }
386
+ declare function validateFeedback(input: FeedbackInput): void;
387
+ /**
388
+ * The write path: publish one feedback entry. Mirrors registerAgent's shape —
389
+ * simulate -> write -> receipt -> decode the NewFeedback EVENT (authoritative;
390
+ * filtered by registry address). The same retry warning applies: a
391
+ * 'feedback_failed' can be a receipt-wait timeout with the tx still landing;
392
+ * feedback is APPEND-ONLY, so a blind retry publishes a duplicate entry
393
+ * (skewing getSummary), not a corrupt one.
394
+ */
395
+ declare function giveFeedback(options: {
396
+ publicClient: Pick<PublicClient, 'simulateContract' | 'waitForTransactionReceipt'>;
397
+ walletClient: Pick<WalletClient<Transport, Chain, Account>, 'writeContract' | 'account'>;
398
+ registry?: RegistryRef;
399
+ input: FeedbackInput;
400
+ }): Promise<PublishedFeedback>;
401
+ /** Every address that has published feedback about the agent. */
402
+ declare function feedbackClients(client: RegistryChainReader, options: {
403
+ agentId: bigint;
404
+ registry?: RegistryRef;
405
+ }): Promise<readonly string[]>;
406
+ /**
407
+ * getSummary over the real registry. Filters: clients omitted = ALL known
408
+ * clients, tags '' = any. LIVE-VERIFIED (S19): the deployed contract reverts
409
+ * "clientAddresses required" on an empty list — unlike the repo's main-branch
410
+ * source — so an omitted/empty filter is resolved through getClients(agentId)
411
+ * first; an agent with no feedback at all short-circuits to zeros.
412
+ */
413
+ declare function readSummary(client: RegistryChainReader, options: {
414
+ agentId: bigint;
415
+ clients?: readonly string[];
416
+ tag1?: string;
417
+ tag2?: string;
418
+ registry?: RegistryRef;
419
+ }): Promise<FeedbackSummary>;
420
+ /** One stored entry. Reverts (as 'feedback_failed') on an out-of-bounds index. */
421
+ declare function readFeedbackEntry(client: RegistryChainReader, options: {
422
+ agentId: bigint;
423
+ clientAddress: string;
424
+ index: bigint;
425
+ registry?: RegistryRef;
426
+ }): Promise<FeedbackEntry>;
427
+ /** How many entries this client has published about the agent (0 = none). */
428
+ declare function lastFeedbackIndex(client: RegistryChainReader, options: {
429
+ agentId: bigint;
430
+ clientAddress: string;
431
+ registry?: RegistryRef;
432
+ }): Promise<bigint>;
433
+ /**
434
+ * Map a graph score onto the feedback wire: value = the 0-100 headline score
435
+ * (decimals 0 — the spec's quality-rating convention), tag1 = the Rein filter
436
+ * tag, tag2 = the confidence as an integer percentage. The score's SUBJECT
437
+ * must already be erc8004-canonical (that is what links.ts makes true for
438
+ * registered agents) — the tokenId is parsed straight out of it, so feedback
439
+ * can never be published about a different identity than the evidence keys by.
440
+ */
441
+ declare function scoreToFeedback(score: ReputationScore, opts?: {
442
+ /** When given, the score's embedded registry ref must match (chain + address). */
443
+ identity?: RegistryRef;
444
+ endpoint?: string;
445
+ feedbackURI?: string;
446
+ feedbackHash?: `0x${string}`;
447
+ }): FeedbackInput;
448
+ interface FeedbackEvidence {
449
+ /** The off-chain JSON document (spec-example shape + Rein's explanation). */
450
+ doc: Record<string, unknown>;
451
+ json: string;
452
+ /** keccak256 of the exact json bytes — what goes on-chain as feedbackHash. */
453
+ feedbackHash: `0x${string}`;
454
+ /** data: URI carrying the json itself — content-addressed, no hosting needed. */
455
+ dataUri: string;
456
+ }
457
+ /**
458
+ * Build the off-chain evidence document for a score. Field names follow the
459
+ * spec's feedbackURI example (agentRegistry/agentId/clientAddress/createdAt/
460
+ * value/valueDecimals); Rein's component breakdown rides along under `rein`,
461
+ * so anyone can recompute WHY the number is what it is. The data: URI form is
462
+ * self-contained and self-verifying (hash the content, compare on-chain).
463
+ */
464
+ declare function feedbackEvidence(score: ReputationScore, opts: {
465
+ clientAddress: string;
466
+ }): FeedbackEvidence;
467
+ /**
468
+ * The whole loop in one call: evidence doc -> hash -> data: URI ->
469
+ * giveFeedback on-chain. Returns the published pointer AND the score with
470
+ * `evidenceUri` filled in (core's ReputationScore field exists for exactly
471
+ * this — a hash-anchored attestation, now actually anchored).
472
+ */
473
+ declare function publishAgentScore(options: {
474
+ publicClient: Pick<PublicClient, 'simulateContract' | 'waitForTransactionReceipt'>;
475
+ walletClient: Pick<WalletClient<Transport, Chain, Account>, 'writeContract' | 'account'>;
476
+ score: ReputationScore;
477
+ registry?: RegistryRef;
478
+ identity?: RegistryRef;
479
+ endpoint?: string;
480
+ }): Promise<{
481
+ published: PublishedFeedback;
482
+ score: ReputationScore;
483
+ evidenceUri: string;
484
+ }>;
485
+
486
+ /**
487
+ * In-memory twin of the ERC-8004 Identity Registry — the offline counterpart
488
+ * of `identityRegistryReader`, for demos, tests, and the console world.
489
+ *
490
+ * Mirrors the real contract's observable behavior: sequential tokenIds from 1,
491
+ * `agentWallet` auto-set to the owner at registration (the reserved-key rule),
492
+ * `ownerOf`/`agentURI` throw for nonexistent agents while `agentWallet`
493
+ * returns undefined (the real read never reverts). One documented divergence:
494
+ * `setAgentWallet` skips the EIP-712/ERC-1271 signature check and trusts the
495
+ * caller — mock custody, mock proofs.
496
+ *
497
+ * The default ref is a FAKE address on Base's chain id: a mock must not claim
498
+ * facts about the real deployment.
499
+ */
500
+ declare class MockIdentityRegistry implements IdentityRegistryReader {
501
+ readonly ref: RegistryRef;
502
+ private readonly agents;
503
+ private nextId;
504
+ constructor(options?: {
505
+ chainId?: number;
506
+ address?: string;
507
+ });
508
+ /** ERC-721-ish mint. agentWallet starts as the owner, per the spec. */
509
+ register(input: {
510
+ owner: string;
511
+ agentURI?: string;
512
+ }): {
513
+ tokenId: bigint;
514
+ erc8004Id: string;
515
+ };
516
+ /** Rotate the verified payment wallet. DIVERGENCE: no signature verification. */
517
+ setAgentWallet(tokenId: bigint, wallet: string): void;
518
+ /**
519
+ * Hydration primitive: reinsert a known registration verbatim (world boot
520
+ * rebuilding "the chain" from persisted agent docs). Keeps minted ids stable
521
+ * across restarts — the next register() mints past the highest loaded id.
522
+ */
523
+ load(record: {
524
+ tokenId: bigint;
525
+ owner: string;
526
+ agentWallet?: string;
527
+ agentURI?: string;
528
+ }): void;
529
+ /** The canonical id string this mock would put on a doc for `tokenId`. */
530
+ idOf(tokenId: bigint): string;
531
+ ownerOf(tokenId: bigint): Promise<string>;
532
+ agentWallet(tokenId: bigint): Promise<string | undefined>;
533
+ agentURI(tokenId: bigint): Promise<string>;
534
+ }
535
+ /**
536
+ * In-memory twin of the ERC-8004 Reputation Registry, tied to a
537
+ * MockIdentityRegistry the way the real contracts are tied (the identity
538
+ * registry answers existence + the self-feedback restriction).
539
+ *
540
+ * Mirrors the real contract's observable behavior: 1-based feedbackIndex per
541
+ * (agentId, clientAddress); giveFeedback reverts for a nonexistent agent
542
+ * (via ownerOf) and for the agent's own owner ("Self-feedback not allowed");
543
+ * getSummary WAD-averages non-revoked matching rows and scales the result to
544
+ * the mode of the matched valueDecimals; readFeedback reverts out-of-bounds.
545
+ * Documented divergences: no ERC-721 operator approvals (only the OWNER is
546
+ * blocked from self-feedback); no revokeFeedback/appendResponse surface; and
547
+ * getSummary here treats an empty client filter as ALL clients — matching
548
+ * feedback.ts's readSummary HELPER semantics, not the raw deployed contract,
549
+ * which live-reverts "clientAddresses required" on an empty list (S19).
550
+ */
551
+ declare class MockReputationRegistry {
552
+ private readonly identity;
553
+ readonly ref: RegistryRef;
554
+ /** agentId -> clientAddress (lowercased) -> rows (index i = feedbackIndex i+1). */
555
+ private readonly rows;
556
+ private txCounter;
557
+ constructor(identity: MockIdentityRegistry, options?: {
558
+ address?: string;
559
+ });
560
+ giveFeedback(clientAddress: string, input: FeedbackInput): Promise<PublishedFeedback>;
561
+ getSummary(agentId: bigint, opts?: {
562
+ clients?: readonly string[];
563
+ tag1?: string;
564
+ tag2?: string;
565
+ }): Promise<FeedbackSummary>;
566
+ readFeedback(agentId: bigint, clientAddress: string, feedbackIndex: bigint): Promise<FeedbackEntry>;
567
+ getLastIndex(agentId: bigint, clientAddress: string): Promise<bigint>;
568
+ /** Every client that has published about the agent (lowercased here). */
569
+ getClients(agentId: bigint): Promise<readonly string[]>;
570
+ }
571
+
572
+ /**
573
+ * Turning on-chain identity facts into reputation-graph link facts.
574
+ *
575
+ * Agents are ERC-8004-CANONICAL: a registered agent's evidence keys by its
576
+ * on-chain identity (`eip155:{chainId}:{registry}/{tokenId}`), and the local
577
+ * engine id plus every wallet fold in as aliases — one on-chain identity, one
578
+ * reputation, portable across deployments; two local agents claiming the same
579
+ * registration merge.
580
+ *
581
+ * Vendors stay HOST-canonical: hosts are the enforcement key (`syncVendors`
582
+ * pushes host -> score into the engine; intents carry hosts), so a vendor's
583
+ * on-chain identity and wallets fold INTO the host row, never the other way.
584
+ *
585
+ * TRUST NOTE: these helpers verify the identity's FACTS (owner, agentWallet)
586
+ * but not the claimant's ENTITLEMENT — any doc can claim any tokenId and its
587
+ * local history folds in. That is deliberate inside one trust domain (an
588
+ * operator's own registry of agents; same-registration merge is a feature).
589
+ * A multi-tenant deployment must gate claims first (e.g. require the doc's
590
+ * wallet to be the identity's owner or agentWallet).
591
+ */
592
+ /** Structurally satisfied by ReputationGraph — this package never imports @reinconsole/graph. */
593
+ interface LinkSink {
594
+ link(canonical: ReputationSubject, alias: ReputationSubject): void;
595
+ }
596
+ interface LinkPair {
597
+ canonical: ReputationSubject;
598
+ alias: ReputationSubject;
599
+ }
600
+ /**
601
+ * PURE: the link pairs for a registered agent. Canonical = the parse->format
602
+ * normalized erc8004 id (never hand-build the string — casing is load-bearing).
603
+ * Throws Erc8004Error('bad_id') on an unparseable id.
604
+ */
605
+ declare function agentLinkPairs(facts: {
606
+ erc8004Id: string;
607
+ localId?: string;
608
+ wallets?: readonly string[];
609
+ }): LinkPair[];
610
+ /** PURE: vendors keep the host canonical; identity + wallets fold into it. */
611
+ declare function vendorLinkPairs(facts: {
612
+ host: string;
613
+ erc8004Id?: string;
614
+ wallets?: readonly string[];
615
+ }): LinkPair[];
616
+ interface LinkedIdentity {
617
+ /** The subject the party's evidence now keys by. */
618
+ canonical: ReputationSubject;
619
+ /** How many alias pairs were asserted (0 = nothing to fold). */
620
+ pairs: number;
621
+ /** 'erc8004' when on-chain facts resolved; 'local' on the lenient fallback. */
622
+ source: 'erc8004' | 'local';
623
+ }
624
+ /**
625
+ * Resolve an agent's on-chain identity facts (ownerOf + agentWallet) and
626
+ * assert the links. LENIENT by design — a world boot must not die on one
627
+ * stale doc: a missing/unparseable/foreign-registry erc8004Id, or one the
628
+ * registry does not know, falls back to today's local semantics (ULID
629
+ * canonical, doc wallets as aliases). Network errors stay loud.
630
+ *
631
+ * Callers MUST await this — the registry reads are async, and evidence
632
+ * arriving before the alias map is populated mints a stray row (S15 lesson).
633
+ */
634
+ declare function linkAgentFromRegistry(sink: LinkSink, registry: IdentityRegistryReader, agent: {
635
+ id: string;
636
+ erc8004Id?: string;
637
+ wallets: readonly {
638
+ address: string;
639
+ }[];
640
+ }): Promise<LinkedIdentity>;
641
+ /**
642
+ * Vendor twin: fold the vendor's on-chain identity (id string, owner,
643
+ * agentWallet — typically the treasury the gate pays to) into the HOST row.
644
+ * Same leniency: unresolvable identity facts assert nothing.
645
+ */
646
+ declare function linkVendorFromRegistry(sink: LinkSink, registry: IdentityRegistryReader, vendor: {
647
+ host: string;
648
+ erc8004Id: string;
649
+ }): Promise<LinkedIdentity>;
650
+
651
+ export { BASE_SEPOLIA_CHAIN_ID, BASE_SEPOLIA_REGISTRY, BASE_SEPOLIA_REPUTATION, Erc8004Error, type Erc8004ErrorCode, type FeedbackEntry, type FeedbackEvidence, type FeedbackInput, type FeedbackSummary, IDENTITY_REGISTRY_MAINNET, IDENTITY_REGISTRY_TESTNET, type IdentityRegistryReader, type LinkPair, type LinkSink, type LinkedIdentity, MockIdentityRegistry, MockReputationRegistry, type PublishedFeedback, REIN_SCORE_TAG, REPUTATION_REGISTRY_TESTNET, type RegisteredAgent, type RegistryChainReader, type RegistryRef, agentLinkPairs, feedbackClients, feedbackEvidence, getIdentityRegistryAddress, giveFeedback, identityRegistryAbi, identityRegistryReader, lastFeedbackIndex, linkAgentFromRegistry, linkVendorFromRegistry, publishAgentScore, readFeedbackEntry, readSummary, registerAgent, reputationRegistryAbi, scoreToFeedback, validateFeedback, vendorLinkPairs };