@agentic-trust/agentic-trust-sdk 1.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +213 -0
  3. package/abis/BaseRegistrarImplementation.json +1013 -0
  4. package/abis/ETHRegistrarController.json +1004 -0
  5. package/abis/IdentityRegistry.json +1044 -0
  6. package/abis/NameWrapper.json +2026 -0
  7. package/abis/PublicResolver.json +1772 -0
  8. package/abis/ReputationRegistry.json +701 -0
  9. package/abis/ValidationRegistry.json +505 -0
  10. package/dist/AIAgentAssociationClient.d.ts +58 -0
  11. package/dist/AIAgentAssociationClient.d.ts.map +1 -0
  12. package/dist/AIAgentAssociationClient.js +100 -0
  13. package/dist/AIAgentAssociationClient.js.map +1 -0
  14. package/dist/AIAgentDiscoveryClient.d.ts +673 -0
  15. package/dist/AIAgentDiscoveryClient.d.ts.map +1 -0
  16. package/dist/AIAgentDiscoveryClient.js +3184 -0
  17. package/dist/AIAgentDiscoveryClient.js.map +1 -0
  18. package/dist/AIAgentENSClient.d.ts +149 -0
  19. package/dist/AIAgentENSClient.d.ts.map +1 -0
  20. package/dist/AIAgentENSClient.js +958 -0
  21. package/dist/AIAgentENSClient.js.map +1 -0
  22. package/dist/AIAgentIdentityClient.d.ts +159 -0
  23. package/dist/AIAgentIdentityClient.d.ts.map +1 -0
  24. package/dist/AIAgentIdentityClient.js +660 -0
  25. package/dist/AIAgentIdentityClient.js.map +1 -0
  26. package/dist/AIAgentL2ENSDurenClient.d.ts +120 -0
  27. package/dist/AIAgentL2ENSDurenClient.d.ts.map +1 -0
  28. package/dist/AIAgentL2ENSDurenClient.js +735 -0
  29. package/dist/AIAgentL2ENSDurenClient.js.map +1 -0
  30. package/dist/AIAgentL2ENSNamespaceClient.d.ts +58 -0
  31. package/dist/AIAgentL2ENSNamespaceClient.d.ts.map +1 -0
  32. package/dist/AIAgentL2ENSNamespaceClient.js +214 -0
  33. package/dist/AIAgentL2ENSNamespaceClient.js.map +1 -0
  34. package/dist/AIAgentReputationClient.d.ts +69 -0
  35. package/dist/AIAgentReputationClient.d.ts.map +1 -0
  36. package/dist/AIAgentReputationClient.js +203 -0
  37. package/dist/AIAgentReputationClient.js.map +1 -0
  38. package/dist/AIAgentValidationClient.d.ts +60 -0
  39. package/dist/AIAgentValidationClient.d.ts.map +1 -0
  40. package/dist/AIAgentValidationClient.js +123 -0
  41. package/dist/AIAgentValidationClient.js.map +1 -0
  42. package/dist/OrgIdentityClient.d.ts +27 -0
  43. package/dist/OrgIdentityClient.d.ts.map +1 -0
  44. package/dist/OrgIdentityClient.js +169 -0
  45. package/dist/OrgIdentityClient.js.map +1 -0
  46. package/dist/abis/BaseRegistrarImplementation.json +1013 -0
  47. package/dist/abis/ETHRegistrarController.json +1004 -0
  48. package/dist/abis/IdentityRegistry.json +1044 -0
  49. package/dist/abis/NameWrapper.json +2026 -0
  50. package/dist/abis/PublicResolver.json +1772 -0
  51. package/dist/abis/ReputationRegistry.json +701 -0
  52. package/dist/abis/ValidationRegistry.json +505 -0
  53. package/dist/index.d.ts +25 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +24 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/schema.d.ts +13 -0
  58. package/dist/schema.d.ts.map +1 -0
  59. package/dist/schema.js +696 -0
  60. package/dist/schema.js.map +1 -0
  61. package/dist/schemaKb.d.ts +12 -0
  62. package/dist/schemaKb.d.ts.map +1 -0
  63. package/dist/schemaKb.js +593 -0
  64. package/dist/schemaKb.js.map +1 -0
  65. package/dist/tsconfig.tsbuildinfo +1 -0
  66. package/dist/utils/did8004.d.ts +57 -0
  67. package/dist/utils/did8004.d.ts.map +1 -0
  68. package/dist/utils/did8004.js +127 -0
  69. package/dist/utils/did8004.js.map +1 -0
  70. package/dist/utils/didEns.d.ts +46 -0
  71. package/dist/utils/didEns.d.ts.map +1 -0
  72. package/dist/utils/didEns.js +107 -0
  73. package/dist/utils/didEns.js.map +1 -0
  74. package/dist/utils/didEthr.d.ts +40 -0
  75. package/dist/utils/didEthr.d.ts.map +1 -0
  76. package/dist/utils/didEthr.js +87 -0
  77. package/dist/utils/didEthr.js.map +1 -0
  78. package/package.json +79 -0
