chain-insights 0.59.1 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{capabilities-BnuroVv_.mjs → capabilities-BBVDIECk.mjs} +2 -2
- package/dist/{capabilities-BnuroVv_.mjs.map → capabilities-BBVDIECk.mjs.map} +1 -1
- package/dist/{capabilities-CF-KwJ6B.cjs → capabilities-DGavTSoW.cjs} +1 -1
- package/dist/cli.cjs +4 -4
- package/dist/cli.mjs +4 -4
- package/dist/{graph-schema-DrU7Zaa5.cjs → graph-schema-C_7aZ8RL.cjs} +1 -1
- package/dist/{graph-schema-DdriUxEe.mjs → graph-schema-DHd2fGbr.mjs} +2 -2
- package/dist/{graph-schema-DdriUxEe.mjs.map → graph-schema-DHd2fGbr.mjs.map} +1 -1
- package/dist/mcp-proxy.cjs +2 -2
- package/dist/mcp-proxy.mjs +2 -2
- package/docs/contributing.md +1 -2
- package/docs/graph-tools.md +2 -2
- package/package.json +1 -1
- package/skills/chain-insights-cypher/SKILL.md +1 -1
|
@@ -241,7 +241,7 @@ function laneDisplayName(lane) {
|
|
|
241
241
|
function layerDisplayName(layer) {
|
|
242
242
|
return layer;
|
|
243
243
|
}
|
|
244
|
-
/** A family prints by its plain name: `
|
|
244
|
+
/** A family prints by its plain name: `smart_accounts` prints as "smart accounts". */
|
|
245
245
|
function familyDisplayName(family) {
|
|
246
246
|
return family.replace(/_/g, " ");
|
|
247
247
|
}
|
|
@@ -383,4 +383,4 @@ function formatNetworkCapability(network) {
|
|
|
383
383
|
//#endregion
|
|
384
384
|
export { findNetworkCapability as n, mirrorGraphNetworkCapabilities as r, capabilities_exports as t };
|
|
385
385
|
|
|
386
|
-
//# sourceMappingURL=capabilities-
|
|
386
|
+
//# sourceMappingURL=capabilities-BBVDIECk.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities-BnuroVv_.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { applyMcpAuthHeaders, resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkTopologyLayer {\n enabled: boolean\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n live?: NetworkTopologyLayer\n archive?: NetworkTopologyLayer\n /** The relationships a facts layer serves, when the server lists them. */\n relationships?: string[]\n}\n\n/**\n * A block the server publishes about one layer's limits. The mirror repeats it\n * exactly as it came: the members named below are the ones known today, and\n * any other member the server adds is kept. Read a limit from the block, never\n * from a number written elsewhere.\n */\nexport type NetworkAdmissionBlock = Record<string, unknown>\n\n/** `USE chain`: the lookups, the ceilings and the slots of the chain layer. */\nexport interface ChainAdmission extends NetworkAdmissionBlock {\n enabled: boolean\n /** ok, or the reason the layer is off or behind. */\n status?: string\n rules_version?: string\n grammar?: string\n /** The labels of `USE chain`, such as Transaction, Block, Head and Address. */\n lookups?: string[]\n ceiling_seconds?: Record<string, number>\n slots?: number\n past_slots?: number\n slots_per_caller?: number\n calls_per_second_per_caller?: number\n batch_max?: number\n call_gas?: number\n head_ttl_ms?: number\n max_head_age_seconds?: number\n /** How many blocks below the tip a past `Address` read (`at_block`) must be. Read it here, never write it down. */\n at_block_min_depth?: number\n}\n\n/** `USE topology`: the shape rules, when the server publishes them. */\nexport interface TopologyAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n max_hops_per_path?: number\n max_limit?: number\n slots?: number\n}\n\n/** `USE facts`: the read contract, when the server publishes it. */\nexport interface FactsAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n window_days?: number\n max_rows?: number\n max_hops?: number\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n /** The layers as the server sent them. Absent when the server sent none. */\n layers?: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n /** The three layer blocks below are repeated exactly as sent, and absent when not sent. */\n chain_admission?: ChainAdmission\n topology_admission?: TopologyAdmission\n facts_admission?: FactsAdmission\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n /** The chain node's own head block, and how old that reading is. */\n chain_head_block?: number\n chain_head_age_seconds?: number\n warehouse_blocks_behind_head?: number\n graph_blocks_behind_head?: number\n }\n lane_progress?: NetworkLaneProgress[]\n graph_progress?: NetworkGraphProgress\n}\n\n/** One writer lane's committed height. null: the lane has not started. */\nexport interface NetworkLaneProgress {\n lane: string\n height: number | null\n}\n\n/**\n * How far each block layer of the topology graph reaches, and how far the\n * money family reaches (`complete_through_block`). `families` is absent on a\n * server that does not group its layers into families.\n */\nexport interface NetworkGraphProgress {\n complete_through_block: number\n layers: { layer: string; position: number }[]\n families?: NetworkGraphFamily[]\n}\n\n/**\n * One layer family as the server reports it. A block family carries the block\n * it is complete through and its lag behind the head (null on a server that\n * knows no head). `annotations` follows runs, not blocks: both numbers are null\n * and `fresh` says whether its last run is current.\n */\nexport interface NetworkGraphFamily {\n name: string\n complete_through_block: number | null\n blocks_behind_head: number | null\n fresh?: boolean\n layers: string[]\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nconst AVAILABLE_TOOLS_PER_LINE = 3\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction advertisedTools(raw: unknown): Record<string, string> {\n if (!isRecord(raw)) return {}\n const tools: Record<string, string> = {}\n for (const [name, status] of Object.entries(raw)) {\n if (name.trim() !== '' && typeof status === 'string') tools[name] = status\n }\n return tools\n}\n\nfunction advertisedLaneProgress(raw: unknown): NetworkLaneProgress[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const lanes: NetworkLaneProgress[] = []\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.lane !== 'string' || entry.lane.trim() === '') continue\n if (typeof entry.height === 'number') lanes.push({ lane: entry.lane, height: entry.height })\n else if (entry.height === null) lanes.push({ lane: entry.lane, height: null })\n }\n return lanes\n}\n\nfunction isNumberOrNull(value: unknown): value is number | null {\n return value === null || typeof value === 'number'\n}\n\n/** One family entry, or null when any member has the wrong shape. */\nfunction advertisedGraphFamily(entry: unknown): NetworkGraphFamily | null {\n if (!isRecord(entry) || typeof entry.name !== 'string' || entry.name.trim() === '') return null\n if (!isNumberOrNull(entry.complete_through_block)) return null\n const behind = entry.blocks_behind_head === undefined ? null : entry.blocks_behind_head\n if (!isNumberOrNull(behind)) return null\n if (entry.fresh !== undefined && typeof entry.fresh !== 'boolean') return null\n if (!Array.isArray(entry.layers) || !entry.layers.every((layer) => typeof layer === 'string'))\n return null\n return {\n name: entry.name,\n complete_through_block: entry.complete_through_block,\n blocks_behind_head: behind,\n ...(typeof entry.fresh === 'boolean' ? { fresh: entry.fresh } : {}),\n layers: [...(entry.layers as string[])],\n }\n}\n\n/** The families list, whole or not at all: one entry with a bad shape drops it. */\nfunction advertisedGraphFamilies(raw: unknown): NetworkGraphFamily[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const families: NetworkGraphFamily[] = []\n for (const entry of raw) {\n const family = advertisedGraphFamily(entry)\n if (family === null) return undefined\n families.push(family)\n }\n return families\n}\n\nfunction advertisedGraphProgress(raw: unknown): NetworkGraphProgress | undefined {\n if (!isRecord(raw) || typeof raw.complete_through_block !== 'number') return undefined\n if (!Array.isArray(raw.layers)) return undefined\n const layers: NetworkGraphProgress['layers'] = []\n for (const entry of raw.layers) {\n if (!isRecord(entry) || typeof entry.layer !== 'string' || typeof entry.position !== 'number')\n continue\n layers.push({ layer: entry.layer, position: entry.position })\n }\n const progress: NetworkGraphProgress = {\n complete_through_block: raw.complete_through_block,\n layers,\n }\n const families = advertisedGraphFamilies(raw.families)\n if (families) progress.families = families\n return progress\n}\n\nfunction advertisedNetwork(raw: unknown): NetworkCapability | null {\n if (!isRecord(raw) || typeof raw.network !== 'string' || raw.network.trim() === '') {\n return null\n }\n const network = raw.network.trim()\n const capability: NetworkCapability = {\n network,\n display_name:\n typeof raw.display_name === 'string' && raw.display_name.trim() !== ''\n ? raw.display_name\n : network,\n status: typeof raw.status === 'string' && raw.status.trim() !== '' ? raw.status : 'live',\n tools: advertisedTools(raw.tools),\n }\n // The layer blocks are the server's own words about its limits. They pass\n // through exactly as sent: nothing is added, no default is invented and no\n // member is dropped. A block the server did not send stays absent.\n if (isRecord(raw.layers)) capability.layers = raw.layers as NetworkCapability['layers']\n if (isRecord(raw.chain_admission)) {\n capability.chain_admission = raw.chain_admission as unknown as ChainAdmission\n }\n if (isRecord(raw.topology_admission)) {\n capability.topology_admission = raw.topology_admission as TopologyAdmission\n }\n if (isRecord(raw.facts_admission)) {\n capability.facts_admission = raw.facts_admission as FactsAdmission\n }\n if (raw.default === true) capability.default = true\n if (raw.default === false) capability.default = false\n if (isRecord(raw.coverage)) capability.coverage = raw.coverage as NetworkCapability['coverage']\n if (isRecord(raw.freshness))\n capability.freshness = raw.freshness as NetworkCapability['freshness']\n const laneProgress = advertisedLaneProgress(raw.lane_progress)\n if (laneProgress) capability.lane_progress = laneProgress\n const graphProgress = advertisedGraphProgress(raw.graph_progress)\n if (graphProgress) capability.graph_progress = graphProgress\n return capability\n}\n\n/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */\nexport function mirrorGraphNetworkCapabilities(document: {\n networks: unknown[]\n}): NetworkCapabilitiesDocument {\n return {\n schema: 'chain-insights.network-capabilities.v1',\n networks: document.networks\n .map((network) => advertisedNetwork(network))\n .filter((network): network is NetworkCapability => network !== null),\n }\n}\n\nfunction publicNetworkCapabilities(\n document: NetworkCapabilitiesDocument\n): NetworkCapabilitiesDocument {\n return mirrorGraphNetworkCapabilities(document)\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint'>\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim()\n if (token) {\n applyMcpAuthHeaders(headers, token)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = (await response.json()) as NetworkCapabilitiesDocument\n if (\n parsed.schema !== 'chain-insights.network-capabilities.v1' ||\n !Array.isArray(parsed.networks)\n ) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return publicNetworkCapabilities(parsed)\n}\n\nfunction availableTools(network: NetworkCapability): string[] {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.sort()\n}\n\nfunction availableToolLines(network: NetworkCapability): string[] {\n const tools = availableTools(network)\n if (tools.length === 0) return ['none']\n const lines: string[] = []\n for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) {\n lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(', '))\n }\n return lines\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange =\n coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange =\n coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : ''\n if (blockRange === 'blocks unknown' && dateRange === '') return 'unknown'\n if (blockRange === 'blocks unknown') return dateRange\n if (dateRange === '') return blockRange\n return `${blockRange} / ${dateRange}`\n}\n\nfunction displayName(network: NetworkCapability): string {\n return network.display_name || network.network\n}\n\nfunction statusLabel(network: NetworkCapability): string {\n return network.default ? `${network.status} (default)` : network.status\n}\n\nfunction formatTable(headers: string[], rows: string[][], minimumWidths: number[]): string {\n const widths = headers.map((header, index) =>\n Math.max(\n minimumWidths[index] ?? 0,\n header.length,\n ...rows.map((row) => row[index]?.length ?? 0)\n )\n )\n const row = (values: string[]) =>\n values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [row(headers), widths.map((width) => '-'.repeat(width)).join(' '), ...rows.map(row)].join(\n '\\n'\n )\n}\n\nexport function formatNetworkOverview(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const rows = document.networks.map((network) => [\n displayName(network),\n statusLabel(network),\n datasetLabel(network),\n ])\n return formatTable(['Network', 'Status', 'Dataset'], rows, [14, 10, 38])\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const networkRows = document.networks.flatMap((network) => {\n const toolLines = availableToolLines(network)\n return toolLines.map((toolLine, index) => [\n index === 0 ? displayName(network) : '',\n index === 0 ? datasetLabel(network) : '',\n toolLine,\n ])\n })\n return formatTable(['Network', 'Dataset', 'Chain Insights tools'], networkRows, [14, 38, 64])\n}\n\nexport function findNetworkCapability(\n document: NetworkCapabilitiesDocument,\n name: string\n): NetworkCapability | undefined {\n const normalizedName = name.trim().toLowerCase()\n if (!normalizedName) return undefined\n return document.networks.find((network) =>\n [network.network, network.display_name].some(\n (candidate) => candidate?.trim().toLowerCase() === normalizedName\n )\n )\n}\n\n/** The writer lanes a customer sees, in screen order, by their plain names. */\nconst KNOWN_LANES: readonly (readonly [string, string])[] = [\n ['core_transfers_source_head', 'receipts'],\n ['core_transfers_internal_follow', 'traces'],\n ['core_dex_follow', 'dex'],\n ['core_bridge_follow', 'bridge'],\n ['core_smart_account_follow', 'smart accounts'],\n]\n\n/** The evidence worker's lane. It has its own table and never joins Indexing. */\nconst EVIDENCE_LANE = 'evidence_sync'\n\n/** The plain name a lane prints as. An unknown lane prints as sent. */\nexport function laneDisplayName(lane: string): string {\n if (lane === EVIDENCE_LANE) return 'evidence'\n return KNOWN_LANES.find(([name]) => name === lane)?.[1] ?? lane\n}\n\n/** A graph layer prints as sent: it is the link kind a query names. */\nexport function layerDisplayName(layer: string): string {\n return layer\n}\n\n/** A family prints by its plain name: `chain_facts` prints as \"chain facts\". */\nexport function familyDisplayName(family: string): string {\n return family.replace(/_/g, ' ')\n}\n\n/** 87642448 -> \"87,642,448\", the same on every machine (no locale). */\nexport function groupThousands(value: number): string {\n const sign = value < 0 ? '-' : ''\n const digits = String(Math.trunc(Math.abs(value)))\n return sign + digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n}\n\n/** One row of a progress table. `behind` is absent with no head or no height. */\ninterface ProgressRow {\n label: string\n height: number | null\n /** Text printed in the height column instead of a number. */\n note?: string\n behind?: number\n}\n\nconst DETAIL_LABEL_WIDTH = 18\nconst TABLE_LABEL_WIDTH = 30\nconst HEIGHT_MIN_WIDTH = 10\nconst LAG_HEADER = 'behind head'\n\nfunction progressRows(\n entries: { label: string; height: number | null }[],\n head: number | undefined\n): ProgressRow[] {\n return entries.map(({ label, height }) => ({\n label,\n height,\n ...(height === null ? { note: 'not started' } : {}),\n ...(height !== null && head !== undefined ? { behind: Math.max(0, head - height) } : {}),\n }))\n}\n\nfunction notReportedRow(label: string): ProgressRow {\n return { label, height: null, note: 'not reported' }\n}\n\n/**\n * The family rows, in the order the server sends them. A block family's lag is\n * the server's `blocks_behind_head`, or head minus height when the server knows\n * no head. A family with no block (annotations) prints \"fresh\" or \"walking\" and\n * no lag.\n */\nfunction familyRows(families: NetworkGraphFamily[], head: number | undefined): ProgressRow[] {\n return families.map((family) => {\n const label = familyDisplayName(family.name)\n const height = family.complete_through_block\n if (height === null) {\n const note = family.fresh === undefined ? 'not started' : family.fresh ? 'fresh' : 'walking'\n return { label, height: null, note }\n }\n if (head === undefined) return { label, height }\n const behind = family.blocks_behind_head ?? Math.max(0, head - height)\n return { label, height, behind }\n })\n}\n\nfunction formatProgressTables(\n tables: { title: string; rows: ProgressRow[] }[],\n withLag: boolean\n): string[] {\n const allRows = tables.flatMap((table) => table.rows)\n const labelWidth = Math.max(\n TABLE_LABEL_WIDTH,\n ...allRows.map((row) => row.label.length + 4),\n ...tables.map((table) => table.title.length + 2)\n )\n const heightWidth = Math.max(\n HEIGHT_MIN_WIDTH,\n ...allRows.map((row) => (row.height === null ? 0 : groupThousands(row.height).length))\n )\n const lagWidth = Math.max(\n LAG_HEADER.length,\n ...allRows.map((row) => (row.behind === undefined ? 0 : groupThousands(row.behind).length))\n )\n const lines: string[] = []\n for (const table of tables) {\n if (lines.length > 0) lines.push('')\n const header = table.title.padEnd(labelWidth) + 'height'\n lines.push(withLag ? header.padEnd(labelWidth + heightWidth + 2) + LAG_HEADER : header)\n for (const row of table.rows) {\n let line = ` ${row.label}`.padEnd(labelWidth)\n if (row.height === null) {\n line += row.note ?? ''\n } else {\n line += groupThousands(row.height).padStart(heightWidth)\n if (withLag && row.behind !== undefined) {\n line += ' ' + groupThousands(row.behind).padStart(lagWidth)\n }\n }\n lines.push(line.trimEnd())\n }\n }\n return lines\n}\n\n/**\n * `cia network <name>`: how far this network is indexed. The chain node's head\n * comes first; every row reads against it. Known lanes print by plain name,\n * unknown lanes as sent after them, the layer families by plain name in the\n * server's order, graph layers as sent, and evidence in its own table. Heights\n * are right-aligned, so a shorter height lines up by its last digit.\n * `--json` never comes through here.\n */\nexport function formatNetworkCapability(network: NetworkCapability): string {\n const headBlock = network.freshness?.chain_head_block\n const head = typeof headBlock === 'number' ? headBlock : undefined\n const detail: [string, string][] = [\n ['Network', displayName(network)],\n ['Identifier', network.network],\n ['Status', statusLabel(network)],\n ]\n if (head !== undefined) {\n const age = network.freshness?.chain_head_age_seconds\n const ageText = typeof age === 'number' ? ` (${groupThousands(age)} s old)` : ''\n detail.push(['Blockchain head', `${groupThousands(head)}${ageText}`])\n }\n\n const lanes = network.lane_progress ?? []\n const known = KNOWN_LANES.flatMap(([name, label]) =>\n lanes.filter((lane) => lane.lane === name).map((lane) => ({ label, height: lane.height }))\n )\n const unknown = lanes\n .filter(\n (lane) => lane.lane !== EVIDENCE_LANE && !KNOWN_LANES.some(([name]) => name === lane.lane)\n )\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n const indexing = [...known, ...unknown]\n const layers = (network.graph_progress?.layers ?? []).map((layer) => ({\n label: layerDisplayName(layer.layer),\n height: layer.position,\n }))\n const evidence = lanes\n .filter((lane) => lane.lane === EVIDENCE_LANE)\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n\n const families = network.graph_progress?.families\n\n const tables = [\n {\n title: 'Indexing',\n rows: indexing.length > 0 ? progressRows(indexing, head) : [notReportedRow('indexing')],\n },\n // Absent on a server that sends no families: the table is left out, so the\n // screen of such a server stays as it was.\n ...(families ? [{ title: 'Families', rows: familyRows(families, head) }] : []),\n {\n title: 'Graph',\n rows: layers.length > 0 ? progressRows(layers, head) : [notReportedRow('graph')],\n },\n {\n title: 'Evidence',\n rows: evidence.length > 0 ? progressRows(evidence, head) : [notReportedRow('evidence')],\n },\n ]\n\n return [\n ...detail.map(([label, value]) => `${label.padEnd(DETAIL_LABEL_WIDTH)}${value}`),\n '',\n ...formatProgressTables(tables, head !== undefined),\n ].join('\\n')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"capabilities-BBVDIECk.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { applyMcpAuthHeaders, resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkTopologyLayer {\n enabled: boolean\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n live?: NetworkTopologyLayer\n archive?: NetworkTopologyLayer\n /** The relationships a facts layer serves, when the server lists them. */\n relationships?: string[]\n}\n\n/**\n * A block the server publishes about one layer's limits. The mirror repeats it\n * exactly as it came: the members named below are the ones known today, and\n * any other member the server adds is kept. Read a limit from the block, never\n * from a number written elsewhere.\n */\nexport type NetworkAdmissionBlock = Record<string, unknown>\n\n/** `USE chain`: the lookups, the ceilings and the slots of the chain layer. */\nexport interface ChainAdmission extends NetworkAdmissionBlock {\n enabled: boolean\n /** ok, or the reason the layer is off or behind. */\n status?: string\n rules_version?: string\n grammar?: string\n /** The labels of `USE chain`, such as Transaction, Block, Head and Address. */\n lookups?: string[]\n ceiling_seconds?: Record<string, number>\n slots?: number\n past_slots?: number\n slots_per_caller?: number\n calls_per_second_per_caller?: number\n batch_max?: number\n call_gas?: number\n head_ttl_ms?: number\n max_head_age_seconds?: number\n /** How many blocks below the tip a past `Address` read (`at_block`) must be. Read it here, never write it down. */\n at_block_min_depth?: number\n}\n\n/** `USE topology`: the shape rules, when the server publishes them. */\nexport interface TopologyAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n max_hops_per_path?: number\n max_limit?: number\n slots?: number\n}\n\n/** `USE facts`: the read contract, when the server publishes it. */\nexport interface FactsAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n window_days?: number\n max_rows?: number\n max_hops?: number\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n /** The layers as the server sent them. Absent when the server sent none. */\n layers?: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n /** The three layer blocks below are repeated exactly as sent, and absent when not sent. */\n chain_admission?: ChainAdmission\n topology_admission?: TopologyAdmission\n facts_admission?: FactsAdmission\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n /** The chain node's own head block, and how old that reading is. */\n chain_head_block?: number\n chain_head_age_seconds?: number\n warehouse_blocks_behind_head?: number\n graph_blocks_behind_head?: number\n }\n lane_progress?: NetworkLaneProgress[]\n graph_progress?: NetworkGraphProgress\n}\n\n/** One writer lane's committed height. null: the lane has not started. */\nexport interface NetworkLaneProgress {\n lane: string\n height: number | null\n}\n\n/**\n * How far each block layer of the topology graph reaches, and how far the\n * money family reaches (`complete_through_block`). `families` is absent on a\n * server that does not group its layers into families.\n */\nexport interface NetworkGraphProgress {\n complete_through_block: number\n layers: { layer: string; position: number }[]\n families?: NetworkGraphFamily[]\n}\n\n/**\n * One layer family as the server reports it. A block family carries the block\n * it is complete through and its lag behind the head (null on a server that\n * knows no head). `annotations` follows runs, not blocks: both numbers are null\n * and `fresh` says whether its last run is current.\n */\nexport interface NetworkGraphFamily {\n name: string\n complete_through_block: number | null\n blocks_behind_head: number | null\n fresh?: boolean\n layers: string[]\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nconst AVAILABLE_TOOLS_PER_LINE = 3\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction advertisedTools(raw: unknown): Record<string, string> {\n if (!isRecord(raw)) return {}\n const tools: Record<string, string> = {}\n for (const [name, status] of Object.entries(raw)) {\n if (name.trim() !== '' && typeof status === 'string') tools[name] = status\n }\n return tools\n}\n\nfunction advertisedLaneProgress(raw: unknown): NetworkLaneProgress[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const lanes: NetworkLaneProgress[] = []\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.lane !== 'string' || entry.lane.trim() === '') continue\n if (typeof entry.height === 'number') lanes.push({ lane: entry.lane, height: entry.height })\n else if (entry.height === null) lanes.push({ lane: entry.lane, height: null })\n }\n return lanes\n}\n\nfunction isNumberOrNull(value: unknown): value is number | null {\n return value === null || typeof value === 'number'\n}\n\n/** One family entry, or null when any member has the wrong shape. */\nfunction advertisedGraphFamily(entry: unknown): NetworkGraphFamily | null {\n if (!isRecord(entry) || typeof entry.name !== 'string' || entry.name.trim() === '') return null\n if (!isNumberOrNull(entry.complete_through_block)) return null\n const behind = entry.blocks_behind_head === undefined ? null : entry.blocks_behind_head\n if (!isNumberOrNull(behind)) return null\n if (entry.fresh !== undefined && typeof entry.fresh !== 'boolean') return null\n if (!Array.isArray(entry.layers) || !entry.layers.every((layer) => typeof layer === 'string'))\n return null\n return {\n name: entry.name,\n complete_through_block: entry.complete_through_block,\n blocks_behind_head: behind,\n ...(typeof entry.fresh === 'boolean' ? { fresh: entry.fresh } : {}),\n layers: [...(entry.layers as string[])],\n }\n}\n\n/** The families list, whole or not at all: one entry with a bad shape drops it. */\nfunction advertisedGraphFamilies(raw: unknown): NetworkGraphFamily[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const families: NetworkGraphFamily[] = []\n for (const entry of raw) {\n const family = advertisedGraphFamily(entry)\n if (family === null) return undefined\n families.push(family)\n }\n return families\n}\n\nfunction advertisedGraphProgress(raw: unknown): NetworkGraphProgress | undefined {\n if (!isRecord(raw) || typeof raw.complete_through_block !== 'number') return undefined\n if (!Array.isArray(raw.layers)) return undefined\n const layers: NetworkGraphProgress['layers'] = []\n for (const entry of raw.layers) {\n if (!isRecord(entry) || typeof entry.layer !== 'string' || typeof entry.position !== 'number')\n continue\n layers.push({ layer: entry.layer, position: entry.position })\n }\n const progress: NetworkGraphProgress = {\n complete_through_block: raw.complete_through_block,\n layers,\n }\n const families = advertisedGraphFamilies(raw.families)\n if (families) progress.families = families\n return progress\n}\n\nfunction advertisedNetwork(raw: unknown): NetworkCapability | null {\n if (!isRecord(raw) || typeof raw.network !== 'string' || raw.network.trim() === '') {\n return null\n }\n const network = raw.network.trim()\n const capability: NetworkCapability = {\n network,\n display_name:\n typeof raw.display_name === 'string' && raw.display_name.trim() !== ''\n ? raw.display_name\n : network,\n status: typeof raw.status === 'string' && raw.status.trim() !== '' ? raw.status : 'live',\n tools: advertisedTools(raw.tools),\n }\n // The layer blocks are the server's own words about its limits. They pass\n // through exactly as sent: nothing is added, no default is invented and no\n // member is dropped. A block the server did not send stays absent.\n if (isRecord(raw.layers)) capability.layers = raw.layers as NetworkCapability['layers']\n if (isRecord(raw.chain_admission)) {\n capability.chain_admission = raw.chain_admission as unknown as ChainAdmission\n }\n if (isRecord(raw.topology_admission)) {\n capability.topology_admission = raw.topology_admission as TopologyAdmission\n }\n if (isRecord(raw.facts_admission)) {\n capability.facts_admission = raw.facts_admission as FactsAdmission\n }\n if (raw.default === true) capability.default = true\n if (raw.default === false) capability.default = false\n if (isRecord(raw.coverage)) capability.coverage = raw.coverage as NetworkCapability['coverage']\n if (isRecord(raw.freshness))\n capability.freshness = raw.freshness as NetworkCapability['freshness']\n const laneProgress = advertisedLaneProgress(raw.lane_progress)\n if (laneProgress) capability.lane_progress = laneProgress\n const graphProgress = advertisedGraphProgress(raw.graph_progress)\n if (graphProgress) capability.graph_progress = graphProgress\n return capability\n}\n\n/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */\nexport function mirrorGraphNetworkCapabilities(document: {\n networks: unknown[]\n}): NetworkCapabilitiesDocument {\n return {\n schema: 'chain-insights.network-capabilities.v1',\n networks: document.networks\n .map((network) => advertisedNetwork(network))\n .filter((network): network is NetworkCapability => network !== null),\n }\n}\n\nfunction publicNetworkCapabilities(\n document: NetworkCapabilitiesDocument\n): NetworkCapabilitiesDocument {\n return mirrorGraphNetworkCapabilities(document)\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint'>\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim()\n if (token) {\n applyMcpAuthHeaders(headers, token)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = (await response.json()) as NetworkCapabilitiesDocument\n if (\n parsed.schema !== 'chain-insights.network-capabilities.v1' ||\n !Array.isArray(parsed.networks)\n ) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return publicNetworkCapabilities(parsed)\n}\n\nfunction availableTools(network: NetworkCapability): string[] {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.sort()\n}\n\nfunction availableToolLines(network: NetworkCapability): string[] {\n const tools = availableTools(network)\n if (tools.length === 0) return ['none']\n const lines: string[] = []\n for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) {\n lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(', '))\n }\n return lines\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange =\n coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange =\n coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : ''\n if (blockRange === 'blocks unknown' && dateRange === '') return 'unknown'\n if (blockRange === 'blocks unknown') return dateRange\n if (dateRange === '') return blockRange\n return `${blockRange} / ${dateRange}`\n}\n\nfunction displayName(network: NetworkCapability): string {\n return network.display_name || network.network\n}\n\nfunction statusLabel(network: NetworkCapability): string {\n return network.default ? `${network.status} (default)` : network.status\n}\n\nfunction formatTable(headers: string[], rows: string[][], minimumWidths: number[]): string {\n const widths = headers.map((header, index) =>\n Math.max(\n minimumWidths[index] ?? 0,\n header.length,\n ...rows.map((row) => row[index]?.length ?? 0)\n )\n )\n const row = (values: string[]) =>\n values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [row(headers), widths.map((width) => '-'.repeat(width)).join(' '), ...rows.map(row)].join(\n '\\n'\n )\n}\n\nexport function formatNetworkOverview(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const rows = document.networks.map((network) => [\n displayName(network),\n statusLabel(network),\n datasetLabel(network),\n ])\n return formatTable(['Network', 'Status', 'Dataset'], rows, [14, 10, 38])\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const networkRows = document.networks.flatMap((network) => {\n const toolLines = availableToolLines(network)\n return toolLines.map((toolLine, index) => [\n index === 0 ? displayName(network) : '',\n index === 0 ? datasetLabel(network) : '',\n toolLine,\n ])\n })\n return formatTable(['Network', 'Dataset', 'Chain Insights tools'], networkRows, [14, 38, 64])\n}\n\nexport function findNetworkCapability(\n document: NetworkCapabilitiesDocument,\n name: string\n): NetworkCapability | undefined {\n const normalizedName = name.trim().toLowerCase()\n if (!normalizedName) return undefined\n return document.networks.find((network) =>\n [network.network, network.display_name].some(\n (candidate) => candidate?.trim().toLowerCase() === normalizedName\n )\n )\n}\n\n/** The writer lanes a customer sees, in screen order, by their plain names. */\nconst KNOWN_LANES: readonly (readonly [string, string])[] = [\n ['core_transfers_source_head', 'receipts'],\n ['core_transfers_internal_follow', 'traces'],\n ['core_dex_follow', 'dex'],\n ['core_bridge_follow', 'bridge'],\n ['core_smart_account_follow', 'smart accounts'],\n]\n\n/** The evidence worker's lane. It has its own table and never joins Indexing. */\nconst EVIDENCE_LANE = 'evidence_sync'\n\n/** The plain name a lane prints as. An unknown lane prints as sent. */\nexport function laneDisplayName(lane: string): string {\n if (lane === EVIDENCE_LANE) return 'evidence'\n return KNOWN_LANES.find(([name]) => name === lane)?.[1] ?? lane\n}\n\n/** A graph layer prints as sent: it is the link kind a query names. */\nexport function layerDisplayName(layer: string): string {\n return layer\n}\n\n/** A family prints by its plain name: `smart_accounts` prints as \"smart accounts\". */\nexport function familyDisplayName(family: string): string {\n return family.replace(/_/g, ' ')\n}\n\n/** 87642448 -> \"87,642,448\", the same on every machine (no locale). */\nexport function groupThousands(value: number): string {\n const sign = value < 0 ? '-' : ''\n const digits = String(Math.trunc(Math.abs(value)))\n return sign + digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n}\n\n/** One row of a progress table. `behind` is absent with no head or no height. */\ninterface ProgressRow {\n label: string\n height: number | null\n /** Text printed in the height column instead of a number. */\n note?: string\n behind?: number\n}\n\nconst DETAIL_LABEL_WIDTH = 18\nconst TABLE_LABEL_WIDTH = 30\nconst HEIGHT_MIN_WIDTH = 10\nconst LAG_HEADER = 'behind head'\n\nfunction progressRows(\n entries: { label: string; height: number | null }[],\n head: number | undefined\n): ProgressRow[] {\n return entries.map(({ label, height }) => ({\n label,\n height,\n ...(height === null ? { note: 'not started' } : {}),\n ...(height !== null && head !== undefined ? { behind: Math.max(0, head - height) } : {}),\n }))\n}\n\nfunction notReportedRow(label: string): ProgressRow {\n return { label, height: null, note: 'not reported' }\n}\n\n/**\n * The family rows, in the order the server sends them. A block family's lag is\n * the server's `blocks_behind_head`, or head minus height when the server knows\n * no head. A family with no block (annotations) prints \"fresh\" or \"walking\" and\n * no lag.\n */\nfunction familyRows(families: NetworkGraphFamily[], head: number | undefined): ProgressRow[] {\n return families.map((family) => {\n const label = familyDisplayName(family.name)\n const height = family.complete_through_block\n if (height === null) {\n const note = family.fresh === undefined ? 'not started' : family.fresh ? 'fresh' : 'walking'\n return { label, height: null, note }\n }\n if (head === undefined) return { label, height }\n const behind = family.blocks_behind_head ?? Math.max(0, head - height)\n return { label, height, behind }\n })\n}\n\nfunction formatProgressTables(\n tables: { title: string; rows: ProgressRow[] }[],\n withLag: boolean\n): string[] {\n const allRows = tables.flatMap((table) => table.rows)\n const labelWidth = Math.max(\n TABLE_LABEL_WIDTH,\n ...allRows.map((row) => row.label.length + 4),\n ...tables.map((table) => table.title.length + 2)\n )\n const heightWidth = Math.max(\n HEIGHT_MIN_WIDTH,\n ...allRows.map((row) => (row.height === null ? 0 : groupThousands(row.height).length))\n )\n const lagWidth = Math.max(\n LAG_HEADER.length,\n ...allRows.map((row) => (row.behind === undefined ? 0 : groupThousands(row.behind).length))\n )\n const lines: string[] = []\n for (const table of tables) {\n if (lines.length > 0) lines.push('')\n const header = table.title.padEnd(labelWidth) + 'height'\n lines.push(withLag ? header.padEnd(labelWidth + heightWidth + 2) + LAG_HEADER : header)\n for (const row of table.rows) {\n let line = ` ${row.label}`.padEnd(labelWidth)\n if (row.height === null) {\n line += row.note ?? ''\n } else {\n line += groupThousands(row.height).padStart(heightWidth)\n if (withLag && row.behind !== undefined) {\n line += ' ' + groupThousands(row.behind).padStart(lagWidth)\n }\n }\n lines.push(line.trimEnd())\n }\n }\n return lines\n}\n\n/**\n * `cia network <name>`: how far this network is indexed. The chain node's head\n * comes first; every row reads against it. Known lanes print by plain name,\n * unknown lanes as sent after them, the layer families by plain name in the\n * server's order, graph layers as sent, and evidence in its own table. Heights\n * are right-aligned, so a shorter height lines up by its last digit.\n * `--json` never comes through here.\n */\nexport function formatNetworkCapability(network: NetworkCapability): string {\n const headBlock = network.freshness?.chain_head_block\n const head = typeof headBlock === 'number' ? headBlock : undefined\n const detail: [string, string][] = [\n ['Network', displayName(network)],\n ['Identifier', network.network],\n ['Status', statusLabel(network)],\n ]\n if (head !== undefined) {\n const age = network.freshness?.chain_head_age_seconds\n const ageText = typeof age === 'number' ? ` (${groupThousands(age)} s old)` : ''\n detail.push(['Blockchain head', `${groupThousands(head)}${ageText}`])\n }\n\n const lanes = network.lane_progress ?? []\n const known = KNOWN_LANES.flatMap(([name, label]) =>\n lanes.filter((lane) => lane.lane === name).map((lane) => ({ label, height: lane.height }))\n )\n const unknown = lanes\n .filter(\n (lane) => lane.lane !== EVIDENCE_LANE && !KNOWN_LANES.some(([name]) => name === lane.lane)\n )\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n const indexing = [...known, ...unknown]\n const layers = (network.graph_progress?.layers ?? []).map((layer) => ({\n label: layerDisplayName(layer.layer),\n height: layer.position,\n }))\n const evidence = lanes\n .filter((lane) => lane.lane === EVIDENCE_LANE)\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n\n const families = network.graph_progress?.families\n\n const tables = [\n {\n title: 'Indexing',\n rows: indexing.length > 0 ? progressRows(indexing, head) : [notReportedRow('indexing')],\n },\n // Absent on a server that sends no families: the table is left out, so the\n // screen of such a server stays as it was.\n ...(families ? [{ title: 'Families', rows: familyRows(families, head) }] : []),\n {\n title: 'Graph',\n rows: layers.length > 0 ? progressRows(layers, head) : [notReportedRow('graph')],\n },\n {\n title: 'Evidence',\n rows: evidence.length > 0 ? progressRows(evidence, head) : [notReportedRow('evidence')],\n },\n ]\n\n return [\n ...detail.map(([label, value]) => `${label.padEnd(DETAIL_LABEL_WIDTH)}${value}`),\n '',\n ...formatProgressTables(tables, head !== undefined),\n ].join('\\n')\n}\n"],"mappings":"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"}
|
|
@@ -241,7 +241,7 @@ function laneDisplayName(lane) {
|
|
|
241
241
|
function layerDisplayName(layer) {
|
|
242
242
|
return layer;
|
|
243
243
|
}
|
|
244
|
-
/** A family prints by its plain name: `
|
|
244
|
+
/** A family prints by its plain name: `smart_accounts` prints as "smart accounts". */
|
|
245
245
|
function familyDisplayName(family) {
|
|
246
246
|
return family.replace(/_/g, " ");
|
|
247
247
|
}
|
package/dist/cli.cjs
CHANGED
|
@@ -120,21 +120,21 @@ async function withGraphMcpClient(name, fn) {
|
|
|
120
120
|
}
|
|
121
121
|
async function printNetworkCapabilities(opts) {
|
|
122
122
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-BxtMoo9c.cjs")).then((n) => n.config_exports);
|
|
123
|
-
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await Promise.resolve().then(() => require("./capabilities-
|
|
123
|
+
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await Promise.resolve().then(() => require("./capabilities-DGavTSoW.cjs")).then((n) => n.capabilities_exports);
|
|
124
124
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
125
125
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
126
126
|
else console.log(formatNetworkCapabilities(document));
|
|
127
127
|
}
|
|
128
128
|
async function printNetworkOverview(opts) {
|
|
129
129
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-BxtMoo9c.cjs")).then((n) => n.config_exports);
|
|
130
|
-
const { fetchNetworkCapabilities, formatNetworkOverview } = await Promise.resolve().then(() => require("./capabilities-
|
|
130
|
+
const { fetchNetworkCapabilities, formatNetworkOverview } = await Promise.resolve().then(() => require("./capabilities-DGavTSoW.cjs")).then((n) => n.capabilities_exports);
|
|
131
131
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
132
132
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
133
133
|
else console.log(formatNetworkOverview(document));
|
|
134
134
|
}
|
|
135
135
|
async function printNetworkCapability(name, opts) {
|
|
136
136
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-BxtMoo9c.cjs")).then((n) => n.config_exports);
|
|
137
|
-
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await Promise.resolve().then(() => require("./capabilities-
|
|
137
|
+
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await Promise.resolve().then(() => require("./capabilities-DGavTSoW.cjs")).then((n) => n.capabilities_exports);
|
|
138
138
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
139
139
|
const network = findNetworkCapability(document, name);
|
|
140
140
|
if (!network) {
|
|
@@ -152,7 +152,7 @@ async function printNetworkCapability(name, opts) {
|
|
|
152
152
|
async function printNetworkSchema(name, opts) {
|
|
153
153
|
const { loadConfig } = await Promise.resolve().then(() => require("./config-BxtMoo9c.cjs")).then((n) => n.config_exports);
|
|
154
154
|
const { resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-Ypw9BNbn.cjs")).then((n) => n.client_exports);
|
|
155
|
-
const { formatGraphSchemaText, getGraphSchema } = await Promise.resolve().then(() => require("./graph-schema-
|
|
155
|
+
const { formatGraphSchemaText, getGraphSchema } = await Promise.resolve().then(() => require("./graph-schema-C_7aZ8RL.cjs")).then((n) => n.graph_schema_exports);
|
|
156
156
|
const config = await loadConfig();
|
|
157
157
|
const schema = await getGraphSchema({
|
|
158
158
|
network: name,
|
package/dist/cli.mjs
CHANGED
|
@@ -118,21 +118,21 @@ async function withGraphMcpClient(name, fn) {
|
|
|
118
118
|
}
|
|
119
119
|
async function printNetworkCapabilities(opts) {
|
|
120
120
|
const { loadConfig } = await import("./config-9MabJh5R.mjs").then((n) => n.t);
|
|
121
|
-
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-
|
|
121
|
+
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-BBVDIECk.mjs").then((n) => n.t);
|
|
122
122
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
123
123
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
124
124
|
else console.log(formatNetworkCapabilities(document));
|
|
125
125
|
}
|
|
126
126
|
async function printNetworkOverview(opts) {
|
|
127
127
|
const { loadConfig } = await import("./config-9MabJh5R.mjs").then((n) => n.t);
|
|
128
|
-
const { fetchNetworkCapabilities, formatNetworkOverview } = await import("./capabilities-
|
|
128
|
+
const { fetchNetworkCapabilities, formatNetworkOverview } = await import("./capabilities-BBVDIECk.mjs").then((n) => n.t);
|
|
129
129
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
130
130
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
131
131
|
else console.log(formatNetworkOverview(document));
|
|
132
132
|
}
|
|
133
133
|
async function printNetworkCapability(name, opts) {
|
|
134
134
|
const { loadConfig } = await import("./config-9MabJh5R.mjs").then((n) => n.t);
|
|
135
|
-
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await import("./capabilities-
|
|
135
|
+
const { fetchNetworkCapabilities, findNetworkCapability, formatNetworkCapability } = await import("./capabilities-BBVDIECk.mjs").then((n) => n.t);
|
|
136
136
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
137
137
|
const network = findNetworkCapability(document, name);
|
|
138
138
|
if (!network) {
|
|
@@ -150,7 +150,7 @@ async function printNetworkCapability(name, opts) {
|
|
|
150
150
|
async function printNetworkSchema(name, opts) {
|
|
151
151
|
const { loadConfig } = await import("./config-9MabJh5R.mjs").then((n) => n.t);
|
|
152
152
|
const { resolveGraphMcpEndpoint } = await import("./client-gsLi528K.mjs").then((n) => n.r);
|
|
153
|
-
const { formatGraphSchemaText, getGraphSchema } = await import("./graph-schema-
|
|
153
|
+
const { formatGraphSchemaText, getGraphSchema } = await import("./graph-schema-DHd2fGbr.mjs").then((n) => n.i);
|
|
154
154
|
const config = await loadConfig();
|
|
155
155
|
const schema = await getGraphSchema({
|
|
156
156
|
network: name,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
|
|
2
2
|
const require_client = require("./client-Ypw9BNbn.cjs");
|
|
3
|
-
const require_capabilities = require("./capabilities-
|
|
3
|
+
const require_capabilities = require("./capabilities-DGavTSoW.cjs");
|
|
4
4
|
const require_cache_location = require("./cache-location-CBVzUIxq.cjs");
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
2
|
import { t as PaymentRequiredError } from "./client-gsLi528K.mjs";
|
|
3
|
-
import { n as findNetworkCapability, r as mirrorGraphNetworkCapabilities } from "./capabilities-
|
|
3
|
+
import { n as findNetworkCapability, r as mirrorGraphNetworkCapabilities } from "./capabilities-BBVDIECk.mjs";
|
|
4
4
|
import { n as chainInsightsHome, t as CACHE_TTL_MS } from "./cache-location-BO2XkfgI.mjs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
@@ -1177,4 +1177,4 @@ async function handleMetaSchema(args, deps) {
|
|
|
1177
1177
|
//#endregion
|
|
1178
1178
|
export { handleMetaSchema as a, GRAPH_EXPAND_TITLE as c, graph_schema_exports as i, GRAPH_EXPAND_TOOL as l, META_SCHEMA_DESCRIPTION as n, FLOWS_MAX_OFFSET as o, META_SCHEMA_TITLE as r, GRAPH_EXPAND_DESCRIPTION as s, GraphSchemaError as t, handleGraphExpand as u };
|
|
1179
1179
|
|
|
1180
|
-
//# sourceMappingURL=graph-schema-
|
|
1180
|
+
//# sourceMappingURL=graph-schema-DHd2fGbr.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-schema-DdriUxEe.mjs","names":["errorResult","isRecord"],"sources":["../src/mcp/flows.ts","../src/mcp/graph-schema-cache.ts","../src/mcp/graph-schema.ts"],"sourcesContent":["/**\n * graph_expand, the one dedicated tool of the local proxy, called by the Chain\n * Insights view (ui://chain-insights/view) when the investigator clicks. Its\n * _meta says visibility [\"app\"], so a host keeps it away from the model: the\n * model writes its own graph_query, and the view draws the rows by their column\n * names.\n *\n * Two forms, told apart by the arguments:\n *\n * - node, {network, address, in_offset?, out_offset?}: one address with up to\n * 250 newest senders and 250 newest receivers, a text summary and\n * chain-insights.flows.v1 in structuredContent. Composed from at most five\n * anchored graph_query reads on the topology graph: the node, then its\n * outgoing and its incoming FLOWS_TO, newest first, two reads a side because\n * a reply holds at most 200 rows.\n * - link, {network, from, to, day}: the transfers between two known addresses\n * on one UTC day, newest first, at most 50, a short text summary and\n * chain-insights.transfers.v1 in structuredContent. One anchored USE facts\n * graph_query read.\n * - link page, {network, from, to, day, limit, skip?, first_day?}: a page of\n * `limit` transfers (1 to 50) newest first, read from `day` back one UTC day\n * at a time, at most TRANSFERS_MAX_DAYS (a week of) reads a call, never before\n * first_day. The answer carries `next` ({day, skip}, or null when every\n * transfer back to first_day is listed) which the next call sends back as\n * day and skip. The view's \"More transfers\".\n * - details, {network, address, details: true}: the lifetime totals, counts,\n * role and labels of one address, chain-insights.address.v1, one anchored\n * topology read. The view fills its panel with it when the rows of a query\n * carried no totals.\n *\n * Every read goes through the proxy's remote client, the same path and payment\n * wrapping a graph_query call takes, and is billed as a graph_query. Every\n * argument is checked before the first read, so a refused call reaches no graph\n * endpoint and costs nothing.\n */\n\nexport const FLOWS_SCHEMA = 'chain-insights.flows.v1'\nexport const GRAPH_EXPAND_TOOL = 'graph_expand'\nexport const FLOWS_NETWORK = 'robinhood'\n\n/** How many senders and how many receivers one page shows, at most. */\nexport const FLOWS_PER_SIDE = 250\n/** The view data of one answer: at most this many addresses, the centre included... */\nexport const FLOWS_MAX_NODES = 500\n/**\n * ...and under this many characters of JSON. Hosts drop an app tool result\n * over about 150,000 characters.\n */\nexport const FLOWS_MAX_CHARS = 140_000\n/**\n * The graph endpoint cuts a reply at 200 rows, whatever the LIMIT, so one side\n * of a page is read in reads of at most this many rows.\n */\nexport const FLOWS_READ_ROWS = 200\n/** The text of a node answer holds at most this many lines. */\nexport const FLOWS_SUMMARY_MAX_LINES = 20\n/** Where a graph_expand page may start on each side, at most. */\nexport const FLOWS_MAX_OFFSET = 10_000\n\nexport const TRANSFERS_SCHEMA = 'chain-insights.transfers.v1'\n/** A link answer lists at most this many transfers... */\nexport const TRANSFERS_MAX = 50\n/**\n * ...from the rows one read brings back. A facts read takes no ORDER BY and\n * the server cuts a reply at 200 rows, so the read asks for the whole cap and\n * the newest rows are picked here.\n */\nexport const TRANSFERS_READ_ROWS = 200\n/** The text of a link answer names at most this many transfers. */\nexport const TRANSFERS_SUMMARY_ROWS = 5\n/** A paged link answer reads at most this many days (a week) in one call: a sparse link costs at most this many reads a page, and a week of it comes back at once. */\nexport const TRANSFERS_MAX_DAYS = 7\n/** Where a paged link answer stops reading back when the caller names no first_day. */\nexport const TRANSFERS_FLOOR_DAY = '2015-01-01'\n\nexport const ADDRESS_SCHEMA = 'chain-insights.address.v1'\n\nexport type FlowNode = {\n address: string\n role: string\n labels: string[]\n total_in_usd: number\n total_out_usd: number\n degree_in: number\n degree_out: number\n}\n\nexport type FlowEdge = {\n from: string\n to: string\n usd: number\n tx_count: number\n first_seen_ms: number\n last_seen_ms: number\n}\n\nexport type FlowCursor = { in_offset: number; out_offset: number }\n\n/** The links of the centre not loaded yet, past the cursor, on each side. */\nexport type FlowRemaining = { senders: number; receivers: number }\n\nexport type FlowView = {\n schema: typeof FLOWS_SCHEMA\n network: string\n center: string\n nodes: FlowNode[]\n edges: FlowEdge[]\n cursor: FlowCursor\n truncated: boolean\n /** Set on every node page. A view that does not know it ignores it. */\n remaining?: FlowRemaining\n}\n\ntype Row = Record<string, unknown>\n\nexport type FlowsPage = {\n center: Row\n senders: Row[]\n receivers: Row[]\n inOffset: number\n outOffset: number\n}\n\n/** One graph_query answer as the remote client returns it. */\nexport type GraphQueryAnswer = {\n content?: Array<{ type: string; text?: string }>\n structuredContent?: Record<string, unknown>\n isError?: boolean\n}\n\nexport type FlowsToolResult = {\n content: Array<{ type: 'text'; text: string }>\n structuredContent?: Record<string, unknown>\n isError: boolean\n}\n\nexport type FlowsDependencies = {\n /** One graph_query call through the proxy's remote client. */\n graphQuery: (args: { network: string; query: string }) => Promise<GraphQueryAnswer>\n /** The text of a call that threw (payment required, transport failure). */\n describeFailure: (err: unknown) => string\n /** Why no read can run (the graph endpoint is not connected), checked after the arguments. */\n unavailable?: () => string | undefined\n now?: () => Date\n}\n\nexport const INVALID_ADDRESS_TEXT =\n 'invalid_address: a full 0x address of 40 hexadecimal characters is needed, for example 0x04911a118f11c75667e4d0dfb8e640af5a353550'\nexport const OFFSET_TOO_LARGE_TEXT = `invalid_offset: in_offset and out_offset must each be at most ${FLOWS_MAX_OFFSET}`\nexport const OFFSET_NEGATIVE_TEXT =\n 'invalid_offset: in_offset and out_offset must each be a whole number, 0 or more'\n\nexport const INVALID_DAY_TEXT =\n 'invalid_day: day must be a calendar date written YYYY-MM-DD (UTC), for example 2026-07-10'\nexport const BOTH_FORMS_TEXT =\n 'invalid_arguments: pass address (with in_offset and out_offset) to expand a node, or from, to and day to list the transfers of a link, not both'\nexport const INVALID_LIMIT_TEXT = 'invalid_limit: limit must be a whole number from 1 to 50'\nexport const INVALID_SKIP_TEXT = 'invalid_skip: skip must be a whole number of 0 or more'\nexport const INVALID_FIRST_DAY_TEXT =\n 'invalid_first_day: first_day must be a calendar date written YYYY-MM-DD (UTC), not after day'\nexport const DETAILS_FORM_TEXT =\n 'invalid_arguments: details takes address alone; pass no from, to, day or offsets with it'\n\nconst ADDRESS_PATTERN = /^0x[0-9a-f]{40}$/\nconst DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst CODE_PREFIX = /^([a-z][a-z0-9_]*):\\s*/\n\n/**\n * The address lower-cased, or null when it is not 0x and 40 hexadecimal\n * characters. Only an address that passes is ever written into a read, so the\n * reads can carry it as a literal.\n */\nexport function normalizeFlowsAddress(raw: unknown): string | null {\n if (typeof raw !== 'string') return null\n const address = raw.trim().toLowerCase()\n return ADDRESS_PATTERN.test(address) ? address : null\n}\n\n/**\n * The day as YYYY-MM-DD when it is a real calendar date, or null. Only a day\n * that passes is ever written into a read.\n */\nexport function normalizeFlowsDay(raw: unknown): string | null {\n if (typeof raw !== 'string') return null\n const day = raw.trim()\n if (!DAY_PATTERN.test(day)) return null\n const time = Date.parse(`${day}T00:00:00Z`)\n if (Number.isNaN(time) || new Date(time).toISOString().slice(0, 10) !== day) return null\n return day\n}\n\n/**\n * The one facts read of a link: the transfers from one address to another on\n * one day. A facts read names the pair with one day, takes no ORDER BY and\n * holds at most 200 rows, so the read asks for the whole cap and the order is\n * made in buildTransfersView. Both addresses and the day have passed their\n * checks, so they are written into the read as literals.\n */\nexport function transfersReadFor(from: string, to: string, day: string): string {\n return (\n `USE facts MATCH (a:Address {address: \"${from}\"})-[t:TRANSFER]->(b:Address {address: \"${to}\"}) ` +\n `WHERE t.block_date = \"${day}\" ` +\n 'RETURN t.tx_id AS tx_id, t.block_timestamp AS block_timestamp, t.amount AS amount, ' +\n `t.asset_symbol AS asset_symbol, t.amount_usd AS amount_usd LIMIT ${TRANSFERS_READ_ROWS}`\n )\n}\n\n/** The UTC day before a YYYY-MM-DD day. */\nexport function dayBefore(day: string): string {\n return new Date(Date.parse(`${day}T00:00:00Z`) - 86_400_000).toISOString().slice(0, 10)\n}\n\n/**\n * The one topology read of a details call: the address alone, with its\n * lifetime rollups and activity span. The address has passed its check, so it\n * is written into the read as a literal.\n */\nexport function detailsReadFor(address: string): string {\n return (\n `USE topology MATCH (b:Address {address: \"${address}\"}) RETURN ${NODE_FIELDS}, ` +\n 'b.first_activity_timestamp AS first_activity_timestamp, b.last_activity_timestamp AS last_activity_timestamp LIMIT 1'\n )\n}\n\nexport type AddressView = FlowNode & {\n schema: typeof ADDRESS_SCHEMA\n network: string\n is_exchange: boolean\n is_sanctioned: boolean\n is_scam: boolean\n is_victim: boolean\n pool: boolean\n first_activity_timestamp: number | null\n last_activity_timestamp: number | null\n}\n\n/** chain-insights.address.v1 from the one row of a details read. */\nexport function buildAddressView(network: string, address: string, row: Row): AddressView {\n const node = flowsNodeOf({ ...row, address })\n return {\n schema: ADDRESS_SCHEMA,\n network,\n ...node,\n is_exchange: row.is_exchange === true,\n is_sanctioned: row.is_sanctioned === true,\n is_scam: row.is_scam === true,\n is_victim: row.is_victim === true,\n pool: row.pool === true,\n first_activity_timestamp: flowsNumberOrNull(row.first_activity_timestamp),\n last_activity_timestamp: flowsNumberOrNull(row.last_activity_timestamp),\n }\n}\n\n/** The one line of a details answer. */\nexport function addressSummary(view: AddressView): string {\n return (\n `${view.address} on ${view.network}: ${view.role}; lifetime ${formatFlowsUsd(view.total_in_usd)} in, ` +\n `${formatFlowsUsd(view.total_out_usd)} out; ${view.degree_in} senders and ${view.degree_out} receivers in the topology graph` +\n (view.labels.length ? `; labels ${view.labels.join(', ')}` : '') +\n '.'\n )\n}\n\nconst NODE_FIELDS =\n 'b.address AS address, b.labels AS labels, b.is_exchange AS is_exchange, ' +\n 'b.is_sanctioned AS is_sanctioned, b.is_scam AS is_scam, b.is_victim AS is_victim, b:Pool AS pool, ' +\n 'b.total_in_usd AS total_in_usd, b.total_out_usd AS total_out_usd, b.degree_in AS degree_in, b.degree_out AS degree_out'\nconst PARTY_FIELDS =\n NODE_FIELDS +\n ', f.amount_usd_sum AS usd, f.tx_count AS tx_count, ' +\n 'f.first_seen_timestamp AS first_seen, f.last_seen_timestamp AS last_seen'\n// Newest link first, the address breaking a tie, so a page and the next one\n// never share or skip a link.\nconst PAGE_ORDER = ' ORDER BY f.last_seen_timestamp DESC, b.address'\n\n/** One anchored read of a page, with the SKIP and LIMIT it carries. */\nexport type FlowsRead = { id: 'node' | 'out' | 'in'; query: string; skip: number; limit: number }\n\n/**\n * The anchored reads of one page: the node, then the receivers, then the\n * senders. Each side asks for one row more than it shows: the extra row says\n * another page exists. A reply holds at most FLOWS_READ_ROWS rows, so a side\n * is read in chunks on the same order, each starting where the last one ends.\n */\nexport function flowsReadsFor(address: string, inOffset: number, outOffset: number): FlowsRead[] {\n const side = (id: 'out' | 'in', offset: number, match: string): FlowsRead[] => {\n const reads: FlowsRead[] = []\n for (let done = 0; done < FLOWS_PER_SIDE + 1; done += FLOWS_READ_ROWS) {\n const skip = offset + done\n const limit = Math.min(FLOWS_READ_ROWS, FLOWS_PER_SIDE + 1 - done)\n reads.push({\n id,\n query: `USE topology MATCH ${match} RETURN ${PARTY_FIELDS}${PAGE_ORDER}${skip > 0 ? ` SKIP ${skip}` : ''} LIMIT ${limit}`,\n skip,\n limit,\n })\n }\n return reads\n }\n return [\n {\n id: 'node',\n query: `USE topology MATCH (b:Address {address: '${address}'}) RETURN ${NODE_FIELDS} LIMIT 1`,\n skip: 0,\n limit: 1,\n },\n ...side('out', outOffset, `(a:Address {address: '${address}'})-[f:FLOWS_TO]->(b:Address)`),\n ...side('in', inOffset, `(b:Address)-[f:FLOWS_TO]->(a:Address {address: '${address}'})`),\n ]\n}\n\n/** The role word of an address, first match wins. */\nexport function flowsRole(row: Row): string {\n if (row.is_exchange === true) return 'exchange'\n if (row.is_sanctioned === true) return 'sanctioned'\n if (row.is_scam === true) return 'scam'\n if (row.is_victim === true) return 'victim'\n if (row.pool === true) return 'pool'\n return 'other'\n}\n\nfunction flowsFloat(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0\n}\n\nfunction flowsInt(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : 0\n}\n\nfunction flowsNodeOf(row: Row): FlowNode {\n const labels = Array.isArray(row.labels)\n ? row.labels.filter((label): label is string => typeof label === 'string')\n : []\n return {\n address: typeof row.address === 'string' ? row.address : '',\n role: flowsRole(row),\n labels,\n total_in_usd: flowsFloat(row.total_in_usd),\n total_out_usd: flowsFloat(row.total_out_usd),\n degree_in: flowsInt(row.degree_in),\n degree_out: flowsInt(row.degree_out),\n }\n}\n\nfunction assembleFlowView(\n network: string,\n page: FlowsPage,\n senders: Row[],\n receivers: Row[],\n truncated: boolean\n): FlowView {\n const center = flowsNodeOf(page.center)\n const cursor = {\n in_offset: page.inOffset + senders.length,\n out_offset: page.outOffset + receivers.length,\n }\n const view: FlowView = {\n schema: FLOWS_SCHEMA,\n network,\n center: center.address,\n nodes: [center],\n edges: [],\n cursor,\n truncated,\n remaining: {\n senders: Math.max(0, center.degree_in - cursor.in_offset),\n receivers: Math.max(0, center.degree_out - cursor.out_offset),\n },\n }\n const seen = new Set([center.address])\n const add = (row: Row, incoming: boolean) => {\n const node = flowsNodeOf(row)\n if (!seen.has(node.address)) {\n seen.add(node.address)\n view.nodes.push(node)\n }\n view.edges.push({\n from: incoming ? node.address : center.address,\n to: incoming ? center.address : node.address,\n usd: flowsFloat(row.usd),\n tx_count: flowsInt(row.tx_count),\n first_seen_ms: flowsInt(row.first_seen),\n last_seen_ms: flowsInt(row.last_seen),\n })\n }\n for (const row of senders) add(row, true)\n for (const row of receivers) add(row, false)\n return view\n}\n\nfunction fitsFlowBudget(view: FlowView): boolean {\n return view.nodes.length <= FLOWS_MAX_NODES && JSON.stringify(view).length < FLOWS_MAX_CHARS\n}\n\n/**\n * How many senders and receivers stay after `drops` of the oldest links go,\n * one at a time from the longer side, the senders first on a tie.\n */\nfunction keptAfterDrops(senders: number, receivers: number, drops: number): [number, number] {\n let s = senders\n let r = receivers\n for (let left = Math.min(drops, s + r); left > 0; left -= 1) {\n if (s >= r) s -= 1\n else r -= 1\n }\n return [s, r]\n}\n\n/**\n * flows.v1 from one page: the centre first, then the senders and the\n * receivers, each newest first, at most FLOWS_PER_SIDE a side. When the view\n * data would hold more than FLOWS_MAX_NODES addresses or reach FLOWS_MAX_CHARS\n * characters, the oldest links go first, the view is marked truncated, and the\n * cursor still says where the next page starts.\n *\n * Each dropped link only makes the view smaller, so the fewest drops that fit\n * are found by halving: about ten views are measured for a full page, not one\n * per dropped link.\n */\nexport function buildFlowView(network: string, page: FlowsPage): FlowView {\n const overflow = page.senders.length > FLOWS_PER_SIDE || page.receivers.length > FLOWS_PER_SIDE\n const senders = page.senders.slice(0, FLOWS_PER_SIDE)\n const receivers = page.receivers.slice(0, FLOWS_PER_SIDE)\n const viewAfter = (drops: number) => {\n const [s, r] = keptAfterDrops(senders.length, receivers.length, drops)\n return assembleFlowView(\n network,\n page,\n senders.slice(0, s),\n receivers.slice(0, r),\n overflow || drops > 0\n )\n }\n // Dropping every link is the floor: that view is answered even when it does\n // not fit.\n let low = 0\n let high = senders.length + receivers.length\n while (low < high) {\n const mid = (low + high) >> 1\n if (fitsFlowBudget(viewAfter(mid))) high = mid\n else low = mid + 1\n }\n return viewAfter(low)\n}\n\nexport function formatFlowsUsd(value: number): string {\n if (!(value > 0)) return '$0'\n if (value < 0.01) return 'under $0.01'\n const whole = Math.floor(value)\n const cents = Math.round((value - whole) * 100)\n if (cents === 100) return formatFlowsUsd(whole + 1)\n const grouped = whole.toFixed(0).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n return `$${grouped}.${String(cents).padStart(2, '0')}`\n}\n\nfunction formatFlowsAge(lastSeenMs: number, now: Date): string {\n if (lastSeenMs <= 0) return 'at an unknown time'\n const date = new Date(lastSeenMs).toISOString().slice(0, 10)\n const days = Math.trunc((now.getTime() - lastSeenMs) / 86_400_000)\n if (days < 1) return `${date} (less than a day ago)`\n if (days === 1) return `${date} (1 day ago)`\n return `${date} (${days} days ago)`\n}\n\n/** The role word, with up to three address labels beside it. */\nfunction roleText(node: FlowNode | undefined): string {\n if (!node) return 'other'\n if (node.labels.length === 0) return node.role\n return `${node.role} (${node.labels.slice(0, 3).join(', ')})`\n}\n\nfunction sideLines(title: string, parties: string[]): string[] {\n if (parties.length === 0) return [`${title}, newest first: none.`]\n const lines = [`${title}, newest first (${parties.length}):`]\n // A side with more than six counterparties lists two on a line, so a full\n // page stays within the line budget.\n const perLine = parties.length > 6 ? 2 : 1\n for (let start = 0; start < parties.length; start += perLine) {\n lines.push(`- ${parties.slice(start, start + perLine).join('; ')}`)\n }\n return lines\n}\n\n/**\n * The text of a node answer: the centre, its role and lifetime totals, then\n * every counterparty of the view with its USD, transaction count and last-seen\n * date and age. At most FLOWS_SUMMARY_MAX_LINES lines.\n */\nexport function flowsSummary(view: FlowView, now: Date): string {\n const nodes = new Map(view.nodes.map((node) => [node.address, node]))\n const center = nodes.get(view.center)\n const party = (address: string, edge: FlowEdge) =>\n `${address} ${roleText(nodes.get(address))}, ${formatFlowsUsd(edge.usd)} in ${edge.tx_count} tx, last seen ${formatFlowsAge(edge.last_seen_ms, now)}`\n const senders: string[] = []\n const receivers: string[] = []\n for (const edge of view.edges) {\n if (edge.to === view.center) senders.push(party(edge.from, edge))\n else receivers.push(party(edge.to, edge))\n }\n const lines = [\n `Money flows of ${view.center} on ${view.network}: ${roleText(center)}; lifetime ${formatFlowsUsd(center?.total_in_usd ?? 0)} in, ${formatFlowsUsd(center?.total_out_usd ?? 0)} out; ${center?.degree_in ?? 0} senders and ${center?.degree_out ?? 0} receivers in the topology graph.`,\n ...sideLines('Recent senders', senders),\n ...sideLines('Recent receivers', receivers),\n ]\n const body = lines.slice(0, FLOWS_SUMMARY_MAX_LINES - 2)\n const footer = pagingLines(view, senders.length, receivers.length, center)\n return [...body, ...footer].join('\\n')\n}\n\n/** Where this page sits, and that the picture loads the rest when an address is clicked. */\nfunction pagingLines(\n view: FlowView,\n shownSenders: number,\n shownReceivers: number,\n center: FlowNode | undefined\n): string[] {\n const totalIn = center?.degree_in ?? 0\n const totalOut = center?.degree_out ?? 0\n const { in_offset: nextIn, out_offset: nextOut } = view.cursor\n const range = (shown: number, next: number, total: number) =>\n shown === 0 ? `none of ${total}` : `${next - shown + 1} to ${next} of ${total}`\n const where = `Showing senders ${range(shownSenders, nextIn, totalIn)} and receivers ${range(shownReceivers, nextOut, totalOut)}, newest first.`\n if (!view.truncated && nextIn >= totalIn && nextOut >= totalOut) return [where]\n return [\n where,\n 'More senders and receivers load when an address in the picture is double-clicked.',\n ]\n}\n\nexport type TransferRow = {\n tx_id: string\n block_timestamp: number | null\n amount: number | string | null\n asset_symbol: string | null\n amount_usd: number | null\n}\n\nexport type TransfersCursor = { day: string; skip: number }\n\nexport type TransfersView = {\n schema: typeof TRANSFERS_SCHEMA\n network: string\n from: string\n to: string\n day: string\n transfers: TransferRow[]\n truncated: boolean\n /** Paged answers only: the earliest day read, how many days the call read, and where the next page starts (null: every transfer back to first_day is listed). */\n through_day?: string\n days_read?: number\n next?: TransfersCursor | null\n}\n\nexport type TransfersPageRequest = { skip: number; firstDay: string | null; limit: number }\n\n/** A finite number, or a decimal string read as one, or null. */\nfunction flowsNumberOrNull(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction transferOf(row: Row): TransferRow {\n const amount =\n typeof row.amount === 'number' && Number.isFinite(row.amount)\n ? row.amount\n : typeof row.amount === 'string' && row.amount.trim() !== ''\n ? row.amount\n : null\n return {\n tx_id: typeof row.tx_id === 'string' ? row.tx_id : '',\n block_timestamp: flowsNumberOrNull(row.block_timestamp),\n amount,\n asset_symbol:\n typeof row.asset_symbol === 'string' && row.asset_symbol !== '' ? row.asset_symbol : null,\n amount_usd: flowsNumberOrNull(row.amount_usd),\n }\n}\n\n/**\n * chain-insights.transfers.v1 from the rows of one facts read: newest first,\n * at most TRANSFERS_MAX, truncated when more rows than that came back. The\n * facts layer takes no ORDER BY, so the order is made here.\n */\nexport function buildTransfersView(\n network: string,\n from: string,\n to: string,\n day: string,\n rows: Row[]\n): TransfersView {\n const newestFirst = rows\n .map(transferOf)\n .sort((a, b) => (b.block_timestamp ?? -Infinity) - (a.block_timestamp ?? -Infinity))\n return {\n schema: TRANSFERS_SCHEMA,\n network,\n from,\n to,\n day,\n transfers: newestFirst.slice(0, TRANSFERS_MAX),\n truncated: newestFirst.length > TRANSFERS_MAX,\n }\n}\n\nfunction transferLine(transfer: TransferRow): string {\n const time =\n transfer.block_timestamp === null\n ? 'at an unknown time'\n : `${new Date(transfer.block_timestamp).toISOString().slice(0, 19).replace('T', ' ')} UTC`\n const amount = transfer.amount === null ? 'an unknown amount' : String(transfer.amount)\n const asset = transfer.asset_symbol ? ` ${transfer.asset_symbol}` : ''\n const usd = transfer.amount_usd === null ? 'no USD price' : formatFlowsUsd(transfer.amount_usd)\n return `- ${time}, ${amount}${asset}, ${usd}, tx ${transfer.tx_id}`\n}\n\n/** The short text of a link answer: the pair, the day, the count and the newest few transfers. */\nexport function transfersSummary(view: TransfersView): string {\n const paged = view.through_day !== undefined\n const span = paged && view.through_day !== view.day ? `${view.through_day} to ${view.day}` : view.day\n const head = `Transfers from ${view.from} to ${view.to} on ${span} (UTC) on ${view.network}`\n if (view.transfers.length === 0) {\n return paged && view.next ? `${head}: none; earlier days remain before ${view.next.day}.` : `${head}: none.`\n }\n const count = view.truncated\n ? paged\n ? `${view.transfers.length}, and a day holds more than one read lists`\n : `the newest ${view.transfers.length}, and the day holds more`\n : `${view.transfers.length}`\n const lines = [\n `${head}: ${count}, newest first.`,\n ...view.transfers.slice(0, TRANSFERS_SUMMARY_ROWS).map(transferLine),\n ]\n const rest = view.transfers.length - TRANSFERS_SUMMARY_ROWS\n if (rest > 0) lines.push(`- and ${rest} more in the picture.`)\n if (paged && view.next) lines.push(`- earlier transfers remain, from ${view.next.day} back.`)\n return lines.join('\\n')\n}\n\nfunction errorResult(text: string): FlowsToolResult {\n return { content: [{ type: 'text', text }], isError: true }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction answerText(answer: GraphQueryAnswer): string {\n return (answer.content ?? [])\n .map((block) => (block.type === 'text' && typeof block.text === 'string' ? block.text : ''))\n .filter(Boolean)\n .join('\\n')\n .trim()\n}\n\n/**\n * The refusal of a read that did not answer, naming its code first:\n * error_detail.code when the graph endpoint sent its typed refusal, the code\n * its text starts with otherwise, and graph_query_failed when it names none.\n * The view marks the address it was expanding.\n */\nexport function refusalText(answer: GraphQueryAnswer): string {\n const text = answerText(answer)\n const detail = isRecord(answer.structuredContent)\n ? answer.structuredContent.error_detail\n : undefined\n const detailCode = isRecord(detail) && typeof detail.code === 'string' ? detail.code : ''\n const textCode = CODE_PREFIX.exec(text)?.[1] ?? ''\n const code = detailCode || textCode || 'graph_query_failed'\n const rest = textCode === code ? text.replace(CODE_PREFIX, '') : text\n return rest ? `${code}: ${rest}` : `${code}: the graph endpoint refused the read`\n}\n\n/** The endpoint says it cut the reply short (facts.query.truncated). */\nfunction cutByEndpoint(answer: GraphQueryAnswer): boolean {\n const facts = isRecord(answer.structuredContent) ? answer.structuredContent.facts : undefined\n const query = isRecord(facts) ? facts.query : undefined\n return isRecord(query) && query.truncated === true\n}\n\nfunction rowsOf(answer: GraphQueryAnswer): Row[] | null {\n const facts = isRecord(answer.structuredContent) ? answer.structuredContent.facts : undefined\n const query = isRecord(facts) ? facts.query : undefined\n const results = isRecord(query) ? query.results : undefined\n return Array.isArray(results) ? results.filter(isRecord) : null\n}\n\ntype Checked<T> = T | { error: string }\n\nfunction checkNetwork(args: Record<string, unknown>): Checked<string> {\n const rawNetwork = typeof args.network === 'string' ? args.network.trim().toLowerCase() : ''\n if (!rawNetwork) return { error: 'invalid_network: network is required; pass robinhood' }\n if (rawNetwork !== FLOWS_NETWORK) {\n return {\n error: `invalid_network: graph_expand reads the robinhood graph only; got \"${String(args.network).trim()}\"`,\n }\n }\n return FLOWS_NETWORK\n}\n\n/** The address of a node or of one end of a link; `name` says which end in a link refusal. */\nfunction checkAddress(raw: unknown, name?: string): Checked<string> {\n const address = normalizeFlowsAddress(raw)\n if (address) return address\n return {\n error: name\n ? INVALID_ADDRESS_TEXT.replace('invalid_address:', `invalid_address (${name}):`)\n : INVALID_ADDRESS_TEXT,\n }\n}\n\nfunction checkDay(raw: unknown): Checked<string> {\n const day = normalizeFlowsDay(raw)\n return day ?? { error: INVALID_DAY_TEXT }\n}\n\nfunction checkLimit(value: unknown): Checked<number> {\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 1 || value > TRANSFERS_MAX) {\n return { error: INVALID_LIMIT_TEXT }\n }\n return value\n}\n\nfunction checkSkip(value: unknown): Checked<number> {\n if (value === undefined || value === null) return 0\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) return { error: INVALID_SKIP_TEXT }\n return value\n}\n\nfunction checkFirstDay(value: unknown, day: string): Checked<string | null> {\n if (value === undefined || value === null) return null\n const first = normalizeFlowsDay(value)\n if (!first || first > day) return { error: INVALID_FIRST_DAY_TEXT }\n return first\n}\n\nfunction checkOffset(value: unknown): Checked<number> {\n if (value === undefined || value === null) return 0\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {\n return { error: OFFSET_NEGATIVE_TEXT }\n }\n if (value > FLOWS_MAX_OFFSET) return { error: OFFSET_TOO_LARGE_TEXT }\n return value\n}\n\n/**\n * Read and answer one node page. The reads run one after another: the node\n * first, so an address the graph does not hold costs one read; then the\n * receivers and the senders, the chunks of a side joined in order. A chunk that\n * comes back short, and not cut by the endpoint, ends its side, so the next\n * chunk of that side is not read or billed. The first read that does not\n * answer ends the call.\n */\nasync function runNode(\n deps: FlowsDependencies,\n network: string,\n address: string,\n inOffset: number,\n outOffset: number\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n const rows: Record<FlowsRead['id'], Row[]> = { node: [], out: [], in: [] }\n const ended = new Set<FlowsRead['id']>()\n for (const read of flowsReadsFor(address, inOffset, outOffset)) {\n if (ended.has(read.id)) continue\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: read.query })\n } catch (err) {\n return errorResult(deps.describeFailure(err))\n }\n if (answer.isError === true) return errorResult(refusalText(answer))\n const found = rowsOf(answer)\n if (!found) {\n return errorResult(\n 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows'\n )\n }\n rows[read.id].push(...found)\n if (read.id === 'node' && found.length === 0) {\n return errorResult(`${address} is not in the ${network} topology graph`)\n }\n if (found.length < read.limit && !cutByEndpoint(answer)) ended.add(read.id)\n }\n const view = buildFlowView(network, {\n center: rows.node[0] ?? {},\n senders: rows.in,\n receivers: rows.out,\n inOffset,\n outOffset,\n })\n const now = deps.now ? deps.now() : new Date()\n return {\n content: [{ type: 'text', text: flowsSummary(view, now) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\n/** Read and answer the transfers of one link on one day: a single facts read. */\nasync function runLink(\n deps: FlowsDependencies,\n network: string,\n from: string,\n to: string,\n day: string\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: transfersReadFor(from, to, day) })\n } catch (err) {\n return errorResult(deps.describeFailure(err))\n }\n if (answer.isError === true) return errorResult(refusalText(answer))\n const rows = rowsOf(answer)\n if (!rows) {\n return errorResult(\n 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows'\n )\n }\n const view = buildTransfersView(network, from, to, day, rows)\n return {\n content: [{ type: 'text', text: transfersSummary(view) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\n/** One facts read of a day, as the paged walk needs it: the rows newest first, or the error that ends the call. */\nasync function readDay(\n deps: FlowsDependencies,\n network: string,\n from: string,\n to: string,\n day: string\n): Promise<{ rows: TransferRow[]; cut: boolean } | { error: string }> {\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: transfersReadFor(from, to, day) })\n } catch (err) {\n return { error: deps.describeFailure(err) }\n }\n if (answer.isError === true) return { error: refusalText(answer) }\n const rows = rowsOf(answer)\n if (!rows) {\n return { error: 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows' }\n }\n const newestFirst = rows\n .map(transferOf)\n .sort((a, b) => (b.block_timestamp ?? -Infinity) - (a.block_timestamp ?? -Infinity))\n return { rows: newestFirst, cut: rows.length >= TRANSFERS_READ_ROWS }\n}\n\n/**\n * Read and answer one page of a link's transfers: from `day` back, one day a\n * read, until the page holds `limit` transfers, TRANSFERS_MAX_DAYS days were\n * read, or the day before first_day is reached. `skip` passes over the\n * transfers of `day` an earlier page listed. The first read that does not\n * answer ends the call.\n */\nasync function runLinkPage(\n deps: FlowsDependencies,\n network: string,\n from: string,\n to: string,\n day: string,\n page: TransfersPageRequest\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n const floor = page.firstDay ?? TRANSFERS_FLOOR_DAY\n const transfers: TransferRow[] = []\n let current = day\n let skip = page.skip\n let daysRead = 0\n let through = day\n let truncated = false\n let next: TransfersCursor | null = null\n for (;;) {\n if (current < floor) break\n if (daysRead >= TRANSFERS_MAX_DAYS) {\n next = { day: current, skip }\n break\n }\n const read = await readDay(deps, network, from, to, current)\n if ('error' in read) return errorResult(read.error)\n daysRead += 1\n through = current\n if (read.cut) truncated = true\n const rest = read.rows.slice(skip)\n const room = page.limit - transfers.length\n if (rest.length > room) {\n transfers.push(...rest.slice(0, room))\n next = { day: current, skip: skip + room }\n break\n }\n transfers.push(...rest)\n const earlier = dayBefore(current)\n if (transfers.length >= page.limit) {\n next = earlier < floor ? null : { day: earlier, skip: 0 }\n break\n }\n current = earlier\n skip = 0\n }\n const view: TransfersView = {\n schema: TRANSFERS_SCHEMA,\n network,\n from,\n to,\n day,\n transfers,\n truncated,\n through_day: through,\n days_read: daysRead,\n next,\n }\n return {\n content: [{ type: 'text', text: transfersSummary(view) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\n/** Read and answer the details of one address: a single anchored topology read. */\nasync function runDetails(\n deps: FlowsDependencies,\n network: string,\n address: string\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: detailsReadFor(address) })\n } catch (err) {\n return errorResult(deps.describeFailure(err))\n }\n if (answer.isError === true) return errorResult(refusalText(answer))\n const rows = rowsOf(answer)\n if (!rows) {\n return errorResult(\n 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows'\n )\n }\n if (rows.length === 0) return errorResult(`${address} is not in the ${network} topology graph`)\n const view = buildAddressView(network, address, rows[0]!)\n return {\n content: [{ type: 'text', text: addressSummary(view) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\nfunction given(record: Record<string, unknown>, keys: string[]): boolean {\n return keys.some((key) => record[key] !== undefined && record[key] !== null)\n}\n\n/**\n * graph_expand, the node form {network, address, in_offset?, out_offset?} or the\n * link form {network, from, to, day}. Every argument is checked before the\n * first read.\n */\nexport async function handleGraphExpand(\n args: unknown,\n deps: FlowsDependencies\n): Promise<FlowsToolResult> {\n const record = isRecord(args) ? args : {}\n const network = checkNetwork(record)\n if (typeof network !== 'string') return errorResult(network.error)\n\n const isLink = given(record, ['from', 'to', 'day'])\n if (isLink && given(record, ['address', 'in_offset', 'out_offset', 'details']))\n return errorResult(BOTH_FORMS_TEXT)\n\n if (isLink) {\n const from = checkAddress(record.from, 'from')\n if (typeof from !== 'string') return errorResult(from.error)\n const to = checkAddress(record.to, 'to')\n if (typeof to !== 'string') return errorResult(to.error)\n const day = checkDay(record.day)\n if (typeof day !== 'string') return errorResult(day.error)\n // A limit, a skip or a first_day asks for a page across days; without them the call is the one-day read.\n if (given(record, ['limit', 'skip', 'first_day'])) {\n const limit = checkLimit(record.limit)\n if (typeof limit !== 'number') return errorResult(limit.error)\n const skip = checkSkip(record.skip)\n if (typeof skip !== 'number') return errorResult(skip.error)\n const firstDay = checkFirstDay(record.first_day, day)\n if (firstDay !== null && typeof firstDay !== 'string') return errorResult(firstDay.error)\n return runLinkPage(deps, network, from, to, day, { skip, firstDay, limit })\n }\n return runLink(deps, network, from, to, day)\n }\n\n if (record.details !== undefined && record.details !== null) {\n if (record.details !== true) return errorResult(DETAILS_FORM_TEXT)\n if (given(record, ['in_offset', 'out_offset'])) return errorResult(DETAILS_FORM_TEXT)\n const address = checkAddress(record.address)\n if (typeof address !== 'string') return errorResult(address.error)\n return runDetails(deps, network, address)\n }\n\n const address = checkAddress(record.address)\n if (typeof address !== 'string') return errorResult(address.error)\n const inOffset = checkOffset(record.in_offset)\n if (typeof inOffset !== 'number') return errorResult(inOffset.error)\n const outOffset = checkOffset(record.out_offset)\n if (typeof outOffset !== 'number') return errorResult(outOffset.error)\n return runNode(deps, network, address, inOffset, outOffset)\n}\n\nexport const GRAPH_EXPAND_TITLE = 'Expand a node or a link in the picture'\n\nexport const GRAPH_EXPAND_DESCRIPTION =\n 'Expand an open Chain Insights picture. Called by the view when the investigator clicks, never by the model. ' +\n 'A node, {network, address, in_offset?, out_offset?}, loads the newest senders and receivers of one address ' +\n '(up to 250 a side, from the offsets); billed as at most five graph_query topology reads. A link, {network, from, to, day}, ' +\n 'lists the transfers between two addresses on one UTC day (YYYY-MM-DD), newest first, at most 50; billed as ' +\n 'one graph_query facts read. A link page, {network, from, to, day, limit, skip?, first_day?}, lists `limit` (1-50) ' +\n 'transfers from `day` back across days, newest first, and answers a `next` cursor ({day, skip}) while older ones ' +\n 'remain; billed as at most seven facts reads (a week). Details, {network, address, details: true}, answer the lifetime totals, ' +\n 'counts, role and labels of one address; billed as one topology read. Read-only; an argument refused before a read costs nothing.'\n","import { createHash } from 'node:crypto'\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { CACHE_TTL_MS, chainInsightsHome } from './cache-location.js'\nimport type { GraphSchemaDocument } from './graph-schema.js'\n\n// The graph schema of one network on one endpoint, kept on disk beside the tool\n// list cache (src/mcp/schema-cache.ts) with the same home and the same 24 hour\n// lifetime: ~/.chain-insights/cache/schema-<network>-<endpoint hash>.json.\n//\n// A schema built while a read failed is kept for a short time only. Kept for\n// 24 hours, one refused or rate-limited read would hide a section of the schema\n// for a whole day.\n\n/** How long a schema with a failed read stays fresh. */\nexport const PARTIAL_SCHEMA_TTL_MS = 10 * 60 * 1000\n\ninterface GraphSchemaCacheFile {\n cached_at: number\n complete: boolean\n endpoint: string\n document: GraphSchemaDocument\n}\n\nfunction endpointHash(endpoint: string): string {\n return createHash('sha256').update(endpoint).digest('hex').slice(0, 12)\n}\n\n/** The cache file of a network on an endpoint. The network is an identifier already checked by the caller. */\nexport function graphSchemaCachePath(network: string, endpoint: string): string {\n return path.join(chainInsightsHome(), 'cache', `schema-${network}-${endpointHash(endpoint)}.json`)\n}\n\n/**\n * The cached schema, marked cached, or null when there is none, it is stale, or\n * the file cannot be read. A cache is derived data: a damaged file is a miss and\n * the next build replaces it.\n */\nexport async function loadGraphSchemaCache(\n network: string,\n endpoint: string,\n now: Date\n): Promise<GraphSchemaDocument | null> {\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(graphSchemaCachePath(network, endpoint), 'utf8'))\n } catch {\n return null\n }\n const file = parsed as Partial<GraphSchemaCacheFile> | null\n if (\n !file ||\n typeof file.cached_at !== 'number' ||\n file.endpoint !== endpoint ||\n file.document?.schema !== 'chain-insights.graph-schema.v1'\n ) {\n return null\n }\n const lifetime = file.complete === true ? CACHE_TTL_MS : PARTIAL_SCHEMA_TTL_MS\n const age = now.getTime() - file.cached_at\n if (age < 0 || age > lifetime) return null\n return { ...file.document, cached: true }\n}\n\n/** `network` is the name the caller asked for, the same one a later load asks with. */\nexport async function saveGraphSchemaCache(\n network: string,\n document: GraphSchemaDocument,\n endpoint: string,\n complete: boolean,\n now: Date\n): Promise<void> {\n const target = graphSchemaCachePath(network, endpoint)\n const file: GraphSchemaCacheFile = {\n cached_at: now.getTime(),\n complete,\n endpoint,\n document: { ...document, cached: false },\n }\n await mkdir(path.dirname(target), { recursive: true })\n // Written beside the target and renamed, so a second proxy reading the file\n // never sees half of it.\n const scratch = `${target}.${process.pid}.tmp`\n await writeFile(scratch, JSON.stringify(file, null, 2) + '\\n', { mode: 0o600 })\n await rename(scratch, target)\n}\n","/**\n * meta_schema: the live graph schema of one network, read from the graph\n * endpoint and kept on disk for 24 hours.\n *\n * The graph serves four read-only catalog statements on `USE topology` through\n * the ordinary graph_query tool: `CALL db.labels()`, `CALL db.relationshipTypes()`,\n * `CALL db.propertyKeys()` and `SHOW INDEXES`. They name the labels, the link\n * types, every property key and the indexes. They do not say which keys belong to\n * which kind, so the main kinds are sampled with the two probe forms the server\n * admits (`keys(a)` on addresses, `keys(r)` on a link). The facts relationships\n * and the chain lookups come from the network's published capabilities.\n *\n * One build is one MCP session and runs its reads one after another: the public\n * endpoint allows about 30 requests a minute for each IP address, and opening a\n * session costs three of them. A read that fails leaves its section empty and\n * adds a note. It never fails the tool.\n */\n\nimport { findNetworkCapability, mirrorGraphNetworkCapabilities } from './capabilities.js'\nimport type { NetworkCapability } from './capabilities.js'\nimport { PaymentRequiredError } from './client.js'\nimport { refusalText, type GraphQueryAnswer } from './flows.js'\nimport { loadGraphSchemaCache, saveGraphSchemaCache } from './graph-schema-cache.js'\n\nexport const GRAPH_SCHEMA_ID = 'chain-insights.graph-schema.v1' as const\nexport const GRAPH_SCHEMA_SOURCE = 'live catalog' as const\nexport const META_SCHEMA_TITLE = 'Graph schema'\nexport const META_SCHEMA_DESCRIPTION =\n 'Return the live graph schema of one network: the labels, the link types, the field names of an address and of each main link, the indexes, the USE facts relationships with the TRANSFER columns, and the USE chain lookups. ' +\n 'Call it first when you need field names. It is cached for 24 hours; pass refresh=true to rebuild it.'\n\n/** Fields of an address come from this many sampled nodes, and of a link from this many sampled links. */\nexport const ADDRESS_SAMPLE_LIMIT = 20\nexport const LINK_SAMPLE_LIMIT = 5\n\n/** The link types an agent queries. Each is sampled only when the graph lists it. */\nexport const SAMPLED_LINK_TYPES = [\n 'FLOWS_TO',\n 'OPERATED_BY',\n 'LINKED',\n 'SWAPPED',\n 'BRIDGED',\n 'ADDED_LIQUIDITY',\n 'REMOVED_LIQUIDITY',\n] as const\n\n// The TRANSFER columns the cypher skill lists, in its order. tests/graph-schema.test.ts\n// holds this list to the skill and to the columns the graph server maps on TRANSFER\n// (tests/fixtures/facts-columns.json).\nexport const FACTS_TRANSFER_COLUMNS = [\n 'tx_id',\n 'block_date',\n 'block_height',\n 'block_timestamp',\n 'event_index',\n 'edge_index',\n 'kind',\n 'asset_contract',\n 'asset_symbol',\n 'amount',\n 'amount_usd',\n 'price_usd',\n 'price_missing',\n 'token_id',\n 'token_standard',\n 'operator_address',\n 'raw_amount',\n 'decimals',\n] as const\n\n// `CALL db.schema.visualization()` is not read. It bills about 427 units where\n// the four statements below bill 13 to 133 each, and it adds only which labels a\n// link type joins, which the cypher skill and the served hints already say.\nconst CATALOG_READS = {\n labels: { label: 'labels', query: 'USE topology CALL db.labels()' },\n linkTypes: { label: 'link types', query: 'USE topology CALL db.relationshipTypes()' },\n propertyKeys: { label: 'property keys', query: 'USE topology CALL db.propertyKeys()' },\n indexes: { label: 'indexes', query: 'USE topology SHOW INDEXES' },\n} as const\n\nexport function addressSampleQuery(): string {\n return `USE topology MATCH (a:Address) RETURN keys(a) AS keys LIMIT ${ADDRESS_SAMPLE_LIMIT}`\n}\n\nexport function linkSampleQuery(linkType: string): string {\n return `USE topology MATCH ()-[r:${linkType}]->() RETURN keys(r) AS keys LIMIT ${LINK_SAMPLE_LIMIT}`\n}\n\nexport interface GraphSchemaIndex {\n name: string\n entity: 'node' | 'link'\n /** The labels of a node index, the link types of a link index. */\n types: string[]\n properties: string[]\n state: string\n}\n\nexport interface GraphSchemaDocument {\n schema: typeof GRAPH_SCHEMA_ID\n network: string\n built_at: string\n source: typeof GRAPH_SCHEMA_SOURCE\n cached: boolean\n /** Units the reads of the build billed, summed from the replies. A cached document keeps the units of its build. */\n billed_units: number\n layers: {\n topology: {\n labels: string[]\n link_types: string[]\n property_keys: string[]\n indexes: GraphSchemaIndex[]\n samples: Record<string, string[]>\n }\n facts: { relationships: string[]; transfer_columns: string[] }\n chain: { lookups: string[] }\n }\n notes: string[]\n}\n\n/** A refusal with its own words for the caller: a bad argument, an unserved network, an endpoint that is down. */\nexport class GraphSchemaError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'GraphSchemaError'\n }\n}\n\nexport interface GraphSchemaClient {\n callTool(request: { name: string; arguments: Record<string, unknown> }): Promise<unknown>\n}\n\ntype Row = Record<string, unknown>\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction sortedUnique(values: Iterable<string>): string[] {\n return [...new Set(values)].sort()\n}\n\nfunction stringList(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((entry): entry is string => typeof entry === 'string' && entry !== '')\n : []\n}\n\nconst NETWORK_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/\n\n/**\n * The network as a lower-case identifier. It becomes part of a file name and of\n * every query, so only an identifier passes.\n */\nexport function normalizeSchemaNetwork(raw: unknown): string {\n const network = typeof raw === 'string' ? raw.trim().toLowerCase() : ''\n if (!network) {\n throw new GraphSchemaError(\n 'invalid_network: network is required. Pass the identifier of a served network, for example robinhood.'\n )\n }\n if (!NETWORK_PATTERN.test(network)) {\n throw new GraphSchemaError(\n `invalid_network: \"${String(raw).trim()}\" is not a network identifier. Pass the identifier of a served network, for example robinhood.`\n )\n }\n return network\n}\n\n// ─── Reading ──────────────────────────────────────────────────────────────────\n\nfunction queryBlock(structured: unknown): Row | null {\n const facts = isRecord(structured) ? structured.facts : undefined\n const query = isRecord(facts) ? facts.query : undefined\n return isRecord(query) ? query : null\n}\n\n/** The `facts.query` block of one graph_query reply, from structuredContent or from the JSON text. */\nfunction answerQuery(answer: GraphQueryAnswer): Row | null {\n const fromStructured = queryBlock(answer.structuredContent)\n if (fromStructured) return fromStructured\n for (const block of answer.content ?? []) {\n if (block.type !== 'text' || typeof block.text !== 'string') continue\n try {\n const parsed = queryBlock(JSON.parse(block.text))\n if (parsed) return parsed\n } catch {\n /* not JSON: try the next block */\n }\n }\n return null\n}\n\n// A failure after which every later read would fail the same way: the endpoint\n// limits requests, or it asks for payment. The build stops sending reads.\ntype Halt = 'rate_limit' | 'payment'\n\nfunction haltOfError(err: unknown): Halt | undefined {\n if (err instanceof PaymentRequiredError) return 'payment'\n const code = isRecord(err) ? err.code : undefined\n const message = err instanceof Error ? err.message : String(err)\n if (code === 429 || /\\b429\\b|too many requests|rate.?limit/i.test(message)) return 'rate_limit'\n if (/\\b402\\b|payment required/i.test(message)) return 'payment'\n return undefined\n}\n\nfunction haltOfAnswer(answer: GraphQueryAnswer): Halt | undefined {\n const detail = isRecord(answer.structuredContent)\n ? answer.structuredContent.error_detail\n : undefined\n return isRecord(detail) && detail.class === 'capacity' ? 'rate_limit' : undefined\n}\n\nexport interface BuildOptions {\n client: GraphSchemaClient\n /** A normalized network identifier. */\n network: string\n now?: () => Date\n /** A wait between two reads, in milliseconds. One build needs none: it stays under the rate limit. */\n pauseMs?: number\n sleep?: (ms: number) => Promise<void>\n}\n\nexport interface BuildResult {\n document: GraphSchemaDocument\n /** False when a read failed: the document has a section missing. */\n complete: boolean\n}\n\nfunction describeIndex(row: Row): GraphSchemaIndex | null {\n const name = typeof row.name === 'string' ? row.name : ''\n const entity =\n row.entityType === 'NODE' ? 'node' : row.entityType === 'RELATIONSHIP' ? 'link' : null\n const properties = stringList(row.properties)\n // A lookup index names no label and no property: it tells an agent nothing.\n if (!name || !entity || properties.length === 0) return null\n return {\n name,\n entity,\n types: stringList(row.labelsOrTypes),\n properties,\n state: typeof row.state === 'string' && row.state ? row.state : 'unknown',\n }\n}\n\nexport async function buildGraphSchema(options: BuildOptions): Promise<BuildResult> {\n const { client } = options\n const now = options.now ?? (() => new Date())\n const pauseMs = options.pauseMs ?? 0\n const sleep = options.sleep ?? ((ms: number) => new Promise<void>((done) => setTimeout(done, ms)))\n\n const problems: string[] = []\n const remarks: string[] = []\n let billed = 0\n let calls = 0\n let halted = false\n\n async function pace(): Promise<void> {\n if (calls > 0 && pauseMs > 0) await sleep(pauseMs)\n calls += 1\n }\n\n function fail(label: string, reason: string, halt?: Halt): null {\n problems.push(`${label} could not be read: ${reason}`)\n if (halt && !halted) {\n halted = true\n if (halt === 'payment') {\n problems.push(\n 'The endpoint asks for payment, so the reads after it were not sent. Fix the payment setup, then call again with refresh=true.'\n )\n } else {\n // The endpoint says how long to wait: \"retry after 23 seconds\".\n const wait = /retry after (\\d+) seconds?/i.exec(reason)?.[1]\n problems.push(\n `The endpoint is limiting requests, so the reads after it were not sent. Call again after ${wait ? `${wait} seconds` : 'a minute'} with refresh=true.`\n )\n }\n }\n return null\n }\n\n async function graphRead(network: string, label: string, query: string): Promise<Row[] | null> {\n if (halted) return null\n await pace()\n let answer: GraphQueryAnswer\n try {\n answer = (await client.callTool({\n name: 'graph_query',\n arguments: { network, query },\n })) as GraphQueryAnswer\n } catch (err) {\n return fail(label, err instanceof Error ? err.message : String(err), haltOfError(err))\n }\n const block = answerQuery(answer ?? {})\n if (block && typeof block.billable_units === 'number') billed += block.billable_units\n if (answer?.isError === true) {\n return fail(label, refusalText(answer), haltOfAnswer(answer))\n }\n if (!block || !Array.isArray(block.results)) {\n return fail(label, 'the reply holds no rows')\n }\n return block.results.filter(isRecord)\n }\n\n // ── The network: capabilities first, so an unserved name costs no graph read.\n let network = options.network\n let capability: NetworkCapability | undefined\n await pace()\n try {\n const answer = (await client.callTool({\n name: 'network_capabilities',\n arguments: {},\n })) as GraphQueryAnswer\n if (answer?.isError === true) {\n fail('network capabilities', refusalText(answer), haltOfAnswer(answer))\n } else {\n const facts = isRecord(answer?.structuredContent) ? answer.structuredContent.facts : undefined\n const capabilities = isRecord(facts) ? facts.capabilities : undefined\n const networks =\n isRecord(capabilities) && Array.isArray(capabilities.networks)\n ? capabilities.networks\n : null\n if (!networks) {\n fail('network capabilities', 'the reply lists no networks')\n } else {\n const document = mirrorGraphNetworkCapabilities({ networks })\n capability = findNetworkCapability(document, options.network)\n if (!capability) {\n const served = document.networks.map((entry) => entry.network).join(', ')\n throw new GraphSchemaError(\n `network_not_served: \"${options.network}\" is not a network this endpoint serves. Served networks: ${served || 'none'}.`\n )\n }\n network = capability.network\n }\n }\n } catch (err) {\n if (err instanceof GraphSchemaError) throw err\n fail('network capabilities', err instanceof Error ? err.message : String(err), haltOfError(err))\n }\n\n // ── Topology: the catalog, then a sample of the main kinds.\n const topology: GraphSchemaDocument['layers']['topology'] = {\n labels: [],\n link_types: [],\n property_keys: [],\n indexes: [],\n samples: {},\n }\n const topologyOff = capability?.layers?.topology?.enabled === false\n if (topologyOff) {\n remarks.push('The topology layer is off on this network.')\n } else {\n const labels = await graphRead(network, CATALOG_READS.labels.label, CATALOG_READS.labels.query)\n if (labels) topology.labels = sortedUnique(labels.flatMap((row) => stringList([row.label])))\n\n const linkTypes = await graphRead(\n network,\n CATALOG_READS.linkTypes.label,\n CATALOG_READS.linkTypes.query\n )\n if (linkTypes) {\n topology.link_types = sortedUnique(\n linkTypes.flatMap((row) => stringList([row.relationshipType]))\n )\n }\n\n const keys = await graphRead(\n network,\n CATALOG_READS.propertyKeys.label,\n CATALOG_READS.propertyKeys.query\n )\n if (keys) {\n topology.property_keys = sortedUnique(keys.flatMap((row) => stringList([row.propertyKey])))\n }\n\n const indexes = await graphRead(\n network,\n CATALOG_READS.indexes.label,\n CATALOG_READS.indexes.query\n )\n if (indexes) {\n topology.indexes = indexes\n .map(describeIndex)\n .filter((index): index is GraphSchemaIndex => index !== null)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n // An address is sampled unless the label list was read and has no Address.\n const labelsRead = labels !== null\n if (!labelsRead || topology.labels.includes('Address')) {\n const rows = await graphRead(network, 'the Address sample', addressSampleQuery())\n if (rows) topology.samples['Address'] = unionOfKeys(rows)\n }\n\n const sampled = SAMPLED_LINK_TYPES.filter((type) => topology.link_types.includes(type))\n if (linkTypes === null && !halted) {\n problems.push('The link samples were skipped: the link types are unknown.')\n }\n for (const type of sampled) {\n const rows = await graphRead(network, `the ${type} sample`, linkSampleQuery(type))\n if (rows) topology.samples[type] = unionOfKeys(rows)\n }\n\n const unsampled = topology.link_types.filter(\n (type) => !(SAMPLED_LINK_TYPES as readonly string[]).includes(type)\n )\n if (unsampled.length > 0) {\n remarks.push(\n `Link types with no sample: ${unsampled.join(', ')}. Read the keys of one with USE topology MATCH ()-[r:<TYPE>]->() RETURN keys(r) AS keys LIMIT ${LINK_SAMPLE_LIMIT}.`\n )\n }\n }\n\n // ── Facts and chain: what the network publishes.\n const facts: GraphSchemaDocument['layers']['facts'] = { relationships: [], transfer_columns: [] }\n const chain: GraphSchemaDocument['layers']['chain'] = { lookups: [] }\n if (capability) {\n if (capability.layers?.facts?.enabled === false) {\n remarks.push('The facts layer is off on this network.')\n } else {\n facts.relationships = stringList(capability.layers?.facts?.relationships)\n if (facts.relationships.includes('TRANSFER')) {\n facts.transfer_columns = [...FACTS_TRANSFER_COLUMNS]\n }\n }\n const admission = capability.chain_admission\n if (admission?.enabled === false) {\n remarks.push(`The chain layer is off (status ${admission.status ?? 'unknown'}).`)\n } else {\n chain.lookups = stringList(admission?.lookups)\n }\n }\n\n const words = [...topology.property_keys, ...Object.values(topology.samples).flat()]\n if (words.includes('synced_through_height') || words.includes('pair_key')) {\n remarks.push(\n 'synced_through_height and pair_key are sync bookkeeping: never filter or sort on them.'\n )\n }\n remarks.push('Fields come from a sample of the live graph. A rare field may be missing.')\n\n return {\n complete: problems.length === 0,\n document: {\n schema: GRAPH_SCHEMA_ID,\n network,\n built_at: now().toISOString(),\n source: GRAPH_SCHEMA_SOURCE,\n cached: false,\n billed_units: billed,\n layers: { topology, facts, chain },\n notes: [...problems, ...remarks],\n },\n }\n}\n\nfunction unionOfKeys(rows: Row[]): string[] {\n return sortedUnique(rows.flatMap((row) => stringList(row.keys)))\n}\n\n// ─── Reading from the cache, or building ──────────────────────────────────────\n\nexport interface GetGraphSchemaOptions {\n /** As the caller wrote it: normalized here. */\n network: unknown\n refresh?: boolean\n endpoint: string\n /**\n * Runs `fn` with one connected client. It is called once for a build and not at\n * all when the cache answers, so a cache hit opens no session.\n */\n withClient: <T>(fn: (client: GraphSchemaClient) => Promise<T>) => Promise<T>\n now?: () => Date\n pauseMs?: number\n sleep?: (ms: number) => Promise<void>\n}\n\n// Builds in flight, by endpoint and network: two callers that ask for one\n// schema at once share one build and pay for it once.\nconst building = new Map<string, Promise<GraphSchemaDocument>>()\n\nexport async function getGraphSchema(options: GetGraphSchemaOptions): Promise<GraphSchemaDocument> {\n const network = normalizeSchemaNetwork(options.network)\n const now = options.now ?? (() => new Date())\n if (options.refresh !== true) {\n const cached = await loadGraphSchemaCache(network, options.endpoint, now())\n if (cached) return cached\n }\n const key = `${options.endpoint}\\n${network}`\n const running = building.get(key)\n if (running) return running\n const build = (async () => {\n const { document, complete } = await options.withClient((client) =>\n buildGraphSchema({\n client,\n network,\n now,\n ...(options.pauseMs === undefined ? {} : { pauseMs: options.pauseMs }),\n ...(options.sleep ? { sleep: options.sleep } : {}),\n })\n )\n try {\n await saveGraphSchemaCache(network, document, options.endpoint, complete, now())\n } catch (err) {\n document.notes.push(\n `The schema could not be saved to the cache: ${err instanceof Error ? err.message : String(err)}`\n )\n }\n return document\n })()\n building.set(key, build)\n try {\n return await build\n } finally {\n building.delete(key)\n }\n}\n\n// ─── Text ─────────────────────────────────────────────────────────────────────\n\nfunction listLine(title: string, values: string[], empty = 'none read'): string {\n return `${title}: ${values.length > 0 ? values.join(', ') : empty}`\n}\n\n/** The schema as the model and the terminal read it. */\nexport function formatGraphSchemaText(document: GraphSchemaDocument): string {\n const { topology, facts, chain } = document.layers\n const origin = document.cached\n ? 'from the cache, kept 24 hours; refresh=true rebuilds it'\n : `read now for ${document.billed_units} billed units and cached for 24 hours`\n const lines = [\n `Graph schema, network ${document.network}`,\n `Source: ${document.source}, built ${document.built_at} (${origin})`,\n '',\n 'USE topology',\n listLine('Labels', topology.labels),\n listLine('Link types', topology.link_types),\n listLine(`Property keys (${topology.property_keys.length})`, topology.property_keys),\n ]\n lines.push(topology.indexes.length > 0 ? 'Indexes:' : 'Indexes: none read')\n for (const index of topology.indexes) {\n lines.push(\n `- ${index.entity} ${index.types.join('|')}(${index.properties.join(', ')}) ${index.state}`\n )\n }\n const sampled = Object.entries(topology.samples)\n lines.push(sampled.length > 0 ? 'Fields, from a sample:' : 'Fields, from a sample: none read')\n for (const [kind, fields] of sampled) lines.push(`- ${kind}: ${fields.join(', ')}`)\n lines.push(\n '',\n 'USE facts',\n listLine('Relationships', facts.relationships),\n listLine('TRANSFER columns', facts.transfer_columns),\n '',\n 'USE chain',\n listLine('Lookups', chain.lookups)\n )\n if (document.notes.length > 0) {\n lines.push('', 'Notes:', ...document.notes.map((note) => `- ${note}`))\n }\n return lines.join('\\n')\n}\n\n// ─── The MCP tool ─────────────────────────────────────────────────────────────\n\nexport interface GraphSchemaDependencies {\n endpoint: string\n withClient: GetGraphSchemaOptions['withClient']\n /** The text of a failure that is not a GraphSchemaError: payment required, transport. */\n describeFailure: (err: unknown) => string\n now?: () => Date\n pauseMs?: number\n sleep?: (ms: number) => Promise<void>\n}\n\nexport type MetaSchemaResult = {\n content: Array<{ type: 'text'; text: string }>\n structuredContent?: Record<string, unknown>\n isError: boolean\n}\n\nfunction errorResult(text: string): MetaSchemaResult {\n return { content: [{ type: 'text', text }], isError: true }\n}\n\n/** meta_schema, {network, refresh?}. Every argument is checked before the first read. */\nexport async function handleMetaSchema(\n args: unknown,\n deps: GraphSchemaDependencies\n): Promise<MetaSchemaResult> {\n const record = isRecord(args) ? args : {}\n const refresh = record.refresh\n if (refresh !== undefined && refresh !== null && typeof refresh !== 'boolean') {\n return errorResult('invalid_refresh: refresh must be true or false')\n }\n try {\n const document = await getGraphSchema({\n network: record.network,\n refresh: refresh === true,\n endpoint: deps.endpoint,\n withClient: deps.withClient,\n ...(deps.now ? { now: deps.now } : {}),\n ...(deps.pauseMs === undefined ? {} : { pauseMs: deps.pauseMs }),\n ...(deps.sleep ? { sleep: deps.sleep } : {}),\n })\n return {\n content: [{ type: 'text', text: formatGraphSchemaText(document) }],\n structuredContent: document as unknown as Record<string, unknown>,\n isError: false,\n }\n } catch (err) {\n return errorResult(err instanceof GraphSchemaError ? err.message : deps.describeFailure(err))\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"graph-schema-DHd2fGbr.mjs","names":["errorResult","isRecord"],"sources":["../src/mcp/flows.ts","../src/mcp/graph-schema-cache.ts","../src/mcp/graph-schema.ts"],"sourcesContent":["/**\n * graph_expand, the one dedicated tool of the local proxy, called by the Chain\n * Insights view (ui://chain-insights/view) when the investigator clicks. Its\n * _meta says visibility [\"app\"], so a host keeps it away from the model: the\n * model writes its own graph_query, and the view draws the rows by their column\n * names.\n *\n * Two forms, told apart by the arguments:\n *\n * - node, {network, address, in_offset?, out_offset?}: one address with up to\n * 250 newest senders and 250 newest receivers, a text summary and\n * chain-insights.flows.v1 in structuredContent. Composed from at most five\n * anchored graph_query reads on the topology graph: the node, then its\n * outgoing and its incoming FLOWS_TO, newest first, two reads a side because\n * a reply holds at most 200 rows.\n * - link, {network, from, to, day}: the transfers between two known addresses\n * on one UTC day, newest first, at most 50, a short text summary and\n * chain-insights.transfers.v1 in structuredContent. One anchored USE facts\n * graph_query read.\n * - link page, {network, from, to, day, limit, skip?, first_day?}: a page of\n * `limit` transfers (1 to 50) newest first, read from `day` back one UTC day\n * at a time, at most TRANSFERS_MAX_DAYS (a week of) reads a call, never before\n * first_day. The answer carries `next` ({day, skip}, or null when every\n * transfer back to first_day is listed) which the next call sends back as\n * day and skip. The view's \"More transfers\".\n * - details, {network, address, details: true}: the lifetime totals, counts,\n * role and labels of one address, chain-insights.address.v1, one anchored\n * topology read. The view fills its panel with it when the rows of a query\n * carried no totals.\n *\n * Every read goes through the proxy's remote client, the same path and payment\n * wrapping a graph_query call takes, and is billed as a graph_query. Every\n * argument is checked before the first read, so a refused call reaches no graph\n * endpoint and costs nothing.\n */\n\nexport const FLOWS_SCHEMA = 'chain-insights.flows.v1'\nexport const GRAPH_EXPAND_TOOL = 'graph_expand'\nexport const FLOWS_NETWORK = 'robinhood'\n\n/** How many senders and how many receivers one page shows, at most. */\nexport const FLOWS_PER_SIDE = 250\n/** The view data of one answer: at most this many addresses, the centre included... */\nexport const FLOWS_MAX_NODES = 500\n/**\n * ...and under this many characters of JSON. Hosts drop an app tool result\n * over about 150,000 characters.\n */\nexport const FLOWS_MAX_CHARS = 140_000\n/**\n * The graph endpoint cuts a reply at 200 rows, whatever the LIMIT, so one side\n * of a page is read in reads of at most this many rows.\n */\nexport const FLOWS_READ_ROWS = 200\n/** The text of a node answer holds at most this many lines. */\nexport const FLOWS_SUMMARY_MAX_LINES = 20\n/** Where a graph_expand page may start on each side, at most. */\nexport const FLOWS_MAX_OFFSET = 10_000\n\nexport const TRANSFERS_SCHEMA = 'chain-insights.transfers.v1'\n/** A link answer lists at most this many transfers... */\nexport const TRANSFERS_MAX = 50\n/**\n * ...from the rows one read brings back. A facts read takes no ORDER BY and\n * the server cuts a reply at 200 rows, so the read asks for the whole cap and\n * the newest rows are picked here.\n */\nexport const TRANSFERS_READ_ROWS = 200\n/** The text of a link answer names at most this many transfers. */\nexport const TRANSFERS_SUMMARY_ROWS = 5\n/** A paged link answer reads at most this many days (a week) in one call: a sparse link costs at most this many reads a page, and a week of it comes back at once. */\nexport const TRANSFERS_MAX_DAYS = 7\n/** Where a paged link answer stops reading back when the caller names no first_day. */\nexport const TRANSFERS_FLOOR_DAY = '2015-01-01'\n\nexport const ADDRESS_SCHEMA = 'chain-insights.address.v1'\n\nexport type FlowNode = {\n address: string\n role: string\n labels: string[]\n total_in_usd: number\n total_out_usd: number\n degree_in: number\n degree_out: number\n}\n\nexport type FlowEdge = {\n from: string\n to: string\n usd: number\n tx_count: number\n first_seen_ms: number\n last_seen_ms: number\n}\n\nexport type FlowCursor = { in_offset: number; out_offset: number }\n\n/** The links of the centre not loaded yet, past the cursor, on each side. */\nexport type FlowRemaining = { senders: number; receivers: number }\n\nexport type FlowView = {\n schema: typeof FLOWS_SCHEMA\n network: string\n center: string\n nodes: FlowNode[]\n edges: FlowEdge[]\n cursor: FlowCursor\n truncated: boolean\n /** Set on every node page. A view that does not know it ignores it. */\n remaining?: FlowRemaining\n}\n\ntype Row = Record<string, unknown>\n\nexport type FlowsPage = {\n center: Row\n senders: Row[]\n receivers: Row[]\n inOffset: number\n outOffset: number\n}\n\n/** One graph_query answer as the remote client returns it. */\nexport type GraphQueryAnswer = {\n content?: Array<{ type: string; text?: string }>\n structuredContent?: Record<string, unknown>\n isError?: boolean\n}\n\nexport type FlowsToolResult = {\n content: Array<{ type: 'text'; text: string }>\n structuredContent?: Record<string, unknown>\n isError: boolean\n}\n\nexport type FlowsDependencies = {\n /** One graph_query call through the proxy's remote client. */\n graphQuery: (args: { network: string; query: string }) => Promise<GraphQueryAnswer>\n /** The text of a call that threw (payment required, transport failure). */\n describeFailure: (err: unknown) => string\n /** Why no read can run (the graph endpoint is not connected), checked after the arguments. */\n unavailable?: () => string | undefined\n now?: () => Date\n}\n\nexport const INVALID_ADDRESS_TEXT =\n 'invalid_address: a full 0x address of 40 hexadecimal characters is needed, for example 0x04911a118f11c75667e4d0dfb8e640af5a353550'\nexport const OFFSET_TOO_LARGE_TEXT = `invalid_offset: in_offset and out_offset must each be at most ${FLOWS_MAX_OFFSET}`\nexport const OFFSET_NEGATIVE_TEXT =\n 'invalid_offset: in_offset and out_offset must each be a whole number, 0 or more'\n\nexport const INVALID_DAY_TEXT =\n 'invalid_day: day must be a calendar date written YYYY-MM-DD (UTC), for example 2026-07-10'\nexport const BOTH_FORMS_TEXT =\n 'invalid_arguments: pass address (with in_offset and out_offset) to expand a node, or from, to and day to list the transfers of a link, not both'\nexport const INVALID_LIMIT_TEXT = 'invalid_limit: limit must be a whole number from 1 to 50'\nexport const INVALID_SKIP_TEXT = 'invalid_skip: skip must be a whole number of 0 or more'\nexport const INVALID_FIRST_DAY_TEXT =\n 'invalid_first_day: first_day must be a calendar date written YYYY-MM-DD (UTC), not after day'\nexport const DETAILS_FORM_TEXT =\n 'invalid_arguments: details takes address alone; pass no from, to, day or offsets with it'\n\nconst ADDRESS_PATTERN = /^0x[0-9a-f]{40}$/\nconst DAY_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/\nconst CODE_PREFIX = /^([a-z][a-z0-9_]*):\\s*/\n\n/**\n * The address lower-cased, or null when it is not 0x and 40 hexadecimal\n * characters. Only an address that passes is ever written into a read, so the\n * reads can carry it as a literal.\n */\nexport function normalizeFlowsAddress(raw: unknown): string | null {\n if (typeof raw !== 'string') return null\n const address = raw.trim().toLowerCase()\n return ADDRESS_PATTERN.test(address) ? address : null\n}\n\n/**\n * The day as YYYY-MM-DD when it is a real calendar date, or null. Only a day\n * that passes is ever written into a read.\n */\nexport function normalizeFlowsDay(raw: unknown): string | null {\n if (typeof raw !== 'string') return null\n const day = raw.trim()\n if (!DAY_PATTERN.test(day)) return null\n const time = Date.parse(`${day}T00:00:00Z`)\n if (Number.isNaN(time) || new Date(time).toISOString().slice(0, 10) !== day) return null\n return day\n}\n\n/**\n * The one facts read of a link: the transfers from one address to another on\n * one day. A facts read names the pair with one day, takes no ORDER BY and\n * holds at most 200 rows, so the read asks for the whole cap and the order is\n * made in buildTransfersView. Both addresses and the day have passed their\n * checks, so they are written into the read as literals.\n */\nexport function transfersReadFor(from: string, to: string, day: string): string {\n return (\n `USE facts MATCH (a:Address {address: \"${from}\"})-[t:TRANSFER]->(b:Address {address: \"${to}\"}) ` +\n `WHERE t.block_date = \"${day}\" ` +\n 'RETURN t.tx_id AS tx_id, t.block_timestamp AS block_timestamp, t.amount AS amount, ' +\n `t.asset_symbol AS asset_symbol, t.amount_usd AS amount_usd LIMIT ${TRANSFERS_READ_ROWS}`\n )\n}\n\n/** The UTC day before a YYYY-MM-DD day. */\nexport function dayBefore(day: string): string {\n return new Date(Date.parse(`${day}T00:00:00Z`) - 86_400_000).toISOString().slice(0, 10)\n}\n\n/**\n * The one topology read of a details call: the address alone, with its\n * lifetime rollups and activity span. The address has passed its check, so it\n * is written into the read as a literal.\n */\nexport function detailsReadFor(address: string): string {\n return (\n `USE topology MATCH (b:Address {address: \"${address}\"}) RETURN ${NODE_FIELDS}, ` +\n 'b.first_activity_timestamp AS first_activity_timestamp, b.last_activity_timestamp AS last_activity_timestamp LIMIT 1'\n )\n}\n\nexport type AddressView = FlowNode & {\n schema: typeof ADDRESS_SCHEMA\n network: string\n is_exchange: boolean\n is_sanctioned: boolean\n is_scam: boolean\n is_victim: boolean\n pool: boolean\n first_activity_timestamp: number | null\n last_activity_timestamp: number | null\n}\n\n/** chain-insights.address.v1 from the one row of a details read. */\nexport function buildAddressView(network: string, address: string, row: Row): AddressView {\n const node = flowsNodeOf({ ...row, address })\n return {\n schema: ADDRESS_SCHEMA,\n network,\n ...node,\n is_exchange: row.is_exchange === true,\n is_sanctioned: row.is_sanctioned === true,\n is_scam: row.is_scam === true,\n is_victim: row.is_victim === true,\n pool: row.pool === true,\n first_activity_timestamp: flowsNumberOrNull(row.first_activity_timestamp),\n last_activity_timestamp: flowsNumberOrNull(row.last_activity_timestamp),\n }\n}\n\n/** The one line of a details answer. */\nexport function addressSummary(view: AddressView): string {\n return (\n `${view.address} on ${view.network}: ${view.role}; lifetime ${formatFlowsUsd(view.total_in_usd)} in, ` +\n `${formatFlowsUsd(view.total_out_usd)} out; ${view.degree_in} senders and ${view.degree_out} receivers in the topology graph` +\n (view.labels.length ? `; labels ${view.labels.join(', ')}` : '') +\n '.'\n )\n}\n\nconst NODE_FIELDS =\n 'b.address AS address, b.labels AS labels, b.is_exchange AS is_exchange, ' +\n 'b.is_sanctioned AS is_sanctioned, b.is_scam AS is_scam, b.is_victim AS is_victim, b:Pool AS pool, ' +\n 'b.total_in_usd AS total_in_usd, b.total_out_usd AS total_out_usd, b.degree_in AS degree_in, b.degree_out AS degree_out'\nconst PARTY_FIELDS =\n NODE_FIELDS +\n ', f.amount_usd_sum AS usd, f.tx_count AS tx_count, ' +\n 'f.first_seen_timestamp AS first_seen, f.last_seen_timestamp AS last_seen'\n// Newest link first, the address breaking a tie, so a page and the next one\n// never share or skip a link.\nconst PAGE_ORDER = ' ORDER BY f.last_seen_timestamp DESC, b.address'\n\n/** One anchored read of a page, with the SKIP and LIMIT it carries. */\nexport type FlowsRead = { id: 'node' | 'out' | 'in'; query: string; skip: number; limit: number }\n\n/**\n * The anchored reads of one page: the node, then the receivers, then the\n * senders. Each side asks for one row more than it shows: the extra row says\n * another page exists. A reply holds at most FLOWS_READ_ROWS rows, so a side\n * is read in chunks on the same order, each starting where the last one ends.\n */\nexport function flowsReadsFor(address: string, inOffset: number, outOffset: number): FlowsRead[] {\n const side = (id: 'out' | 'in', offset: number, match: string): FlowsRead[] => {\n const reads: FlowsRead[] = []\n for (let done = 0; done < FLOWS_PER_SIDE + 1; done += FLOWS_READ_ROWS) {\n const skip = offset + done\n const limit = Math.min(FLOWS_READ_ROWS, FLOWS_PER_SIDE + 1 - done)\n reads.push({\n id,\n query: `USE topology MATCH ${match} RETURN ${PARTY_FIELDS}${PAGE_ORDER}${skip > 0 ? ` SKIP ${skip}` : ''} LIMIT ${limit}`,\n skip,\n limit,\n })\n }\n return reads\n }\n return [\n {\n id: 'node',\n query: `USE topology MATCH (b:Address {address: '${address}'}) RETURN ${NODE_FIELDS} LIMIT 1`,\n skip: 0,\n limit: 1,\n },\n ...side('out', outOffset, `(a:Address {address: '${address}'})-[f:FLOWS_TO]->(b:Address)`),\n ...side('in', inOffset, `(b:Address)-[f:FLOWS_TO]->(a:Address {address: '${address}'})`),\n ]\n}\n\n/** The role word of an address, first match wins. */\nexport function flowsRole(row: Row): string {\n if (row.is_exchange === true) return 'exchange'\n if (row.is_sanctioned === true) return 'sanctioned'\n if (row.is_scam === true) return 'scam'\n if (row.is_victim === true) return 'victim'\n if (row.pool === true) return 'pool'\n return 'other'\n}\n\nfunction flowsFloat(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0\n}\n\nfunction flowsInt(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : 0\n}\n\nfunction flowsNodeOf(row: Row): FlowNode {\n const labels = Array.isArray(row.labels)\n ? row.labels.filter((label): label is string => typeof label === 'string')\n : []\n return {\n address: typeof row.address === 'string' ? row.address : '',\n role: flowsRole(row),\n labels,\n total_in_usd: flowsFloat(row.total_in_usd),\n total_out_usd: flowsFloat(row.total_out_usd),\n degree_in: flowsInt(row.degree_in),\n degree_out: flowsInt(row.degree_out),\n }\n}\n\nfunction assembleFlowView(\n network: string,\n page: FlowsPage,\n senders: Row[],\n receivers: Row[],\n truncated: boolean\n): FlowView {\n const center = flowsNodeOf(page.center)\n const cursor = {\n in_offset: page.inOffset + senders.length,\n out_offset: page.outOffset + receivers.length,\n }\n const view: FlowView = {\n schema: FLOWS_SCHEMA,\n network,\n center: center.address,\n nodes: [center],\n edges: [],\n cursor,\n truncated,\n remaining: {\n senders: Math.max(0, center.degree_in - cursor.in_offset),\n receivers: Math.max(0, center.degree_out - cursor.out_offset),\n },\n }\n const seen = new Set([center.address])\n const add = (row: Row, incoming: boolean) => {\n const node = flowsNodeOf(row)\n if (!seen.has(node.address)) {\n seen.add(node.address)\n view.nodes.push(node)\n }\n view.edges.push({\n from: incoming ? node.address : center.address,\n to: incoming ? center.address : node.address,\n usd: flowsFloat(row.usd),\n tx_count: flowsInt(row.tx_count),\n first_seen_ms: flowsInt(row.first_seen),\n last_seen_ms: flowsInt(row.last_seen),\n })\n }\n for (const row of senders) add(row, true)\n for (const row of receivers) add(row, false)\n return view\n}\n\nfunction fitsFlowBudget(view: FlowView): boolean {\n return view.nodes.length <= FLOWS_MAX_NODES && JSON.stringify(view).length < FLOWS_MAX_CHARS\n}\n\n/**\n * How many senders and receivers stay after `drops` of the oldest links go,\n * one at a time from the longer side, the senders first on a tie.\n */\nfunction keptAfterDrops(senders: number, receivers: number, drops: number): [number, number] {\n let s = senders\n let r = receivers\n for (let left = Math.min(drops, s + r); left > 0; left -= 1) {\n if (s >= r) s -= 1\n else r -= 1\n }\n return [s, r]\n}\n\n/**\n * flows.v1 from one page: the centre first, then the senders and the\n * receivers, each newest first, at most FLOWS_PER_SIDE a side. When the view\n * data would hold more than FLOWS_MAX_NODES addresses or reach FLOWS_MAX_CHARS\n * characters, the oldest links go first, the view is marked truncated, and the\n * cursor still says where the next page starts.\n *\n * Each dropped link only makes the view smaller, so the fewest drops that fit\n * are found by halving: about ten views are measured for a full page, not one\n * per dropped link.\n */\nexport function buildFlowView(network: string, page: FlowsPage): FlowView {\n const overflow = page.senders.length > FLOWS_PER_SIDE || page.receivers.length > FLOWS_PER_SIDE\n const senders = page.senders.slice(0, FLOWS_PER_SIDE)\n const receivers = page.receivers.slice(0, FLOWS_PER_SIDE)\n const viewAfter = (drops: number) => {\n const [s, r] = keptAfterDrops(senders.length, receivers.length, drops)\n return assembleFlowView(\n network,\n page,\n senders.slice(0, s),\n receivers.slice(0, r),\n overflow || drops > 0\n )\n }\n // Dropping every link is the floor: that view is answered even when it does\n // not fit.\n let low = 0\n let high = senders.length + receivers.length\n while (low < high) {\n const mid = (low + high) >> 1\n if (fitsFlowBudget(viewAfter(mid))) high = mid\n else low = mid + 1\n }\n return viewAfter(low)\n}\n\nexport function formatFlowsUsd(value: number): string {\n if (!(value > 0)) return '$0'\n if (value < 0.01) return 'under $0.01'\n const whole = Math.floor(value)\n const cents = Math.round((value - whole) * 100)\n if (cents === 100) return formatFlowsUsd(whole + 1)\n const grouped = whole.toFixed(0).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n return `$${grouped}.${String(cents).padStart(2, '0')}`\n}\n\nfunction formatFlowsAge(lastSeenMs: number, now: Date): string {\n if (lastSeenMs <= 0) return 'at an unknown time'\n const date = new Date(lastSeenMs).toISOString().slice(0, 10)\n const days = Math.trunc((now.getTime() - lastSeenMs) / 86_400_000)\n if (days < 1) return `${date} (less than a day ago)`\n if (days === 1) return `${date} (1 day ago)`\n return `${date} (${days} days ago)`\n}\n\n/** The role word, with up to three address labels beside it. */\nfunction roleText(node: FlowNode | undefined): string {\n if (!node) return 'other'\n if (node.labels.length === 0) return node.role\n return `${node.role} (${node.labels.slice(0, 3).join(', ')})`\n}\n\nfunction sideLines(title: string, parties: string[]): string[] {\n if (parties.length === 0) return [`${title}, newest first: none.`]\n const lines = [`${title}, newest first (${parties.length}):`]\n // A side with more than six counterparties lists two on a line, so a full\n // page stays within the line budget.\n const perLine = parties.length > 6 ? 2 : 1\n for (let start = 0; start < parties.length; start += perLine) {\n lines.push(`- ${parties.slice(start, start + perLine).join('; ')}`)\n }\n return lines\n}\n\n/**\n * The text of a node answer: the centre, its role and lifetime totals, then\n * every counterparty of the view with its USD, transaction count and last-seen\n * date and age. At most FLOWS_SUMMARY_MAX_LINES lines.\n */\nexport function flowsSummary(view: FlowView, now: Date): string {\n const nodes = new Map(view.nodes.map((node) => [node.address, node]))\n const center = nodes.get(view.center)\n const party = (address: string, edge: FlowEdge) =>\n `${address} ${roleText(nodes.get(address))}, ${formatFlowsUsd(edge.usd)} in ${edge.tx_count} tx, last seen ${formatFlowsAge(edge.last_seen_ms, now)}`\n const senders: string[] = []\n const receivers: string[] = []\n for (const edge of view.edges) {\n if (edge.to === view.center) senders.push(party(edge.from, edge))\n else receivers.push(party(edge.to, edge))\n }\n const lines = [\n `Money flows of ${view.center} on ${view.network}: ${roleText(center)}; lifetime ${formatFlowsUsd(center?.total_in_usd ?? 0)} in, ${formatFlowsUsd(center?.total_out_usd ?? 0)} out; ${center?.degree_in ?? 0} senders and ${center?.degree_out ?? 0} receivers in the topology graph.`,\n ...sideLines('Recent senders', senders),\n ...sideLines('Recent receivers', receivers),\n ]\n const body = lines.slice(0, FLOWS_SUMMARY_MAX_LINES - 2)\n const footer = pagingLines(view, senders.length, receivers.length, center)\n return [...body, ...footer].join('\\n')\n}\n\n/** Where this page sits, and that the picture loads the rest when an address is clicked. */\nfunction pagingLines(\n view: FlowView,\n shownSenders: number,\n shownReceivers: number,\n center: FlowNode | undefined\n): string[] {\n const totalIn = center?.degree_in ?? 0\n const totalOut = center?.degree_out ?? 0\n const { in_offset: nextIn, out_offset: nextOut } = view.cursor\n const range = (shown: number, next: number, total: number) =>\n shown === 0 ? `none of ${total}` : `${next - shown + 1} to ${next} of ${total}`\n const where = `Showing senders ${range(shownSenders, nextIn, totalIn)} and receivers ${range(shownReceivers, nextOut, totalOut)}, newest first.`\n if (!view.truncated && nextIn >= totalIn && nextOut >= totalOut) return [where]\n return [\n where,\n 'More senders and receivers load when an address in the picture is double-clicked.',\n ]\n}\n\nexport type TransferRow = {\n tx_id: string\n block_timestamp: number | null\n amount: number | string | null\n asset_symbol: string | null\n amount_usd: number | null\n}\n\nexport type TransfersCursor = { day: string; skip: number }\n\nexport type TransfersView = {\n schema: typeof TRANSFERS_SCHEMA\n network: string\n from: string\n to: string\n day: string\n transfers: TransferRow[]\n truncated: boolean\n /** Paged answers only: the earliest day read, how many days the call read, and where the next page starts (null: every transfer back to first_day is listed). */\n through_day?: string\n days_read?: number\n next?: TransfersCursor | null\n}\n\nexport type TransfersPageRequest = { skip: number; firstDay: string | null; limit: number }\n\n/** A finite number, or a decimal string read as one, or null. */\nfunction flowsNumberOrNull(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction transferOf(row: Row): TransferRow {\n const amount =\n typeof row.amount === 'number' && Number.isFinite(row.amount)\n ? row.amount\n : typeof row.amount === 'string' && row.amount.trim() !== ''\n ? row.amount\n : null\n return {\n tx_id: typeof row.tx_id === 'string' ? row.tx_id : '',\n block_timestamp: flowsNumberOrNull(row.block_timestamp),\n amount,\n asset_symbol:\n typeof row.asset_symbol === 'string' && row.asset_symbol !== '' ? row.asset_symbol : null,\n amount_usd: flowsNumberOrNull(row.amount_usd),\n }\n}\n\n/**\n * chain-insights.transfers.v1 from the rows of one facts read: newest first,\n * at most TRANSFERS_MAX, truncated when more rows than that came back. The\n * facts layer takes no ORDER BY, so the order is made here.\n */\nexport function buildTransfersView(\n network: string,\n from: string,\n to: string,\n day: string,\n rows: Row[]\n): TransfersView {\n const newestFirst = rows\n .map(transferOf)\n .sort((a, b) => (b.block_timestamp ?? -Infinity) - (a.block_timestamp ?? -Infinity))\n return {\n schema: TRANSFERS_SCHEMA,\n network,\n from,\n to,\n day,\n transfers: newestFirst.slice(0, TRANSFERS_MAX),\n truncated: newestFirst.length > TRANSFERS_MAX,\n }\n}\n\nfunction transferLine(transfer: TransferRow): string {\n const time =\n transfer.block_timestamp === null\n ? 'at an unknown time'\n : `${new Date(transfer.block_timestamp).toISOString().slice(0, 19).replace('T', ' ')} UTC`\n const amount = transfer.amount === null ? 'an unknown amount' : String(transfer.amount)\n const asset = transfer.asset_symbol ? ` ${transfer.asset_symbol}` : ''\n const usd = transfer.amount_usd === null ? 'no USD price' : formatFlowsUsd(transfer.amount_usd)\n return `- ${time}, ${amount}${asset}, ${usd}, tx ${transfer.tx_id}`\n}\n\n/** The short text of a link answer: the pair, the day, the count and the newest few transfers. */\nexport function transfersSummary(view: TransfersView): string {\n const paged = view.through_day !== undefined\n const span = paged && view.through_day !== view.day ? `${view.through_day} to ${view.day}` : view.day\n const head = `Transfers from ${view.from} to ${view.to} on ${span} (UTC) on ${view.network}`\n if (view.transfers.length === 0) {\n return paged && view.next ? `${head}: none; earlier days remain before ${view.next.day}.` : `${head}: none.`\n }\n const count = view.truncated\n ? paged\n ? `${view.transfers.length}, and a day holds more than one read lists`\n : `the newest ${view.transfers.length}, and the day holds more`\n : `${view.transfers.length}`\n const lines = [\n `${head}: ${count}, newest first.`,\n ...view.transfers.slice(0, TRANSFERS_SUMMARY_ROWS).map(transferLine),\n ]\n const rest = view.transfers.length - TRANSFERS_SUMMARY_ROWS\n if (rest > 0) lines.push(`- and ${rest} more in the picture.`)\n if (paged && view.next) lines.push(`- earlier transfers remain, from ${view.next.day} back.`)\n return lines.join('\\n')\n}\n\nfunction errorResult(text: string): FlowsToolResult {\n return { content: [{ type: 'text', text }], isError: true }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction answerText(answer: GraphQueryAnswer): string {\n return (answer.content ?? [])\n .map((block) => (block.type === 'text' && typeof block.text === 'string' ? block.text : ''))\n .filter(Boolean)\n .join('\\n')\n .trim()\n}\n\n/**\n * The refusal of a read that did not answer, naming its code first:\n * error_detail.code when the graph endpoint sent its typed refusal, the code\n * its text starts with otherwise, and graph_query_failed when it names none.\n * The view marks the address it was expanding.\n */\nexport function refusalText(answer: GraphQueryAnswer): string {\n const text = answerText(answer)\n const detail = isRecord(answer.structuredContent)\n ? answer.structuredContent.error_detail\n : undefined\n const detailCode = isRecord(detail) && typeof detail.code === 'string' ? detail.code : ''\n const textCode = CODE_PREFIX.exec(text)?.[1] ?? ''\n const code = detailCode || textCode || 'graph_query_failed'\n const rest = textCode === code ? text.replace(CODE_PREFIX, '') : text\n return rest ? `${code}: ${rest}` : `${code}: the graph endpoint refused the read`\n}\n\n/** The endpoint says it cut the reply short (facts.query.truncated). */\nfunction cutByEndpoint(answer: GraphQueryAnswer): boolean {\n const facts = isRecord(answer.structuredContent) ? answer.structuredContent.facts : undefined\n const query = isRecord(facts) ? facts.query : undefined\n return isRecord(query) && query.truncated === true\n}\n\nfunction rowsOf(answer: GraphQueryAnswer): Row[] | null {\n const facts = isRecord(answer.structuredContent) ? answer.structuredContent.facts : undefined\n const query = isRecord(facts) ? facts.query : undefined\n const results = isRecord(query) ? query.results : undefined\n return Array.isArray(results) ? results.filter(isRecord) : null\n}\n\ntype Checked<T> = T | { error: string }\n\nfunction checkNetwork(args: Record<string, unknown>): Checked<string> {\n const rawNetwork = typeof args.network === 'string' ? args.network.trim().toLowerCase() : ''\n if (!rawNetwork) return { error: 'invalid_network: network is required; pass robinhood' }\n if (rawNetwork !== FLOWS_NETWORK) {\n return {\n error: `invalid_network: graph_expand reads the robinhood graph only; got \"${String(args.network).trim()}\"`,\n }\n }\n return FLOWS_NETWORK\n}\n\n/** The address of a node or of one end of a link; `name` says which end in a link refusal. */\nfunction checkAddress(raw: unknown, name?: string): Checked<string> {\n const address = normalizeFlowsAddress(raw)\n if (address) return address\n return {\n error: name\n ? INVALID_ADDRESS_TEXT.replace('invalid_address:', `invalid_address (${name}):`)\n : INVALID_ADDRESS_TEXT,\n }\n}\n\nfunction checkDay(raw: unknown): Checked<string> {\n const day = normalizeFlowsDay(raw)\n return day ?? { error: INVALID_DAY_TEXT }\n}\n\nfunction checkLimit(value: unknown): Checked<number> {\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 1 || value > TRANSFERS_MAX) {\n return { error: INVALID_LIMIT_TEXT }\n }\n return value\n}\n\nfunction checkSkip(value: unknown): Checked<number> {\n if (value === undefined || value === null) return 0\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) return { error: INVALID_SKIP_TEXT }\n return value\n}\n\nfunction checkFirstDay(value: unknown, day: string): Checked<string | null> {\n if (value === undefined || value === null) return null\n const first = normalizeFlowsDay(value)\n if (!first || first > day) return { error: INVALID_FIRST_DAY_TEXT }\n return first\n}\n\nfunction checkOffset(value: unknown): Checked<number> {\n if (value === undefined || value === null) return 0\n if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {\n return { error: OFFSET_NEGATIVE_TEXT }\n }\n if (value > FLOWS_MAX_OFFSET) return { error: OFFSET_TOO_LARGE_TEXT }\n return value\n}\n\n/**\n * Read and answer one node page. The reads run one after another: the node\n * first, so an address the graph does not hold costs one read; then the\n * receivers and the senders, the chunks of a side joined in order. A chunk that\n * comes back short, and not cut by the endpoint, ends its side, so the next\n * chunk of that side is not read or billed. The first read that does not\n * answer ends the call.\n */\nasync function runNode(\n deps: FlowsDependencies,\n network: string,\n address: string,\n inOffset: number,\n outOffset: number\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n const rows: Record<FlowsRead['id'], Row[]> = { node: [], out: [], in: [] }\n const ended = new Set<FlowsRead['id']>()\n for (const read of flowsReadsFor(address, inOffset, outOffset)) {\n if (ended.has(read.id)) continue\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: read.query })\n } catch (err) {\n return errorResult(deps.describeFailure(err))\n }\n if (answer.isError === true) return errorResult(refusalText(answer))\n const found = rowsOf(answer)\n if (!found) {\n return errorResult(\n 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows'\n )\n }\n rows[read.id].push(...found)\n if (read.id === 'node' && found.length === 0) {\n return errorResult(`${address} is not in the ${network} topology graph`)\n }\n if (found.length < read.limit && !cutByEndpoint(answer)) ended.add(read.id)\n }\n const view = buildFlowView(network, {\n center: rows.node[0] ?? {},\n senders: rows.in,\n receivers: rows.out,\n inOffset,\n outOffset,\n })\n const now = deps.now ? deps.now() : new Date()\n return {\n content: [{ type: 'text', text: flowsSummary(view, now) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\n/** Read and answer the transfers of one link on one day: a single facts read. */\nasync function runLink(\n deps: FlowsDependencies,\n network: string,\n from: string,\n to: string,\n day: string\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: transfersReadFor(from, to, day) })\n } catch (err) {\n return errorResult(deps.describeFailure(err))\n }\n if (answer.isError === true) return errorResult(refusalText(answer))\n const rows = rowsOf(answer)\n if (!rows) {\n return errorResult(\n 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows'\n )\n }\n const view = buildTransfersView(network, from, to, day, rows)\n return {\n content: [{ type: 'text', text: transfersSummary(view) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\n/** One facts read of a day, as the paged walk needs it: the rows newest first, or the error that ends the call. */\nasync function readDay(\n deps: FlowsDependencies,\n network: string,\n from: string,\n to: string,\n day: string\n): Promise<{ rows: TransferRow[]; cut: boolean } | { error: string }> {\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: transfersReadFor(from, to, day) })\n } catch (err) {\n return { error: deps.describeFailure(err) }\n }\n if (answer.isError === true) return { error: refusalText(answer) }\n const rows = rowsOf(answer)\n if (!rows) {\n return { error: 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows' }\n }\n const newestFirst = rows\n .map(transferOf)\n .sort((a, b) => (b.block_timestamp ?? -Infinity) - (a.block_timestamp ?? -Infinity))\n return { rows: newestFirst, cut: rows.length >= TRANSFERS_READ_ROWS }\n}\n\n/**\n * Read and answer one page of a link's transfers: from `day` back, one day a\n * read, until the page holds `limit` transfers, TRANSFERS_MAX_DAYS days were\n * read, or the day before first_day is reached. `skip` passes over the\n * transfers of `day` an earlier page listed. The first read that does not\n * answer ends the call.\n */\nasync function runLinkPage(\n deps: FlowsDependencies,\n network: string,\n from: string,\n to: string,\n day: string,\n page: TransfersPageRequest\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n const floor = page.firstDay ?? TRANSFERS_FLOOR_DAY\n const transfers: TransferRow[] = []\n let current = day\n let skip = page.skip\n let daysRead = 0\n let through = day\n let truncated = false\n let next: TransfersCursor | null = null\n for (;;) {\n if (current < floor) break\n if (daysRead >= TRANSFERS_MAX_DAYS) {\n next = { day: current, skip }\n break\n }\n const read = await readDay(deps, network, from, to, current)\n if ('error' in read) return errorResult(read.error)\n daysRead += 1\n through = current\n if (read.cut) truncated = true\n const rest = read.rows.slice(skip)\n const room = page.limit - transfers.length\n if (rest.length > room) {\n transfers.push(...rest.slice(0, room))\n next = { day: current, skip: skip + room }\n break\n }\n transfers.push(...rest)\n const earlier = dayBefore(current)\n if (transfers.length >= page.limit) {\n next = earlier < floor ? null : { day: earlier, skip: 0 }\n break\n }\n current = earlier\n skip = 0\n }\n const view: TransfersView = {\n schema: TRANSFERS_SCHEMA,\n network,\n from,\n to,\n day,\n transfers,\n truncated,\n through_day: through,\n days_read: daysRead,\n next,\n }\n return {\n content: [{ type: 'text', text: transfersSummary(view) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\n/** Read and answer the details of one address: a single anchored topology read. */\nasync function runDetails(\n deps: FlowsDependencies,\n network: string,\n address: string\n): Promise<FlowsToolResult> {\n const unavailable = deps.unavailable?.()\n if (unavailable) return errorResult(unavailable)\n let answer: GraphQueryAnswer\n try {\n answer = await deps.graphQuery({ network, query: detailsReadFor(address) })\n } catch (err) {\n return errorResult(deps.describeFailure(err))\n }\n if (answer.isError === true) return errorResult(refusalText(answer))\n const rows = rowsOf(answer)\n if (!rows) {\n return errorResult(\n 'graph_query_failed: the graph endpoint answered without chain-insights.result.v1 rows'\n )\n }\n if (rows.length === 0) return errorResult(`${address} is not in the ${network} topology graph`)\n const view = buildAddressView(network, address, rows[0]!)\n return {\n content: [{ type: 'text', text: addressSummary(view) }],\n structuredContent: view as unknown as Record<string, unknown>,\n isError: false,\n }\n}\n\nfunction given(record: Record<string, unknown>, keys: string[]): boolean {\n return keys.some((key) => record[key] !== undefined && record[key] !== null)\n}\n\n/**\n * graph_expand, the node form {network, address, in_offset?, out_offset?} or the\n * link form {network, from, to, day}. Every argument is checked before the\n * first read.\n */\nexport async function handleGraphExpand(\n args: unknown,\n deps: FlowsDependencies\n): Promise<FlowsToolResult> {\n const record = isRecord(args) ? args : {}\n const network = checkNetwork(record)\n if (typeof network !== 'string') return errorResult(network.error)\n\n const isLink = given(record, ['from', 'to', 'day'])\n if (isLink && given(record, ['address', 'in_offset', 'out_offset', 'details']))\n return errorResult(BOTH_FORMS_TEXT)\n\n if (isLink) {\n const from = checkAddress(record.from, 'from')\n if (typeof from !== 'string') return errorResult(from.error)\n const to = checkAddress(record.to, 'to')\n if (typeof to !== 'string') return errorResult(to.error)\n const day = checkDay(record.day)\n if (typeof day !== 'string') return errorResult(day.error)\n // A limit, a skip or a first_day asks for a page across days; without them the call is the one-day read.\n if (given(record, ['limit', 'skip', 'first_day'])) {\n const limit = checkLimit(record.limit)\n if (typeof limit !== 'number') return errorResult(limit.error)\n const skip = checkSkip(record.skip)\n if (typeof skip !== 'number') return errorResult(skip.error)\n const firstDay = checkFirstDay(record.first_day, day)\n if (firstDay !== null && typeof firstDay !== 'string') return errorResult(firstDay.error)\n return runLinkPage(deps, network, from, to, day, { skip, firstDay, limit })\n }\n return runLink(deps, network, from, to, day)\n }\n\n if (record.details !== undefined && record.details !== null) {\n if (record.details !== true) return errorResult(DETAILS_FORM_TEXT)\n if (given(record, ['in_offset', 'out_offset'])) return errorResult(DETAILS_FORM_TEXT)\n const address = checkAddress(record.address)\n if (typeof address !== 'string') return errorResult(address.error)\n return runDetails(deps, network, address)\n }\n\n const address = checkAddress(record.address)\n if (typeof address !== 'string') return errorResult(address.error)\n const inOffset = checkOffset(record.in_offset)\n if (typeof inOffset !== 'number') return errorResult(inOffset.error)\n const outOffset = checkOffset(record.out_offset)\n if (typeof outOffset !== 'number') return errorResult(outOffset.error)\n return runNode(deps, network, address, inOffset, outOffset)\n}\n\nexport const GRAPH_EXPAND_TITLE = 'Expand a node or a link in the picture'\n\nexport const GRAPH_EXPAND_DESCRIPTION =\n 'Expand an open Chain Insights picture. Called by the view when the investigator clicks, never by the model. ' +\n 'A node, {network, address, in_offset?, out_offset?}, loads the newest senders and receivers of one address ' +\n '(up to 250 a side, from the offsets); billed as at most five graph_query topology reads. A link, {network, from, to, day}, ' +\n 'lists the transfers between two addresses on one UTC day (YYYY-MM-DD), newest first, at most 50; billed as ' +\n 'one graph_query facts read. A link page, {network, from, to, day, limit, skip?, first_day?}, lists `limit` (1-50) ' +\n 'transfers from `day` back across days, newest first, and answers a `next` cursor ({day, skip}) while older ones ' +\n 'remain; billed as at most seven facts reads (a week). Details, {network, address, details: true}, answer the lifetime totals, ' +\n 'counts, role and labels of one address; billed as one topology read. Read-only; an argument refused before a read costs nothing.'\n","import { createHash } from 'node:crypto'\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { CACHE_TTL_MS, chainInsightsHome } from './cache-location.js'\nimport type { GraphSchemaDocument } from './graph-schema.js'\n\n// The graph schema of one network on one endpoint, kept on disk beside the tool\n// list cache (src/mcp/schema-cache.ts) with the same home and the same 24 hour\n// lifetime: ~/.chain-insights/cache/schema-<network>-<endpoint hash>.json.\n//\n// A schema built while a read failed is kept for a short time only. Kept for\n// 24 hours, one refused or rate-limited read would hide a section of the schema\n// for a whole day.\n\n/** How long a schema with a failed read stays fresh. */\nexport const PARTIAL_SCHEMA_TTL_MS = 10 * 60 * 1000\n\ninterface GraphSchemaCacheFile {\n cached_at: number\n complete: boolean\n endpoint: string\n document: GraphSchemaDocument\n}\n\nfunction endpointHash(endpoint: string): string {\n return createHash('sha256').update(endpoint).digest('hex').slice(0, 12)\n}\n\n/** The cache file of a network on an endpoint. The network is an identifier already checked by the caller. */\nexport function graphSchemaCachePath(network: string, endpoint: string): string {\n return path.join(chainInsightsHome(), 'cache', `schema-${network}-${endpointHash(endpoint)}.json`)\n}\n\n/**\n * The cached schema, marked cached, or null when there is none, it is stale, or\n * the file cannot be read. A cache is derived data: a damaged file is a miss and\n * the next build replaces it.\n */\nexport async function loadGraphSchemaCache(\n network: string,\n endpoint: string,\n now: Date\n): Promise<GraphSchemaDocument | null> {\n let parsed: unknown\n try {\n parsed = JSON.parse(await readFile(graphSchemaCachePath(network, endpoint), 'utf8'))\n } catch {\n return null\n }\n const file = parsed as Partial<GraphSchemaCacheFile> | null\n if (\n !file ||\n typeof file.cached_at !== 'number' ||\n file.endpoint !== endpoint ||\n file.document?.schema !== 'chain-insights.graph-schema.v1'\n ) {\n return null\n }\n const lifetime = file.complete === true ? CACHE_TTL_MS : PARTIAL_SCHEMA_TTL_MS\n const age = now.getTime() - file.cached_at\n if (age < 0 || age > lifetime) return null\n return { ...file.document, cached: true }\n}\n\n/** `network` is the name the caller asked for, the same one a later load asks with. */\nexport async function saveGraphSchemaCache(\n network: string,\n document: GraphSchemaDocument,\n endpoint: string,\n complete: boolean,\n now: Date\n): Promise<void> {\n const target = graphSchemaCachePath(network, endpoint)\n const file: GraphSchemaCacheFile = {\n cached_at: now.getTime(),\n complete,\n endpoint,\n document: { ...document, cached: false },\n }\n await mkdir(path.dirname(target), { recursive: true })\n // Written beside the target and renamed, so a second proxy reading the file\n // never sees half of it.\n const scratch = `${target}.${process.pid}.tmp`\n await writeFile(scratch, JSON.stringify(file, null, 2) + '\\n', { mode: 0o600 })\n await rename(scratch, target)\n}\n","/**\n * meta_schema: the live graph schema of one network, read from the graph\n * endpoint and kept on disk for 24 hours.\n *\n * The graph serves four read-only catalog statements on `USE topology` through\n * the ordinary graph_query tool: `CALL db.labels()`, `CALL db.relationshipTypes()`,\n * `CALL db.propertyKeys()` and `SHOW INDEXES`. They name the labels, the link\n * types, every property key and the indexes. They do not say which keys belong to\n * which kind, so the main kinds are sampled with the two probe forms the server\n * admits (`keys(a)` on addresses, `keys(r)` on a link). The facts relationships\n * and the chain lookups come from the network's published capabilities.\n *\n * One build is one MCP session and runs its reads one after another: the public\n * endpoint allows about 30 requests a minute for each IP address, and opening a\n * session costs three of them. A read that fails leaves its section empty and\n * adds a note. It never fails the tool.\n */\n\nimport { findNetworkCapability, mirrorGraphNetworkCapabilities } from './capabilities.js'\nimport type { NetworkCapability } from './capabilities.js'\nimport { PaymentRequiredError } from './client.js'\nimport { refusalText, type GraphQueryAnswer } from './flows.js'\nimport { loadGraphSchemaCache, saveGraphSchemaCache } from './graph-schema-cache.js'\n\nexport const GRAPH_SCHEMA_ID = 'chain-insights.graph-schema.v1' as const\nexport const GRAPH_SCHEMA_SOURCE = 'live catalog' as const\nexport const META_SCHEMA_TITLE = 'Graph schema'\nexport const META_SCHEMA_DESCRIPTION =\n 'Return the live graph schema of one network: the labels, the link types, the field names of an address and of each main link, the indexes, the USE facts relationships with the TRANSFER columns, and the USE chain lookups. ' +\n 'Call it first when you need field names. It is cached for 24 hours; pass refresh=true to rebuild it.'\n\n/** Fields of an address come from this many sampled nodes, and of a link from this many sampled links. */\nexport const ADDRESS_SAMPLE_LIMIT = 20\nexport const LINK_SAMPLE_LIMIT = 5\n\n/** The link types an agent queries. Each is sampled only when the graph lists it. */\nexport const SAMPLED_LINK_TYPES = [\n 'FLOWS_TO',\n 'OPERATED_BY',\n 'LINKED',\n 'SWAPPED',\n 'BRIDGED',\n 'ADDED_LIQUIDITY',\n 'REMOVED_LIQUIDITY',\n] as const\n\n// The TRANSFER columns the cypher skill lists, in its order. tests/graph-schema.test.ts\n// holds this list to the skill and to the columns the graph server maps on TRANSFER\n// (tests/fixtures/facts-columns.json).\nexport const FACTS_TRANSFER_COLUMNS = [\n 'tx_id',\n 'block_date',\n 'block_height',\n 'block_timestamp',\n 'event_index',\n 'edge_index',\n 'kind',\n 'asset_contract',\n 'asset_symbol',\n 'amount',\n 'amount_usd',\n 'price_usd',\n 'price_missing',\n 'token_id',\n 'token_standard',\n 'operator_address',\n 'raw_amount',\n 'decimals',\n] as const\n\n// `CALL db.schema.visualization()` is not read. It bills about 427 units where\n// the four statements below bill 13 to 133 each, and it adds only which labels a\n// link type joins, which the cypher skill and the served hints already say.\nconst CATALOG_READS = {\n labels: { label: 'labels', query: 'USE topology CALL db.labels()' },\n linkTypes: { label: 'link types', query: 'USE topology CALL db.relationshipTypes()' },\n propertyKeys: { label: 'property keys', query: 'USE topology CALL db.propertyKeys()' },\n indexes: { label: 'indexes', query: 'USE topology SHOW INDEXES' },\n} as const\n\nexport function addressSampleQuery(): string {\n return `USE topology MATCH (a:Address) RETURN keys(a) AS keys LIMIT ${ADDRESS_SAMPLE_LIMIT}`\n}\n\nexport function linkSampleQuery(linkType: string): string {\n return `USE topology MATCH ()-[r:${linkType}]->() RETURN keys(r) AS keys LIMIT ${LINK_SAMPLE_LIMIT}`\n}\n\nexport interface GraphSchemaIndex {\n name: string\n entity: 'node' | 'link'\n /** The labels of a node index, the link types of a link index. */\n types: string[]\n properties: string[]\n state: string\n}\n\nexport interface GraphSchemaDocument {\n schema: typeof GRAPH_SCHEMA_ID\n network: string\n built_at: string\n source: typeof GRAPH_SCHEMA_SOURCE\n cached: boolean\n /** Units the reads of the build billed, summed from the replies. A cached document keeps the units of its build. */\n billed_units: number\n layers: {\n topology: {\n labels: string[]\n link_types: string[]\n property_keys: string[]\n indexes: GraphSchemaIndex[]\n samples: Record<string, string[]>\n }\n facts: { relationships: string[]; transfer_columns: string[] }\n chain: { lookups: string[] }\n }\n notes: string[]\n}\n\n/** A refusal with its own words for the caller: a bad argument, an unserved network, an endpoint that is down. */\nexport class GraphSchemaError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'GraphSchemaError'\n }\n}\n\nexport interface GraphSchemaClient {\n callTool(request: { name: string; arguments: Record<string, unknown> }): Promise<unknown>\n}\n\ntype Row = Record<string, unknown>\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction sortedUnique(values: Iterable<string>): string[] {\n return [...new Set(values)].sort()\n}\n\nfunction stringList(value: unknown): string[] {\n return Array.isArray(value)\n ? value.filter((entry): entry is string => typeof entry === 'string' && entry !== '')\n : []\n}\n\nconst NETWORK_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/\n\n/**\n * The network as a lower-case identifier. It becomes part of a file name and of\n * every query, so only an identifier passes.\n */\nexport function normalizeSchemaNetwork(raw: unknown): string {\n const network = typeof raw === 'string' ? raw.trim().toLowerCase() : ''\n if (!network) {\n throw new GraphSchemaError(\n 'invalid_network: network is required. Pass the identifier of a served network, for example robinhood.'\n )\n }\n if (!NETWORK_PATTERN.test(network)) {\n throw new GraphSchemaError(\n `invalid_network: \"${String(raw).trim()}\" is not a network identifier. Pass the identifier of a served network, for example robinhood.`\n )\n }\n return network\n}\n\n// ─── Reading ──────────────────────────────────────────────────────────────────\n\nfunction queryBlock(structured: unknown): Row | null {\n const facts = isRecord(structured) ? structured.facts : undefined\n const query = isRecord(facts) ? facts.query : undefined\n return isRecord(query) ? query : null\n}\n\n/** The `facts.query` block of one graph_query reply, from structuredContent or from the JSON text. */\nfunction answerQuery(answer: GraphQueryAnswer): Row | null {\n const fromStructured = queryBlock(answer.structuredContent)\n if (fromStructured) return fromStructured\n for (const block of answer.content ?? []) {\n if (block.type !== 'text' || typeof block.text !== 'string') continue\n try {\n const parsed = queryBlock(JSON.parse(block.text))\n if (parsed) return parsed\n } catch {\n /* not JSON: try the next block */\n }\n }\n return null\n}\n\n// A failure after which every later read would fail the same way: the endpoint\n// limits requests, or it asks for payment. The build stops sending reads.\ntype Halt = 'rate_limit' | 'payment'\n\nfunction haltOfError(err: unknown): Halt | undefined {\n if (err instanceof PaymentRequiredError) return 'payment'\n const code = isRecord(err) ? err.code : undefined\n const message = err instanceof Error ? err.message : String(err)\n if (code === 429 || /\\b429\\b|too many requests|rate.?limit/i.test(message)) return 'rate_limit'\n if (/\\b402\\b|payment required/i.test(message)) return 'payment'\n return undefined\n}\n\nfunction haltOfAnswer(answer: GraphQueryAnswer): Halt | undefined {\n const detail = isRecord(answer.structuredContent)\n ? answer.structuredContent.error_detail\n : undefined\n return isRecord(detail) && detail.class === 'capacity' ? 'rate_limit' : undefined\n}\n\nexport interface BuildOptions {\n client: GraphSchemaClient\n /** A normalized network identifier. */\n network: string\n now?: () => Date\n /** A wait between two reads, in milliseconds. One build needs none: it stays under the rate limit. */\n pauseMs?: number\n sleep?: (ms: number) => Promise<void>\n}\n\nexport interface BuildResult {\n document: GraphSchemaDocument\n /** False when a read failed: the document has a section missing. */\n complete: boolean\n}\n\nfunction describeIndex(row: Row): GraphSchemaIndex | null {\n const name = typeof row.name === 'string' ? row.name : ''\n const entity =\n row.entityType === 'NODE' ? 'node' : row.entityType === 'RELATIONSHIP' ? 'link' : null\n const properties = stringList(row.properties)\n // A lookup index names no label and no property: it tells an agent nothing.\n if (!name || !entity || properties.length === 0) return null\n return {\n name,\n entity,\n types: stringList(row.labelsOrTypes),\n properties,\n state: typeof row.state === 'string' && row.state ? row.state : 'unknown',\n }\n}\n\nexport async function buildGraphSchema(options: BuildOptions): Promise<BuildResult> {\n const { client } = options\n const now = options.now ?? (() => new Date())\n const pauseMs = options.pauseMs ?? 0\n const sleep = options.sleep ?? ((ms: number) => new Promise<void>((done) => setTimeout(done, ms)))\n\n const problems: string[] = []\n const remarks: string[] = []\n let billed = 0\n let calls = 0\n let halted = false\n\n async function pace(): Promise<void> {\n if (calls > 0 && pauseMs > 0) await sleep(pauseMs)\n calls += 1\n }\n\n function fail(label: string, reason: string, halt?: Halt): null {\n problems.push(`${label} could not be read: ${reason}`)\n if (halt && !halted) {\n halted = true\n if (halt === 'payment') {\n problems.push(\n 'The endpoint asks for payment, so the reads after it were not sent. Fix the payment setup, then call again with refresh=true.'\n )\n } else {\n // The endpoint says how long to wait: \"retry after 23 seconds\".\n const wait = /retry after (\\d+) seconds?/i.exec(reason)?.[1]\n problems.push(\n `The endpoint is limiting requests, so the reads after it were not sent. Call again after ${wait ? `${wait} seconds` : 'a minute'} with refresh=true.`\n )\n }\n }\n return null\n }\n\n async function graphRead(network: string, label: string, query: string): Promise<Row[] | null> {\n if (halted) return null\n await pace()\n let answer: GraphQueryAnswer\n try {\n answer = (await client.callTool({\n name: 'graph_query',\n arguments: { network, query },\n })) as GraphQueryAnswer\n } catch (err) {\n return fail(label, err instanceof Error ? err.message : String(err), haltOfError(err))\n }\n const block = answerQuery(answer ?? {})\n if (block && typeof block.billable_units === 'number') billed += block.billable_units\n if (answer?.isError === true) {\n return fail(label, refusalText(answer), haltOfAnswer(answer))\n }\n if (!block || !Array.isArray(block.results)) {\n return fail(label, 'the reply holds no rows')\n }\n return block.results.filter(isRecord)\n }\n\n // ── The network: capabilities first, so an unserved name costs no graph read.\n let network = options.network\n let capability: NetworkCapability | undefined\n await pace()\n try {\n const answer = (await client.callTool({\n name: 'network_capabilities',\n arguments: {},\n })) as GraphQueryAnswer\n if (answer?.isError === true) {\n fail('network capabilities', refusalText(answer), haltOfAnswer(answer))\n } else {\n const facts = isRecord(answer?.structuredContent) ? answer.structuredContent.facts : undefined\n const capabilities = isRecord(facts) ? facts.capabilities : undefined\n const networks =\n isRecord(capabilities) && Array.isArray(capabilities.networks)\n ? capabilities.networks\n : null\n if (!networks) {\n fail('network capabilities', 'the reply lists no networks')\n } else {\n const document = mirrorGraphNetworkCapabilities({ networks })\n capability = findNetworkCapability(document, options.network)\n if (!capability) {\n const served = document.networks.map((entry) => entry.network).join(', ')\n throw new GraphSchemaError(\n `network_not_served: \"${options.network}\" is not a network this endpoint serves. Served networks: ${served || 'none'}.`\n )\n }\n network = capability.network\n }\n }\n } catch (err) {\n if (err instanceof GraphSchemaError) throw err\n fail('network capabilities', err instanceof Error ? err.message : String(err), haltOfError(err))\n }\n\n // ── Topology: the catalog, then a sample of the main kinds.\n const topology: GraphSchemaDocument['layers']['topology'] = {\n labels: [],\n link_types: [],\n property_keys: [],\n indexes: [],\n samples: {},\n }\n const topologyOff = capability?.layers?.topology?.enabled === false\n if (topologyOff) {\n remarks.push('The topology layer is off on this network.')\n } else {\n const labels = await graphRead(network, CATALOG_READS.labels.label, CATALOG_READS.labels.query)\n if (labels) topology.labels = sortedUnique(labels.flatMap((row) => stringList([row.label])))\n\n const linkTypes = await graphRead(\n network,\n CATALOG_READS.linkTypes.label,\n CATALOG_READS.linkTypes.query\n )\n if (linkTypes) {\n topology.link_types = sortedUnique(\n linkTypes.flatMap((row) => stringList([row.relationshipType]))\n )\n }\n\n const keys = await graphRead(\n network,\n CATALOG_READS.propertyKeys.label,\n CATALOG_READS.propertyKeys.query\n )\n if (keys) {\n topology.property_keys = sortedUnique(keys.flatMap((row) => stringList([row.propertyKey])))\n }\n\n const indexes = await graphRead(\n network,\n CATALOG_READS.indexes.label,\n CATALOG_READS.indexes.query\n )\n if (indexes) {\n topology.indexes = indexes\n .map(describeIndex)\n .filter((index): index is GraphSchemaIndex => index !== null)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n // An address is sampled unless the label list was read and has no Address.\n const labelsRead = labels !== null\n if (!labelsRead || topology.labels.includes('Address')) {\n const rows = await graphRead(network, 'the Address sample', addressSampleQuery())\n if (rows) topology.samples['Address'] = unionOfKeys(rows)\n }\n\n const sampled = SAMPLED_LINK_TYPES.filter((type) => topology.link_types.includes(type))\n if (linkTypes === null && !halted) {\n problems.push('The link samples were skipped: the link types are unknown.')\n }\n for (const type of sampled) {\n const rows = await graphRead(network, `the ${type} sample`, linkSampleQuery(type))\n if (rows) topology.samples[type] = unionOfKeys(rows)\n }\n\n const unsampled = topology.link_types.filter(\n (type) => !(SAMPLED_LINK_TYPES as readonly string[]).includes(type)\n )\n if (unsampled.length > 0) {\n remarks.push(\n `Link types with no sample: ${unsampled.join(', ')}. Read the keys of one with USE topology MATCH ()-[r:<TYPE>]->() RETURN keys(r) AS keys LIMIT ${LINK_SAMPLE_LIMIT}.`\n )\n }\n }\n\n // ── Facts and chain: what the network publishes.\n const facts: GraphSchemaDocument['layers']['facts'] = { relationships: [], transfer_columns: [] }\n const chain: GraphSchemaDocument['layers']['chain'] = { lookups: [] }\n if (capability) {\n if (capability.layers?.facts?.enabled === false) {\n remarks.push('The facts layer is off on this network.')\n } else {\n facts.relationships = stringList(capability.layers?.facts?.relationships)\n if (facts.relationships.includes('TRANSFER')) {\n facts.transfer_columns = [...FACTS_TRANSFER_COLUMNS]\n }\n }\n const admission = capability.chain_admission\n if (admission?.enabled === false) {\n remarks.push(`The chain layer is off (status ${admission.status ?? 'unknown'}).`)\n } else {\n chain.lookups = stringList(admission?.lookups)\n }\n }\n\n const words = [...topology.property_keys, ...Object.values(topology.samples).flat()]\n if (words.includes('synced_through_height') || words.includes('pair_key')) {\n remarks.push(\n 'synced_through_height and pair_key are sync bookkeeping: never filter or sort on them.'\n )\n }\n remarks.push('Fields come from a sample of the live graph. A rare field may be missing.')\n\n return {\n complete: problems.length === 0,\n document: {\n schema: GRAPH_SCHEMA_ID,\n network,\n built_at: now().toISOString(),\n source: GRAPH_SCHEMA_SOURCE,\n cached: false,\n billed_units: billed,\n layers: { topology, facts, chain },\n notes: [...problems, ...remarks],\n },\n }\n}\n\nfunction unionOfKeys(rows: Row[]): string[] {\n return sortedUnique(rows.flatMap((row) => stringList(row.keys)))\n}\n\n// ─── Reading from the cache, or building ──────────────────────────────────────\n\nexport interface GetGraphSchemaOptions {\n /** As the caller wrote it: normalized here. */\n network: unknown\n refresh?: boolean\n endpoint: string\n /**\n * Runs `fn` with one connected client. It is called once for a build and not at\n * all when the cache answers, so a cache hit opens no session.\n */\n withClient: <T>(fn: (client: GraphSchemaClient) => Promise<T>) => Promise<T>\n now?: () => Date\n pauseMs?: number\n sleep?: (ms: number) => Promise<void>\n}\n\n// Builds in flight, by endpoint and network: two callers that ask for one\n// schema at once share one build and pay for it once.\nconst building = new Map<string, Promise<GraphSchemaDocument>>()\n\nexport async function getGraphSchema(options: GetGraphSchemaOptions): Promise<GraphSchemaDocument> {\n const network = normalizeSchemaNetwork(options.network)\n const now = options.now ?? (() => new Date())\n if (options.refresh !== true) {\n const cached = await loadGraphSchemaCache(network, options.endpoint, now())\n if (cached) return cached\n }\n const key = `${options.endpoint}\\n${network}`\n const running = building.get(key)\n if (running) return running\n const build = (async () => {\n const { document, complete } = await options.withClient((client) =>\n buildGraphSchema({\n client,\n network,\n now,\n ...(options.pauseMs === undefined ? {} : { pauseMs: options.pauseMs }),\n ...(options.sleep ? { sleep: options.sleep } : {}),\n })\n )\n try {\n await saveGraphSchemaCache(network, document, options.endpoint, complete, now())\n } catch (err) {\n document.notes.push(\n `The schema could not be saved to the cache: ${err instanceof Error ? err.message : String(err)}`\n )\n }\n return document\n })()\n building.set(key, build)\n try {\n return await build\n } finally {\n building.delete(key)\n }\n}\n\n// ─── Text ─────────────────────────────────────────────────────────────────────\n\nfunction listLine(title: string, values: string[], empty = 'none read'): string {\n return `${title}: ${values.length > 0 ? values.join(', ') : empty}`\n}\n\n/** The schema as the model and the terminal read it. */\nexport function formatGraphSchemaText(document: GraphSchemaDocument): string {\n const { topology, facts, chain } = document.layers\n const origin = document.cached\n ? 'from the cache, kept 24 hours; refresh=true rebuilds it'\n : `read now for ${document.billed_units} billed units and cached for 24 hours`\n const lines = [\n `Graph schema, network ${document.network}`,\n `Source: ${document.source}, built ${document.built_at} (${origin})`,\n '',\n 'USE topology',\n listLine('Labels', topology.labels),\n listLine('Link types', topology.link_types),\n listLine(`Property keys (${topology.property_keys.length})`, topology.property_keys),\n ]\n lines.push(topology.indexes.length > 0 ? 'Indexes:' : 'Indexes: none read')\n for (const index of topology.indexes) {\n lines.push(\n `- ${index.entity} ${index.types.join('|')}(${index.properties.join(', ')}) ${index.state}`\n )\n }\n const sampled = Object.entries(topology.samples)\n lines.push(sampled.length > 0 ? 'Fields, from a sample:' : 'Fields, from a sample: none read')\n for (const [kind, fields] of sampled) lines.push(`- ${kind}: ${fields.join(', ')}`)\n lines.push(\n '',\n 'USE facts',\n listLine('Relationships', facts.relationships),\n listLine('TRANSFER columns', facts.transfer_columns),\n '',\n 'USE chain',\n listLine('Lookups', chain.lookups)\n )\n if (document.notes.length > 0) {\n lines.push('', 'Notes:', ...document.notes.map((note) => `- ${note}`))\n }\n return lines.join('\\n')\n}\n\n// ─── The MCP tool ─────────────────────────────────────────────────────────────\n\nexport interface GraphSchemaDependencies {\n endpoint: string\n withClient: GetGraphSchemaOptions['withClient']\n /** The text of a failure that is not a GraphSchemaError: payment required, transport. */\n describeFailure: (err: unknown) => string\n now?: () => Date\n pauseMs?: number\n sleep?: (ms: number) => Promise<void>\n}\n\nexport type MetaSchemaResult = {\n content: Array<{ type: 'text'; text: string }>\n structuredContent?: Record<string, unknown>\n isError: boolean\n}\n\nfunction errorResult(text: string): MetaSchemaResult {\n return { content: [{ type: 'text', text }], isError: true }\n}\n\n/** meta_schema, {network, refresh?}. Every argument is checked before the first read. */\nexport async function handleMetaSchema(\n args: unknown,\n deps: GraphSchemaDependencies\n): Promise<MetaSchemaResult> {\n const record = isRecord(args) ? args : {}\n const refresh = record.refresh\n if (refresh !== undefined && refresh !== null && typeof refresh !== 'boolean') {\n return errorResult('invalid_refresh: refresh must be true or false')\n }\n try {\n const document = await getGraphSchema({\n network: record.network,\n refresh: refresh === true,\n endpoint: deps.endpoint,\n withClient: deps.withClient,\n ...(deps.now ? { now: deps.now } : {}),\n ...(deps.pauseMs === undefined ? {} : { pauseMs: deps.pauseMs }),\n ...(deps.sleep ? { sleep: deps.sleep } : {}),\n })\n return {\n content: [{ type: 'text', text: formatGraphSchemaText(document) }],\n structuredContent: document as unknown as Record<string, unknown>,\n isError: false,\n }\n } catch (err) {\n return errorResult(err instanceof GraphSchemaError ? err.message : deps.describeFailure(err))\n }\n}\n"],"mappings":"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"}
|
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -5,8 +5,8 @@ const require_client = require("./client-Ypw9BNbn.cjs");
|
|
|
5
5
|
const require_tool_visibility = require("./tool-visibility-CDe-ft0L.cjs");
|
|
6
6
|
const require_usage_status = require("./usage-status-BdwwgYsu.cjs");
|
|
7
7
|
const require_subscription_status = require("./subscription-status-ru99UCPZ.cjs");
|
|
8
|
-
const require_capabilities = require("./capabilities-
|
|
9
|
-
const require_graph_schema = require("./graph-schema-
|
|
8
|
+
const require_capabilities = require("./capabilities-DGavTSoW.cjs");
|
|
9
|
+
const require_graph_schema = require("./graph-schema-C_7aZ8RL.cjs");
|
|
10
10
|
const require_action_log = require("./action-log-HniMMF7f.cjs");
|
|
11
11
|
let node_url = require("node:url");
|
|
12
12
|
let node_path = require("node:path");
|
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import { t as PaymentRequiredError } from "./client-gsLi528K.mjs";
|
|
|
3
3
|
import { i as isAppOnlyTool, n as PUBLIC_MCP_TOOL_ALLOWED_ARGS, r as PUBLIC_MCP_TOOL_REQUIRED_ARGS, t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-P9wETx-d.mjs";
|
|
4
4
|
import { t as primitiveBackendUsageStatus } from "./usage-status-CXOnrq-s.mjs";
|
|
5
5
|
import { n as unavailableSubscriptionStatus } from "./subscription-status-DA9DCFrC.mjs";
|
|
6
|
-
import { r as mirrorGraphNetworkCapabilities } from "./capabilities-
|
|
7
|
-
import { a as handleMetaSchema, c as GRAPH_EXPAND_TITLE, l as GRAPH_EXPAND_TOOL, n as META_SCHEMA_DESCRIPTION, o as FLOWS_MAX_OFFSET, r as META_SCHEMA_TITLE, s as GRAPH_EXPAND_DESCRIPTION, t as GraphSchemaError, u as handleGraphExpand } from "./graph-schema-
|
|
6
|
+
import { r as mirrorGraphNetworkCapabilities } from "./capabilities-BBVDIECk.mjs";
|
|
7
|
+
import { a as handleMetaSchema, c as GRAPH_EXPAND_TITLE, l as GRAPH_EXPAND_TOOL, n as META_SCHEMA_DESCRIPTION, o as FLOWS_MAX_OFFSET, r as META_SCHEMA_TITLE, s as GRAPH_EXPAND_DESCRIPTION, t as GraphSchemaError, u as handleGraphExpand } from "./graph-schema-DHd2fGbr.mjs";
|
|
8
8
|
import { r as appendActionLog, t as actionLogSignalsFromResult } from "./action-log-D49Par9P.mjs";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import path from "node:path";
|
package/docs/contributing.md
CHANGED
|
@@ -56,8 +56,7 @@ git diff --check
|
|
|
56
56
|
## Repository rules
|
|
57
57
|
### Repository Invariants
|
|
58
58
|
|
|
59
|
-
- `AGENTS.md`
|
|
60
|
-
together, or neither.
|
|
59
|
+
- `AGENTS.md` is the one agent instruction file. Never add a `CLAUDE.md`.
|
|
61
60
|
- Every PR that changes tracked files must bump `package.json`,
|
|
62
61
|
`package-lock.json`, and `CHANGELOG.md`. Enforced by
|
|
63
62
|
`npm run release:check` (`scripts/check-release-gate.mjs`) in
|
package/docs/graph-tools.md
CHANGED
|
@@ -56,13 +56,13 @@ A question about the whole chain (recent activity, the biggest senders, a top li
|
|
|
56
56
|
|
|
57
57
|
The graph layers are grouped into families, and each family is as fresh as
|
|
58
58
|
its slowest layer: `money` (`FLOWS_TO`, `OPERATED_BY`), `dex` (`SWAPPED`,
|
|
59
|
-
`ADDED_LIQUIDITY`, `REMOVED_LIQUIDITY`), `
|
|
59
|
+
`ADDED_LIQUIDITY`, `REMOVED_LIQUIDITY`), `interactions` (`APPROVED`,
|
|
60
60
|
`BRIDGED`, `DEPLOYED_CONTRACT`, `SIGNED_AUTHORIZATION`), `smart_accounts`
|
|
61
61
|
(`SPONSORED`, `BUNDLED`, `SIGNED_FOR`, `LINKED`) and `annotations` (labels
|
|
62
62
|
and kinds, which follow runs, not blocks). `cia network <name>` prints one
|
|
63
63
|
row per family with its height and its blocks behind the head, and
|
|
64
64
|
`annotations` as `fresh` or `walking`. The `Head` node of `USE chain` serves
|
|
65
|
-
`behind_money`, `behind_dex`, `
|
|
65
|
+
`behind_money`, `behind_dex`, `behind_interactions` and
|
|
66
66
|
`behind_smart_accounts` (integers) and `annotations_fresh` (a boolean);
|
|
67
67
|
`graph_blocks_behind` equals `behind_money`. A search bounded only by time is
|
|
68
68
|
refused with `family_behind` when a link type it names belongs to a family
|
package/package.json
CHANGED
|
@@ -486,7 +486,7 @@ MATCH (a:Address {address: "0x04911a118f11c75667e4d0dfb8e640af5a353550", at_bloc
|
|
|
486
486
|
|
|
487
487
|
```cypher
|
|
488
488
|
USE chain
|
|
489
|
-
MATCH (h:Head) RETURN h.block_height, h.age_seconds, h.warehouse_blocks_behind, h.graph_blocks_behind, h.behind_money, h.behind_dex, h.
|
|
489
|
+
MATCH (h:Head) RETURN h.block_height, h.age_seconds, h.warehouse_blocks_behind, h.graph_blocks_behind, h.behind_money, h.behind_dex, h.behind_interactions, h.behind_smart_accounts, h.annotations_fresh
|
|
490
490
|
```
|
|
491
491
|
|
|
492
492
|
An `Address` lookup always answers one row, because every address has a state. `a.balance` is decimal text. Read the kind from `a.is_contract`, `a.nonce` and `a.delegated_to`: `a.is_contract` is `true` for a contract and null for any other address, a `nonce` above 0 with `a.is_contract` null is an account, and `a.delegated_to` names the target of an EIP-7702 delegation, which is an account. `a.code_size` is the size of the code, and the code itself is never returned. `:Account` and `:Contract` are never a chain lookup label.
|