export const CATEGORIES = [ 'rebalancing', 'grid', 'yield', 'health-factor', ] as const; export type Category = (typeof CATEGORIES)[number]; /** * Trust data is provenance-tagged: consumers always know whether a number * came from the 8004scan aggregator or from a direct registry read, and when * it was fetched. The UI surfaces both (data-quality judging criterion). */ export interface TrustData { totalScore: number | null; averageScore: number | null; rank: number | null; healthScore: number | null; totalFeedbacks: number; starCount: number | null; isVerified: boolean; /** Per-dimension breakdown (quality, popularity, activity, …) when available. */ breakdown?: Record; /** Where the record as a whole was fetched from. */ source: '8004scan' | 'registry' | 'the-graph'; /** * Where `totalScore` / `totalFeedbacks` came from, when that is not the * record's own source. Set when a direct ReputationRegistry read overrides a * lagging indexer score: the two numbers then have a different provenance * from `rank`, `healthScore`, and `starCount`, which no on-chain read * produces. Absent means the whole record shares `source`. */ scoreSource?: '8004scan' | 'registry'; asOf: string; } /** * A field an agent's on-chain owner supplied through a signed claim, because * the indexed registration carried nothing for it. Per-field like * `TrustData.scoreSource`, so a card can label exactly the owner-provided parts * of a listing instead of tarring the whole record with one badge. */ export type ClaimedField = 'description' | 'category' | 'website' | 'endpoint'; export interface AgentSummary { /** Stable id: `${chainId}-${tokenId}` (subgraph-shaped). */ id: string; chainId: number; tokenId: string; /** CAIP-flavored id from the registry, e.g. "56:0x8004a169…:258526". */ agentId: string; name: string; description: string; imageUrl: string | null; owner: string; category: Category | null; supportedProtocols: string[]; x402Supported: boolean; /** * The wallet the agent acts from, when the lane reports it on list rows * (the subgraph and 8004scan do; the snapshot does not). Owner-set * metadata, so it names an address, not proof of who controls it. */ agentWallet?: string | null; registeredAt: string | null; trust: TrustData; /** * When >1, this card represents a cluster of indistinguishable low-signal * registrations that share this name (distinct owners, no category / score / * description). Collapsed for a legible directory; the count is shown. */ duplicateCount?: number; /** * True once the identity's on-chain owner has signed a claim for this * listing. Says who filled the gaps, not that anyone vouches for the agent: * a claimed registry listing stays unverified. */ claimed?: boolean; /** Which fields on this record came from that claim rather than from chain. */ claimedFields?: ClaimedField[]; /** Owner-provided links, set only where the indexed record carried none. */ website?: string; endpoint?: string; /** * True when a probe of `endpoint` answered inside the freshness window. Set * by the site from its own stored probe results, never by the index: the * registry records an endpoint, it does not say whether anything is behind * it. Absent means no fresh result, which is not the same as an endpoint that * was probed and did not answer. */ endpointLive?: boolean; } export interface AgentDetail extends AgentSummary { agentURI: string | null; agentWallet: string | null; /** Raw metadata document fetched from agentURI, when resolvable. */ metadata: Record | null; services: unknown[] | null; } export interface Feedback { agentRef: string; client: string; score: number | null; value: string | null; tags: string[]; uri: string | null; txHash: string | null; blockNumber: number | null; revoked: boolean; timestamp: string | null; } export interface Page { items: T[]; /** Opaque cursor; null when exhausted. */ nextCursor: string | null; /** Total item count upstream, when the source reports one. */ total: number | null; asOf: string; source: string; } export interface IndexStats { totalAgents: number | null; /** * True when `totalAgents` counts only the requested chain. 8004scan's public * /stats endpoint ignores `chain_id` and answers with the all-chains figure, * so a consumer must be able to tell a BSC total from a global one before it * labels the number. */ chainScoped: boolean; totalFeedbacks: number | null; asOf: string; source: string; } export interface ListAgentsQuery { chainId: number; category?: Category; cursor?: string; limit?: number; }