@@ -0,0 +1,3184 @@
1
+ /**
2
+ * AI Agent Discovery Client
3
+ *
4
+ * Fronts for discovery-index GraphQL requests to the indexer
5
+ * Provides a clean interface for querying agent data
6
+ */
7
+ import { GraphQLClient } from 'graphql-request';
8
+ const INTROSPECTION_QUERY = `
9
+ query SearchCapabilities {
10
+ __schema {
11
+ queryType {
12
+ fields {
13
+ name
14
+ args {
15
+ name
16
+ type {
17
+ ...TypeRef
18
+ }
19
+ }
20
+ type {
21
+ ...TypeRef
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ fragment TypeRef on __Type {
28
+ kind
29
+ name
30
+ ofType {
31
+ kind
32
+ name
33
+ ofType {
34
+ kind
35
+ name
36
+ ofType {
37
+ kind
38
+ name
39
+ }
40
+ }
41
+ }
42
+ }
43
+ `;
44
+ const TYPE_FIELDS_QUERY = `
45
+ query TypeFields($name: String!) {
46
+ __type(name: $name) {
47
+ kind
48
+ fields {
49
+ name
50
+ type {
51
+ ...TypeRef
52
+ }
53
+ }
54
+ inputFields {
55
+ name
56
+ type {
57
+ ...TypeRef
58
+ }
59
+ }
60
+ }
61
+ }
62
+ fragment TypeRef on __Type {
63
+ kind
64
+ name
65
+ ofType {
66
+ kind
67
+ name
68
+ ofType {
69
+ kind
70
+ name
71
+ ofType {
72
+ kind
73
+ name
74
+ }
75
+ }
76
+ }
77
+ }
78
+ `;
79
+ function unwrapType(type) {
80
+ let current = type;
81
+ while (current && (current.kind === 'NON_NULL' || current.kind === 'LIST')) {
82
+ current = current.ofType ?? null;
83
+ }
84
+ return current ?? null;
85
+ }
86
+ function unwrapToTypeName(type) {
87
+ const named = unwrapType(type);
88
+ return named?.name ?? null;
89
+ }
90
+ function isNonNull(type) {
91
+ return type?.kind === 'NON_NULL';
92
+ }
93
+ function isListOf(type, expectedName) {
94
+ if (!type)
95
+ return false;
96
+ if (type.kind === 'NON_NULL')
97
+ return isListOf(type.ofType, expectedName);
98
+ if (type.kind === 'LIST') {
99
+ const inner = type.ofType || null;
100
+ if (!inner)
101
+ return false;
102
+ if (inner.kind === 'NON_NULL') {
103
+ return isListOf(inner.ofType, expectedName);
104
+ }
105
+ return inner.kind === 'OBJECT' && inner.name === expectedName;
106
+ }
107
+ return false;
108
+ }
109
+ /**
110
+ * AI Agent Discovery Client
111
+ *
112
+ * Provides methods for querying agent data from the indexer
113
+ */
114
+ export class AIAgentDiscoveryClient {
115
+ client;
116
+ config;
117
+ searchStrategy;
118
+ searchStrategyPromise;
119
+ typeFieldsCache = new Map();
120
+ enumValuesCache = new Map();
121
+ tokenMetadataCollectionSupported;
122
+ agentMetadataValueField;
123
+ queryFieldsCache;
124
+ queryFieldsPromise;
125
+ kbV2SupportCache;
126
+ kbV2SupportPromise;
127
+ constructor(config) {
128
+ const endpoint = (() => {
129
+ const raw = (config.endpoint || '').toString().trim().replace(/\/+$/, '');
130
+ if (!raw)
131
+ return raw;
132
+ // Force KB endpoint:
133
+ // - if caller passed ".../graphql", replace with ".../graphql-kb"
134
+ // - if caller passed base URL, append "/graphql-kb"
135
+ if (/\/graphql$/i.test(raw))
136
+ return raw.replace(/\/graphql$/i, '/graphql-kb');
137
+ if (/\/graphql-kb$/i.test(raw))
138
+ return raw;
139
+ return `${raw}/graphql-kb`;
140
+ })();
141
+ this.config = { ...config, endpoint };
142
+ const headers = {
143
+ 'Content-Type': 'application/json',
144
+ ...(config.headers || {}),
145
+ };
146
+ if (config.apiKey) {
147
+ headers['Authorization'] = `Bearer ${config.apiKey}`;
148
+ // Also support API key in header
149
+ headers['X-API-Key'] = config.apiKey;
150
+ // Some deployments use an explicit access-code header
151
+ headers['X-Access-Code'] = config.apiKey;
152
+ }
153
+ this.client = new GraphQLClient(endpoint, {
154
+ headers,
155
+ });
156
+ }
157
+ extractOperationName(query) {
158
+ const m = /\b(query|mutation)\s+([A-Za-z0-9_]+)/.exec(query);
159
+ return m?.[2] ? String(m[2]) : null;
160
+ }
161
+ decorateGraphqlError(error, query) {
162
+ const op = this.extractOperationName(query) ?? 'unknown_operation';
163
+ const endpoint = this.config.endpoint;
164
+ const status = typeof error === 'object' &&
165
+ error !== null &&
166
+ 'response' in error &&
167
+ typeof error.response?.status === 'number'
168
+ ? error.response.status
169
+ : undefined;
170
+ const gqlMessages = [];
171
+ const responseErrors = error?.response?.errors;
172
+ if (Array.isArray(responseErrors)) {
173
+ for (const e of responseErrors) {
174
+ if (typeof e?.message === 'string' && e.message.trim())
175
+ gqlMessages.push(e.message.trim());
176
+ }
177
+ }
178
+ const combined = (gqlMessages.join(' ') || (error instanceof Error ? error.message : '')).trim();
179
+ const lower = combined.toLowerCase();
180
+ const kind = status === 401 || status === 403
181
+ ? 'auth'
182
+ : status === 404
183
+ ? 'missing_endpoint'
184
+ : lower.includes('cannot query field') || lower.includes('unknown argument')
185
+ ? 'schema_mismatch'
186
+ : 'unknown';
187
+ const msg = `[DiscoveryGraphQL:${kind}] ` +
188
+ `operation=${op} status=${typeof status === 'number' ? status : 'unknown'} ` +
189
+ `endpoint=${endpoint} ` +
190
+ (combined ? `message=${combined}` : 'message=Unknown error');
191
+ const wrapped = new Error(msg);
192
+ if (error instanceof Error)
193
+ wrapped.cause = error;
194
+ return wrapped;
195
+ }
196
+ async gqlRequest(query, variables) {
197
+ try {
198
+ return await this.client.request(query, variables);
199
+ }
200
+ catch (error) {
201
+ throw this.decorateGraphqlError(error, query);
202
+ }
203
+ }
204
+ async getQueryFields() {
205
+ if (this.queryFieldsCache !== undefined) {
206
+ return this.queryFieldsCache;
207
+ }
208
+ if (this.queryFieldsPromise) {
209
+ return this.queryFieldsPromise;
210
+ }
211
+ this.queryFieldsPromise = (async () => {
212
+ try {
213
+ const data = await this.gqlRequest(INTROSPECTION_QUERY);
214
+ const fields = data.__schema?.queryType?.fields ?? [];
215
+ this.queryFieldsCache = fields;
216
+ return fields;
217
+ }
218
+ catch (error) {
219
+ console.warn('[AIAgentDiscoveryClient] Failed to introspect query fields:', error);
220
+ this.queryFieldsCache = null;
221
+ return null;
222
+ }
223
+ finally {
224
+ this.queryFieldsPromise = undefined;
225
+ }
226
+ })();
227
+ return this.queryFieldsPromise;
228
+ }
229
+ async getEnumValues(enumName) {
230
+ const cached = this.enumValuesCache.get(enumName);
231
+ if (cached !== undefined)
232
+ return cached;
233
+ try {
234
+ const query = `
235
+ query EnumValues($name: String!) {
236
+ __type(name: $name) {
237
+ kind
238
+ enumValues { name }
239
+ }
240
+ }
241
+ `;
242
+ const data = await this.gqlRequest(query, { name: enumName });
243
+ const values = data?.__type?.kind === 'ENUM' && Array.isArray(data.__type.enumValues)
244
+ ? data.__type.enumValues
245
+ .map((v) => (typeof v?.name === 'string' ? v.name : ''))
246
+ .filter((v) => v.length > 0)
247
+ : null;
248
+ this.enumValuesCache.set(enumName, values);
249
+ return values;
250
+ }
251
+ catch {
252
+ this.enumValuesCache.set(enumName, null);
253
+ return null;
254
+ }
255
+ }
256
+ async pickKbAgentOrderBy(preferred) {
257
+ const values = await this.getEnumValues('KbAgentOrderBy');
258
+ if (!values || values.length === 0)
259
+ return null;
260
+ const set = new Set(values);
261
+ for (const p of preferred) {
262
+ if (set.has(p))
263
+ return p;
264
+ }
265
+ // If nothing matches, use the first enum value to avoid schema mismatch.
266
+ return values[0] ?? null;
267
+ }
268
+ async buildKbIdentityAndAccountsSelectionBase() {
269
+ const fieldNames = async (typeName) => {
270
+ const fields = await this.getTypeFields(typeName);
271
+ return new Set((fields ?? [])
272
+ .map((f) => f?.name)
273
+ .filter((n) => typeof n === 'string' && n.length > 0));
274
+ };
275
+ const typeOfField = async (parentType, fieldName) => {
276
+ const fields = await this.getTypeFields(parentType);
277
+ const f = (fields ?? []).find((x) => x?.name === fieldName);
278
+ return unwrapToTypeName(f?.type);
279
+ };
280
+ const buildDescriptorSelection = async (typeName, opts) => {
281
+ if (!typeName)
282
+ return 'iri name description image';
283
+ const names = await fieldNames(typeName);
284
+ const parts = ['iri'];
285
+ if (names.has('name'))
286
+ parts.push('name');
287
+ if (names.has('description'))
288
+ parts.push('description');
289
+ if (names.has('image'))
290
+ parts.push('image');
291
+ // Optional payload fields (schema-dependent)
292
+ if (opts?.preferAgentCardJson && names.has('agentCardJson'))
293
+ parts.push('agentCardJson');
294
+ else if (names.has('agentCardJson'))
295
+ parts.push('agentCardJson');
296
+ return parts.join(' ');
297
+ };
298
+ // Newer KB schemas expose identities as a list (`KbAgent.identities: [KbAgentIdentity!]!`)
299
+ // instead of named fields (`identity8004`, `identityEns`, etc).
300
+ const kbAgentFields = await fieldNames('KbAgent');
301
+ const hasIdentitiesList = kbAgentFields.has('identities');
302
+ const identityDescriptorType = await typeOfField(hasIdentitiesList ? 'KbAgentIdentity' : 'KbIdentity', 'descriptor');
303
+ const identityDescriptorFields = await fieldNames(identityDescriptorType ?? 'KbIdentityDescriptor');
304
+ if (!identityDescriptorFields.has('registrationJson')) {
305
+ throw new Error('KB schema mismatch: KbIdentityDescriptor.registrationJson is required.');
306
+ }
307
+ if (!identityDescriptorFields.has('nftMetadataJson')) {
308
+ throw new Error('KB schema mismatch: KbIdentityDescriptor.nftMetadataJson is required.');
309
+ }
310
+ const identityDescriptorSelection = [
311
+ 'iri',
312
+ identityDescriptorFields.has('kind') ? 'kind' : '',
313
+ identityDescriptorFields.has('name') ? 'name' : '',
314
+ identityDescriptorFields.has('description') ? 'description' : '',
315
+ identityDescriptorFields.has('image') ? 'image' : '',
316
+ 'registrationJson',
317
+ 'nftMetadataJson',
318
+ identityDescriptorFields.has('registeredBy') ? 'registeredBy' : '',
319
+ identityDescriptorFields.has('registryNamespace') ? 'registryNamespace' : '',
320
+ identityDescriptorFields.has('skills') ? 'skills' : '',
321
+ identityDescriptorFields.has('domains') ? 'domains' : '',
322
+ ]
323
+ .filter(Boolean)
324
+ .join('\n ');
325
+ const endpointDescriptorType = await typeOfField('KbServiceEndpoint', 'descriptor');
326
+ const protocolType = await typeOfField('KbServiceEndpoint', 'protocol');
327
+ const protocolDescriptorType = protocolType ? await typeOfField(protocolType, 'descriptor') : null;
328
+ const endpointDescriptorSelection = await buildDescriptorSelection(endpointDescriptorType, {
329
+ preferAgentCardJson: false,
330
+ });
331
+ const protocolDescriptorSelection = await buildDescriptorSelection(protocolDescriptorType ?? 'KbProtocolDescriptor', {
332
+ preferAgentCardJson: true,
333
+ });
334
+ const serviceEndpointsBlock = `
335
+ serviceEndpoints {
336
+ iri
337
+ name
338
+ descriptor { ${endpointDescriptorSelection} }
339
+ protocol {
340
+ iri
341
+ protocol
342
+ serviceUrl
343
+ protocolVersion
344
+ descriptor { ${protocolDescriptorSelection} }
345
+ skills
346
+ domains
347
+ }
348
+ }`;
349
+ // Identity fields are type-dependent; introspect the concrete types directly when possible.
350
+ const identity8004Fields = await fieldNames('KbIdentity8004');
351
+ const identity8004Extras = [
352
+ identity8004Fields.has('did8004') ? 'did8004' : '',
353
+ identity8004Fields.has('agentId8004') ? 'agentId8004' : '',
354
+ identity8004Fields.has('isSmartAgent') ? 'isSmartAgent' : '',
355
+ ]
356
+ .filter(Boolean)
357
+ .join('\n ');
358
+ const identity8122Fields = await fieldNames('KbIdentity8122');
359
+ const identity8122Extras = [
360
+ identity8122Fields.has('registryName') ? 'registryName' : '',
361
+ identity8122Fields.has('collectionName') ? 'collectionName' : '',
362
+ identity8122Fields.has('registrarName') ? 'registrarName' : '',
363
+ ]
364
+ .filter(Boolean)
365
+ .join('\n ');
366
+ // New schema: identities list with inline fragments.
367
+ const identitiesListBlock = hasIdentitiesList
368
+ ? `
369
+ identities {
370
+ iri
371
+ kind
372
+ did
373
+ descriptor {
374
+ ${identityDescriptorSelection}
375
+ }
376
+ ${serviceEndpointsBlock}
377
+ ... on KbIdentity8004 {
378
+ ${identity8004Extras}
379
+ ownerAccount { iri chainId address accountType didEthr }
380
+ operatorAccount { iri chainId address accountType didEthr }
381
+ walletAccount { iri chainId address accountType didEthr }
382
+ ownerEOAAccount { iri chainId address accountType didEthr }
383
+ agentAccount { iri chainId address accountType didEthr }
384
+ }
385
+ ... on KbIdentity8122 {
386
+ did8122
387
+ agentId8122
388
+ registryAddress
389
+ ${identity8122Extras}
390
+ endpointType
391
+ endpoint
392
+ ownerAccount { iri chainId address accountType didEthr }
393
+ agentAccount { iri chainId address accountType didEthr }
394
+ }
395
+ ... on KbIdentityEns {
396
+ didEns
397
+ }
398
+ ... on KbIdentityHol {
399
+ uaidHOL
400
+ }
401
+ }`
402
+ : '';
403
+ const identity8122Block = !hasIdentitiesList && kbAgentFields.has('identity8122')
404
+ ? (() => {
405
+ return `
406
+ identity8122 {
407
+ iri
408
+ kind
409
+ did
410
+ did8122
411
+ agentId8122
412
+ registryAddress
413
+ ${identity8122Extras}
414
+ endpointType
415
+ endpoint
416
+ descriptor {
417
+ ${identityDescriptorSelection}
418
+ }
419
+ ${serviceEndpointsBlock}
420
+ ownerAccount { iri chainId address accountType didEthr }
421
+ agentAccount { iri chainId address accountType didEthr }
422
+ }`;
423
+ })()
424
+ : '';
425
+ const identity8004Block = !hasIdentitiesList && kbAgentFields.has('identity8004')
426
+ ? `
427
+ identity8004 {
428
+ iri
429
+ kind
430
+ did
431
+ ${identity8004Extras}
432
+ descriptor {
433
+ ${identityDescriptorSelection}
434
+ }
435
+ ${serviceEndpointsBlock}
436
+ ownerAccount { iri chainId address accountType didEthr }
437
+ operatorAccount { iri chainId address accountType didEthr }
438
+ walletAccount { iri chainId address accountType didEthr }
439
+ ownerEOAAccount { iri chainId address accountType didEthr }
440
+ agentAccount { iri chainId address accountType didEthr }
441
+ }`
442
+ : '';
443
+ const identityEnsBlock = !hasIdentitiesList && kbAgentFields.has('identityEns')
444
+ ? `
445
+ identityEns {
446
+ iri
447
+ kind
448
+ did
449
+ uaidHOL
450
+ descriptor {
451
+ ${identityDescriptorSelection}
452
+ }
453
+ ${serviceEndpointsBlock}
454
+ }`
455
+ : '';
456
+ const identityHolBlock = !hasIdentitiesList && kbAgentFields.has('identityHol')
457
+ ? `
458
+ identityHol {
459
+ iri
460
+ kind
461
+ did
462
+ uaidHOL
463
+ descriptor {
464
+ ${identityDescriptorSelection}
465
+ }
466
+ ${serviceEndpointsBlock}
467
+ }`
468
+ : '';
469
+ return `
470
+ ${identitiesListBlock}
471
+ ${identity8004Block}
472
+ ${identity8122Block}
473
+ ${identityEnsBlock}
474
+ ${identityHolBlock}`;
475
+ }
476
+ async hasQueryField(fieldName) {
477
+ const fields = await this.getQueryFields();
478
+ return Array.isArray(fields) ? fields.some((f) => f?.name === fieldName) : false;
479
+ }
480
+ async supportsKbV2Queries() {
481
+ if (typeof this.kbV2SupportCache === 'boolean')
482
+ return this.kbV2SupportCache;
483
+ if (this.kbV2SupportPromise)
484
+ return this.kbV2SupportPromise;
485
+ this.kbV2SupportPromise = (async () => {
486
+ try {
487
+ const fields = await this.getQueryFields();
488
+ if (!Array.isArray(fields) || fields.length === 0) {
489
+ // Introspection disabled or failed → assume legacy.
490
+ this.kbV2SupportCache = false;
491
+ return false;
492
+ }
493
+ const names = new Set(fields.map((f) => f?.name).filter(Boolean));
494
+ // GraphDB-backed KB may expose kbAgentByUaid instead of kbAgent(chainId, agentId8004).
495
+ const hasAgentLookup = names.has('kbAgent') || names.has('kbAgentByUaid');
496
+ const ok = names.has('kbAgents') && hasAgentLookup && (names.has('kbSemanticAgentSearch') || names.has('kbAgents'));
497
+ this.kbV2SupportCache = ok;
498
+ return ok;
499
+ }
500
+ catch {
501
+ this.kbV2SupportCache = false;
502
+ return false;
503
+ }
504
+ finally {
505
+ this.kbV2SupportPromise = undefined;
506
+ }
507
+ })();
508
+ return this.kbV2SupportPromise;
509
+ }
510
+ /**
511
+ * Map a KB v2 agent node into the legacy AgentData shape used across the monorepo.
512
+ */
513
+ mapKbAgentToAgentData(agent) {
514
+ const a = (agent ?? {});
515
+ const aAny = a;
516
+ // Back-compat: newer KB schema returns identities as a list.
517
+ // Normalize into the legacy named slots so the rest of this mapper stays stable.
518
+ const identitiesList = Array.isArray(aAny.identities) ? aAny.identities : null;
519
+ if (identitiesList && identitiesList.length > 0) {
520
+ const pick = (kind) => {
521
+ const k = kind.toLowerCase();
522
+ return (identitiesList.find((x) => String(x?.kind ?? '').toLowerCase() === k) ??
523
+ identitiesList.find((x) => String(x?.kind ?? '').toLowerCase() === (k === '8004' ? 'erc8004' : k)) ??
524
+ null);
525
+ };
526
+ if (!aAny.identity8004)
527
+ aAny.identity8004 = pick('8004') ?? undefined;
528
+ if (!aAny.identity8122)
529
+ aAny.identity8122 = pick('8122') ?? undefined;
530
+ if (!aAny.identityEns)
531
+ aAny.identityEns = pick('ens') ?? undefined;
532
+ if (!aAny.identityHol)
533
+ aAny.identityHol = pick('hol') ?? undefined;
534
+ if (!aAny.identity)
535
+ aAny.identity = aAny.identity8004 ?? aAny.identity8122 ?? aAny.identityEns ?? aAny.identityHol ?? undefined;
536
+ }
537
+ const toFiniteNumberOrUndefined = (value) => {
538
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
539
+ };
540
+ const parseDid8004Parts = (did) => {
541
+ if (typeof did !== 'string')
542
+ return {};
543
+ const trimmed = did.trim();
544
+ // did:8004:<chainId>:<agentId>
545
+ const parts = trimmed.split(':');
546
+ if (parts.length >= 4 && parts[0] === 'did' && parts[1] === '8004') {
547
+ const chainId = Number(parts[2]);
548
+ const agentId = Number(parts[3]);
549
+ return {
550
+ chainId: Number.isFinite(chainId) ? chainId : undefined,
551
+ agentId: Number.isFinite(agentId) ? agentId : undefined,
552
+ };
553
+ }
554
+ return {};
555
+ };
556
+ const parseDid8122Parts = (did) => {
557
+ if (typeof did !== 'string')
558
+ return {};
559
+ const trimmed = did.trim();
560
+ // did:8122:<chainId>:<registryOrAccount>:<agentId>
561
+ const parts = trimmed.split(':');
562
+ if (parts.length >= 5 && parts[0] === 'did' && parts[1] === '8122') {
563
+ const chainId = Number(parts[2]);
564
+ const agentId8122 = String(parts[4] ?? '').trim();
565
+ return {
566
+ chainId: Number.isFinite(chainId) ? chainId : undefined,
567
+ agentId8122: agentId8122 || undefined,
568
+ };
569
+ }
570
+ return {};
571
+ };
572
+ const parseUaidDid8004Parts = (uaid) => {
573
+ if (typeof uaid !== 'string')
574
+ return {};
575
+ const trimmed = uaid.trim();
576
+ // UAID did-target form: uaid:did:<methodSpecificId>;...
577
+ if (!trimmed.startsWith('uaid:did:'))
578
+ return {};
579
+ const rest = trimmed.slice('uaid:did:'.length);
580
+ const msid = rest.split(';')[0] ?? '';
581
+ // For did:8004 target, msid looks like: 8004:<chainId>:<agentId>
582
+ const parts = msid.split(':');
583
+ if (parts.length >= 3 && parts[0] === '8004') {
584
+ const chainId = Number(parts[1]);
585
+ const agentId = Number(parts[2]);
586
+ return {
587
+ chainId: Number.isFinite(chainId) ? chainId : undefined,
588
+ agentId: Number.isFinite(agentId) ? agentId : undefined,
589
+ };
590
+ }
591
+ return {};
592
+ };
593
+ const parseUaidDid8122Parts = (uaid) => {
594
+ if (typeof uaid !== 'string')
595
+ return {};
596
+ const trimmed = uaid.trim();
597
+ // UAID did-target form: uaid:did:<methodSpecificId>;...
598
+ if (!trimmed.startsWith('uaid:did:'))
599
+ return {};
600
+ const rest = trimmed.slice('uaid:did:'.length);
601
+ const msid = rest.split(';')[0] ?? '';
602
+ // For did:8122 target, msid looks like: 8122:<chainId>:<registryOrAccount>:<agentId>
603
+ const parts = msid.split(':');
604
+ if (parts.length >= 4 && parts[0] === '8122') {
605
+ const chainId = Number(parts[1]);
606
+ const agentId8122 = String(parts[3] ?? '').trim();
607
+ return {
608
+ chainId: Number.isFinite(chainId) ? chainId : undefined,
609
+ agentId8122: agentId8122 || undefined,
610
+ };
611
+ }
612
+ return {};
613
+ };
614
+ const pickAccountAddress = (...accounts) => {
615
+ for (const acc of accounts) {
616
+ const addr = acc?.address;
617
+ if (typeof addr === 'string' && addr.trim()) {
618
+ return addr.trim();
619
+ }
620
+ }
621
+ return null;
622
+ };
623
+ // KB agentTypes have evolved over time; accept both legacy + current IRIs.
624
+ const SMART_AGENT_TYPE_IRIS = [
625
+ 'https://agentictrust.io/ontology/core#AISmartAgent',
626
+ 'https://agentictrust.io/ontology/erc8004#SmartAgent',
627
+ ];
628
+ const hasSmartAgentType = Array.isArray(a.agentTypes) &&
629
+ a.agentTypes.some((t) => SMART_AGENT_TYPE_IRIS.includes(String(t ?? '').trim()));
630
+ const didPrimary = (typeof a.identity8004?.did === 'string' && String(a.identity8004.did).trim()
631
+ ? String(a.identity8004.did).trim()
632
+ : null) ??
633
+ (typeof a.identity8122?.did === 'string' && String(a.identity8122.did).trim()
634
+ ? String(a.identity8122.did).trim()
635
+ : null) ??
636
+ (typeof a.did8004 === 'string' && String(a.did8004).trim()
637
+ ? String(a.did8004).trim()
638
+ : null);
639
+ const agentId8004FromField = typeof a.agentId8004 === 'number' && Number.isFinite(a.agentId8004)
640
+ ? a.agentId8004
641
+ : null;
642
+ const agentIdFromParsedDid = didPrimary ? (parseDid8004Parts(didPrimary).agentId ?? null) : null;
643
+ const agentId8004 = agentId8004FromField ?? agentIdFromParsedDid;
644
+ // Best-effort: infer chainId from the most specific account we have.
645
+ const chainIdFromAccounts = (typeof a.identity8004?.agentAccount?.chainId === 'number'
646
+ ? a.identity8004?.agentAccount?.chainId
647
+ : null) ??
648
+ (typeof a.identity8122?.agentAccount?.chainId === 'number'
649
+ ? a.identity8122?.agentAccount?.chainId
650
+ : null) ??
651
+ (typeof a.identity8004?.walletAccount?.chainId === 'number' ? a.identity8004?.walletAccount?.chainId : null) ??
652
+ (typeof a.identity8004?.ownerEOAAccount?.chainId === 'number' ? a.identity8004?.ownerEOAAccount?.chainId : null) ??
653
+ (typeof a.identity8004?.ownerAccount?.chainId === 'number' ? a.identity8004?.ownerAccount?.chainId : null) ??
654
+ (typeof a.identity8122?.ownerAccount?.chainId === 'number'
655
+ ? a.identity8122?.ownerAccount?.chainId
656
+ : null) ??
657
+ null;
658
+ const chainId = chainIdFromAccounts ??
659
+ parseDid8004Parts(didPrimary).chainId ??
660
+ parseDid8122Parts(didPrimary).chainId ??
661
+ parseUaidDid8004Parts(a.uaid).chainId ??
662
+ parseUaidDid8122Parts(a.uaid).chainId ??
663
+ null;
664
+ const agentIdFromParsed = (didPrimary ? parseDid8004Parts(didPrimary).agentId : undefined) ??
665
+ parseUaidDid8004Parts(a.uaid).agentId ??
666
+ (didPrimary ? parseDid8122Parts(didPrimary).agentId8122 : undefined) ??
667
+ parseUaidDid8122Parts(a.uaid).agentId8122 ??
668
+ undefined;
669
+ // "agentAccount" in KB v2 is the SmartAgent-controlled account (AgentAccount).
670
+ // For non-smart agents, fall back to agent/identity wallet/owner accounts.
671
+ const agentAccount = pickAccountAddress(a.identity8004?.agentAccount, a.identity8122?.agentAccount, a.identity8004?.walletAccount, a.identity8004?.ownerEOAAccount, a.identity8004?.ownerAccount, a.identity8122?.ownerAccount) ?? null;
672
+ const isSmartAgent = (() => {
673
+ // Canonical definition: ontology type (agentTypes). Prefer this over any legacy boolean.
674
+ if (Array.isArray(a.agentTypes))
675
+ return hasSmartAgentType;
676
+ if (typeof a.isSmartAgent === 'boolean') {
677
+ return a.isSmartAgent;
678
+ }
679
+ return false;
680
+ })();
681
+ const identityOwner = pickAccountAddress(a.identity8004?.ownerAccount, a.identity8004?.ownerEOAAccount, a.identity8122?.ownerAccount) ?? null;
682
+ const registeredBy = (typeof a.identity8004?.descriptor?.registeredBy === 'string' && a.identity8004.descriptor.registeredBy.trim()
683
+ ? a.identity8004.descriptor.registeredBy.trim()
684
+ : null) ??
685
+ (typeof a.identityEns?.descriptor?.registeredBy === 'string' && a.identityEns.descriptor.registeredBy.trim()
686
+ ? a.identityEns.descriptor.registeredBy.trim()
687
+ : null) ??
688
+ (typeof a.identityHol?.descriptor?.registeredBy === 'string' && a.identityHol.descriptor.registeredBy.trim()
689
+ ? a.identityHol.descriptor.registeredBy.trim()
690
+ : null) ??
691
+ null;
692
+ const registeredByAddress = registeredBy && /^0x[a-fA-F0-9]{40}$/.test(registeredBy) ? registeredBy : null;
693
+ const isOwnerEoa = (a.identity8004?.ownerEOAAccount?.accountType ?? a.identity8004?.ownerAccount?.accountType ?? '')
694
+ .toString()
695
+ .toLowerCase()
696
+ .includes('eoa');
697
+ const identity8122DescriptorJson = typeof a.identity8122?.descriptor?.registrationJson === 'string'
698
+ ? String(a.identity8122.descriptor.registrationJson)
699
+ : null;
700
+ const identity8004DescriptorJson = typeof a.identity8004?.descriptor?.registrationJson === 'string'
701
+ ? String(a.identity8004.descriptor.registrationJson)
702
+ : identity8122DescriptorJson;
703
+ const identityEnsDescriptorJson = typeof a.identityEns?.descriptor?.registrationJson === 'string'
704
+ ? String(a.identityEns.descriptor.registrationJson)
705
+ : null;
706
+ const identityHolDescriptorJson = typeof a.identityHol?.descriptor?.registrationJson === 'string'
707
+ ? String(a.identityHol.descriptor.registrationJson)
708
+ : null;
709
+ // Legacy aggregate: prefer 8004, else ENS, else HOL.
710
+ const rawJson = identity8004DescriptorJson || identityEnsDescriptorJson || identityHolDescriptorJson || null;
711
+ const sanitizeServiceUrl = (value) => {
712
+ if (typeof value !== 'string')
713
+ return null;
714
+ const v = value.trim();
715
+ if (!v)
716
+ return null;
717
+ // Never treat identifiers as endpoints.
718
+ if (/^(uaid:|did:)/i.test(v))
719
+ return null;
720
+ try {
721
+ const u = new URL(v);
722
+ const ok = u.protocol === 'http:' || u.protocol === 'https:' || u.protocol === 'ws:' || u.protocol === 'wss:';
723
+ return ok ? v : null;
724
+ }
725
+ catch {
726
+ return null;
727
+ }
728
+ };
729
+ const identity8122OnchainMetadataJson = typeof a.identity8122?.descriptor?.nftMetadataJson === 'string' &&
730
+ String(a.identity8122.descriptor.nftMetadataJson).trim()
731
+ ? String(a.identity8122.descriptor.nftMetadataJson)
732
+ : null;
733
+ const identity8004OnchainMetadataJson = typeof a.identity8004?.descriptor?.nftMetadataJson === 'string' &&
734
+ String(a.identity8004.descriptor.nftMetadataJson).trim()
735
+ ? String(a.identity8004.descriptor.nftMetadataJson)
736
+ : identity8122OnchainMetadataJson;
737
+ const identityEnsOnchainMetadataJson = typeof a.identityEns?.descriptor?.nftMetadataJson === 'string' &&
738
+ String(a.identityEns.descriptor.nftMetadataJson).trim()
739
+ ? String(a.identityEns.descriptor.nftMetadataJson)
740
+ : null;
741
+ const identityHolOnchainMetadataJson = typeof a.identityHol?.descriptor?.nftMetadataJson === 'string' &&
742
+ String(a.identityHol.descriptor.nftMetadataJson).trim()
743
+ ? String(a.identityHol.descriptor.nftMetadataJson)
744
+ : null;
745
+ const identity8122Did = typeof a.identity8122?.did8122 === 'string' && String(a.identity8122.did8122).trim()
746
+ ? String(a.identity8122.did8122).trim()
747
+ : typeof a.identity8122?.did === 'string' && String(a.identity8122.did).trim()
748
+ ? String(a.identity8122.did).trim()
749
+ : null;
750
+ // Legacy aggregate: prefer 8004, else ENS, else HOL.
751
+ const onchainMetadataJson = identity8004OnchainMetadataJson ?? identityEnsOnchainMetadataJson ?? identityHolOnchainMetadataJson ?? null;
752
+ // Pull registration URI from onchain metadata if present (KB-backed, not on-chain call).
753
+ // Per UAID migration: expect a single canonical field name `agentUri`.
754
+ const agentUriFromOnchainMetadata = (() => {
755
+ const candidates = [
756
+ identity8004OnchainMetadataJson,
757
+ identityEnsOnchainMetadataJson,
758
+ identityHolOnchainMetadataJson,
759
+ ];
760
+ for (const raw of candidates) {
761
+ if (typeof raw !== 'string' || !raw.trim())
762
+ continue;
763
+ try {
764
+ const parsed = JSON.parse(raw);
765
+ const v = typeof parsed.agentUri === 'string' ? parsed.agentUri.trim() : '';
766
+ if (v)
767
+ return v;
768
+ }
769
+ catch {
770
+ // ignore parse errors
771
+ }
772
+ }
773
+ return null;
774
+ })();
775
+ const pickServiceUrl = (identity, protocolName) => {
776
+ const endpoints = Array.isArray(identity?.serviceEndpoints) ? identity.serviceEndpoints : [];
777
+ const target = protocolName.trim().toLowerCase();
778
+ for (const se of endpoints) {
779
+ const p = se?.protocol;
780
+ const proto = typeof p?.protocol === 'string' ? p.protocol.trim().toLowerCase() : '';
781
+ if (proto !== target)
782
+ continue;
783
+ const url = p?.serviceUrl;
784
+ const v = sanitizeServiceUrl(url);
785
+ if (v)
786
+ return v;
787
+ }
788
+ return null;
789
+ };
790
+ // Infer A2A/MCP endpoints from serviceEndpoints (canonical KB v2 shape).
791
+ const a2aEndpoint = pickServiceUrl(a.identity8004, 'a2a') ??
792
+ pickServiceUrl(a.identity8122, 'a2a') ??
793
+ pickServiceUrl(a.identityEns, 'a2a') ??
794
+ pickServiceUrl(a.identityHol, 'a2a') ??
795
+ null;
796
+ const mcpEndpoint = pickServiceUrl(a.identity8004, 'mcp') ??
797
+ pickServiceUrl(a.identity8122, 'mcp') ??
798
+ pickServiceUrl(a.identityEns, 'mcp') ??
799
+ pickServiceUrl(a.identityHol, 'mcp') ??
800
+ null;
801
+ const hasMcp = Boolean(mcpEndpoint);
802
+ // Assertions totals: prefer reviewResponses/validationResponses; fallback to legacy names
803
+ const feedbackCount = toFiniteNumberOrUndefined(a.assertions?.reviewResponses?.total);
804
+ const validationTotal = toFiniteNumberOrUndefined(a.assertions?.validationResponses?.total);
805
+ const parsedIdentity8004Descriptor = (() => {
806
+ if (typeof identity8004DescriptorJson !== 'string' || !identity8004DescriptorJson.trim())
807
+ return null;
808
+ try {
809
+ return JSON.parse(identity8004DescriptorJson);
810
+ }
811
+ catch {
812
+ return null;
813
+ }
814
+ })();
815
+ const normalized = {
816
+ agentId: agentId8004 ?? agentIdFromParsed ?? undefined,
817
+ uaid: typeof a.uaid === 'string' && a.uaid.trim().startsWith('uaid:')
818
+ ? a.uaid.trim()
819
+ : null,
820
+ agentName: typeof a.agentName === 'string' ? a.agentName : undefined,
821
+ agentTypes: Array.isArray(a.agentTypes) ? a.agentTypes : undefined,
822
+ // Preserve the full identity list when present (new KB schema).
823
+ // This enables UIs to render one tab per identifier (e.g. multiple 8004 identities across chains).
824
+ identities: identitiesList ?? undefined,
825
+ description: typeof a.agentDescription === 'string'
826
+ ? a.agentDescription
827
+ : typeof a.agentDescriptor?.description === 'string'
828
+ ? a.agentDescriptor.description
829
+ : typeof parsedIdentity8004Descriptor?.description === 'string'
830
+ ? String(parsedIdentity8004Descriptor.description)
831
+ : undefined,
832
+ image: typeof a.agentImage === 'string'
833
+ ? a.agentImage
834
+ : typeof a.agentDescriptor?.image === 'string'
835
+ ? a.agentDescriptor.image
836
+ : typeof parsedIdentity8004Descriptor?.image === 'string'
837
+ ? String(parsedIdentity8004Descriptor.image)
838
+ : undefined,
839
+ chainId: chainId ?? undefined,
840
+ createdAtBlock: typeof a.createdAtBlock === 'number' ? a.createdAtBlock : undefined,
841
+ createdAtTime: typeof a.createdAtTime === 'number'
842
+ ? a.createdAtTime
843
+ : a.createdAtTime != null
844
+ ? Number(a.createdAtTime)
845
+ : undefined,
846
+ updatedAtTime: typeof a.updatedAtTime === 'number'
847
+ ? a.updatedAtTime
848
+ : a.updatedAtTime != null
849
+ ? Number(a.updatedAtTime)
850
+ : undefined,
851
+ // Trust ledger / ATI (KB v2 fields)
852
+ trustLedgerScore: typeof a.trustLedgerTotalPoints === 'number' && Number.isFinite(a.trustLedgerTotalPoints)
853
+ ? a.trustLedgerTotalPoints
854
+ : undefined,
855
+ trustLedgerBadgeCount: typeof a.trustLedgerBadgeCount === 'number' && Number.isFinite(a.trustLedgerBadgeCount)
856
+ ? a.trustLedgerBadgeCount
857
+ : undefined,
858
+ trustLedgerBadges: Array.isArray(a.trustLedgerBadges)
859
+ ? a.trustLedgerBadges
860
+ : Array.isArray(a.trustLedgerBadgesList)
861
+ ? a.trustLedgerBadgesList
862
+ : typeof a.trustLedgerBadgesJson === 'string'
863
+ ? (() => {
864
+ try {
865
+ const parsed = JSON.parse(String(a.trustLedgerBadgesJson));
866
+ return Array.isArray(parsed) ? parsed : null;
867
+ }
868
+ catch {
869
+ return null;
870
+ }
871
+ })()
872
+ : null,
873
+ // ATI is already part of AgentData and is displayed in agent cards.
874
+ atiOverallScore: typeof a.atiOverallScore === 'number' && Number.isFinite(a.atiOverallScore)
875
+ ? a.atiOverallScore
876
+ : undefined,
877
+ atiOverallConfidence: typeof a.atiOverallConfidence === 'number' && Number.isFinite(a.atiOverallConfidence)
878
+ ? a.atiOverallConfidence
879
+ : undefined,
880
+ atiVersion: typeof a.atiVersion === 'string' ? String(a.atiVersion) : undefined,
881
+ atiComputedAt: typeof a.atiComputedAt === 'number' && Number.isFinite(a.atiComputedAt)
882
+ ? a.atiComputedAt
883
+ : undefined,
884
+ feedbackCount,
885
+ // KB assertions only provide totals (not pending/requested breakdowns). Treat them as "completed".
886
+ validationCompletedCount: validationTotal ?? undefined,
887
+ validationPendingCount: validationTotal !== undefined ? 0 : undefined,
888
+ validationRequestedCount: validationTotal ?? undefined,
889
+ agentAccount: agentAccount ?? undefined,
890
+ agentIdentityOwnerAccount: (registeredByAddress ?? identityOwner) ?? undefined,
891
+ eoaAgentIdentityOwnerAccount: registeredByAddress ?? (isOwnerEoa ? identityOwner : null),
892
+ eoaAgentAccount: isOwnerEoa ? agentAccount : null,
893
+ // Extra KB v2 account fields (flattened)
894
+ identityOwnerAccount: pickAccountAddress(a.identity8004?.ownerAccount) ?? undefined,
895
+ identityWalletAccount: pickAccountAddress(a.identity8004?.walletAccount) ?? undefined,
896
+ identityOperatorAccount: pickAccountAddress(a.identity8004?.operatorAccount) ?? undefined,
897
+ // Agent-scoped fields are not present in KB v2; mirror identity accounts for legacy consumers.
898
+ agentOwnerAccount: pickAccountAddress(a.identity8004?.ownerAccount) ?? undefined,
899
+ agentWalletAccount: pickAccountAddress(a.identity8004?.walletAccount) ?? undefined,
900
+ agentOperatorAccount: pickAccountAddress(a.identity8004?.operatorAccount) ?? undefined,
901
+ agentOwnerEOAAccount: pickAccountAddress(a.identity8004?.ownerEOAAccount) ?? undefined,
902
+ // Preserve for legacy callers, but do not use it to infer smart-agent-ness.
903
+ smartAgentAccount: pickAccountAddress(a.identity8004?.agentAccount) ?? undefined,
904
+ isSmartAgent: isSmartAgent,
905
+ // Identity tab fields (per-identity)
906
+ identity8004Did: typeof a.identity8004?.did === 'string'
907
+ ? a.identity8004.did
908
+ : typeof a.identity8122?.did === 'string'
909
+ ? a.identity8122.did
910
+ : undefined,
911
+ identity8122Did: identity8122Did ?? undefined,
912
+ identityEnsDid: typeof a.identityEns?.did === 'string' ? a.identityEns.did : undefined,
913
+ identityHolDid: typeof a.identityHol?.did === 'string' ? a.identityHol.did : undefined,
914
+ identityHolUaid: typeof a.identityHol?.uaidHOL === 'string' ? a.identityHol.uaidHOL : undefined,
915
+ identity8004DescriptorJson: identity8004DescriptorJson ?? undefined,
916
+ identity8122DescriptorJson: identity8122DescriptorJson ?? undefined,
917
+ identityEnsDescriptorJson: identityEnsDescriptorJson ?? undefined,
918
+ identityHolDescriptorJson: identityHolDescriptorJson ?? undefined,
919
+ identity8004OnchainMetadataJson: identity8004OnchainMetadataJson ?? undefined,
920
+ identity8122OnchainMetadataJson: identity8122OnchainMetadataJson ?? undefined,
921
+ identityEnsOnchainMetadataJson: identityEnsOnchainMetadataJson ?? undefined,
922
+ identityHolOnchainMetadataJson: identityHolOnchainMetadataJson ?? undefined,
923
+ identity8122: a.identity8122 ?? undefined,
924
+ didIdentity: didPrimary ?? undefined,
925
+ did: didPrimary ?? undefined,
926
+ agentUri: agentUriFromOnchainMetadata ?? undefined,
927
+ a2aEndpoint,
928
+ mcpEndpoint: mcpEndpoint ?? undefined,
929
+ mcp: hasMcp,
930
+ rawJson,
931
+ onchainMetadataJson,
932
+ // Minimal capability hints
933
+ active: true,
934
+ };
935
+ return this.normalizeAgent(normalized);
936
+ }
937
+ kbAgentSelectionCache = {};
938
+ kbAgentSelectionPromise = {};
939
+ async getKbAgentSelection(options) {
940
+ const mode = options?.includeIdentityAndAccounts ? 'full' : 'light';
941
+ const cached = this.kbAgentSelectionCache[mode];
942
+ if (typeof cached === 'string') {
943
+ return cached;
944
+ }
945
+ const inflight = this.kbAgentSelectionPromise[mode];
946
+ if (inflight) {
947
+ return inflight;
948
+ }
949
+ this.kbAgentSelectionPromise[mode] = (async () => {
950
+ try {
951
+ const fields = await this.getTypeFields('KbAgent');
952
+ const names = new Set((fields ?? []).map((f) => f?.name).filter(Boolean));
953
+ const identityAndAccountsBase = await this.buildKbIdentityAndAccountsSelectionBase();
954
+ const identityAndAccounts = identityAndAccountsBase +
955
+ (mode === 'full' && names.has('agentAccount')
956
+ ? `\n\n agentAccount { iri chainId address accountType didEthr }\n`
957
+ : '');
958
+ const baseParts = [];
959
+ if (names.has('iri'))
960
+ baseParts.push('iri');
961
+ if (names.has('uaid'))
962
+ baseParts.push('uaid');
963
+ if (names.has('agentName'))
964
+ baseParts.push('agentName');
965
+ if (names.has('agentDescription'))
966
+ baseParts.push('agentDescription');
967
+ if (names.has('agentImage'))
968
+ baseParts.push('agentImage');
969
+ if (names.has('agentDescriptor'))
970
+ baseParts.push('agentDescriptor { iri name description image }');
971
+ if (names.has('agentTypes'))
972
+ baseParts.push('agentTypes');
973
+ if (names.has('createdAtBlock'))
974
+ baseParts.push('createdAtBlock');
975
+ if (names.has('createdAtTime'))
976
+ baseParts.push('createdAtTime');
977
+ if (names.has('updatedAtTime'))
978
+ baseParts.push('updatedAtTime');
979
+ if (names.has('trustLedgerTotalPoints'))
980
+ baseParts.push('trustLedgerTotalPoints');
981
+ if (names.has('trustLedgerBadgeCount'))
982
+ baseParts.push('trustLedgerBadgeCount');
983
+ if (names.has('trustLedgerComputedAt'))
984
+ baseParts.push('trustLedgerComputedAt');
985
+ if (names.has('atiOverallScore'))
986
+ baseParts.push('atiOverallScore');
987
+ if (names.has('atiOverallConfidence'))
988
+ baseParts.push('atiOverallConfidence');
989
+ if (names.has('atiVersion'))
990
+ baseParts.push('atiVersion');
991
+ if (names.has('atiComputedAt'))
992
+ baseParts.push('atiComputedAt');
993
+ // Optional badge list (schema-dependent).
994
+ // In the KB v2 schema this is a structured list (TrustLedgerBadgeAward), so we must select subfields.
995
+ if (names.has('trustLedgerBadges')) {
996
+ baseParts.push(`
997
+ trustLedgerBadges {
998
+ iri
999
+ awardedAt
1000
+ definition {
1001
+ badgeId
1002
+ name
1003
+ iconRef
1004
+ points
1005
+ }
1006
+ }
1007
+ `);
1008
+ }
1009
+ // Older experimental schemas (if any) may expose alternative badge encodings.
1010
+ if (names.has('trustLedgerBadgesList'))
1011
+ baseParts.push('trustLedgerBadgesList');
1012
+ if (names.has('trustLedgerBadgesJson'))
1013
+ baseParts.push('trustLedgerBadgesJson');
1014
+ // Optional legacy/derived fields (may not exist in newer KB schemas)
1015
+ if (names.has('did8004'))
1016
+ baseParts.push('did8004');
1017
+ if (names.has('agentId8004'))
1018
+ baseParts.push('agentId8004');
1019
+ if (names.has('isSmartAgent'))
1020
+ baseParts.push('isSmartAgent');
1021
+ const assertionsParts = [];
1022
+ if (names.has('assertions')) {
1023
+ assertionsParts.push(`
1024
+ assertions {
1025
+ reviewResponses { total }
1026
+ validationResponses { total }
1027
+ total
1028
+ }
1029
+ `);
1030
+ }
1031
+ const assertionsBlock = assertionsParts.length > 0
1032
+ ? assertionsParts.join('\n')
1033
+ : `
1034
+ assertions {
1035
+ reviewResponses { total }
1036
+ validationResponses { total }
1037
+ total
1038
+ }
1039
+ `;
1040
+ // For agent list views (cards), we need identity descriptors to provide image/description
1041
+ // even when KB does not populate agentImage/agentDescription at the agent root.
1042
+ const identityLightBlock = (() => {
1043
+ if (!names.has('identities'))
1044
+ return '';
1045
+ return `
1046
+ identities {
1047
+ kind
1048
+ did
1049
+ descriptor {
1050
+ iri
1051
+ kind
1052
+ name
1053
+ description
1054
+ image
1055
+ registrationJson
1056
+ nftMetadataJson
1057
+ registeredBy
1058
+ registryNamespace
1059
+ skills
1060
+ domains
1061
+ }
1062
+ ... on KbIdentity8122 {
1063
+ registryAddress
1064
+ agentId8122
1065
+ registry { registryName }
1066
+ }
1067
+ }
1068
+ `;
1069
+ })();
1070
+ const selection = [
1071
+ baseParts.join('\n'),
1072
+ assertionsBlock,
1073
+ mode === 'light' ? identityLightBlock : '',
1074
+ mode === 'full' ? identityAndAccounts : '',
1075
+ ]
1076
+ .filter((part) => typeof part === 'string' && part.trim().length > 0)
1077
+ .join('\n');
1078
+ this.kbAgentSelectionCache[mode] = selection;
1079
+ return selection;
1080
+ }
1081
+ catch {
1082
+ // Fallback selection when introspection fails: keep it minimal and schema-stable.
1083
+ const selection = [
1084
+ [
1085
+ 'iri',
1086
+ 'uaid',
1087
+ 'agentName',
1088
+ 'agentDescription',
1089
+ 'agentImage',
1090
+ 'agentDescriptor { iri name description image }',
1091
+ 'agentTypes',
1092
+ 'createdAtBlock',
1093
+ 'createdAtTime',
1094
+ 'updatedAtTime',
1095
+ 'trustLedgerTotalPoints',
1096
+ 'trustLedgerBadgeCount',
1097
+ 'trustLedgerComputedAt',
1098
+ `
1099
+ trustLedgerBadges {
1100
+ iri
1101
+ awardedAt
1102
+ definition { badgeId name iconRef points }
1103
+ }
1104
+ `,
1105
+ 'atiOverallScore',
1106
+ 'atiOverallConfidence',
1107
+ 'atiVersion',
1108
+ 'atiComputedAt',
1109
+ ].join('\n'),
1110
+ `
1111
+ assertions {
1112
+ reviewResponses { total }
1113
+ validationResponses { total }
1114
+ total
1115
+ }
1116
+ `,
1117
+ mode === 'full'
1118
+ ? `
1119
+ identities { iri kind did }
1120
+ `
1121
+ : '',
1122
+ ]
1123
+ .filter((part) => typeof part === 'string' && part.trim().length > 0)
1124
+ .join('\n');
1125
+ this.kbAgentSelectionCache[mode] = selection;
1126
+ return selection;
1127
+ }
1128
+ finally {
1129
+ this.kbAgentSelectionPromise[mode] = undefined;
1130
+ }
1131
+ })();
1132
+ return this.kbAgentSelectionPromise[mode];
1133
+ }
1134
+ async supportsQueryField(fieldName) {
1135
+ const fields = await this.getQueryFields();
1136
+ if (!fields)
1137
+ return false;
1138
+ return fields.some((f) => f.name === fieldName);
1139
+ }
1140
+ normalizeAgent(agent) {
1141
+ const record = (agent ?? {});
1142
+ const toOptionalString = (value) => {
1143
+ if (value === undefined || value === null) {
1144
+ return undefined;
1145
+ }
1146
+ return String(value);
1147
+ };
1148
+ const toOptionalStringOrNull = (value) => {
1149
+ if (value === undefined) {
1150
+ return undefined;
1151
+ }
1152
+ if (value === null) {
1153
+ return null;
1154
+ }
1155
+ return String(value);
1156
+ };
1157
+ const toOptionalNumber = (value) => {
1158
+ if (value === undefined || value === null) {
1159
+ return undefined;
1160
+ }
1161
+ const numeric = typeof value === 'number' ? value : Number(value);
1162
+ return Number.isFinite(numeric) ? numeric : undefined;
1163
+ };
1164
+ const toOptionalNumberOrNull = (value) => {
1165
+ if (value === undefined) {
1166
+ return undefined;
1167
+ }
1168
+ if (value === null) {
1169
+ return null;
1170
+ }
1171
+ const numeric = typeof value === 'number' ? value : Number(value);
1172
+ return Number.isFinite(numeric) ? numeric : null;
1173
+ };
1174
+ // Parse rawJson to extract all metadata fields
1175
+ let parsedMetadata = {};
1176
+ if (record.rawJson && typeof record.rawJson === 'string') {
1177
+ try {
1178
+ const parsed = JSON.parse(record.rawJson);
1179
+ if (parsed && typeof parsed === 'object') {
1180
+ // Extract all fields from the registration JSON
1181
+ parsedMetadata = parsed;
1182
+ }
1183
+ }
1184
+ catch (error) {
1185
+ // Silently ignore JSON parse errors
1186
+ }
1187
+ }
1188
+ const normalized = {
1189
+ ...record,
1190
+ // Merge all metadata from parsed rawJson
1191
+ ...parsedMetadata,
1192
+ };
1193
+ // UAID is required (do not synthesize from did:8004).
1194
+ const uaidRaw = record.uaid;
1195
+ const uaidStr = typeof uaidRaw === 'string' ? uaidRaw.trim() : '';
1196
+ if (!uaidStr) {
1197
+ const agentId8004 = typeof record.agentId === 'string' || typeof record.agentId === 'number'
1198
+ ? String(record.agentId)
1199
+ : '';
1200
+ const chainId = typeof record.chainId === 'number' || typeof record.chainId === 'string'
1201
+ ? String(record.chainId)
1202
+ : '';
1203
+ throw new Error(`[Discovery] Missing uaid for agent (chainId=${chainId || '?'}, agentId=${agentId8004 || '?'}) from KB GraphQL. Ensure Query.kbAgents / Query.kbOwnedAgentsAllChains returns KbAgent.uaid.`);
1204
+ }
1205
+ if (!uaidStr.startsWith('uaid:')) {
1206
+ const agentId8004 = typeof record.agentId === 'string' || typeof record.agentId === 'number'
1207
+ ? String(record.agentId)
1208
+ : '';
1209
+ const chainId = typeof record.chainId === 'number' || typeof record.chainId === 'string'
1210
+ ? String(record.chainId)
1211
+ : '';
1212
+ throw new Error(`[Discovery] Invalid uaid value for agent (chainId=${chainId || '?'}, agentId=${agentId8004 || '?'}, uaid=${uaidStr}). Expected uaid to start with "uaid:". Your KB is currently returning a DID (e.g. "did:8004:...") in the uaid field.`);
1213
+ }
1214
+ normalized.uaid = uaidStr;
1215
+ const agentAccount = toOptionalString(record.agentAccount);
1216
+ if (agentAccount !== undefined) {
1217
+ normalized.agentAccount = agentAccount;
1218
+ }
1219
+ const agentIdentityOwnerAccount = toOptionalString(record.agentIdentityOwnerAccount);
1220
+ if (agentIdentityOwnerAccount !== undefined) {
1221
+ normalized.agentIdentityOwnerAccount = agentIdentityOwnerAccount;
1222
+ }
1223
+ const eoaAgentIdentityOwnerAccount = toOptionalStringOrNull(record.eoaAgentIdentityOwnerAccount);
1224
+ if (eoaAgentIdentityOwnerAccount !== undefined) {
1225
+ normalized.eoaAgentIdentityOwnerAccount = eoaAgentIdentityOwnerAccount;
1226
+ }
1227
+ const eoaAgentAccount = toOptionalStringOrNull(record.eoaAgentAccount);
1228
+ if (eoaAgentAccount !== undefined) {
1229
+ normalized.eoaAgentAccount = eoaAgentAccount;
1230
+ }
1231
+ const agentCategory = toOptionalStringOrNull(record.agentCategory);
1232
+ if (agentCategory !== undefined) {
1233
+ normalized.agentCategory = agentCategory;
1234
+ }
1235
+ const didIdentity = toOptionalStringOrNull(record.didIdentity);
1236
+ if (didIdentity !== undefined) {
1237
+ normalized.didIdentity = didIdentity;
1238
+ }
1239
+ const didAccount = toOptionalStringOrNull(record.didAccount);
1240
+ if (didAccount !== undefined) {
1241
+ normalized.didAccount = didAccount;
1242
+ }
1243
+ const didName = toOptionalStringOrNull(record.didName);
1244
+ if (didName !== undefined) {
1245
+ normalized.didName = didName;
1246
+ }
1247
+ const agentUri = toOptionalStringOrNull(record.agentUri);
1248
+ if (agentUri !== undefined) {
1249
+ normalized.agentUri = agentUri;
1250
+ }
1251
+ const validationPendingCount = toOptionalNumberOrNull(record.validationPendingCount);
1252
+ if (validationPendingCount !== undefined) {
1253
+ normalized.validationPendingCount = validationPendingCount;
1254
+ }
1255
+ const validationCompletedCount = toOptionalNumberOrNull(record.validationCompletedCount);
1256
+ if (validationCompletedCount !== undefined) {
1257
+ normalized.validationCompletedCount = validationCompletedCount;
1258
+ }
1259
+ const validationRequestedCount = toOptionalNumberOrNull(record.validationRequestedCount);
1260
+ if (validationRequestedCount !== undefined) {
1261
+ normalized.validationRequestedCount = validationRequestedCount;
1262
+ }
1263
+ const initiatedAssociationCount = toOptionalNumberOrNull(record.initiatedAssociationCount);
1264
+ if (initiatedAssociationCount !== undefined) {
1265
+ normalized.initiatedAssociationCount = initiatedAssociationCount;
1266
+ }
1267
+ const approvedAssociationCount = toOptionalNumberOrNull(record.approvedAssociationCount);
1268
+ if (approvedAssociationCount !== undefined) {
1269
+ normalized.approvedAssociationCount = approvedAssociationCount;
1270
+ }
1271
+ const atiOverallScore = toOptionalNumberOrNull(record.atiOverallScore);
1272
+ if (atiOverallScore !== undefined) {
1273
+ normalized.atiOverallScore = atiOverallScore;
1274
+ }
1275
+ const atiOverallConfidence = toOptionalNumberOrNull(record.atiOverallConfidence);
1276
+ if (atiOverallConfidence !== undefined) {
1277
+ normalized.atiOverallConfidence = atiOverallConfidence;
1278
+ }
1279
+ const atiVersion = toOptionalStringOrNull(record.atiVersion);
1280
+ if (atiVersion !== undefined) {
1281
+ normalized.atiVersion = atiVersion;
1282
+ }
1283
+ const atiComputedAt = toOptionalNumberOrNull(record.atiComputedAt);
1284
+ if (atiComputedAt !== undefined) {
1285
+ normalized.atiComputedAt = atiComputedAt;
1286
+ }
1287
+ const atiBundleJson = toOptionalStringOrNull(record.atiBundleJson);
1288
+ if (atiBundleJson !== undefined) {
1289
+ normalized.atiBundleJson = atiBundleJson;
1290
+ }
1291
+ const trustLedgerScore = toOptionalNumberOrNull(record.trustLedgerScore);
1292
+ if (trustLedgerScore !== undefined) {
1293
+ normalized.trustLedgerScore = trustLedgerScore;
1294
+ }
1295
+ const trustLedgerBadgeCount = toOptionalNumberOrNull(record.trustLedgerBadgeCount);
1296
+ if (trustLedgerBadgeCount !== undefined) {
1297
+ normalized.trustLedgerBadgeCount = trustLedgerBadgeCount;
1298
+ }
1299
+ const trustLedgerOverallRank = toOptionalNumberOrNull(record.trustLedgerOverallRank);
1300
+ if (trustLedgerOverallRank !== undefined) {
1301
+ normalized.trustLedgerOverallRank = trustLedgerOverallRank;
1302
+ }
1303
+ const trustLedgerCapabilityRank = toOptionalNumberOrNull(record.trustLedgerCapabilityRank);
1304
+ if (trustLedgerCapabilityRank !== undefined) {
1305
+ normalized.trustLedgerCapabilityRank = trustLedgerCapabilityRank;
1306
+ }
1307
+ const description = toOptionalStringOrNull(record.description);
1308
+ if (description !== undefined) {
1309
+ normalized.description = description;
1310
+ }
1311
+ const image = toOptionalStringOrNull(record.image);
1312
+ if (image !== undefined) {
1313
+ normalized.image = image;
1314
+ }
1315
+ const a2aEndpoint = toOptionalStringOrNull(record.a2aEndpoint);
1316
+ if (a2aEndpoint !== undefined) {
1317
+ normalized.a2aEndpoint = a2aEndpoint;
1318
+ }
1319
+ const agentCardJson = toOptionalStringOrNull(record.agentCardJson);
1320
+ if (agentCardJson !== undefined) {
1321
+ normalized.agentCardJson = agentCardJson;
1322
+ }
1323
+ const agentCardReadAt = toOptionalNumberOrNull(record.agentCardReadAt);
1324
+ if (agentCardReadAt !== undefined) {
1325
+ normalized.agentCardReadAt = agentCardReadAt;
1326
+ }
1327
+ const supportedTrust = toOptionalString(record.supportedTrust);
1328
+ if (supportedTrust !== undefined) {
1329
+ normalized.supportedTrust = supportedTrust;
1330
+ }
1331
+ const did = toOptionalStringOrNull(record.did);
1332
+ if (did !== undefined) {
1333
+ normalized.did = did;
1334
+ }
1335
+ // Handle agentName: prefer non-empty values from multiple sources
1336
+ // Priority: 1) direct agentName field, 2) name from parsedMetadata, 3) agentName from parsedMetadata
1337
+ let agentName = undefined;
1338
+ // Check direct agentName field (must be non-empty after trim)
1339
+ const rawAgentName = record.agentName;
1340
+ const directAgentName = typeof rawAgentName === 'string' && rawAgentName.trim().length > 0
1341
+ ? rawAgentName.trim()
1342
+ : undefined;
1343
+ if (directAgentName) {
1344
+ agentName = directAgentName;
1345
+ }
1346
+ else {
1347
+ // Check parsedMetadata for name or agentName
1348
+ const metadataName = typeof parsedMetadata.name === 'string' && parsedMetadata.name.trim().length > 0
1349
+ ? parsedMetadata.name.trim()
1350
+ : undefined;
1351
+ const metadataAgentName = typeof parsedMetadata.agentName === 'string' && parsedMetadata.agentName.trim().length > 0
1352
+ ? parsedMetadata.agentName.trim()
1353
+ : undefined;
1354
+ agentName = metadataAgentName || metadataName;
1355
+ if (agentName) {
1356
+ console.log('[AIAgentDiscoveryClient.normalizeAgent] Using metadata name:', {
1357
+ fromMetadataAgentName: !!metadataAgentName,
1358
+ fromMetadataName: !!metadataName,
1359
+ agentName,
1360
+ });
1361
+ }
1362
+ }
1363
+ // Set agentName: use found value, or undefined if original was empty and no replacement found
1364
+ // This ensures empty strings are converted to undefined
1365
+ if (agentName && agentName.length > 0) {
1366
+ normalized.agentName = agentName;
1367
+ }
1368
+ else if (typeof rawAgentName === 'string' && rawAgentName.trim().length === 0) {
1369
+ // Original was empty string, and we didn't find a replacement - set to undefined
1370
+ normalized.agentName = undefined;
1371
+ console.log('[AIAgentDiscoveryClient.normalizeAgent] Original was empty string, set to undefined');
1372
+ }
1373
+ // If rawAgentName was undefined/null, leave it as-is (don't overwrite)
1374
+ return normalized;
1375
+ }
1376
+ /**
1377
+ * List agents with a deterministic default ordering (agentId DESC).
1378
+ *
1379
+ * @param limit - Maximum number of agents to return per page
1380
+ * @param offset - Number of agents to skip
1381
+ * @returns List of agents
1382
+ */
1383
+ async listAgents(limit, offset) {
1384
+ const effectiveLimit = limit ?? 100;
1385
+ const effectiveOffset = offset ?? 0;
1386
+ const selection = await this.getKbAgentSelection({ includeIdentityAndAccounts: false });
1387
+ const orderBy = (await this.pickKbAgentOrderBy(['createdAtTime', 'updatedAtTime', 'agentId8004', 'uaid', 'agentName'])) ??
1388
+ 'agentName';
1389
+ const query = `
1390
+ query ListKbAgents($first: Int, $skip: Int, $orderBy: KbAgentOrderBy) {
1391
+ kbAgents(first: $first, skip: $skip, orderBy: $orderBy, orderDirection: DESC) {
1392
+ agents { ${selection} }
1393
+ total
1394
+ hasMore
1395
+ }
1396
+ }
1397
+ `;
1398
+ try {
1399
+ const data = await this.gqlRequest(query, {
1400
+ first: effectiveLimit,
1401
+ skip: effectiveOffset,
1402
+ orderBy,
1403
+ });
1404
+ const list = data?.kbAgents?.agents ?? [];
1405
+ return list.map((a) => this.mapKbAgentToAgentData(a));
1406
+ }
1407
+ catch (error) {
1408
+ throw error;
1409
+ }
1410
+ }
1411
+ /**
1412
+ * Run a semantic search over agents using the discovery indexer's
1413
+ * `semanticAgentSearch` GraphQL field.
1414
+ *
1415
+ * NOTE: This expects the KB GraphQL schema. If the backend does not expose
1416
+ * `kbSemanticAgentSearch`, it will throw.
1417
+ */
1418
+ async semanticAgentSearch(params) {
1419
+ const rawText = typeof params?.text === 'string' ? params.text : '';
1420
+ const text = rawText.trim();
1421
+ const rawIntentJson = typeof params?.intentJson === 'string' ? params.intentJson : '';
1422
+ const intentJson = rawIntentJson.trim();
1423
+ const topK = typeof params?.topK === 'number' && Number.isFinite(params.topK) && params.topK > 0
1424
+ ? Math.floor(params.topK)
1425
+ : undefined;
1426
+ // Nothing to search.
1427
+ if (!text && !intentJson) {
1428
+ return { total: 0, matches: [] };
1429
+ }
1430
+ const agentSelection = await this.getKbAgentSelection({ includeIdentityAndAccounts: false });
1431
+ const selection = `
1432
+ total
1433
+ matches {
1434
+ score
1435
+ matchReasons
1436
+ agent {
1437
+ ${agentSelection}
1438
+ }
1439
+ }
1440
+ `;
1441
+ const requiredSkills = Array.isArray(params.requiredSkills) ? params.requiredSkills : undefined;
1442
+ // Note: intentType is not sent to GraphQL - backend should extract it from intentJson
1443
+ // We keep it in params for logging/debugging but don't include it in the GraphQL query
1444
+ const query = `
1445
+ query KbSemanticAgentSearch($input: SemanticAgentSearchInput!) {
1446
+ kbSemanticAgentSearch(input: $input) {
1447
+ ${selection}
1448
+ }
1449
+ }
1450
+ `;
1451
+ try {
1452
+ const input = {};
1453
+ if (text)
1454
+ input.text = text;
1455
+ if (intentJson)
1456
+ input.intentJson = intentJson;
1457
+ if (typeof topK === 'number')
1458
+ input.topK = topK;
1459
+ if (Array.isArray(requiredSkills) && requiredSkills.length > 0)
1460
+ input.requiredSkills = requiredSkills;
1461
+ const data = await this.client.request(query, {
1462
+ input,
1463
+ });
1464
+ const root = data.kbSemanticAgentSearch;
1465
+ if (!root) {
1466
+ return { total: 0, matches: [] };
1467
+ }
1468
+ const total = typeof root.total === 'number' && Number.isFinite(root.total) && root.total >= 0
1469
+ ? root.total
1470
+ : Array.isArray(root.matches)
1471
+ ? root.matches.length
1472
+ : 0;
1473
+ const matches = [];
1474
+ const rawMatches = Array.isArray(root.matches) ? root.matches : [];
1475
+ for (const item of rawMatches) {
1476
+ if (!item || !item.agent) {
1477
+ continue;
1478
+ }
1479
+ const normalizedAgent = this.mapKbAgentToAgentData(item.agent);
1480
+ matches.push({
1481
+ score: typeof item.score === 'number' && Number.isFinite(item.score)
1482
+ ? item.score
1483
+ : null,
1484
+ matchReasons: Array.isArray(item.matchReasons)
1485
+ ? item.matchReasons.map((reason) => String(reason))
1486
+ : null,
1487
+ agent: normalizedAgent,
1488
+ });
1489
+ }
1490
+ return {
1491
+ total,
1492
+ matches,
1493
+ };
1494
+ }
1495
+ catch (error) {
1496
+ throw error;
1497
+ }
1498
+ }
1499
+ /**
1500
+ * Fetch OASF skills taxonomy from the discovery GraphQL endpoint (best-effort).
1501
+ * Returns [] if the backend does not expose `oasfSkills`.
1502
+ */
1503
+ async oasfSkills(params) {
1504
+ const query = `
1505
+ query OasfSkills(
1506
+ $key: String
1507
+ $nameKey: String
1508
+ $category: String
1509
+ $extendsKey: String
1510
+ $limit: Int
1511
+ $offset: Int
1512
+ $orderBy: String
1513
+ $orderDirection: String
1514
+ ) {
1515
+ oasfSkills(
1516
+ key: $key
1517
+ nameKey: $nameKey
1518
+ category: $category
1519
+ extendsKey: $extendsKey
1520
+ limit: $limit
1521
+ offset: $offset
1522
+ orderBy: $orderBy
1523
+ orderDirection: $orderDirection
1524
+ ) {
1525
+ key
1526
+ nameKey
1527
+ uid
1528
+ caption
1529
+ extendsKey
1530
+ category
1531
+ }
1532
+ }
1533
+ `;
1534
+ try {
1535
+ const variables = {};
1536
+ if (typeof params?.limit === 'number')
1537
+ variables.limit = params.limit;
1538
+ if (typeof params?.offset === 'number')
1539
+ variables.offset = params.offset;
1540
+ if (params?.orderBy)
1541
+ variables.orderBy = params.orderBy;
1542
+ if (params?.orderDirection)
1543
+ variables.orderDirection = params.orderDirection;
1544
+ if (params?.key)
1545
+ variables.key = params.key;
1546
+ if (params?.nameKey)
1547
+ variables.nameKey = params.nameKey;
1548
+ if (params?.category)
1549
+ variables.category = params.category;
1550
+ if (params?.extendsKey)
1551
+ variables.extendsKey = params.extendsKey;
1552
+ const data = await this.client.request(query, variables);
1553
+ return Array.isArray(data?.oasfSkills) ? data.oasfSkills : [];
1554
+ }
1555
+ catch (error) {
1556
+ const message = error instanceof Error ? error.message : String(error);
1557
+ if (message.includes('Cannot query field "oasfSkills"')) {
1558
+ throw new Error('Discovery KB schema missing Query.oasfSkills');
1559
+ }
1560
+ if (/Cannot return null for non-nullable field\s+Query\.oasfSkills\b/i.test(message)) {
1561
+ throw new Error('Discovery KB resolver bug: Query.oasfSkills returned null');
1562
+ }
1563
+ throw error;
1564
+ }
1565
+ }
1566
+ /**
1567
+ * Fetch OASF domains taxonomy from the discovery GraphQL endpoint (best-effort).
1568
+ * Returns [] if the backend does not expose `oasfDomains`.
1569
+ */
1570
+ async oasfDomains(params) {
1571
+ const query = `
1572
+ query OasfDomains(
1573
+ $key: String
1574
+ $nameKey: String
1575
+ $category: String
1576
+ $extendsKey: String
1577
+ $limit: Int
1578
+ $offset: Int
1579
+ $orderBy: String
1580
+ $orderDirection: String
1581
+ ) {
1582
+ oasfDomains(
1583
+ key: $key
1584
+ nameKey: $nameKey
1585
+ category: $category
1586
+ extendsKey: $extendsKey
1587
+ limit: $limit
1588
+ offset: $offset
1589
+ orderBy: $orderBy
1590
+ orderDirection: $orderDirection
1591
+ ) {
1592
+ key
1593
+ nameKey
1594
+ uid
1595
+ caption
1596
+ extendsKey
1597
+ category
1598
+ }
1599
+ }
1600
+ `;
1601
+ try {
1602
+ const variables = {};
1603
+ if (typeof params?.limit === 'number')
1604
+ variables.limit = params.limit;
1605
+ if (typeof params?.offset === 'number')
1606
+ variables.offset = params.offset;
1607
+ if (params?.orderBy)
1608
+ variables.orderBy = params.orderBy;
1609
+ if (params?.orderDirection)
1610
+ variables.orderDirection = params.orderDirection;
1611
+ if (params?.key)
1612
+ variables.key = params.key;
1613
+ if (params?.nameKey)
1614
+ variables.nameKey = params.nameKey;
1615
+ if (params?.category)
1616
+ variables.category = params.category;
1617
+ if (params?.extendsKey)
1618
+ variables.extendsKey = params.extendsKey;
1619
+ const data = await this.client.request(query, variables);
1620
+ return Array.isArray(data?.oasfDomains) ? data.oasfDomains : [];
1621
+ }
1622
+ catch (error) {
1623
+ const message = error instanceof Error ? error.message : String(error);
1624
+ if (message.includes('Cannot query field "oasfDomains"')) {
1625
+ throw new Error('Discovery KB schema missing Query.oasfDomains');
1626
+ }
1627
+ if (/Cannot return null for non-nullable field\s+Query\.oasfDomains\b/i.test(message)) {
1628
+ throw new Error('Discovery KB resolver bug: Query.oasfDomains returned null');
1629
+ }
1630
+ throw error;
1631
+ }
1632
+ }
1633
+ /**
1634
+ * Fetch intent types from the discovery GraphQL endpoint (best-effort).
1635
+ * Returns [] if the backend does not expose `intentTypes`.
1636
+ */
1637
+ async intentTypes(params) {
1638
+ const query = `
1639
+ query IntentTypes($key: String, $label: String, $limit: Int, $offset: Int) {
1640
+ intentTypes(key: $key, label: $label, limit: $limit, offset: $offset) {
1641
+ key
1642
+ label
1643
+ description
1644
+ }
1645
+ }
1646
+ `;
1647
+ try {
1648
+ const variables = {
1649
+ limit: typeof params?.limit === 'number' ? params.limit : 10000,
1650
+ offset: typeof params?.offset === 'number' ? params.offset : 0,
1651
+ };
1652
+ if (params?.key)
1653
+ variables.key = params.key;
1654
+ if (params?.label)
1655
+ variables.label = params.label;
1656
+ const data = await this.client.request(query, variables);
1657
+ return Array.isArray(data?.intentTypes) ? data.intentTypes : [];
1658
+ }
1659
+ catch (error) {
1660
+ const message = error instanceof Error ? error.message : String(error);
1661
+ if (message.includes('Cannot query field "intentTypes"')) {
1662
+ throw new Error('Discovery KB schema missing Query.intentTypes');
1663
+ }
1664
+ if (/Cannot return null for non-nullable field\s+Query\.intentTypes\b/i.test(message)) {
1665
+ throw new Error('Discovery KB resolver bug: Query.intentTypes returned null');
1666
+ }
1667
+ throw error;
1668
+ }
1669
+ }
1670
+ /**
1671
+ * Fetch task types from the discovery GraphQL endpoint (best-effort).
1672
+ * Returns [] if the backend does not expose `taskTypes`.
1673
+ */
1674
+ async taskTypes(params) {
1675
+ const query = `
1676
+ query TaskTypes($key: String, $label: String, $limit: Int, $offset: Int) {
1677
+ taskTypes(key: $key, label: $label, limit: $limit, offset: $offset) {
1678
+ key
1679
+ label
1680
+ description
1681
+ }
1682
+ }
1683
+ `;
1684
+ try {
1685
+ const variables = {
1686
+ limit: typeof params?.limit === 'number' ? params.limit : 10000,
1687
+ offset: typeof params?.offset === 'number' ? params.offset : 0,
1688
+ };
1689
+ if (params?.key)
1690
+ variables.key = params.key;
1691
+ if (params?.label)
1692
+ variables.label = params.label;
1693
+ const data = await this.client.request(query, variables);
1694
+ return Array.isArray(data?.taskTypes) ? data.taskTypes : [];
1695
+ }
1696
+ catch (error) {
1697
+ const message = error instanceof Error ? error.message : String(error);
1698
+ if (message.includes('Cannot query field "taskTypes"')) {
1699
+ throw new Error('Discovery KB schema missing Query.taskTypes');
1700
+ }
1701
+ if (/Cannot return null for non-nullable field\s+Query\.taskTypes\b/i.test(message)) {
1702
+ throw new Error('Discovery KB resolver bug: Query.taskTypes returned null');
1703
+ }
1704
+ throw error;
1705
+ }
1706
+ }
1707
+ /**
1708
+ * Fetch intent-task mappings from the discovery GraphQL endpoint (best-effort).
1709
+ * Returns [] if the backend does not expose `intentTaskMappings`.
1710
+ */
1711
+ async intentTaskMappings(params) {
1712
+ const query = `
1713
+ query IntentTaskMappings($intentKey: String, $taskKey: String, $limit: Int, $offset: Int) {
1714
+ intentTaskMappings(intentKey: $intentKey, taskKey: $taskKey, limit: $limit, offset: $offset) {
1715
+ intent { key label description }
1716
+ task { key label description }
1717
+ requiredSkills
1718
+ optionalSkills
1719
+ }
1720
+ }
1721
+ `;
1722
+ try {
1723
+ const variables = {
1724
+ limit: typeof params?.limit === 'number' ? params.limit : 10000,
1725
+ offset: typeof params?.offset === 'number' ? params.offset : 0,
1726
+ };
1727
+ if (params?.intentKey)
1728
+ variables.intentKey = params.intentKey;
1729
+ if (params?.taskKey)
1730
+ variables.taskKey = params.taskKey;
1731
+ const data = await this.client.request(query, variables);
1732
+ return Array.isArray(data?.intentTaskMappings) ? data.intentTaskMappings : [];
1733
+ }
1734
+ catch (error) {
1735
+ const message = error instanceof Error ? error.message : String(error);
1736
+ if (message.includes('Cannot query field "intentTaskMappings"')) {
1737
+ throw new Error('Discovery KB schema missing Query.intentTaskMappings');
1738
+ }
1739
+ if (/Cannot return null for non-nullable field\s+Query\.intentTaskMappings\b/i.test(message)) {
1740
+ throw new Error('Discovery KB resolver bug: Query.intentTaskMappings returned null');
1741
+ }
1742
+ throw error;
1743
+ }
1744
+ }
1745
+ async searchAgentsAdvanced(options) {
1746
+ console.log('>>>>>>>>>>>>>>>>>> searchAgentsAdvanced', options);
1747
+ const strategy = await this.detectSearchStrategy();
1748
+ const { query, params, limit, offset } = options;
1749
+ const trimmedQuery = typeof query === 'string' ? query.trim() : '';
1750
+ const hasQuery = trimmedQuery.length > 0;
1751
+ const hasParams = params && Object.keys(params).length > 0;
1752
+ if (!hasQuery && !hasParams) {
1753
+ return null;
1754
+ }
1755
+ // If no detected strategy (introspection disabled), attempt a direct list-form searchAgents call.
1756
+ // Only use this fallback if we have a query string, since the GraphQL query requires a non-null query parameter.
1757
+ // If we only have params but no query, return null to trigger local filtering fallback.
1758
+ console.log('>>>>>>>>>>>>>>>>>> 012 strategy', strategy);
1759
+ if (!strategy) {
1760
+ console.log('>>>>>>>>>>>>>>>>>> 012 hasQuery', hasQuery);
1761
+ if (hasQuery) {
1762
+ try {
1763
+ console.log('>>>>>>>>>>>>>>>>>> 012 trimmedQuery', trimmedQuery);
1764
+ console.log('>>>>>>>>>>>>>>>>>> 012 limit', limit);
1765
+ console.log('>>>>>>>>>>>>>>>>>> 012 offset', offset);
1766
+ console.log('>>>>>>>>>>>>>>>>>> 012 options.orderBy', options.orderBy);
1767
+ console.log('>>>>>>>>>>>>>>>>>> 012 options.orderDirection', options.orderDirection);
1768
+ const queryText = `
1769
+ query SearchAgentsFallback($query: String!, $limit: Int, $offset: Int, $orderBy: String, $orderDirection: String) {
1770
+ searchAgents(query: $query, limit: $limit, offset: $offset, orderBy: $orderBy, orderDirection: $orderDirection) {
1771
+ chainId
1772
+ agentId
1773
+ agentName
1774
+ agentAccount
1775
+ agentIdentityOwnerAccount
1776
+ eoaAgentIdentityOwnerAccount
1777
+ eoaAgentAccount
1778
+ agentCategory
1779
+ didIdentity
1780
+ didAccount
1781
+ didName
1782
+ agentUri
1783
+ createdAtBlock
1784
+ createdAtTime
1785
+ updatedAtTime
1786
+ type
1787
+ description
1788
+ image
1789
+ a2aEndpoint
1790
+ did
1791
+ mcp
1792
+ x402support
1793
+ active
1794
+ supportedTrust
1795
+ rawJson
1796
+ agentCardJson
1797
+ agentCardReadAt
1798
+ feedbackCount
1799
+ feedbackAverageScore
1800
+ validationPendingCount
1801
+ validationCompletedCount
1802
+ validationRequestedCount
1803
+ initiatedAssociationCount
1804
+ approvedAssociationCount
1805
+ atiOverallScore
1806
+ atiOverallConfidence
1807
+ atiVersion
1808
+ atiComputedAt
1809
+ atiBundleJson
1810
+ trustLedgerScore
1811
+ trustLedgerBadgeCount
1812
+ trustLedgerOverallRank
1813
+ trustLedgerCapabilityRank
1814
+ }
1815
+ }
1816
+ `;
1817
+ const variables = {
1818
+ query: trimmedQuery,
1819
+ limit: typeof limit === 'number' ? limit : undefined,
1820
+ offset: typeof offset === 'number' ? offset : undefined,
1821
+ orderBy: options.orderBy,
1822
+ orderDirection: options.orderDirection,
1823
+ };
1824
+ const data = await this.client.request(queryText, variables);
1825
+ const list = data?.searchAgents;
1826
+ console.log('>>>>>>>>>>>>>>>>>> 012 list.length', list?.length);
1827
+ if (list && list.length > 0) {
1828
+ console.log('>>>>>>>>>>>>>>>>>> 012 First raw agent sample:', JSON.stringify(list[0], null, 2));
1829
+ }
1830
+ if (Array.isArray(list)) {
1831
+ const normalizedList = list
1832
+ .filter(Boolean)
1833
+ .map((item) => {
1834
+ const rawAgent = item;
1835
+ const normalized = this.normalizeAgent(rawAgent);
1836
+ console.log('[AIAgentDiscoveryClient.searchAgentsAdvanced] Normalized agent (fallback):', {
1837
+ agentId: normalized.agentId,
1838
+ rawAgentName: rawAgent.agentName,
1839
+ normalizedAgentName: normalized.agentName,
1840
+ agentNameType: typeof normalized.agentName,
1841
+ hasRawJson: !!normalized.rawJson,
1842
+ });
1843
+ return normalized;
1844
+ });
1845
+ console.log('[AIAgentDiscoveryClient.searchAgentsAdvanced] Returning normalized agents (fallback):', {
1846
+ count: normalizedList.length,
1847
+ agentNames: normalizedList.map(a => ({
1848
+ agentId: a.agentId,
1849
+ agentName: a.agentName,
1850
+ agentNameType: typeof a.agentName,
1851
+ })),
1852
+ });
1853
+ // Ensure fallback respects the requested ordering, even if the
1854
+ // underlying searchAgents resolver uses its own default order.
1855
+ const orderBy = typeof options.orderBy === 'string' ? options.orderBy.trim() : undefined;
1856
+ const orderDirectionRaw = typeof options.orderDirection === 'string'
1857
+ ? options.orderDirection.toUpperCase()
1858
+ : 'DESC';
1859
+ const orderDirection = orderDirectionRaw === 'DESC' ? 'DESC' : 'ASC';
1860
+ if (orderBy === 'agentName') {
1861
+ normalizedList.sort((a, b) => {
1862
+ const aName = (a.agentName ?? '').toLowerCase();
1863
+ const bName = (b.agentName ?? '').toLowerCase();
1864
+ return orderDirection === 'ASC'
1865
+ ? aName.localeCompare(bName)
1866
+ : bName.localeCompare(aName);
1867
+ });
1868
+ }
1869
+ else if (orderBy === 'agentId') {
1870
+ normalizedList.sort((a, b) => {
1871
+ const idA = typeof a.agentId === 'number'
1872
+ ? a.agentId
1873
+ : Number(a.agentId ?? 0) || 0;
1874
+ const idB = typeof b.agentId === 'number'
1875
+ ? b.agentId
1876
+ : Number(b.agentId ?? 0) || 0;
1877
+ return orderDirection === 'ASC' ? idA - idB : idB - idA;
1878
+ });
1879
+ }
1880
+ else if (orderBy === 'createdAtTime') {
1881
+ normalizedList.sort((a, b) => {
1882
+ const tA = typeof a.createdAtTime === 'number'
1883
+ ? a.createdAtTime
1884
+ : Number(a.createdAtTime ?? 0) || 0;
1885
+ const tB = typeof b.createdAtTime === 'number'
1886
+ ? b.createdAtTime
1887
+ : Number(b.createdAtTime ?? 0) || 0;
1888
+ return orderDirection === 'ASC' ? tA - tB : tB - tA;
1889
+ });
1890
+ }
1891
+ else if (orderBy === 'createdAtBlock') {
1892
+ normalizedList.sort((a, b) => {
1893
+ const bA = typeof a.createdAtBlock === 'number'
1894
+ ? a.createdAtBlock
1895
+ : Number(a.createdAtBlock ?? 0) || 0;
1896
+ const bB = typeof b.createdAtBlock === 'number'
1897
+ ? b.createdAtBlock
1898
+ : Number(b.createdAtBlock ?? 0) || 0;
1899
+ return orderDirection === 'ASC' ? bA - bB : bB - bA;
1900
+ });
1901
+ }
1902
+ console.log('>>>>>>>>>>>>>>>>>> 345 AdvancedSearch', normalizedList);
1903
+ return { agents: normalizedList, total: undefined };
1904
+ }
1905
+ }
1906
+ catch (error) {
1907
+ console.warn('[AIAgentDiscoveryClient] Fallback searchAgents call failed:', error);
1908
+ }
1909
+ }
1910
+ // If no strategy and no query (only params), return null to trigger local filtering fallback
1911
+ return null;
1912
+ }
1913
+ const variables = {};
1914
+ const variableDefinitions = [];
1915
+ const argumentAssignments = [];
1916
+ const agentSelection = `
1917
+ chainId
1918
+ agentId
1919
+ agentName
1920
+ agentAccount
1921
+ agentIdentityOwnerAccount
1922
+ eoaAgentIdentityOwnerAccount
1923
+ eoaAgentAccount
1924
+ agentCategory
1925
+ didIdentity
1926
+ didAccount
1927
+ didName
1928
+ agentUri
1929
+ createdAtBlock
1930
+ createdAtTime
1931
+ updatedAtTime
1932
+ type
1933
+ description
1934
+ image
1935
+ a2aEndpoint
1936
+ did
1937
+ mcp
1938
+ x402support
1939
+ active
1940
+ supportedTrust
1941
+ rawJson
1942
+ feedbackCount
1943
+ feedbackAverageScore
1944
+ validationPendingCount
1945
+ validationCompletedCount
1946
+ validationRequestedCount
1947
+ `;
1948
+ const addStringArg = (arg, value) => {
1949
+ if (!arg)
1950
+ return !value;
1951
+ if (!value) {
1952
+ return arg.isNonNull ? false : true;
1953
+ }
1954
+ const typeName = arg.typeName ?? 'String';
1955
+ variableDefinitions.push(`$${arg.name}: ${typeName}${arg.isNonNull ? '!' : ''}`);
1956
+ argumentAssignments.push(`${arg.name}: $${arg.name}`);
1957
+ variables[arg.name] = value;
1958
+ return true;
1959
+ };
1960
+ const addInputArg = (arg, value) => {
1961
+ if (!arg)
1962
+ return !value;
1963
+ if (!value || Object.keys(value).length === 0) {
1964
+ return arg.isNonNull ? false : true;
1965
+ }
1966
+ const typeName = arg.typeName ?? 'JSON';
1967
+ variableDefinitions.push(`$${arg.name}: ${typeName}${arg.isNonNull ? '!' : ''}`);
1968
+ argumentAssignments.push(`${arg.name}: $${arg.name}`);
1969
+ variables[arg.name] = value;
1970
+ return true;
1971
+ };
1972
+ const addIntArg = (arg, value) => {
1973
+ if (!arg)
1974
+ return;
1975
+ if (value === undefined || value === null) {
1976
+ if (arg.isNonNull) {
1977
+ return;
1978
+ }
1979
+ return;
1980
+ }
1981
+ const typeName = arg.typeName ?? 'Int';
1982
+ variableDefinitions.push(`$${arg.name}: ${typeName}${arg.isNonNull ? '!' : ''}`);
1983
+ argumentAssignments.push(`${arg.name}: $${arg.name}`);
1984
+ variables[arg.name] = value;
1985
+ };
1986
+ if (strategy.kind === 'connection') {
1987
+ // Add query arg only if we have a query, or if queryArg is optional
1988
+ // If queryArg is required (non-null) but we don't have a query, only proceed if we have params
1989
+ const queryArgAdded = addStringArg(strategy.queryArg, hasQuery ? trimmedQuery : undefined);
1990
+ if (!queryArgAdded && strategy.queryArg?.isNonNull && !hasParams) {
1991
+ // Required query arg but no query and no params - can't proceed
1992
+ return null;
1993
+ }
1994
+ // Add filter arg if we have params
1995
+ const filterArgAdded = addInputArg(strategy.filterArg, hasParams ? params : undefined);
1996
+ if (!filterArgAdded && strategy.filterArg?.isNonNull && !hasQuery) {
1997
+ // Required filter arg but no params and no query - can't proceed
1998
+ return null;
1999
+ }
2000
+ // If neither query nor params were added, and both are optional, we need at least one
2001
+ if (!queryArgAdded && !filterArgAdded && (!strategy.queryArg || !strategy.filterArg)) {
2002
+ return null;
2003
+ }
2004
+ addIntArg(strategy.limitArg, typeof limit === 'number' ? limit : undefined);
2005
+ addIntArg(strategy.offsetArg, typeof offset === 'number' ? offset : undefined);
2006
+ addStringArg(strategy.orderByArg, options.orderBy);
2007
+ addStringArg(strategy.orderDirectionArg, options.orderDirection);
2008
+ if (argumentAssignments.length === 0) {
2009
+ return null;
2010
+ }
2011
+ console.log('>>>>>>>>>>>>>>>>>> AdvancedSearch', variableDefinitions, argumentAssignments);
2012
+ const queryText = `
2013
+ query AdvancedSearch(${variableDefinitions.join(', ')}) {
2014
+ ${strategy.fieldName}(${argumentAssignments.join(', ')}) {
2015
+ ${strategy.totalFieldName ? `${strategy.totalFieldName}` : ''}
2016
+ ${strategy.listFieldName} {
2017
+ chainId
2018
+ agentId
2019
+ agentAccount
2020
+ agentName
2021
+ agentIdentityOwnerAccount
2022
+ eoaAgentIdentityOwnerAccount
2023
+ eoaAgentAccount
2024
+ agentCategory
2025
+ didIdentity
2026
+ didAccount
2027
+ didName
2028
+ agentUri
2029
+ createdAtBlock
2030
+ createdAtTime
2031
+ updatedAtTime
2032
+ type
2033
+ description
2034
+ image
2035
+ a2aEndpoint
2036
+ did
2037
+ mcp
2038
+ x402support
2039
+ active
2040
+ supportedTrust
2041
+ rawJson
2042
+ agentCardJson
2043
+ agentCardReadAt
2044
+ }
2045
+ }
2046
+ }
2047
+ `;
2048
+ try {
2049
+ const data = await this.client.request(queryText, variables);
2050
+ const node = data?.[strategy.fieldName];
2051
+ if (!node)
2052
+ return null;
2053
+ const list = node?.[strategy.listFieldName];
2054
+ if (!Array.isArray(list))
2055
+ return null;
2056
+ const totalValue = typeof strategy.totalFieldName === 'string' ? node?.[strategy.totalFieldName] : undefined;
2057
+ console.log('>>>>>>>>>>>>>>>>>> 123 AdvancedSearch', list);
2058
+ return {
2059
+ agents: list.filter(Boolean),
2060
+ total: typeof totalValue === 'number' ? totalValue : undefined,
2061
+ };
2062
+ }
2063
+ catch (error) {
2064
+ console.warn('[AIAgentDiscoveryClient] Advanced connection search failed:', error);
2065
+ this.searchStrategy = null;
2066
+ return null;
2067
+ }
2068
+ }
2069
+ if (strategy.kind === 'list') {
2070
+ console.log('>>>>>>>>>>>>>>>>>> AdvancedSearchList', variableDefinitions, argumentAssignments);
2071
+ if (!addStringArg(strategy.queryArg, hasQuery ? trimmedQuery : undefined)) {
2072
+ return null;
2073
+ }
2074
+ addIntArg(strategy.limitArg, typeof limit === 'number' ? limit : undefined);
2075
+ addIntArg(strategy.offsetArg, typeof offset === 'number' ? offset : undefined);
2076
+ addStringArg(strategy.orderByArg, options.orderBy);
2077
+ addStringArg(strategy.orderDirectionArg, options.orderDirection);
2078
+ if (argumentAssignments.length === 0) {
2079
+ return null;
2080
+ }
2081
+ const queryText = `
2082
+ query AdvancedSearchList(${variableDefinitions.join(', ')}) {
2083
+ ${strategy.fieldName}(${argumentAssignments.join(', ')}) {
2084
+ ${agentSelection}
2085
+ }
2086
+ }
2087
+ `;
2088
+ try {
2089
+ const data = await this.client.request(queryText, variables);
2090
+ const list = data?.[strategy.fieldName];
2091
+ if (!Array.isArray(list))
2092
+ return null;
2093
+ const agents = list
2094
+ .filter(Boolean)
2095
+ .map((item) => {
2096
+ const rawAgent = item;
2097
+ const normalized = this.normalizeAgent(rawAgent);
2098
+ console.log('[AIAgentDiscoveryClient.searchAgentsAdvanced] Normalized agent (strategy):', {
2099
+ agentId: normalized.agentId,
2100
+ rawAgentName: rawAgent.agentName,
2101
+ normalizedAgentName: normalized.agentName,
2102
+ agentNameType: typeof normalized.agentName,
2103
+ hasRawJson: !!normalized.rawJson,
2104
+ });
2105
+ return normalized;
2106
+ });
2107
+ console.log('[AIAgentDiscoveryClient.searchAgentsAdvanced] Returning normalized agents (strategy):', {
2108
+ count: agents.length,
2109
+ agentNames: agents.map(a => ({
2110
+ agentId: a.agentId,
2111
+ agentName: a.agentName,
2112
+ agentNameType: typeof a.agentName,
2113
+ })),
2114
+ });
2115
+ return {
2116
+ agents,
2117
+ total: undefined,
2118
+ };
2119
+ }
2120
+ catch (error) {
2121
+ console.warn('[AIAgentDiscoveryClient] Advanced list search failed:', error);
2122
+ this.searchStrategy = null;
2123
+ return null;
2124
+ }
2125
+ }
2126
+ return null;
2127
+ }
2128
+ /**
2129
+ * Search agents using the strongly-typed AgentWhereInput / searchAgentsGraph API.
2130
+ * This is tailored to the indexer schema that exposes AgentWhereInput and
2131
+ * searchAgentsGraph(where:, first:, skip:, orderBy:, orderDirection:).
2132
+ */
2133
+ async searchAgentsGraph(options) {
2134
+ const selection = await this.getKbAgentSelection({ includeIdentityAndAccounts: false });
2135
+ const query = `
2136
+ query KbAgents(
2137
+ $where: KbAgentWhereInput
2138
+ $first: Int
2139
+ $skip: Int
2140
+ $orderBy: KbAgentOrderBy
2141
+ $orderDirection: OrderDirection
2142
+ ) {
2143
+ kbAgents(
2144
+ where: $where
2145
+ first: $first
2146
+ skip: $skip
2147
+ orderBy: $orderBy
2148
+ orderDirection: $orderDirection
2149
+ ) {
2150
+ agents {
2151
+ ${selection}
2152
+ }
2153
+ total
2154
+ hasMore
2155
+ }
2156
+ }
2157
+ `;
2158
+ // Default ordering when not explicitly provided: newest agents first
2159
+ // by agentId DESC.
2160
+ const effectiveOrderDirection = (options.orderDirection ?? 'DESC').toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
2161
+ // Map legacy orderBy to KB orderBy (schema-aware).
2162
+ const effectiveOrderByKb = options.orderBy === 'agentName'
2163
+ ? ((await this.pickKbAgentOrderBy(['agentName'])) ?? 'agentName')
2164
+ : ((await this.pickKbAgentOrderBy(['createdAtTime', 'updatedAtTime', 'agentId8004', 'uaid', 'agentName'])) ??
2165
+ 'agentName');
2166
+ const whereIn = (options.where ?? {});
2167
+ const kbWhere = {};
2168
+ // chainId: v1 can provide chainId or chainId_in.
2169
+ if (typeof whereIn.chainId === 'number')
2170
+ kbWhere.chainId = whereIn.chainId;
2171
+ if (!('chainId' in kbWhere) && Array.isArray(whereIn.chainId_in) && whereIn.chainId_in.length === 1) {
2172
+ const v = whereIn.chainId_in[0];
2173
+ if (typeof v === 'number')
2174
+ kbWhere.chainId = v;
2175
+ }
2176
+ // agentId: v1 can provide agentId or agentId_in.
2177
+ const agentIdCandidate = typeof whereIn.agentId === 'string' || typeof whereIn.agentId === 'number'
2178
+ ? whereIn.agentId
2179
+ : Array.isArray(whereIn.agentId_in) && whereIn.agentId_in.length === 1
2180
+ ? whereIn.agentId_in[0]
2181
+ : undefined;
2182
+ if (typeof agentIdCandidate === 'string' || typeof agentIdCandidate === 'number') {
2183
+ const n = Number(agentIdCandidate);
2184
+ if (Number.isFinite(n)) {
2185
+ // KB v2 prefers a string matcher instead of numeric agentId8004.
2186
+ kbWhere.agentIdentifierMatch = String(Math.floor(n));
2187
+ }
2188
+ }
2189
+ // did: v1 can provide did/didIdentity or did_contains_nocase.
2190
+ const didCandidate = (typeof whereIn.didIdentity === 'string' && whereIn.didIdentity) ||
2191
+ (typeof whereIn.did === 'string' && whereIn.did) ||
2192
+ (typeof whereIn.did_contains_nocase === 'string' && whereIn.did_contains_nocase) ||
2193
+ undefined;
2194
+ if (typeof didCandidate === 'string' && didCandidate.trim().startsWith('did:')) {
2195
+ kbWhere.did8004 = didCandidate.trim();
2196
+ }
2197
+ // agentName: v1 commonly uses agentName_contains_nocase.
2198
+ const nameCandidate = (typeof whereIn.agentName_contains === 'string' && whereIn.agentName_contains) ||
2199
+ (typeof whereIn.agentName === 'string' && whereIn.agentName) ||
2200
+ (typeof whereIn.agentName_contains_nocase === 'string' && whereIn.agentName_contains_nocase) ||
2201
+ undefined;
2202
+ if (typeof nameCandidate === 'string' && nameCandidate.trim()) {
2203
+ kbWhere.agentName_contains = nameCandidate.trim();
2204
+ }
2205
+ // A2A: v1 uses hasA2aEndpoint or a2aEndpoint_not: null.
2206
+ const hasA2aEndpoint = (typeof whereIn.hasA2aEndpoint === 'boolean' && whereIn.hasA2aEndpoint) ||
2207
+ (whereIn.a2aEndpoint_not === null);
2208
+ if (hasA2aEndpoint) {
2209
+ kbWhere.hasA2a = true;
2210
+ }
2211
+ // Assertions: allow KB-native hasAssertions filter.
2212
+ if (typeof whereIn.hasAssertions === 'boolean') {
2213
+ kbWhere.hasAssertions = whereIn.hasAssertions;
2214
+ }
2215
+ // Aggregated assertion minimums (KB v2).
2216
+ // The v1 search layer expresses these as *_gte fields; map them onto KbAgentWhereInput.
2217
+ const minFeedback = typeof whereIn.feedbackCount_gte === 'number'
2218
+ ? whereIn.feedbackCount_gte
2219
+ : undefined;
2220
+ const minValidations = typeof whereIn.validationCompletedCount_gte === 'number'
2221
+ ? whereIn.validationCompletedCount_gte
2222
+ : undefined;
2223
+ const minAvgRating = typeof whereIn.feedbackAverageScore_gte === 'number'
2224
+ ? whereIn.feedbackAverageScore_gte
2225
+ : undefined;
2226
+ const hasNumericFiltersRequested = (typeof minFeedback === 'number' && Number.isFinite(minFeedback) && minFeedback > 0) ||
2227
+ (typeof minValidations === 'number' && Number.isFinite(minValidations) && minValidations > 0) ||
2228
+ (typeof minAvgRating === 'number' && Number.isFinite(minAvgRating) && minAvgRating > 0);
2229
+ const kbWhereFieldNames = hasNumericFiltersRequested
2230
+ ? new Set((await this.getTypeFields('KbAgentWhereInput') ?? [])
2231
+ .map((f) => f?.name)
2232
+ .filter((name) => typeof name === 'string' && name.length > 0))
2233
+ : new Set();
2234
+ const requireKbWhereField = (fieldName, context) => {
2235
+ if (!kbWhereFieldNames.has(fieldName)) {
2236
+ const hint = Array.from(kbWhereFieldNames)
2237
+ .filter((n) => /feedback|validation|score/i.test(n))
2238
+ .slice(0, 25);
2239
+ throw new Error(`[Discovery][graphql-kb] Unsupported filter (${context}). ` +
2240
+ `KbAgentWhereInput is missing field "${fieldName}". ` +
2241
+ `Available relevant fields: ${hint.join(', ') || '(none)'}`);
2242
+ }
2243
+ };
2244
+ const pickKbWhereField = (candidates) => {
2245
+ for (const name of candidates) {
2246
+ if (kbWhereFieldNames.has(name))
2247
+ return name;
2248
+ }
2249
+ return null;
2250
+ };
2251
+ if (typeof minFeedback === 'number' && Number.isFinite(minFeedback) && minFeedback > 0) {
2252
+ requireKbWhereField('minReviewAssertionCount', 'minFeedbackCount');
2253
+ requireKbWhereField('hasReviews', 'minFeedbackCount');
2254
+ kbWhere.minReviewAssertionCount = Math.floor(minFeedback);
2255
+ kbWhere.hasReviews = true;
2256
+ }
2257
+ if (typeof minValidations === 'number' && Number.isFinite(minValidations) && minValidations > 0) {
2258
+ requireKbWhereField('minValidationAssertionCount', 'minValidationCompletedCount');
2259
+ requireKbWhereField('hasValidations', 'minValidationCompletedCount');
2260
+ kbWhere.minValidationAssertionCount = Math.floor(minValidations);
2261
+ kbWhere.hasValidations = true;
2262
+ }
2263
+ if (typeof minAvgRating === 'number' && Number.isFinite(minAvgRating) && minAvgRating > 0) {
2264
+ const chosen = pickKbWhereField([
2265
+ 'minFeedbackAverageScore8004',
2266
+ 'minFeedbackAverageScore',
2267
+ 'feedbackAverageScore_gte',
2268
+ ]);
2269
+ if (!chosen) {
2270
+ const hint = Array.from(kbWhereFieldNames)
2271
+ .filter((n) => /review|feedback|score|average/i.test(n))
2272
+ .slice(0, 25);
2273
+ throw new Error(`[Discovery][graphql-kb] Unsupported filter (minFeedbackAverageScore). ` +
2274
+ `KbAgentWhereInput does not expose a review average score filter. ` +
2275
+ `Available relevant fields: ${hint.join(', ') || '(none)'}`);
2276
+ }
2277
+ kbWhere[chosen] = minAvgRating;
2278
+ if (kbWhereFieldNames.has('hasReviews')) {
2279
+ kbWhere.hasReviews = true;
2280
+ }
2281
+ }
2282
+ // Smart agent: v1 may provide isSmartAgent.
2283
+ if (typeof whereIn.isSmartAgent === 'boolean') {
2284
+ const kbWhereInputFields = new Set((await this.getTypeFields('KbAgentWhereInput') ?? [])
2285
+ .map((f) => f?.name)
2286
+ .filter((name) => typeof name === 'string' && name.length > 0));
2287
+ if (kbWhereInputFields.has('isSmartAgent')) {
2288
+ kbWhere.isSmartAgent = whereIn.isSmartAgent;
2289
+ }
2290
+ }
2291
+ const variables = {
2292
+ where: Object.keys(kbWhere).length ? kbWhere : undefined,
2293
+ first: typeof options.first === 'number' ? options.first : undefined,
2294
+ skip: typeof options.skip === 'number' ? options.skip : undefined,
2295
+ orderBy: effectiveOrderByKb,
2296
+ orderDirection: effectiveOrderDirection,
2297
+ };
2298
+ const data = await this.client.request(query, variables);
2299
+ const result = data.kbAgents ?? { agents: [], total: 0, hasMore: false };
2300
+ const agents = (result.agents ?? []).map((agent) => this.mapKbAgentToAgentData(agent));
2301
+ return {
2302
+ agents,
2303
+ total: typeof result.total === 'number' ? result.total : agents.length,
2304
+ hasMore: Boolean(result.hasMore),
2305
+ };
2306
+ }
2307
+ async erc8122Registries(params) {
2308
+ const chainId = Math.floor(params.chainId);
2309
+ if (!Number.isFinite(chainId)) {
2310
+ throw new Error('erc8122Registries requires chainId');
2311
+ }
2312
+ const first = typeof params.first === 'number' && Number.isFinite(params.first) && params.first > 0
2313
+ ? Math.floor(params.first)
2314
+ : 50;
2315
+ const skip = typeof params.skip === 'number' && Number.isFinite(params.skip) && params.skip >= 0
2316
+ ? Math.floor(params.skip)
2317
+ : 0;
2318
+ const query = `
2319
+ query Registries8122($chainId: Int!, $first: Int, $skip: Int) {
2320
+ kbErc8122Registries(chainId: $chainId, first: $first, skip: $skip) {
2321
+ iri
2322
+ chainId
2323
+ registryAddress
2324
+ registrarAddress
2325
+ registryName
2326
+ registryImplementationAddress
2327
+ registrarImplementationAddress
2328
+ registeredAgentCount
2329
+ lastAgentUpdatedAtTime
2330
+ }
2331
+ }
2332
+ `;
2333
+ const data = await this.gqlRequest(query, { chainId, first, skip });
2334
+ const rows = Array.isArray(data?.kbErc8122Registries) ? data.kbErc8122Registries : [];
2335
+ const out = [];
2336
+ for (const r of rows) {
2337
+ if (!r || typeof r !== 'object')
2338
+ continue;
2339
+ const registryAddressRaw = r.registryAddress;
2340
+ const registryAddress = typeof registryAddressRaw === 'string' ? registryAddressRaw : '';
2341
+ if (!registryAddress)
2342
+ continue;
2343
+ out.push({
2344
+ iri: typeof r.iri === 'string' ? r.iri : null,
2345
+ chainId,
2346
+ registryAddress,
2347
+ registrarAddress: typeof r.registrarAddress === 'string' ? r.registrarAddress : null,
2348
+ registryName: typeof r.registryName === 'string' ? r.registryName : null,
2349
+ registryImplementationAddress: typeof r.registryImplementationAddress === 'string'
2350
+ ? r.registryImplementationAddress
2351
+ : null,
2352
+ registrarImplementationAddress: typeof r.registrarImplementationAddress === 'string'
2353
+ ? r.registrarImplementationAddress
2354
+ : null,
2355
+ registeredAgentCount: typeof r.registeredAgentCount === 'number' ? r.registeredAgentCount : null,
2356
+ lastAgentUpdatedAtTime: typeof r.lastAgentUpdatedAtTime === 'number' ? r.lastAgentUpdatedAtTime : null,
2357
+ });
2358
+ }
2359
+ return out;
2360
+ }
2361
+ async detectSearchStrategy() {
2362
+ if (this.searchStrategy !== undefined) {
2363
+ return this.searchStrategy;
2364
+ }
2365
+ if (this.searchStrategyPromise) {
2366
+ return this.searchStrategyPromise;
2367
+ }
2368
+ this.searchStrategyPromise = (async () => {
2369
+ try {
2370
+ const data = await this.client.request(INTROSPECTION_QUERY);
2371
+ const fields = data.__schema?.queryType?.fields ?? [];
2372
+ const candidateNames = ['searchAgentsAdvanced', 'searchAgents'];
2373
+ for (const candidate of candidateNames) {
2374
+ const field = fields.find((f) => f.name === candidate);
2375
+ if (!field)
2376
+ continue;
2377
+ const strategy = await this.buildStrategyFromField(field);
2378
+ if (strategy) {
2379
+ this.searchStrategy = strategy;
2380
+ return strategy;
2381
+ }
2382
+ }
2383
+ }
2384
+ catch (error) {
2385
+ console.warn('[AIAgentDiscoveryClient] Failed to introspect search capabilities:', error);
2386
+ }
2387
+ finally {
2388
+ this.searchStrategyPromise = undefined;
2389
+ }
2390
+ this.searchStrategy = null;
2391
+ return null;
2392
+ })();
2393
+ return this.searchStrategyPromise;
2394
+ }
2395
+ async buildStrategyFromField(field) {
2396
+ const baseReturn = unwrapType(field.type);
2397
+ if (!baseReturn)
2398
+ return null;
2399
+ const limitArg = field.args.find((arg) => arg.name === 'limit') ??
2400
+ field.args.find((arg) => arg.name === 'first');
2401
+ const offsetArg = field.args.find((arg) => arg.name === 'offset') ??
2402
+ field.args.find((arg) => arg.name === 'skip');
2403
+ const queryArg = field.args.find((arg) => arg.name === 'query') ??
2404
+ field.args.find((arg) => arg.name === 'term') ??
2405
+ field.args.find((arg) => arg.name === 'search');
2406
+ const filterArg = field.args.find((arg) => arg.name === 'params') ??
2407
+ field.args.find((arg) => arg.name === 'filters');
2408
+ const orderByArg = field.args.find((arg) => arg.name === 'orderBy');
2409
+ const orderDirectionArg = field.args.find((arg) => arg.name === 'orderDirection');
2410
+ if (baseReturn.kind === 'OBJECT' && baseReturn.name) {
2411
+ const connectionFields = await this.getTypeFields(baseReturn.name);
2412
+ if (!connectionFields) {
2413
+ return null;
2414
+ }
2415
+ const listField = connectionFields.find((f) => isListOf(f.type, 'Agent'));
2416
+ if (!listField) {
2417
+ return null;
2418
+ }
2419
+ const totalField = connectionFields.find((f) => f.name === 'total') ??
2420
+ connectionFields.find((f) => f.name === 'totalCount') ??
2421
+ connectionFields.find((f) => f.name === 'count');
2422
+ return {
2423
+ kind: 'connection',
2424
+ fieldName: field.name,
2425
+ listFieldName: listField.name,
2426
+ totalFieldName: totalField?.name,
2427
+ queryArg: queryArg
2428
+ ? {
2429
+ name: queryArg.name,
2430
+ typeName: unwrapToTypeName(queryArg.type),
2431
+ isNonNull: isNonNull(queryArg.type),
2432
+ }
2433
+ : undefined,
2434
+ filterArg: filterArg
2435
+ ? {
2436
+ name: filterArg.name,
2437
+ typeName: unwrapToTypeName(filterArg.type),
2438
+ isNonNull: isNonNull(filterArg.type),
2439
+ }
2440
+ : undefined,
2441
+ limitArg: limitArg
2442
+ ? {
2443
+ name: limitArg.name,
2444
+ typeName: unwrapToTypeName(limitArg.type),
2445
+ isNonNull: isNonNull(limitArg.type),
2446
+ }
2447
+ : undefined,
2448
+ offsetArg: offsetArg
2449
+ ? {
2450
+ name: offsetArg.name,
2451
+ typeName: unwrapToTypeName(offsetArg.type),
2452
+ isNonNull: isNonNull(offsetArg.type),
2453
+ }
2454
+ : undefined,
2455
+ orderByArg: orderByArg
2456
+ ? {
2457
+ name: orderByArg.name,
2458
+ typeName: unwrapToTypeName(orderByArg.type),
2459
+ isNonNull: isNonNull(orderByArg.type),
2460
+ }
2461
+ : undefined,
2462
+ orderDirectionArg: orderDirectionArg
2463
+ ? {
2464
+ name: orderDirectionArg.name,
2465
+ typeName: unwrapToTypeName(orderDirectionArg.type),
2466
+ isNonNull: isNonNull(orderDirectionArg.type),
2467
+ }
2468
+ : undefined,
2469
+ };
2470
+ }
2471
+ if (isListOf(field.type, 'Agent')) {
2472
+ return {
2473
+ kind: 'list',
2474
+ fieldName: field.name,
2475
+ queryArg: queryArg
2476
+ ? {
2477
+ name: queryArg.name,
2478
+ typeName: unwrapToTypeName(queryArg.type),
2479
+ isNonNull: isNonNull(queryArg.type),
2480
+ }
2481
+ : undefined,
2482
+ limitArg: limitArg
2483
+ ? {
2484
+ name: limitArg.name,
2485
+ typeName: unwrapToTypeName(limitArg.type),
2486
+ isNonNull: isNonNull(limitArg.type),
2487
+ }
2488
+ : undefined,
2489
+ offsetArg: offsetArg
2490
+ ? {
2491
+ name: offsetArg.name,
2492
+ typeName: unwrapToTypeName(offsetArg.type),
2493
+ isNonNull: isNonNull(offsetArg.type),
2494
+ }
2495
+ : undefined,
2496
+ orderByArg: orderByArg
2497
+ ? {
2498
+ name: orderByArg.name,
2499
+ typeName: unwrapToTypeName(orderByArg.type),
2500
+ isNonNull: isNonNull(orderByArg.type),
2501
+ }
2502
+ : undefined,
2503
+ orderDirectionArg: orderDirectionArg
2504
+ ? {
2505
+ name: orderDirectionArg.name,
2506
+ typeName: unwrapToTypeName(orderDirectionArg.type),
2507
+ isNonNull: isNonNull(orderDirectionArg.type),
2508
+ }
2509
+ : undefined,
2510
+ };
2511
+ }
2512
+ return null;
2513
+ }
2514
+ async getTypeFields(typeName) {
2515
+ if (this.typeFieldsCache.has(typeName)) {
2516
+ return this.typeFieldsCache.get(typeName) ?? null;
2517
+ }
2518
+ try {
2519
+ const data = await this.client.request(TYPE_FIELDS_QUERY, { name: typeName });
2520
+ const kind = data.__type?.kind ?? null;
2521
+ const fields = kind === 'INPUT_OBJECT'
2522
+ ? (data.__type?.inputFields ?? null)
2523
+ : (data.__type?.fields ?? null);
2524
+ this.typeFieldsCache.set(typeName, fields ?? null);
2525
+ return fields ?? null;
2526
+ }
2527
+ catch (error) {
2528
+ console.warn(`[AIAgentDiscoveryClient] Failed to introspect type fields for ${typeName}:`, error);
2529
+ this.typeFieldsCache.set(typeName, null);
2530
+ return null;
2531
+ }
2532
+ }
2533
+ /**
2534
+ * Some indexers expose `metadata { key valueText }`, others expose `metadata { key value }`.
2535
+ * Introspect once and cache so we can query metadata reliably.
2536
+ */
2537
+ async getAgentMetadataValueField() {
2538
+ if (this.agentMetadataValueField !== undefined) {
2539
+ return this.agentMetadataValueField;
2540
+ }
2541
+ try {
2542
+ const agentFields = await this.getTypeFields('Agent');
2543
+ const metadataField = agentFields?.find((f) => f?.name === 'metadata');
2544
+ const metadataType = unwrapType(metadataField?.type);
2545
+ const metadataTypeName = metadataType?.name ?? null;
2546
+ if (!metadataTypeName) {
2547
+ this.agentMetadataValueField = null;
2548
+ return null;
2549
+ }
2550
+ const metadataFields = await this.getTypeFields(metadataTypeName);
2551
+ const fieldNames = new Set((metadataFields ?? [])
2552
+ .map((f) => f?.name)
2553
+ .filter((name) => typeof name === 'string' && name.length > 0));
2554
+ if (fieldNames.has('valueText')) {
2555
+ this.agentMetadataValueField = 'valueText';
2556
+ return 'valueText';
2557
+ }
2558
+ if (fieldNames.has('value')) {
2559
+ this.agentMetadataValueField = 'value';
2560
+ return 'value';
2561
+ }
2562
+ this.agentMetadataValueField = null;
2563
+ return null;
2564
+ }
2565
+ catch {
2566
+ // If schema blocks introspection, fall back to historical `valueText`.
2567
+ this.agentMetadataValueField = 'valueText';
2568
+ return 'valueText';
2569
+ }
2570
+ }
2571
+ /**
2572
+ * Get all token metadata from The Graph indexer for an agent
2573
+ * Uses agentMetadata_collection (The Graph subgraph) or agentMetadata (custom schema) query
2574
+ * to get all metadata key-value pairs. Tries subgraph format first, falls back to custom schema.
2575
+ * Handles pagination if an agent has more than 1000 metadata entries
2576
+ * @param chainId - Chain ID
2577
+ * @param agentId - Agent ID
2578
+ * @returns Record of all metadata key-value pairs, or null if not available
2579
+ */
2580
+ /**
2581
+ * @deprecated Use getAllAgentMetadata instead. This method name is misleading.
2582
+ */
2583
+ async getTokenMetadata(chainId, agentId) {
2584
+ return this.getAllAgentMetadata(chainId, agentId);
2585
+ }
2586
+ /**
2587
+ * Get all agent metadata entries from the discovery GraphQL backend.
2588
+ * Uses agentMetadata_collection (The Graph subgraph) or agentMetadata (custom schema) query.
2589
+ * Tries subgraph format first, falls back to custom schema.
2590
+ * Handles pagination if an agent has more than 1000 metadata entries.
2591
+ * @param chainId - Chain ID
2592
+ * @param agentId - Agent ID
2593
+ * @returns Record of all metadata key-value pairs, or null if not available
2594
+ */
2595
+ async getAllAgentMetadata(chainId, agentId) {
2596
+ // Legacy metadata queries are removed. With KB v2, the client should use:
2597
+ // - registration JSON: identity*.descriptor.json (-> `rawJson`)
2598
+ // - info JSON: identity*.descriptor.onchainMetadataJson (-> `onchainMetadataJson`)
2599
+ // Or fall back to on-chain reads.
2600
+ void chainId;
2601
+ void agentId;
2602
+ return null;
2603
+ }
2604
+ /**
2605
+ * Fallback method: Uses agentMetadata query (custom schema format) to get all metadata key-value pairs
2606
+ * @param chainId - Chain ID
2607
+ * @param agentId - Agent ID
2608
+ * @returns Record of all metadata key-value pairs, or null if not available
2609
+ */
2610
+ async getTokenMetadataCustomSchema(chainId, agentId) {
2611
+ // Legacy query removed.
2612
+ void chainId;
2613
+ void agentId;
2614
+ return null;
2615
+ }
2616
+ /**
2617
+ * Get a single agent by chainId+agentId (convenience).
2618
+ * Discovery is UAID-only: builds uaid and calls getAgentByUaid(uaid).
2619
+ */
2620
+ async getAgent(chainId, agentId) {
2621
+ const id = typeof agentId === 'number' ? agentId : Number.parseInt(String(agentId), 10);
2622
+ if (!Number.isFinite(id))
2623
+ return null;
2624
+ const uaid = `uaid:did:8004:${chainId}:${id}`;
2625
+ return this.getAgentByUaid(uaid);
2626
+ }
2627
+ async getAgentByName(agentName) {
2628
+ const trimmed = agentName?.trim();
2629
+ if (!trimmed)
2630
+ return null;
2631
+ const selection = await this.getKbAgentSelection({ includeIdentityAndAccounts: false });
2632
+ const orderBy = (await this.pickKbAgentOrderBy(['createdAtTime', 'updatedAtTime', 'agentId8004', 'uaid', 'agentName'])) ??
2633
+ 'agentName';
2634
+ const query = `
2635
+ query KbAgentsByName($where: KbAgentWhereInput, $first: Int, $orderBy: KbAgentOrderBy) {
2636
+ kbAgents(where: $where, first: $first, orderBy: $orderBy, orderDirection: DESC) {
2637
+ agents { ${selection} }
2638
+ total
2639
+ hasMore
2640
+ }
2641
+ }
2642
+ `;
2643
+ try {
2644
+ const data = await this.client.request(query, {
2645
+ where: { agentName_contains: trimmed },
2646
+ first: 20,
2647
+ orderBy,
2648
+ });
2649
+ const list = data?.kbAgents?.agents ?? [];
2650
+ if (!list.length)
2651
+ return null;
2652
+ const exact = list.find((a) => String(a.agentName ?? '').toLowerCase() === trimmed.toLowerCase()) ??
2653
+ list[0];
2654
+ return exact ? this.mapKbAgentToAgentData(exact) : null;
2655
+ }
2656
+ catch (error) {
2657
+ console.error('[AIAgentDiscoveryClient.getAgentByName] Error fetching agent:', error);
2658
+ return null;
2659
+ }
2660
+ }
2661
+ /**
2662
+ * Resolve a single agent by UAID (KB v2). UAID-only; no fallback to chainId/agentId.
2663
+ */
2664
+ async getAgentByUaid(uaid) {
2665
+ const trimmed = String(uaid ?? '').trim();
2666
+ if (!trimmed)
2667
+ return null;
2668
+ const uaidForKb = this.normalizeUaidForKb(trimmed);
2669
+ const selection = await this.getKbAgentSelection({ includeIdentityAndAccounts: false });
2670
+ const query = `
2671
+ query KbAgentByUaid($uaid: String!) {
2672
+ kbAgentByUaid(uaid: $uaid) {
2673
+ ${selection}
2674
+ }
2675
+ }
2676
+ `;
2677
+ try {
2678
+ const t0 = Date.now();
2679
+ const data = await this.gqlRequest(query, { uaid: uaidForKb });
2680
+ const t1 = Date.now();
2681
+ if (process.env.NODE_ENV === 'development') {
2682
+ console.log('[AIAgentDiscoveryClient.getAgentByUaid] kbAgentByUaid ms:', t1 - t0);
2683
+ }
2684
+ const agent = data?.kbAgentByUaid ?? null;
2685
+ return agent ? this.mapKbAgentToAgentData(agent) : null;
2686
+ }
2687
+ catch (error) {
2688
+ console.warn('[AIAgentDiscoveryClient.getAgentByUaid] kbAgentByUaid failed:', error);
2689
+ return null;
2690
+ }
2691
+ }
2692
+ /**
2693
+ * Resolve a single agent by UAID including identity/accounts (KB v2). UAID-only; no fallback.
2694
+ */
2695
+ async getAgentByUaidFull(uaid) {
2696
+ const trimmed = String(uaid ?? '').trim();
2697
+ if (!trimmed)
2698
+ return null;
2699
+ const uaidForKb = this.normalizeUaidForKb(trimmed);
2700
+ const selection = await this.getKbAgentSelection({ includeIdentityAndAccounts: true });
2701
+ const query = `
2702
+ query KbAgentByUaidFull($uaid: String!) {
2703
+ kbAgentByUaid(uaid: $uaid) {
2704
+ ${selection}
2705
+ }
2706
+ }
2707
+ `;
2708
+ try {
2709
+ const t0 = Date.now();
2710
+ const data = await this.gqlRequest(query, { uaid: uaidForKb });
2711
+ const t1 = Date.now();
2712
+ if (process.env.NODE_ENV === 'development') {
2713
+ console.log('[AIAgentDiscoveryClient.getAgentByUaidFull] kbAgentByUaid ms:', t1 - t0);
2714
+ }
2715
+ const agent = data?.kbAgentByUaid ?? null;
2716
+ return agent ? this.mapKbAgentToAgentData(agent) : null;
2717
+ }
2718
+ catch (error) {
2719
+ console.warn('[AIAgentDiscoveryClient.getAgentByUaidFull] kbAgentByUaid failed:', error);
2720
+ return null;
2721
+ }
2722
+ }
2723
+ /**
2724
+ * Search agents by name
2725
+ * @param searchTerm - Search term to match against agent names
2726
+ * @param limit - Maximum number of results
2727
+ * @returns List of matching agents
2728
+ */
2729
+ async searchAgents(searchTerm, limit) {
2730
+ const query = `
2731
+ query SearchAgents($query: String!, $limit: Int) {
2732
+ searchAgents(query: $query, limit: $limit) {
2733
+ chainId
2734
+ agentId
2735
+ agentAccount
2736
+ agentName
2737
+ agentIdentityOwnerAccount
2738
+ eoaAgentIdentityOwnerAccount
2739
+ eoaAgentAccount
2740
+ agentCategory
2741
+ didIdentity
2742
+ didAccount
2743
+ didName
2744
+ agentUri
2745
+ createdAtBlock
2746
+ createdAtTime
2747
+ updatedAtTime
2748
+ type
2749
+ description
2750
+ image
2751
+ a2aEndpoint
2752
+ did
2753
+ mcp
2754
+ x402support
2755
+ active
2756
+ supportedTrust
2757
+ rawJson
2758
+ agentCardJson
2759
+ agentCardReadAt
2760
+ atiOverallScore
2761
+ atiOverallConfidence
2762
+ atiVersion
2763
+ atiComputedAt
2764
+ atiBundleJson
2765
+ trustLedgerScore
2766
+ trustLedgerBadgeCount
2767
+ trustLedgerOverallRank
2768
+ trustLedgerCapabilityRank
2769
+ }
2770
+ }
2771
+ `;
2772
+ try {
2773
+ const data = await this.client.request(query, {
2774
+ query: searchTerm,
2775
+ limit: limit || 100,
2776
+ });
2777
+ const agents = data.searchAgents || [];
2778
+ return agents.map((agent) => this.normalizeAgent(agent));
2779
+ }
2780
+ catch (error) {
2781
+ console.error('[AIAgentDiscoveryClient.searchAgents] Error searching agents:', error);
2782
+ throw error;
2783
+ }
2784
+ }
2785
+ /**
2786
+ * Refresh/Index an agent in the indexer
2787
+ * Triggers the indexer to re-index the specified agent
2788
+ * @param agentId - Agent ID to refresh (required)
2789
+ * @param chainId - Optional chain ID (if not provided, indexer may use default)
2790
+ * @param apiKey - Optional API key override (uses config API key if not provided)
2791
+ * @returns Refresh result with success status and processed chains
2792
+ */
2793
+ async refreshAgent(agentId, chainId, apiKey) {
2794
+ const mutation = `
2795
+ mutation IndexAgent($agentId: String!, $chainId: Int) {
2796
+ indexAgent(agentId: $agentId, chainId: $chainId) {
2797
+ success
2798
+ message
2799
+ processedChains
2800
+ }
2801
+ }
2802
+ `;
2803
+ const variables = {
2804
+ agentId: String(agentId),
2805
+ };
2806
+ if (chainId !== undefined) {
2807
+ variables.chainId = chainId;
2808
+ }
2809
+ // If API key override is provided, create a temporary client with that key
2810
+ let clientToUse = this.client;
2811
+ if (apiKey) {
2812
+ const headers = {
2813
+ 'Content-Type': 'application/json',
2814
+ ...(this.config.headers || {}),
2815
+ 'Authorization': `Bearer ${apiKey}`,
2816
+ };
2817
+ clientToUse = new GraphQLClient(this.config.endpoint, {
2818
+ headers,
2819
+ });
2820
+ }
2821
+ try {
2822
+ const data = await clientToUse.request(mutation, variables);
2823
+ return data.indexAgent;
2824
+ }
2825
+ catch (error) {
2826
+ console.error('[AIAgentDiscoveryClient.refreshAgent] Error refreshing agent:', error);
2827
+ throw new Error(`Failed to refresh agent: ${error instanceof Error ? error.message : 'Unknown error'}`);
2828
+ }
2829
+ }
2830
+ /**
2831
+ * Normalize identifier to UAID form required by KB GraphQL.
2832
+ *
2833
+ * Important: kbAgentByUaid expects the *canonical UAID key* (prefix up to the first ';').
2834
+ * Any routing metadata after ';' is not part of the lookup key.
2835
+ */
2836
+ normalizeUaidForKb(uaid) {
2837
+ const t = uaid.trim();
2838
+ const withPrefix = t.startsWith('uaid:') ? t : `uaid:${t}`;
2839
+ const idx = withPrefix.indexOf(';');
2840
+ return idx === -1 ? withPrefix : withPrefix.slice(0, idx);
2841
+ }
2842
+ /**
2843
+ * Search validation requests for an agent by UAID (GraphQL kbAgentByUaid + validationAssertions)
2844
+ */
2845
+ async searchValidationRequestsAdvanced(options) {
2846
+ const { uaid, limit = 10, offset = 0 } = options;
2847
+ const uaidTrimmed = typeof uaid === 'string' ? uaid.trim() : '';
2848
+ if (!uaidTrimmed) {
2849
+ throw new Error('uaid is required for searchValidationRequestsAdvanced');
2850
+ }
2851
+ const uaidForKb = this.normalizeUaidForKb(uaidTrimmed);
2852
+ const queryText = `
2853
+ query KbValidationAssertionsByUaid($uaid: String!, $first: Int, $skip: Int) {
2854
+ kbAgentByUaid(uaid: $uaid) {
2855
+ validationAssertions(first: $first, skip: $skip) {
2856
+ total
2857
+ items {
2858
+ iri
2859
+ agentDid8004
2860
+ json
2861
+ record { txHash blockNumber timestamp rawJson }
2862
+ }
2863
+ }
2864
+ }
2865
+ }
2866
+ `;
2867
+ const variables = {
2868
+ uaid: uaidForKb,
2869
+ first: typeof limit === 'number' ? limit : undefined,
2870
+ skip: typeof offset === 'number' ? offset : undefined,
2871
+ };
2872
+ const data = await this.client.request(queryText, variables);
2873
+ const connection = data?.kbAgentByUaid?.validationAssertions;
2874
+ const items = Array.isArray(connection?.items) ? connection.items : [];
2875
+ const parseJson = (value) => {
2876
+ if (typeof value !== 'string' || !value.trim())
2877
+ return null;
2878
+ try {
2879
+ return JSON.parse(value);
2880
+ }
2881
+ catch {
2882
+ return null;
2883
+ }
2884
+ };
2885
+ const toNumberOrUndefined = (value) => {
2886
+ if (typeof value === 'number' && Number.isFinite(value))
2887
+ return value;
2888
+ if (typeof value === 'string' && value.trim()) {
2889
+ const n = Number(value);
2890
+ if (Number.isFinite(n))
2891
+ return n;
2892
+ }
2893
+ return undefined;
2894
+ };
2895
+ const mapped = items
2896
+ .filter(Boolean)
2897
+ .map((item) => {
2898
+ const iri = typeof item?.iri === 'string' ? item.iri : undefined;
2899
+ const record = item?.record ?? null;
2900
+ const recordTxHash = typeof record?.txHash === 'string' ? record.txHash : undefined;
2901
+ const recordBlockNumber = toNumberOrUndefined(record?.blockNumber);
2902
+ const recordTimestamp = typeof record?.timestamp === 'number' || typeof record?.timestamp === 'string'
2903
+ ? record.timestamp
2904
+ : undefined;
2905
+ const parsedTop = parseJson(item?.json);
2906
+ const parsedRecord = parseJson(record?.rawJson);
2907
+ const recordResponseJsonText = typeof parsedRecord?.responseJson === 'string' ? parsedRecord.responseJson : null;
2908
+ const parsedResponseJson = parseJson(recordResponseJsonText);
2909
+ const parsed = parsedTop ?? parsedResponseJson;
2910
+ const requestHash = typeof parsed?.requestHash === 'string' ? parsed.requestHash : undefined;
2911
+ const validatorAddress = typeof parsed?.validatorAddress === 'string' ? parsed.validatorAddress : undefined;
2912
+ const createdAt = typeof parsed?.createdAt === 'string' ? parsed.createdAt : undefined;
2913
+ const rawId = typeof parsedRecord?.id === 'string'
2914
+ ? parsedRecord.id
2915
+ : typeof parsed?.id === 'string'
2916
+ ? parsed.id
2917
+ : undefined;
2918
+ const agentDid = typeof item?.agentDid8004 === 'string' ? item.agentDid8004 : undefined;
2919
+ const didMatch = agentDid ? /^did:8004:(\d+):(\d+)$/.exec(agentDid) : null;
2920
+ const parsedAgentId = didMatch?.[2] != null ? parseInt(didMatch[2], 10) : undefined;
2921
+ return {
2922
+ iri,
2923
+ id: rawId ?? iri,
2924
+ agentId: parsedAgentId != null ? String(parsedAgentId) : undefined,
2925
+ agentId8004: parsedAgentId,
2926
+ validatorAddress,
2927
+ requestUri: iri,
2928
+ responseUri: iri,
2929
+ requestJson: typeof item?.json === 'string'
2930
+ ? item.json
2931
+ : typeof recordResponseJsonText === 'string'
2932
+ ? recordResponseJsonText
2933
+ : undefined,
2934
+ responseJson: recordResponseJsonText ?? undefined,
2935
+ requestHash,
2936
+ txHash: recordTxHash ?? (typeof parsedRecord?.txHash === 'string' ? parsedRecord.txHash : undefined),
2937
+ blockNumber: recordBlockNumber ??
2938
+ toNumberOrUndefined(parsedRecord?.blockNumber) ??
2939
+ toNumberOrUndefined(parsed?.blockNumber),
2940
+ timestamp: recordTimestamp ??
2941
+ (typeof parsedRecord?.timestamp === 'string' || typeof parsedRecord?.timestamp === 'number'
2942
+ ? parsedRecord.timestamp
2943
+ : undefined),
2944
+ createdAt,
2945
+ };
2946
+ });
2947
+ return { validationRequests: mapped };
2948
+ }
2949
+ /**
2950
+ * Search reviews for an agent by UAID (GraphQL kbAgentByUaid + reviewAssertions)
2951
+ */
2952
+ async searchReviewsAdvanced(options) {
2953
+ const { uaid, limit = 10, offset = 0 } = options;
2954
+ const uaidTrimmed = typeof uaid === 'string' ? uaid.trim() : '';
2955
+ if (!uaidTrimmed) {
2956
+ throw new Error('uaid is required for searchReviewsAdvanced');
2957
+ }
2958
+ const uaidForKb = this.normalizeUaidForKb(uaidTrimmed);
2959
+ const queryText = `
2960
+ query KbReviewAssertionsByUaid($uaid: String!, $first: Int, $skip: Int) {
2961
+ kbAgentByUaid(uaid: $uaid) {
2962
+ reviewAssertions(first: $first, skip: $skip) {
2963
+ total
2964
+ items {
2965
+ iri
2966
+ agentDid8004
2967
+ json
2968
+ record { txHash blockNumber timestamp rawJson }
2969
+ }
2970
+ }
2971
+ }
2972
+ }
2973
+ `;
2974
+ const variables = {
2975
+ uaid: uaidForKb,
2976
+ first: typeof limit === 'number' ? limit : undefined,
2977
+ skip: typeof offset === 'number' ? offset : undefined,
2978
+ };
2979
+ const data = await this.client.request(queryText, variables);
2980
+ const connection = data?.kbAgentByUaid?.reviewAssertions;
2981
+ const items = Array.isArray(connection?.items) ? connection.items : [];
2982
+ const parseJson = (value) => {
2983
+ if (typeof value !== 'string' || !value.trim())
2984
+ return null;
2985
+ try {
2986
+ return JSON.parse(value);
2987
+ }
2988
+ catch {
2989
+ return null;
2990
+ }
2991
+ };
2992
+ const mapped = items
2993
+ .filter(Boolean)
2994
+ .map((item) => {
2995
+ const iri = typeof item?.iri === 'string' ? item.iri : undefined;
2996
+ const record = item?.record ?? null;
2997
+ const recordTxHash = typeof record?.txHash === 'string' ? record.txHash : undefined;
2998
+ const recordBlockNumber = typeof record?.blockNumber === 'number' ? record.blockNumber : undefined;
2999
+ const recordTimestamp = typeof record?.timestamp === 'number' ? record.timestamp : undefined;
3000
+ const parsedTop = parseJson(item?.json);
3001
+ const parsedRecord = parseJson(record?.rawJson);
3002
+ const parsedFeedbackJson = parseJson(parsedRecord?.feedbackJson);
3003
+ const clientAddress = typeof parsedRecord?.clientAddress === 'string'
3004
+ ? parsedRecord.clientAddress
3005
+ : typeof parsedFeedbackJson?.proofOfPayment?.fromAddress === 'string'
3006
+ ? parsedFeedbackJson.proofOfPayment.fromAddress
3007
+ : undefined;
3008
+ const scoreRaw = parsedTop?.score ??
3009
+ parsedTop?.rating ??
3010
+ parsedFeedbackJson?.score ??
3011
+ parsedFeedbackJson?.rating ??
3012
+ undefined;
3013
+ const score = typeof scoreRaw === 'number'
3014
+ ? scoreRaw
3015
+ : typeof scoreRaw === 'string' && scoreRaw.trim()
3016
+ ? Number(scoreRaw)
3017
+ : undefined;
3018
+ const comment = typeof parsedTop?.comment === 'string'
3019
+ ? parsedTop.comment
3020
+ : typeof parsedTop?.text === 'string'
3021
+ ? parsedTop.text
3022
+ : typeof parsedFeedbackJson?.comment === 'string'
3023
+ ? parsedFeedbackJson.comment
3024
+ : typeof parsedFeedbackJson?.text === 'string'
3025
+ ? parsedFeedbackJson.text
3026
+ : undefined;
3027
+ const agentDid = typeof item?.agentDid8004 === 'string' ? item.agentDid8004 : undefined;
3028
+ const didMatch = agentDid ? /^did:8004:(\d+):(\d+)$/.exec(agentDid) : null;
3029
+ const parsedAgentId = didMatch?.[2] != null ? parseInt(didMatch[2], 10) : undefined;
3030
+ return {
3031
+ iri,
3032
+ id: iri,
3033
+ agentId: parsedAgentId != null ? String(parsedAgentId) : undefined,
3034
+ clientAddress,
3035
+ score: Number.isFinite(score) ? score : undefined,
3036
+ comment,
3037
+ reviewJson: typeof item?.json === 'string' ? item.json : (typeof parsedRecord?.feedbackJson === 'string' ? parsedRecord.feedbackJson : undefined),
3038
+ txHash: recordTxHash ?? (typeof parsedRecord?.txHash === 'string' ? parsedRecord.txHash : undefined),
3039
+ blockNumber: recordBlockNumber ?? (typeof parsedRecord?.blockNumber === 'number' ? parsedRecord.blockNumber : undefined),
3040
+ timestamp: recordTimestamp ?? (typeof parsedRecord?.timestamp === 'number' ? parsedRecord.timestamp : undefined),
3041
+ isRevoked: typeof parsedRecord?.isRevoked === 'boolean' ? parsedRecord.isRevoked : undefined,
3042
+ };
3043
+ });
3044
+ return { reviews: mapped };
3045
+ }
3046
+ /**
3047
+ * Search feedback/reviews for an agent (UAID or legacy chainId+agentId). Prefer searchReviewsAdvanced(uaid).
3048
+ */
3049
+ async searchFeedbackAdvanced(options) {
3050
+ const { uaid, chainId, agentId, limit = 10, offset = 0 } = options;
3051
+ let uaidResolved;
3052
+ if (typeof uaid === 'string' && uaid.trim()) {
3053
+ uaidResolved = uaid.trim();
3054
+ }
3055
+ else if (typeof chainId === 'number' &&
3056
+ Number.isFinite(chainId) &&
3057
+ (typeof agentId === 'number' || (typeof agentId === 'string' && agentId.trim()))) {
3058
+ const aid = typeof agentId === 'number' ? agentId : parseInt(String(agentId), 10);
3059
+ if (!Number.isFinite(aid) || aid <= 0) {
3060
+ throw new Error(`Invalid agentId for searchFeedbackAdvanced: ${agentId}`);
3061
+ }
3062
+ uaidResolved = this.normalizeUaidForKb(`did:8004:${chainId}:${aid}`);
3063
+ }
3064
+ else {
3065
+ throw new Error('searchFeedbackAdvanced requires uaid or (chainId and agentId)');
3066
+ }
3067
+ const res = await this.searchReviewsAdvanced({ uaid: uaidResolved, limit, offset });
3068
+ if (!res?.reviews)
3069
+ return null;
3070
+ const feedbacks = res.reviews.map((r) => ({
3071
+ ...r,
3072
+ feedbackUri: r.feedbackUri,
3073
+ feedbackJson: r.reviewJson ?? r.feedbackJson,
3074
+ }));
3075
+ return { feedbacks };
3076
+ }
3077
+ /**
3078
+ * Execute a raw GraphQL query
3079
+ * @param query - GraphQL query string
3080
+ * @param variables - Query variables
3081
+ * @returns Query response
3082
+ */
3083
+ async request(query, variables) {
3084
+ return this.client.request(query, variables);
3085
+ }
3086
+ /**
3087
+ * Execute a raw GraphQL mutation
3088
+ * @param mutation - GraphQL mutation string
3089
+ * @param variables - Mutation variables
3090
+ * @returns Mutation response
3091
+ */
3092
+ async mutate(mutation, variables) {
3093
+ return this.client.request(mutation, variables);
3094
+ }
3095
+ /**
3096
+ * Get the underlying GraphQLClient instance
3097
+ * @returns The GraphQLClient instance
3098
+ */
3099
+ getClient() {
3100
+ return this.client;
3101
+ }
3102
+ /**
3103
+ * Get agents owned by a specific EOA address
3104
+ * @param eoaAddress - The EOA (Externally Owned Account) address to search for
3105
+ * @param options - Optional search options (limit, offset, orderBy, orderDirection)
3106
+ * @returns List of agents owned by the EOA address
3107
+ */
3108
+ async getOwnedAgents(eoaAddress, options) {
3109
+ if (!eoaAddress || typeof eoaAddress !== 'string' || !eoaAddress.startsWith('0x')) {
3110
+ throw new Error('Invalid EOA address. Must be a valid Ethereum address starting with 0x');
3111
+ }
3112
+ const limit = options?.limit ?? 100;
3113
+ const offset = options?.offset ?? 0;
3114
+ const orderBy = options?.orderBy ?? 'agentId';
3115
+ const orderDirection = options?.orderDirection ?? 'DESC';
3116
+ const effectiveOrderDirection = (orderDirection ?? 'DESC').toUpperCase() === 'ASC' ? 'ASC' : 'DESC';
3117
+ const orderByKb = orderBy === 'agentName'
3118
+ ? ((await this.pickKbAgentOrderBy(['agentName'])) ?? 'agentName')
3119
+ : ((await this.pickKbAgentOrderBy(['createdAtTime', 'updatedAtTime', 'agentId8004', 'uaid', 'agentName'])) ??
3120
+ 'agentName');
3121
+ const selection = await this.getKbAgentSelection({ includeIdentityAndAccounts: false });
3122
+ const query = `
3123
+ query KbOwnedAgentsAllChains(
3124
+ $ownerAddress: String!
3125
+ $first: Int
3126
+ $skip: Int
3127
+ $orderBy: KbAgentOrderBy
3128
+ $orderDirection: OrderDirection
3129
+ ) {
3130
+ kbOwnedAgentsAllChains(
3131
+ ownerAddress: $ownerAddress
3132
+ first: $first
3133
+ skip: $skip
3134
+ orderBy: $orderBy
3135
+ orderDirection: $orderDirection
3136
+ ) {
3137
+ agents { ${selection} }
3138
+ total
3139
+ hasMore
3140
+ }
3141
+ }
3142
+ `;
3143
+ try {
3144
+ const data = await this.gqlRequest(query, {
3145
+ ownerAddress: eoaAddress,
3146
+ first: limit,
3147
+ skip: offset,
3148
+ orderBy: orderByKb,
3149
+ orderDirection: effectiveOrderDirection,
3150
+ });
3151
+ const list = data?.kbOwnedAgentsAllChains?.agents ?? [];
3152
+ return list.map((a) => this.mapKbAgentToAgentData(a));
3153
+ }
3154
+ catch (error) {
3155
+ const msg = error instanceof Error ? error.message : String(error);
3156
+ if (msg.includes('Cannot return null for non-nullable field') && msg.includes('kbOwnedAgentsAllChains')) {
3157
+ console.warn('[AIAgentDiscoveryClient.getOwnedAgents] Backend returned null for kbOwnedAgentsAllChains (resolver bug); returning empty list.');
3158
+ return [];
3159
+ }
3160
+ throw error;
3161
+ }
3162
+ }
3163
+ /**
3164
+ * UAID-native ownership check (KB v2).
3165
+ */
3166
+ async isOwnerByUaid(uaid, walletAddress) {
3167
+ const u = String(uaid ?? '').trim();
3168
+ const w = String(walletAddress ?? '').trim();
3169
+ if (!u || !w)
3170
+ return false;
3171
+ const uaidForKb = this.normalizeUaidForKb(u);
3172
+ const query = `
3173
+ query KbIsOwner($uaid: String!, $walletAddress: String!) {
3174
+ kbIsOwner(uaid: $uaid, walletAddress: $walletAddress)
3175
+ }
3176
+ `;
3177
+ const data = await this.gqlRequest(query, {
3178
+ uaid: uaidForKb,
3179
+ walletAddress: w,
3180
+ });
3181
+ return Boolean(data?.kbIsOwner);
3182
+ }
3183
+ }
3184
+ //# sourceMappingURL=AIAgentDiscoveryClient.js.map