chain-insights 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/bin/cli.js +10 -0
  4. package/bin/install.cjs +252 -0
  5. package/bin/mcp-proxy.cjs +10 -0
  6. package/dist/active-BSrxLKwn.mjs +50 -0
  7. package/dist/active-BSrxLKwn.mjs.map +1 -0
  8. package/dist/active-Dv7Tu-O4.cjs +68 -0
  9. package/dist/app-BjjuQM0B.mjs +155 -0
  10. package/dist/app-BjjuQM0B.mjs.map +1 -0
  11. package/dist/app-Dq1TdB6p.cjs +161 -0
  12. package/dist/artifact-server-DoxJ7fCx.cjs +47 -0
  13. package/dist/artifact-server-Dxz5YbuQ.mjs +48 -0
  14. package/dist/artifact-server-Dxz5YbuQ.mjs.map +1 -0
  15. package/dist/assets/bg-pattern.png +0 -0
  16. package/dist/assets/logo.png +0 -0
  17. package/dist/call-args-DQA2QcRA.cjs +27 -0
  18. package/dist/call-args-Lk_wOJxd.mjs +29 -0
  19. package/dist/call-args-Lk_wOJxd.mjs.map +1 -0
  20. package/dist/capabilities-CB97WMA5.cjs +83 -0
  21. package/dist/capabilities-DliMBim-.mjs +84 -0
  22. package/dist/capabilities-DliMBim-.mjs.map +1 -0
  23. package/dist/cases-By7INiOa.mjs +6 -0
  24. package/dist/cases-CDcNU91B.cjs +9 -0
  25. package/dist/chunk-CZWwpsFl.cjs +43 -0
  26. package/dist/cli.cjs +752 -0
  27. package/dist/cli.d.cts +1 -0
  28. package/dist/cli.d.mts +1 -0
  29. package/dist/cli.mjs +753 -0
  30. package/dist/cli.mjs.map +1 -0
  31. package/dist/client-D4Bq0rp9.mjs +111 -0
  32. package/dist/client-D4Bq0rp9.mjs.map +1 -0
  33. package/dist/client-D4fZgIaO.cjs +132 -0
  34. package/dist/config-Bmdl5hdk.cjs +67 -0
  35. package/dist/config-BwrBYmiC.mjs +44 -0
  36. package/dist/config-BwrBYmiC.mjs.map +1 -0
  37. package/dist/data-extractor-BNGj7ECT.cjs +347 -0
  38. package/dist/data-extractor-DFzsa5CS.mjs +336 -0
  39. package/dist/data-extractor-DFzsa5CS.mjs.map +1 -0
  40. package/dist/dossier-BsroDgD3.mjs +76 -0
  41. package/dist/dossier-BsroDgD3.mjs.map +1 -0
  42. package/dist/dossier-DtxREpPm.cjs +76 -0
  43. package/dist/evidence-BGcdKxuV.cjs +200 -0
  44. package/dist/evidence-BhvFW-y_.mjs +195 -0
  45. package/dist/evidence-BhvFW-y_.mjs.map +1 -0
  46. package/dist/format-Ce1RObVl.mjs +22 -0
  47. package/dist/format-Ce1RObVl.mjs.map +1 -0
  48. package/dist/format-DOrPvXEr.cjs +20 -0
  49. package/dist/frontmatter-D8wWCeOa.mjs +26 -0
  50. package/dist/frontmatter-D8wWCeOa.mjs.map +1 -0
  51. package/dist/frontmatter-DgAuai7E.cjs +35 -0
  52. package/dist/graph-normalizer-Cv9yK9Pg.mjs +130 -0
  53. package/dist/graph-normalizer-Cv9yK9Pg.mjs.map +1 -0
  54. package/dist/graph-normalizer-DeIj6Ses.cjs +133 -0
  55. package/dist/graph-reports-C4TBjCkM.mjs +63 -0
  56. package/dist/graph-reports-C4TBjCkM.mjs.map +1 -0
  57. package/dist/graph-reports-DU05YCei.cjs +64 -0
  58. package/dist/html-generator-CAv81IWH.cjs +85 -0
  59. package/dist/html-generator-V6Bp0uRb.mjs +68 -0
  60. package/dist/html-generator-V6Bp0uRb.mjs.map +1 -0
  61. package/dist/index.cjs +31 -0
  62. package/dist/index.d.cts +187 -0
  63. package/dist/index.d.cts.map +1 -0
  64. package/dist/index.d.mts +187 -0
  65. package/dist/index.d.mts.map +1 -0
  66. package/dist/index.mjs +9 -0
  67. package/dist/init-BjuFt54X.cjs +232 -0
  68. package/dist/init-CaOsHTIo.mjs +232 -0
  69. package/dist/init-CaOsHTIo.mjs.map +1 -0
  70. package/dist/mcp-proxy.cjs +1257 -0
  71. package/dist/mcp-proxy.d.cts +12 -0
  72. package/dist/mcp-proxy.d.cts.map +1 -0
  73. package/dist/mcp-proxy.d.mts +12 -0
  74. package/dist/mcp-proxy.d.mts.map +1 -0
  75. package/dist/mcp-proxy.mjs +1255 -0
  76. package/dist/mcp-proxy.mjs.map +1 -0
  77. package/dist/output-root-CFYms3ad.cjs +43 -0
  78. package/dist/output-root-CmWM7aV2.mjs +33 -0
  79. package/dist/output-root-CmWM7aV2.mjs.map +1 -0
  80. package/dist/parser-BUIWW1OH.cjs +182 -0
  81. package/dist/parser-DO0_SssG.mjs +182 -0
  82. package/dist/parser-DO0_SssG.mjs.map +1 -0
  83. package/dist/public-tools-D4UI-Zb0.mjs +2554 -0
  84. package/dist/public-tools-D4UI-Zb0.mjs.map +1 -0
  85. package/dist/public-tools-XSpkz2ky.cjs +2556 -0
  86. package/dist/resolver-C2ZS7oC8.mjs +201 -0
  87. package/dist/resolver-C2ZS7oC8.mjs.map +1 -0
  88. package/dist/resolver-zYbu4wDV.cjs +203 -0
  89. package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
  90. package/dist/runner-1Eq55OYb.cjs +148 -0
  91. package/dist/runner-BhUHbiHG.mjs +149 -0
  92. package/dist/runner-BhUHbiHG.mjs.map +1 -0
  93. package/dist/schema-4XpzDFQM.cjs +55 -0
  94. package/dist/schema-8d0rVIdZ.mjs +37 -0
  95. package/dist/schema-8d0rVIdZ.mjs.map +1 -0
  96. package/dist/schema-cache-9CksD7tX.mjs +34 -0
  97. package/dist/schema-cache-9CksD7tX.mjs.map +1 -0
  98. package/dist/schema-cache-CgWRCN2N.cjs +36 -0
  99. package/dist/selector-CkFcTXzz.cjs +10 -0
  100. package/dist/selector-xjm6NTHI.mjs +12 -0
  101. package/dist/selector-xjm6NTHI.mjs.map +1 -0
  102. package/dist/server-BkM5xrXb.mjs +45 -0
  103. package/dist/server-BkM5xrXb.mjs.map +1 -0
  104. package/dist/server-DXowbpfi.cjs +54 -0
  105. package/dist/session-BpNylyuJ.cjs +115 -0
  106. package/dist/session-CcTgYxsj.mjs +115 -0
  107. package/dist/session-CcTgYxsj.mjs.map +1 -0
  108. package/dist/setup-DOpKPrlx.cjs +81 -0
  109. package/dist/setup-DyrWHuwQ.mjs +80 -0
  110. package/dist/setup-DyrWHuwQ.mjs.map +1 -0
  111. package/dist/store-BiUhQOIf.cjs +230 -0
  112. package/dist/store-BoWE-Gtl.mjs +225 -0
  113. package/dist/store-BoWE-Gtl.mjs.map +1 -0
  114. package/dist/templates/graph.html +1406 -0
  115. package/dist/tool-visibility-3Z_KvO9Q.mjs +28 -0
  116. package/dist/tool-visibility-3Z_KvO9Q.mjs.map +1 -0
  117. package/dist/tool-visibility-CwgY205r.cjs +36 -0
  118. package/dist/tools-Cp2jAAAb.mjs +100 -0
  119. package/dist/tools-Cp2jAAAb.mjs.map +1 -0
  120. package/dist/tools-f_vJUZAF.cjs +139 -0
  121. package/dist/topup-server-BZuQifvh.cjs +940 -0
  122. package/dist/topup-server-DUjyFftI.mjs +919 -0
  123. package/dist/topup-server-DUjyFftI.mjs.map +1 -0
  124. package/dist/version-1gP19Lhi.mjs +8 -0
  125. package/dist/version-1gP19Lhi.mjs.map +1 -0
  126. package/dist/version-BNGtdpmH.cjs +18 -0
  127. package/dist/viz-BlCJe6Tk.mjs +35 -0
  128. package/dist/viz-BlCJe6Tk.mjs.map +1 -0
  129. package/dist/viz-ClezVXrJ.cjs +44 -0
  130. package/dist/wallet-BMelXBYP.mjs +104 -0
  131. package/dist/wallet-BMelXBYP.mjs.map +1 -0
  132. package/dist/wallet-RnvvSpV2.cjs +146 -0
  133. package/docs/architecture.md +145 -0
  134. package/docs/contributing.md +68 -0
  135. package/docs/debugging.md +68 -0
  136. package/docs/development.md +44 -0
  137. package/docs/graph-tools.md +251 -0
  138. package/docs/images/graph-mcp-iframe.png +0 -0
  139. package/docs/images/graph-visualization.png +0 -0
  140. package/docs/images/topup-page.png +0 -0
  141. package/docs/investigation-workspaces.md +151 -0
  142. package/docs/mcp-proxy.md +180 -0
  143. package/package.json +59 -0
  144. package/skills/chain-insights-developer-experience/SKILL.md +101 -0
  145. package/skills/chain-insights-investigation/SKILL.md +285 -0
  146. package/skills/chain-insights-investigation/agents/openai.yaml +4 -0
  147. package/skills/chain-insights-investigation/scripts/run-target-uat.sh +197 -0
  148. package/skills/chain-insights-trace-funds/SKILL.md +249 -0
  149. package/skills/ci-case/SKILL.md +43 -0
  150. package/skills/ci-status/SKILL.md +45 -0
  151. package/skills/test-chain-insights-graphrag-mcp/SKILL.md +75 -0
  152. package/skills/test-chain-insights-graphrag-mcp/agents/openai.yaml +4 -0
  153. package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +414 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-tools-D4UI-Zb0.mjs","names":["GRAPH_QUERY_BATCH_TIMEOUT_SECONDS","GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS","clampInt","escapeCypherString","sanitizeSegment","textFromToolResult","parseGraphBatchResult","topologyGraphQuery","callGraphBatch","flowEdgeMap","pathNodeMap","edgeKey","numberValue","stringArrayValue","buildGraph","summarize","parseAddressList","numberValue","escapeCypherString","textFromToolResult","parseGraphBatchResult","callGraphBatch"],"sources":["../src/investigation/trace-funds.ts","../src/investigation/scam-topology.ts","../src/investigation/public-tools.ts"],"sourcesContent":["import { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport type { ContentBlock } from '@modelcontextprotocol/sdk/types.js'\nimport type { InvestigatorConfig } from '../config/schema.js'\nimport { normalizeGraphPayload } from '../viz/graph-normalizer.js'\nimport { workspaceOutputPaths, type WorkspaceOutputPaths } from '../workspace/output-root.js'\n\ntype RemoteToolResult = {\n content?: ContentBlock[]\n isError?: boolean\n}\n\nexport interface TraceFundsOptions {\n seedAddress: string\n network: string\n caseId?: string\n maxHops?: number\n perAddressLimit?: number\n minAmountSum?: number\n}\n\nexport interface TraceFlow {\n hop: number\n src: string\n dst: string\n amount_sum: number\n amount_usd_sum?: number\n tx_count?: number\n first_tx_id?: string\n last_tx_id?: string\n src_labels?: string[]\n dst_labels?: string[]\n src_node?: GraphNodeMetadata\n dst_node?: GraphNodeMetadata\n dst_degree_in?: number\n dst_degree_out?: number\n terminal_exchange: boolean\n}\n\nexport interface TraceFundsResult {\n summaryText: string\n compactEvidence: Record<string, unknown>\n graphData: Record<string, unknown>\n files: {\n schema: string\n compactEvidence: string\n graph: string\n graphHtml: string\n table: string\n tableHtml: string\n report: string\n }\n continuation: {\n nextHopAddresses: string[]\n depositAddresses: string[]\n exchangeAddresses: string[]\n hint: string\n }\n addressMap: Record<string, string>\n}\n\ninterface TraceDeposit {\n address: string\n exchangeAddress: string\n exchangeLabels?: string[]\n exchangeNode?: GraphNodeMetadata\n amount_sum?: number\n amount_usd_sum?: number\n hops: number\n path: string[]\n pathNodes?: GraphNodeMetadata[]\n}\n\ninterface SourceMatch {\n deposit_address: string\n source_exchange: string\n source_labels?: string[]\n sourceNode?: GraphNodeMetadata\n hops: number\n path: string[]\n pathNodes?: GraphNodeMetadata[]\n}\n\ninterface ReverseLead {\n address: string\n labels?: string[]\n node?: GraphNodeMetadata\n deposit_address: string\n amount_usd?: number\n degree_in?: number\n degree_out?: number\n total_volume_usd?: number\n reason: string\n}\n\ninterface GraphNodeMetadata {\n address: string\n labels?: string[]\n system_labels?: string[]\n address_type?: string\n address_subtypes?: string[]\n}\n\nclass AliasTracker {\n private readonly byAddress = new Map<string, string>()\n private readonly byAlias = new Map<string, string>()\n private readonly counters = new Map<string, number>()\n\n assign(address: string, prefix: string): string {\n const existing = this.byAddress.get(address)\n if (existing) return existing\n const next = (this.counters.get(prefix) ?? 0) + 1\n this.counters.set(prefix, next)\n const alias = `${prefix}${next}`\n this.byAddress.set(address, alias)\n this.byAlias.set(alias, address)\n return alias\n }\n\n alias(address: string): string | undefined {\n return this.byAddress.get(address)\n }\n\n addressMap(): Record<string, string> {\n return Object.fromEntries([...this.byAlias.entries()].sort(([a], [b]) => a.localeCompare(b, undefined, { numeric: true })))\n }\n\n compactAddressMap(maxIntermediaries = 20, maxSourceExchanges = 20, maxLeads = 20): Record<string, string> {\n const counts = new Map<string, number>()\n const entries = [...this.byAlias.entries()].filter(([alias]) => {\n const prefix = alias.slice(0, 1)\n if (['V', 'D', 'E'].includes(prefix)) return true\n const next = (counts.get(prefix) ?? 0) + 1\n counts.set(prefix, next)\n if (prefix === 'I') return next <= maxIntermediaries\n if (prefix === 'X') return next <= maxSourceExchanges\n if (prefix === 'L') return next <= maxLeads\n return true\n })\n return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b, undefined, { numeric: true })))\n }\n}\n\ninterface ParsedGraphBatch {\n facts?: {\n queries?: Array<{\n id?: string\n ok?: boolean\n results?: Array<Record<string, unknown>>\n error?: string\n }>\n }\n}\n\nconst GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 120\nconst GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS = 5 * 60 * 1000\n\nconst SCHEMA_QUERY_SET = [\n {\n id: 'node_labels',\n query: 'MATCH (n:Address) RETURN \"Address\" AS node_label, count(n) AS sample_count LIMIT 1',\n },\n {\n id: 'relationship_types',\n query: 'MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN \"FLOWS_TO\" AS rel_name, count(r) AS sample_count LIMIT 1',\n },\n {\n id: 'address_property_keys',\n query: 'MATCH (n:Address) RETURN \"address\" AS property_key, count(n) AS sample_count LIMIT 1',\n },\n {\n id: 'flows_to_property_keys',\n query: 'MATCH (:Address)-[r:FLOWS_TO]->(:Address) RETURN \"amount_sum\" AS property_key, count(r) AS sample_count LIMIT 1',\n },\n]\n\nfunction clampInt(value: number | undefined, fallback: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return fallback\n return Math.max(min, Math.min(max, Math.trunc(value as number)))\n}\n\nfunction escapeCypherString(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')\n}\n\nfunction sanitizeSegment(value: string): string {\n const sanitized = value.toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/(^-|-$)/g, '').slice(0, 80)\n return sanitized || 'trace'\n}\n\nasync function ensureDirs(paths: WorkspaceOutputPaths): Promise<void> {\n await mkdir(paths.schemaDir, { recursive: true, mode: 0o700 })\n await mkdir(paths.reportsRoot, { recursive: true, mode: 0o700 })\n await mkdir(paths.reportGraphsRoot, { recursive: true, mode: 0o700 })\n await mkdir(paths.reportTablesRoot, { recursive: true, mode: 0o700 })\n await mkdir(paths.logsRoot, { recursive: true, mode: 0o700 })\n}\n\nfunction textFromToolResult(result: RemoteToolResult): string {\n return (result.content ?? [])\n .filter((item): item is Extract<ContentBlock, { type: 'text' }> => item.type === 'text')\n .map((item) => item.text)\n .join('\\n')\n}\n\nfunction parseGraphBatchResult(result: RemoteToolResult): ParsedGraphBatch {\n const text = textFromToolResult(result).trim()\n if (!text) throw new Error('graph_query_batch returned no text content')\n const parsed = JSON.parse(text) as ParsedGraphBatch\n if (!parsed.facts?.queries) throw new Error('graph_query_batch response did not include facts.queries')\n return parsed\n}\n\nfunction topologyGraphQuery(query: string): string {\n const trimmed = query.trim()\n if (/^USE\\s+/i.test(trimmed)) return trimmed\n return `USE live_topology ${trimmed}`\n}\n\nasync function callGraphBatch(\n remoteClient: Client,\n network: string,\n queries: Array<{ id: string; query: string }>,\n): Promise<ParsedGraphBatch> {\n const result = await remoteClient.callTool(\n {\n name: 'graph_query_batch',\n arguments: {\n network,\n queries: queries.map((query) => ({\n ...query,\n query: topologyGraphQuery(query.query),\n })),\n per_query_timeout_seconds: GRAPH_QUERY_BATCH_TIMEOUT_SECONDS,\n },\n },\n undefined,\n {\n timeout: GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS,\n maxTotalTimeout: GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS,\n },\n ) as RemoteToolResult\n if (result.isError) throw new Error(textFromToolResult(result) || 'graph_query_batch failed')\n return parseGraphBatchResult(result)\n}\n\nfunction resultsFor(batch: ParsedGraphBatch, id: string): Array<Record<string, unknown>> {\n const query = batch.facts?.queries?.find((entry) => entry.id === id)\n if (!query) return []\n if (query.ok === false) throw new Error(query.error || `Query failed: ${id}`)\n return query.results ?? []\n}\n\nfunction schemaFromGraphBatch(network: string, batch: ParsedGraphBatch): Record<string, unknown> {\n return {\n schema: 'chain-insights.runtime_graph_schema.v1',\n network,\n source: 'graph_query_batch',\n node_labels: resultsFor(batch, 'node_labels'),\n relationship_types: resultsFor(batch, 'relationship_types'),\n address_property_keys: resultsFor(batch, 'address_property_keys').map((row) => row['property_key']),\n flows_to_property_keys: resultsFor(batch, 'flows_to_property_keys').map((row) => row['property_key']),\n recommended_flow_projection: [\n 'src.address AS src',\n 'dst.address AS dst',\n 'r.amount_sum AS amount_sum',\n 'r.amount_usd_sum AS amount_usd_sum',\n 'r.tx_count AS tx_count',\n 'r.first_tx_id AS first_tx_id',\n 'r.last_tx_id AS last_tx_id',\n 'dst.labels AS dst_labels',\n 'dst.lifetime_degree_in AS dst_degree_in',\n 'dst.lifetime_degree_out AS dst_degree_out',\n ],\n }\n}\n\nasync function loadOrCaptureTopologySchema(\n remoteClient: Client,\n paths: WorkspaceOutputPaths,\n network: string,\n): Promise<{ schema: Record<string, unknown>; filePath: string }> {\n const filePath = path.join(paths.schemaDir, `${sanitizeSegment(network)}.graph-schema.json`)\n try {\n return { schema: JSON.parse(await readFile(filePath, 'utf8')) as Record<string, unknown>, filePath }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err\n }\n\n const batch = await callGraphBatch(\n remoteClient,\n network,\n SCHEMA_QUERY_SET,\n )\n const schema = schemaFromGraphBatch(network, batch)\n await writeFile(filePath, JSON.stringify(schema, null, 2) + '\\n', { mode: 0o600 })\n return { schema, filePath }\n}\n\nfunction flowEdgeMap(variableName: string): string {\n return `{amount_sum: ${variableName}.amount_sum, amount_usd_sum: ${variableName}.amount_usd_sum, tx_count: ${variableName}.tx_count, first_tx_id: ${variableName}.first_tx_id, last_tx_id: ${variableName}.last_tx_id}`\n}\n\nfunction pathNodeMap(variableName: string): string {\n return `{address: ${variableName}.address, labels: ${variableName}.labels, system_labels: ${variableName}.labels, address_type: ${variableName}.address_type, address_subtypes: ${variableName}.address_subtypes}`\n}\n\nfunction forwardExchangeQueries(address: string, limit: number, minAmountSum: number, maxHops: number): Array<{ id: string; query: string }> {\n return Array.from({ length: maxHops }, (_, index) => forwardExchangeQueryAtDepth(address, limit, minAmountSum, index + 1))\n}\n\nfunction forwardExchangeQueryAtDepth(address: string, limit: number, minAmountSum: number, depth: number): { id: string; query: string } {\n const intermediateVariables = Array.from({ length: Math.max(depth - 1, 0) }, (_, index) => `n${index + 1}`)\n const nodeVariables = ['s', ...intermediateVariables, 't']\n const edgeVariables = Array.from({ length: depth }, (_, index) => `r${index + 1}`)\n const relationshipChain = edgeVariables.map((edgeVariable, index) => {\n const targetVariable = index === edgeVariables.length - 1 ? 't' : intermediateVariables[index]!\n return `-[${edgeVariable}:FLOWS_TO]->(${targetVariable}:Address)`\n }).join('')\n const amountPredicates = edgeVariables.map((edgeVariable) => `${edgeVariable}.amount_sum IS NOT NULL${minAmountSum > 0 ? ` AND ${edgeVariable}.amount_sum >= ${minAmountSum}` : ''}`)\n const intermediatePredicates = intermediateVariables.map((nodeVariable) => `${nodeVariable}.is_exchange IS NULL`)\n const predicates = ['s <> t', 't.is_exchange IS NOT NULL', ...intermediatePredicates, ...amountPredicates]\n const depositVariable = nodeVariables[nodeVariables.length - 2]!\n return {\n id: `forward_exchange_paths_${depth}`,\n query: [\n `MATCH (s:Address {address: \"${escapeCypherString(address)}\"})${relationshipChain}`,\n `WHERE ${predicates.join(' AND ')}`,\n `RETURN [${nodeVariables.map((nodeVariable) => `${nodeVariable}.address`).join(', ')}] AS addresses, [${nodeVariables.map((nodeVariable) => `${nodeVariable}.labels`).join(', ')}] AS node_labels, [${nodeVariables.map(pathNodeMap).join(', ')}] AS path_nodes, [${edgeVariables.map(flowEdgeMap).join(', ')}] AS edge_props, t.address AS exchange_address, t.labels AS exchange_display_labels, t.labels AS exchange_labels, t.address_type AS exchange_address_type, t.address_subtypes AS exchange_address_subtypes, ${depositVariable}.address AS deposit_address, ${depth} AS hops`,\n 'ORDER BY hops ASC',\n `LIMIT ${limit}`,\n ].join(' '),\n }\n}\n\nfunction backwardSourceQueries(idPrefix: string, depositAddress: string, maxHops: number): Array<{ id: string; query: string }> {\n return Array.from({ length: maxHops }, (_, index) => backwardSourceQueryAtDepth(`${idPrefix}_${index + 1}`, depositAddress, index + 1))\n}\n\nfunction backwardSourceQueryAtDepth(id: string, depositAddress: string, depth: number): { id: string; query: string } {\n const intermediateVariables = Array.from({ length: Math.max(depth - 1, 0) }, (_, index) => `n${index + 1}`)\n const nodeVariables = ['dep', ...intermediateVariables, 'source']\n const edgeVariables = Array.from({ length: depth }, (_, index) => `r${index + 1}`)\n const relationshipChain = edgeVariables.map((edgeVariable, index) => {\n const targetVariable = index === edgeVariables.length - 1 ? 'source' : intermediateVariables[index]!\n return `<-[${edgeVariable}:FLOWS_TO]-(${targetVariable}:Address)`\n }).join('')\n const intermediatePredicates = intermediateVariables.map((nodeVariable) => `${nodeVariable}.is_exchange IS NULL`)\n return {\n id,\n query: [\n `MATCH (dep:Address {address: \"${escapeCypherString(depositAddress)}\"})`,\n `MATCH (dep)${relationshipChain}`,\n `WHERE source <> dep AND source.is_exchange IS NOT NULL${intermediatePredicates.length > 0 ? ` AND ${intermediatePredicates.join(' AND ')}` : ''}`,\n `RETURN dep.address AS deposit_address, source.address AS source_exchange, source.labels AS source_display_labels, source.labels AS source_labels, source.address_type AS source_address_type, source.address_subtypes AS source_address_subtypes, ${depth} AS hops, [${nodeVariables.map((nodeVariable) => `${nodeVariable}.address`).join(', ')}] AS addresses, [${nodeVariables.map((nodeVariable) => `${nodeVariable}.labels`).join(', ')}] AS node_labels, [${nodeVariables.map(pathNodeMap).join(', ')}] AS path_nodes`,\n 'LIMIT 20',\n ].join(' '),\n }\n}\n\nfunction reverseLeadsQuery(depositAddresses: string[]): { id: string; query: string } {\n const depositPredicates = depositAddresses.map((address) => `deposit.address = \"${escapeCypherString(address)}\"`)\n return {\n id: 'reverse_1hop',\n query: [\n 'MATCH (sender:Address)-[r:FLOWS_TO]->(deposit:Address)',\n `WHERE (${depositPredicates.join(' OR ')}) AND sender.is_exchange IS NULL AND sender.address <> deposit.address`,\n 'RETURN DISTINCT sender.address AS address, sender.labels AS display_labels, sender.labels AS system_labels, sender.address_type AS address_type, sender.address_subtypes AS address_subtypes, coalesce(sender.lifetime_degree_in, 0) AS degree_in, coalesce(sender.lifetime_degree_out, 0) AS degree_out, coalesce(sender.total_volume_usd, 0) AS total_volume_usd, deposit.address AS deposit_address, r.amount_usd_sum AS amount_usd',\n 'ORDER BY r.amount_usd_sum DESC',\n `LIMIT ${Math.max(50, depositAddresses.length * 50)}`,\n ].join(' '),\n }\n}\n\nfunction edgeKey(src: string, dst: string): string {\n return `${src}\\u0000${dst}`\n}\n\nfunction directEdgePropsQuery(flows: TraceFlow[]): { id: string; query: string } | null {\n const pairs = [...new Map(flows.map((flow) => [edgeKey(flow.src, flow.dst), { src: flow.src, dst: flow.dst }])).values()]\n if (pairs.length === 0) return null\n const predicates = pairs.map((pair) =>\n `(a.address = \"${escapeCypherString(pair.src)}\" AND b.address = \"${escapeCypherString(pair.dst)}\")`\n )\n return {\n id: 'direct_edge_props',\n query: [\n 'MATCH (a:Address)-[r:FLOWS_TO]->(b:Address)',\n `WHERE (${predicates.join(' OR ')})`,\n 'RETURN a.address AS src, b.address AS dst, r.amount_sum AS amount_sum, r.amount_usd_sum AS amount_usd_sum, r.tx_count AS tx_count, r.first_tx_id AS first_tx_id, r.last_tx_id AS last_tx_id',\n `LIMIT ${pairs.length}`,\n ].join(' '),\n }\n}\n\nfunction numberValue(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n return undefined\n}\n\nfunction rowTerminalAmount(row: Record<string, unknown>): number | undefined {\n const edgeProps = Array.isArray(row['edge_props']) ? row['edge_props'] as Array<Record<string, unknown>> : []\n const terminalEdge = edgeProps[edgeProps.length - 1]\n if (!terminalEdge) return undefined\n return numberValue(terminalEdge['amount_sum']) ?? numberValue(terminalEdge['amount_usd_sum'])\n}\n\nfunction rowsMatchingMinimumAmount(rows: Array<Record<string, unknown>>, minAmountSum: number): Array<Record<string, unknown>> {\n if (minAmountSum <= 0) return rows\n return rows.filter((row) => (rowTerminalAmount(row) ?? 0) >= minAmountSum)\n}\n\nfunction stringArrayValue(value: unknown): string[] | undefined {\n if (Array.isArray(value)) return value.map(String)\n if (typeof value === 'string' && value.trim()) return [value]\n return undefined\n}\n\nfunction uniqueStrings(values: string[] | undefined): string[] {\n return [...new Set(values ?? [])]\n}\n\nfunction nodeMetadataFromValue(value: unknown, fallbackAddress?: string): GraphNodeMetadata | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return fallbackAddress ? { address: fallbackAddress } : undefined\n }\n const record = value as Record<string, unknown>\n const address = typeof record['address'] === 'string' ? record['address'] : fallbackAddress\n if (!address) return undefined\n return {\n address,\n labels: stringArrayValue(record['labels']),\n system_labels: stringArrayValue(record['system_labels']),\n address_type: typeof record['address_type'] === 'string' ? record['address_type'] : undefined,\n address_subtypes: stringArrayValue(record['address_subtypes']),\n }\n}\n\nfunction isExchangeFlow(flow: TraceFlow): boolean {\n return flow.terminal_exchange || flow.dst_labels?.includes('Exchange') === true || flow.dst_node?.system_labels?.includes('Exchange') === true\n}\n\nfunction depositFromRow(row: Record<string, unknown>): TraceDeposit | null {\n const pathAddresses = stringArrayValue(row['addresses']) ?? []\n if (pathAddresses.length < 2) return null\n const exchangeAddress = typeof row['exchange_address'] === 'string' ? row['exchange_address'] : pathAddresses[pathAddresses.length - 1]\n const edgeProps = Array.isArray(row['edge_props']) ? row['edge_props'] as Array<Record<string, unknown>> : []\n const terminalEdge = edgeProps[edgeProps.length - 1] ?? {}\n const pathNodes = Array.isArray(row['path_nodes'])\n ? row['path_nodes'].map((node, index) => nodeMetadataFromValue(node, pathAddresses[index])).filter((node): node is GraphNodeMetadata => Boolean(node))\n : undefined\n const exchangeNode = {\n address: exchangeAddress,\n labels: stringArrayValue(row['exchange_display_labels']),\n system_labels: stringArrayValue(row['exchange_system_labels']) ?? stringArrayValue(row['exchange_labels']),\n address_type: typeof row['exchange_address_type'] === 'string' ? row['exchange_address_type'] : undefined,\n address_subtypes: stringArrayValue(row['exchange_address_subtypes']),\n }\n return {\n address: pathAddresses[pathAddresses.length - 2]!,\n exchangeAddress,\n exchangeLabels: stringArrayValue(row['exchange_labels']),\n exchangeNode,\n amount_sum: numberValue(terminalEdge['amount_sum']),\n amount_usd_sum: numberValue(terminalEdge['amount_usd_sum']),\n hops: numberValue(row['hops']) ?? pathAddresses.length - 1,\n path: pathAddresses,\n pathNodes,\n }\n}\n\nfunction flowsFromForwardRows(rows: Array<Record<string, unknown>>): { flows: TraceFlow[]; deposits: TraceDeposit[] } {\n const flows: TraceFlow[] = []\n const deposits: TraceDeposit[] = []\n const seenEdges = new Set<string>()\n for (const row of rows) {\n const pathAddresses = stringArrayValue(row['addresses']) ?? []\n const nodeLabels = Array.isArray(row['node_labels']) ? row['node_labels'].map((labels) => stringArrayValue(labels) ?? []) : []\n const pathNodes = Array.isArray(row['path_nodes'])\n ? row['path_nodes'].map((node, index) => nodeMetadataFromValue(node, pathAddresses[index]))\n : []\n const edgeProps = Array.isArray(row['edge_props']) ? row['edge_props'] as Array<Record<string, unknown>> : []\n const deposit = depositFromRow(row)\n if (deposit) deposits.push(deposit)\n for (let index = 0; index < pathAddresses.length - 1; index += 1) {\n const src = pathAddresses[index]!\n const dst = pathAddresses[index + 1]!\n const edge = edgeProps[index] ?? {}\n const amount = numberValue(edge['amount_sum']) ?? numberValue(edge['amount_usd_sum']) ?? 0\n const terminal = index === pathAddresses.length - 2\n const key = `${src}->${dst}`\n if (seenEdges.has(key)) continue\n seenEdges.add(key)\n flows.push({\n hop: index + 1,\n src,\n dst,\n amount_sum: amount,\n amount_usd_sum: numberValue(edge['amount_usd_sum']),\n tx_count: numberValue(edge['tx_count']),\n first_tx_id: typeof edge['first_tx_id'] === 'string' ? edge['first_tx_id'] : undefined,\n last_tx_id: typeof edge['last_tx_id'] === 'string' ? edge['last_tx_id'] : undefined,\n src_labels: nodeLabels[index],\n dst_labels: nodeLabels[index + 1],\n src_node: pathNodes[index],\n dst_node: pathNodes[index + 1],\n terminal_exchange: terminal,\n })\n }\n }\n return { flows, deposits }\n}\n\nasync function hydrateDirectEdgeProps(remoteClient: Client, network: string, flows: TraceFlow[], deposits: TraceDeposit[]): Promise<void> {\n const query = directEdgePropsQuery(flows)\n if (!query) return\n\n const batch = await callGraphBatch(remoteClient, network, [query])\n const edgeProps = new Map<string, Record<string, unknown>>()\n for (const row of resultsFor(batch, 'direct_edge_props')) {\n const src = typeof row['src'] === 'string' ? row['src'] : ''\n const dst = typeof row['dst'] === 'string' ? row['dst'] : ''\n if (!src || !dst) continue\n edgeProps.set(edgeKey(src, dst), row)\n }\n\n for (const flow of flows) {\n const props = edgeProps.get(edgeKey(flow.src, flow.dst))\n if (!props) continue\n flow.amount_sum = numberValue(props['amount_sum']) ?? flow.amount_sum\n flow.amount_usd_sum = numberValue(props['amount_usd_sum'])\n flow.tx_count = numberValue(props['tx_count'])\n flow.first_tx_id = typeof props['first_tx_id'] === 'string' ? props['first_tx_id'] : undefined\n flow.last_tx_id = typeof props['last_tx_id'] === 'string' ? props['last_tx_id'] : undefined\n }\n\n for (const deposit of deposits) {\n const props = edgeProps.get(edgeKey(deposit.address, deposit.exchangeAddress))\n if (!props) continue\n deposit.amount_sum = numberValue(props['amount_sum'])\n deposit.amount_usd_sum = numberValue(props['amount_usd_sum'])\n }\n}\n\nasync function collectProbeTrace(\n remoteClient: Client,\n options: Required<Pick<TraceFundsOptions, 'seedAddress' | 'network' | 'maxHops' | 'perAddressLimit' | 'minAmountSum'>>,\n): Promise<{ flows: TraceFlow[]; deposits: TraceDeposit[]; sourceMatches: SourceMatch[]; reverseLeads: ReverseLead[] }> {\n const forwardBatch = await callGraphBatch(remoteClient, options.network, [\n ...forwardExchangeQueries(options.seedAddress, Math.max(options.perAddressLimit * 20, 200), options.minAmountSum, options.maxHops),\n ])\n const forwardRows = rowsMatchingMinimumAmount((forwardBatch.facts?.queries ?? [])\n .filter((query) => query.id?.startsWith('forward_exchange_paths_'))\n .flatMap((query) => {\n if (query.ok === false) throw new Error(query.error || `Query failed: ${query.id}`)\n return query.results ?? []\n }), options.minAmountSum)\n const { flows, deposits } = flowsFromForwardRows(forwardRows)\n await hydrateDirectEdgeProps(remoteClient, options.network, flows, deposits)\n const uniqueDepositAddresses = [...new Set(deposits.map((deposit) => deposit.address))]\n\n const sourceMatches: SourceMatch[] = []\n if (uniqueDepositAddresses.length > 0) {\n const backwardBatch = await callGraphBatch(\n remoteClient,\n options.network,\n uniqueDepositAddresses.slice(0, Math.max(1, Math.floor(20 / options.maxHops))).flatMap((address, index) => backwardSourceQueries(`backward_from_deposit_${index + 1}`, address, options.maxHops)),\n )\n for (const query of backwardBatch.facts?.queries ?? []) {\n for (const row of query.results ?? []) {\n const pathAddresses = stringArrayValue(row['addresses']) ?? []\n const pathNodes = Array.isArray(row['path_nodes'])\n ? row['path_nodes'].map((node, index) => nodeMetadataFromValue(node, pathAddresses[index])).filter((node): node is GraphNodeMetadata => Boolean(node))\n : undefined\n const depositAddress = typeof row['deposit_address'] === 'string' ? row['deposit_address'] : pathAddresses[0]\n const sourceExchange = typeof row['source_exchange'] === 'string' ? row['source_exchange'] : pathAddresses[pathAddresses.length - 1]\n if (!depositAddress || !sourceExchange) continue\n const sourceNode = {\n address: sourceExchange,\n labels: stringArrayValue(row['source_display_labels']),\n system_labels: stringArrayValue(row['source_system_labels']) ?? stringArrayValue(row['source_labels']),\n address_type: typeof row['source_address_type'] === 'string' ? row['source_address_type'] : undefined,\n address_subtypes: stringArrayValue(row['source_address_subtypes']),\n }\n sourceMatches.push({\n deposit_address: depositAddress,\n source_exchange: sourceExchange,\n source_labels: stringArrayValue(row['source_labels']),\n sourceNode,\n hops: numberValue(row['hops']) ?? Math.max(pathAddresses.length - 1, 0),\n path: pathAddresses,\n pathNodes,\n })\n }\n }\n }\n\n const reverseLeads: ReverseLead[] = []\n if (uniqueDepositAddresses.length > 0) {\n const reverseBatch = await callGraphBatch(remoteClient, options.network, [reverseLeadsQuery(uniqueDepositAddresses)])\n for (const row of resultsFor(reverseBatch, 'reverse_1hop')) {\n const address = typeof row['address'] === 'string' ? row['address'] : ''\n const depositAddress = typeof row['deposit_address'] === 'string' ? row['deposit_address'] : ''\n if (!address || !depositAddress) continue\n const labels = stringArrayValue(row['display_labels']) ?? stringArrayValue(row['labels']) ?? []\n const degreeIn = numberValue(row['degree_in']) ?? 0\n const degreeOut = numberValue(row['degree_out']) ?? 0\n const totalVolume = numberValue(row['total_volume_usd']) ?? 0\n const reason = labels.length > 0 ? 'labeled_entity' : degreeIn > 50 ? 'fan_in_hub' : degreeOut > 50 ? 'fan_out_hub' : totalVolume > 100000 ? 'high_volume_sender' : ''\n if (!reason) continue\n reverseLeads.push({\n address,\n labels,\n node: {\n address,\n labels,\n system_labels: stringArrayValue(row['system_labels']),\n address_type: typeof row['address_type'] === 'string' ? row['address_type'] : undefined,\n address_subtypes: stringArrayValue(row['address_subtypes']),\n },\n degree_in: degreeIn,\n degree_out: degreeOut,\n total_volume_usd: totalVolume,\n deposit_address: depositAddress,\n amount_usd: numberValue(row['amount_usd']),\n reason,\n })\n }\n }\n\n return { flows, deposits, sourceMatches, reverseLeads }\n}\n\nfunction buildAliases(seedAddress: string, deposits: TraceDeposit[], sourceMatches: SourceMatch[], reverseLeads: ReverseLead[]): AliasTracker {\n const aliases = new AliasTracker()\n aliases.assign(seedAddress, 'V')\n for (const deposit of deposits) {\n for (const address of deposit.path.slice(1, -2)) aliases.assign(address, 'I')\n aliases.assign(deposit.address, 'D')\n aliases.assign(deposit.exchangeAddress, 'E')\n }\n for (const source of sourceMatches) {\n aliases.assign(source.source_exchange, 'X')\n for (const address of source.path.slice(1, -1)) aliases.assign(address, 'I')\n }\n for (const lead of reverseLeads) aliases.assign(lead.address, 'L')\n return aliases\n}\n\nfunction buildGraph(seedAddress: string, network: string, flows: TraceFlow[], deposits: TraceDeposit[], sourceMatches: SourceMatch[], reverseLeads: ReverseLead[]): Record<string, unknown> {\n type NodeAccumulator = {\n in: number\n out: number\n labels: string[]\n systemLabels: string[]\n addressType?: string\n addressSubtypes: string[]\n roles: Set<string>\n }\n\n const totals = new Map<string, NodeAccumulator>()\n const ensure = (address: string) => {\n if (!totals.has(address)) {\n totals.set(address, {\n in: 0,\n out: 0,\n labels: [],\n systemLabels: [],\n addressSubtypes: [],\n roles: new Set(address === seedAddress ? ['seed'] : []),\n })\n }\n return totals.get(address)!\n }\n const mergeNode = (address: string, metadata?: GraphNodeMetadata, role?: string, systemLabelsFallback?: string[]) => {\n const node = ensure(address)\n node.labels = uniqueStrings([...node.labels, ...(metadata?.labels ?? [])])\n node.systemLabels = uniqueStrings([...node.systemLabels, ...(metadata?.system_labels ?? []), ...(systemLabelsFallback ?? [])])\n if (metadata?.address_type) node.addressType = metadata.address_type\n node.addressSubtypes = uniqueStrings([...node.addressSubtypes, ...(metadata?.address_subtypes ?? [])])\n if (role) node.roles.add(role)\n return node\n }\n\n for (const flow of flows) {\n const src = mergeNode(flow.src, flow.src_node, undefined, flow.src_labels)\n src.out += flow.amount_usd_sum ?? flow.amount_sum\n const dst = mergeNode(flow.dst, flow.dst_node, undefined, flow.dst_labels)\n dst.in += flow.amount_usd_sum ?? flow.amount_sum\n if (isExchangeFlow(flow)) dst.roles.add('exchange')\n }\n for (const deposit of deposits) {\n for (const node of deposit.pathNodes ?? []) mergeNode(node.address, node)\n mergeNode(deposit.address, deposit.pathNodes?.find((node) => node.address === deposit.address), 'deposit_candidate')\n mergeNode(deposit.exchangeAddress, deposit.exchangeNode, 'exchange', deposit.exchangeLabels)\n }\n for (const source of sourceMatches) {\n for (const node of source.pathNodes ?? []) mergeNode(node.address, node)\n mergeNode(source.source_exchange, source.sourceNode, 'exchange', source.source_labels)\n }\n for (const lead of reverseLeads) {\n mergeNode(lead.address, lead.node ?? { address: lead.address, labels: lead.labels }, 'lead')\n const deposit = ensure(lead.deposit_address)\n deposit.in += lead.amount_usd ?? 0\n }\n const sourceMatchEdges = sourceMatches.flatMap((source) => {\n const path = source.path.length >= 2 ? source.path : [source.deposit_address, source.source_exchange]\n const edges: Array<Record<string, unknown>> = []\n for (let index = path.length - 1; index > 0; index -= 1) {\n edges.push({\n source: path[index],\n target: path[index - 1],\n edge_type: 'flows_to',\n usd_amount: 0,\n amount_sum: 0,\n tx_count: 0,\n direction: 'traceback',\n })\n }\n return edges\n })\n\n return normalizeGraphPayload({\n schema: 'chain-insights.graph.v1',\n nodes: [...totals.entries()].map(([address, data]) => ({\n id: address,\n address,\n node_type: 'address',\n labels: uniqueStrings(data.labels),\n ...(data.systemLabels.length > 0 ? { system_labels: uniqueStrings(data.systemLabels) } : {}),\n ...(data.addressType ? { address_type: data.addressType } : {}),\n ...(data.addressSubtypes.length > 0 ? { address_subtypes: uniqueStrings(data.addressSubtypes) } : {}),\n ...(data.roles.size > 0 ? { roles: [...data.roles] } : {}),\n flow_in_usd: data.in,\n flow_out_usd: data.out,\n })),\n edges: [\n ...flows.map((flow) => ({\n source: flow.src,\n target: flow.dst,\n edge_type: 'flows_to',\n usd_amount: flow.amount_usd_sum ?? flow.amount_sum,\n amount_sum: flow.amount_sum,\n tx_count: flow.tx_count ?? 0,\n first_tx_id: flow.first_tx_id,\n last_tx_id: flow.last_tx_id,\n terminal_exchange: flow.terminal_exchange,\n })),\n ...sourceMatchEdges,\n ...reverseLeads.map((lead) => ({\n source: lead.address,\n target: lead.deposit_address,\n edge_type: 'flows_to',\n usd_amount: lead.amount_usd ?? 0,\n amount_sum: lead.amount_usd ?? 0,\n tx_count: 0,\n direction: 'reverse_1hop_lead',\n })),\n ],\n flows,\n deposits,\n source_matches: sourceMatches,\n reverse_leads: reverseLeads,\n edge_anchors: [],\n metadata: {\n seed_address: seedAddress,\n network,\n generated_at: new Date().toISOString(),\n },\n })\n}\n\nfunction buildMarkdownReport(seedAddress: string, network: string, flows: TraceFlow[], deposits: TraceDeposit[], sourceMatches: SourceMatch[], reverseLeads: ReverseLead[], aliases: AliasTracker, graphPath: string, schemaPath: string): string {\n const lines = [\n `# Trace Funds: ${seedAddress}`,\n '',\n `Network: \\`${network}\\``,\n `Schema: \\`${schemaPath}\\``,\n `Graph: \\`${graphPath}\\``,\n '',\n '## Probe Summary',\n '',\n `- Exchange endpoint(s): ${[...new Set(deposits.map((deposit) => aliases.alias(deposit.exchangeAddress) ?? deposit.exchangeAddress))].join(', ') || 'none'}`,\n `- Deposit candidate(s): ${[...new Set(deposits.map((deposit) => aliases.alias(deposit.address) ?? deposit.address))].join(', ') || 'none'}`,\n `- Traceback source exchange path(s): ${sourceMatches.length}`,\n `- Reverse 1-hop lead(s): ${reverseLeads.length}`,\n '',\n '## Flow Table',\n '',\n '| Hop | Source | Destination | amount_sum | amount_usd_sum | tx_count | first_tx_id | terminal_exchange |',\n '|---:|---|---|---:|---:|---:|---|---|',\n ...flows.map((flow) => [\n `| ${flow.hop}`,\n `\\`${flow.src}\\``,\n `\\`${flow.dst}\\``,\n flow.amount_sum,\n flow.amount_usd_sum ?? '',\n flow.tx_count ?? '',\n flow.first_tx_id ? `\\`${flow.first_tx_id}\\`` : '',\n flow.terminal_exchange ? 'yes' : 'no',\n ].join(' | ') + ' |'),\n '',\n '## Mermaid',\n '',\n '```mermaid',\n 'flowchart LR',\n ...flows.map((flow, index) =>\n ` n${index}[\"${flow.src.slice(0, 8)}...\"] -->|\"amount_sum ${flow.amount_sum}${flow.terminal_exchange ? '; exchange endpoint' : ''}\"| m${index}[\"${flow.dst.slice(0, 8)}...\"]`\n ),\n '```',\n ]\n return lines.join('\\n') + '\\n'\n}\n\nfunction probeEvidence(seedAddress: string, network: string, schemaPath: string, aliases: AliasTracker, flows: TraceFlow[], deposits: TraceDeposit[], sourceMatches: SourceMatch[], reverseLeads: ReverseLead[]): Record<string, unknown> {\n return {\n schema: 'chain-insights.probe_evidence.v1',\n source: 'track_funds',\n network,\n seed_address: seedAddress,\n schema_ref: schemaPath,\n address_map: aliases.addressMap(),\n fund_flows: [\n ...deposits.map((deposit, index) => ({\n id: `F${index + 1}`,\n type: 'deposit',\n path: deposit.path.map((address) => aliases.alias(address) ?? address),\n deposit: aliases.alias(deposit.address),\n exchange: aliases.alias(deposit.exchangeAddress),\n amount_sum: deposit.amount_sum,\n amount_usd_sum: deposit.amount_usd_sum,\n hops: deposit.hops,\n })),\n ...sourceMatches.map((source, index) => ({\n id: `S${index + 1}`,\n type: 'source',\n path: [...source.path].reverse().map((address) => aliases.alias(address) ?? address),\n source_exchange: aliases.alias(source.source_exchange),\n deposit: aliases.alias(source.deposit_address),\n hops: source.hops,\n })),\n ],\n reverse_leads: reverseLeads.map((lead) => ({\n alias: aliases.alias(lead.address),\n address: lead.address,\n reason: lead.reason,\n labels: lead.labels,\n deposit: aliases.alias(lead.deposit_address),\n amount_usd: lead.amount_usd,\n })),\n outgoing_flows: flows.map((flow) => ({\n hop: flow.hop,\n src: aliases.alias(flow.src) ?? flow.src,\n dst: aliases.alias(flow.dst) ?? flow.dst,\n amount_sum: flow.amount_sum,\n amount_usd_sum: flow.amount_usd_sum,\n tx_count: flow.tx_count,\n first_tx_id: flow.first_tx_id,\n last_tx_id: flow.last_tx_id,\n terminal_exchange: flow.terminal_exchange,\n })),\n }\n}\n\nfunction tableCsv(flows: TraceFlow[]): string {\n const rows = ['hop,src,dst,amount_sum,amount_usd_sum,tx_count,first_tx_id,last_tx_id,terminal_exchange']\n for (const flow of flows) {\n rows.push([\n flow.hop,\n flow.src,\n flow.dst,\n flow.amount_sum,\n flow.amount_usd_sum ?? '',\n flow.tx_count ?? '',\n flow.first_tx_id ?? '',\n flow.last_tx_id ?? '',\n flow.terminal_exchange ? 'true' : 'false',\n ].map((value) => JSON.stringify(String(value))).join(','))\n }\n return rows.join('\\n') + '\\n'\n}\n\nfunction htmlEscape(value: unknown): string {\n return String(value ?? '')\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll(\"'\", '&#39;')\n}\n\nfunction buildTableHtml(seedAddress: string, network: string, flows: TraceFlow[], deposits: TraceDeposit[], sourceMatches: SourceMatch[], reverseLeads: ReverseLead[]): string {\n const headers = [\n 'hop',\n 'src',\n 'dst',\n 'amount_sum',\n 'amount_usd_sum',\n 'tx_count',\n 'first_tx_id',\n 'last_tx_id',\n 'terminal_exchange_display',\n ] as const\n const headerLabels: Record<typeof headers[number], string> = {\n hop: 'Hop',\n src: 'Source',\n dst: 'Destination',\n amount_sum: 'amount_sum',\n amount_usd_sum: 'amount_usd_sum',\n tx_count: 'tx_count',\n first_tx_id: 'first_tx_id',\n last_tx_id: 'last_tx_id',\n terminal_exchange_display: 'terminal_exchange',\n }\n const rows = flows.map((flow) => {\n const values: Record<string, unknown> = {\n ...flow,\n terminal_exchange_display: flow.terminal_exchange ? 'yes' : 'no',\n }\n return `<tr>${headers.map((header) => `<td>${htmlEscape(values[header])}</td>`).join('')}</tr>`\n }).join('\\n')\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Trace Funds Table - ${htmlEscape(seedAddress)}</title>\n<style>\n :root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: #0b0d12; color: #f4f2ea; }\n body { margin: 0; background: #0b0d12; color: #f4f2ea; }\n main { padding: 24px; }\n h1 { font-size: 20px; margin: 0 0 8px; font-weight: 650; }\n .meta { display: grid; gap: 6px; margin: 0 0 20px; color: rgba(244,242,234,.72); font-size: 13px; }\n .summary { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 20px; }\n .pill { border: 1px solid rgba(242,221,166,.25); background: rgba(242,221,166,.08); border-radius: 999px; padding: 6px 10px; font-size: 12px; color: #f2dda6; }\n .table-wrap { overflow: auto; border: 1px solid rgba(255,255,255,.1); border-radius: 8px; background: #10131b; }\n table { border-collapse: collapse; width: 100%; min-width: 1180px; font-size: 12px; }\n th, td { border-bottom: 1px solid rgba(255,255,255,.08); padding: 8px 10px; text-align: left; vertical-align: top; }\n th { position: sticky; top: 0; background: #161a24; color: #f2dda6; font-weight: 600; z-index: 1; }\n td { color: rgba(244,242,234,.86); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n tr:hover td { background: rgba(242,221,166,.045); }\n</style>\n</head>\n<body>\n<main>\n <h1>Trace Funds Table</h1>\n <div class=\"meta\">\n <div>Network: <strong>${htmlEscape(network)}</strong></div>\n <div>Seed: <strong>${htmlEscape(seedAddress)}</strong></div>\n <div>Generated: <strong>${htmlEscape(new Date().toISOString())}</strong></div>\n </div>\n <div class=\"summary\">\n <span class=\"pill\">${flows.length} FLOWS_TO edges</span>\n <span class=\"pill\">${deposits.length} deposit candidates</span>\n <span class=\"pill\">${sourceMatches.length} traceback source paths</span>\n <span class=\"pill\">${reverseLeads.length} reverse 1-hop leads</span>\n </div>\n <div class=\"table-wrap\">\n <table>\n <thead><tr>${headers.map((header) => `<th>${htmlEscape(headerLabels[header])}</th>`).join('')}</tr></thead>\n <tbody>\n${rows}\n </tbody>\n </table>\n </div>\n</main>\n</body>\n</html>\n`\n}\n\nfunction summarize(seedAddress: string, network: string, flows: TraceFlow[], sourceMatches: SourceMatch[], reverseLeads: ReverseLead[], aliases: AliasTracker, files: TraceFundsResult['files'], continuation: TraceFundsResult['continuation']): string {\n const totalAmount = flows.reduce((sum, flow) => sum + flow.amount_sum, 0)\n const byHop = new Map<number, number>()\n for (const flow of flows) byHop.set(flow.hop, (byHop.get(flow.hop) ?? 0) + 1)\n const depositCount = continuation.depositAddresses.length\n const exchangeCount = continuation.exchangeAddresses.length\n return [\n `Trace complete for ${network}:${seedAddress}`,\n '',\n `Facts: ${flows.length} FLOWS_TO edge(s), sum of traced edge amount_sum values ${Number(totalAmount.toFixed(8))}.`,\n `By hop: ${[...byHop.entries()].map(([hop, count]) => `hop ${hop}: ${count}`).join(', ') || 'none'}.`,\n `Exchange endpoints reached: ${exchangeCount}. Deposit candidate address(es): ${depositCount}.`,\n `Traceback source path(s): ${sourceMatches.length}. Reverse 1-hop lead(s): ${reverseLeads.length}.`,\n '',\n 'Files written:',\n `- schema: ${files.schema}`,\n `- compact evidence JSON: ${files.compactEvidence}`,\n `- graph JSON: ${files.graph}`,\n `- graph HTML: ${files.graphHtml}`,\n `- table CSV: ${files.table}`,\n `- table HTML: ${files.tableHtml}`,\n `- report: ${files.report}`,\n '',\n `Continuation hint: ${continuation.hint}`,\n continuation.depositAddresses.length > 0\n ? `Deposit candidates: ${continuation.depositAddresses.map((address) => aliases.alias(address) ?? address).join(', ')}`\n : 'Deposit candidates: none reached in this bounded trace.',\n continuation.nextHopAddresses.length > 0\n ? `Next addresses: ${continuation.nextHopAddresses.join(', ')}`\n : 'Next addresses: none found in this trace.',\n ].join('\\n')\n}\n\nexport async function runFundFlowProbe(\n remoteClient: Client,\n _config: Pick<InvestigatorConfig, 'dataDir' | 'serverPort'>,\n options: TraceFundsOptions,\n): Promise<TraceFundsResult> {\n const seedAddress = options.seedAddress.trim()\n const network = options.network.trim()\n if (!seedAddress) throw new Error('seed_address is required')\n if (!network) throw new Error('network is required')\n\n const maxHops = clampInt(options.maxHops, 3, 1, 5)\n const perAddressLimit = clampInt(options.perAddressLimit, 5, 1, 10)\n const minAmountSum = Math.max(0, options.minAmountSum ?? 0)\n const paths = workspaceOutputPaths()\n await ensureDirs(paths)\n\n const schemaResult = await loadOrCaptureTopologySchema(remoteClient, paths, network)\n const { flows, deposits, sourceMatches, reverseLeads } = await collectProbeTrace(remoteClient, { seedAddress, network, maxHops, perAddressLimit, minAmountSum })\n const aliases = buildAliases(seedAddress, deposits, sourceMatches, reverseLeads)\n const slug = `${new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}Z$/, 'Z')}_${sanitizeSegment(seedAddress.slice(0, 16))}`\n const compact = probeEvidence(seedAddress, network, schemaResult.filePath, aliases, flows, deposits, sourceMatches, reverseLeads)\n const graph = buildGraph(seedAddress, network, flows, deposits, sourceMatches, reverseLeads)\n\n const compactPath = path.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`)\n const graphPath = path.join(paths.reportGraphsRoot, `${slug}.graph.json`)\n const graphHtmlPath = path.join(paths.reportsRoot, `${slug}.graph.html`)\n const tablePath = path.join(paths.reportTablesRoot, `${slug}.flows.csv`)\n const tableHtmlPath = path.join(paths.reportsRoot, `${slug}.table.html`)\n const reportPath = path.join(paths.reportsRoot, `${slug}.trace-report.md`)\n const { generateInlineGraphHtml } = await import('../viz/html-generator.js')\n\n await writeFile(compactPath, JSON.stringify(compact, null, 2) + '\\n', { mode: 0o600 })\n await writeFile(graphPath, JSON.stringify(graph, null, 2) + '\\n', { mode: 0o600 })\n await writeFile(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 0o600 })\n await writeFile(tablePath, tableCsv(flows), { mode: 0o600 })\n await writeFile(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 0o600 })\n await writeFile(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 0o600 })\n\n if (options.caseId) {\n const { EvidenceStore } = await import('../cases/index.js')\n await EvidenceStore.append(options.caseId, {\n source: 'track_funds',\n queryParams: `network=${network} seed_address=${seedAddress} max_hops=${maxHops} per_address_limit=${perAddressLimit} min_amount_sum=${minAmountSum}`,\n content: JSON.stringify({\n schema: 'chain-insights.evidence_pointer.v1',\n source: 'track_funds',\n network,\n seed_address: seedAddress,\n address_map: aliases.compactAddressMap(),\n files: {\n compactEvidence: compactPath,\n graph: graphPath,\n graphHtml: graphHtmlPath,\n table: tablePath,\n tableHtml: tableHtmlPath,\n report: reportPath,\n },\n facts: {\n flow_count: flows.length,\n deposit_candidates: [...new Set(deposits.map((deposit) => aliases.alias(deposit.address) ?? deposit.address))],\n exchange_endpoints: [...new Set(deposits.map((deposit) => aliases.alias(deposit.exchangeAddress) ?? deposit.exchangeAddress))],\n traceback_source_paths: sourceMatches.length,\n reverse_leads: reverseLeads.length,\n },\n }, null, 2),\n })\n }\n\n const depositAddresses = [...new Set(deposits.map((deposit) => deposit.address))]\n const exchangeAddresses = [...new Set(deposits.map((deposit) => deposit.exchangeAddress))]\n const leaves: string[] = []\n const continuation = {\n nextHopAddresses: leaves.slice(0, 20),\n depositAddresses,\n exchangeAddresses,\n hint: depositAddresses.length > 0\n ? `Found ${depositAddresses.length} deposit candidate(s), defined as the address one hop before an exchange endpoint. Do not continue through exchange nodes.`\n : leaves.length > 0\n ? `No exchange endpoint reached yet. Continue from ${leaves.length} non-exchange leaf destination(s) with the same tool, or raise the result budget if the current trace stopped early.`\n : 'No exchange endpoint or non-exchange leaf destinations found; inspect graph/report files or lower min_amount_sum.',\n }\n const files = {\n schema: schemaResult.filePath,\n compactEvidence: compactPath,\n graph: graphPath,\n graphHtml: graphHtmlPath,\n table: tablePath,\n tableHtml: tableHtmlPath,\n report: reportPath,\n }\n\n return {\n summaryText: summarize(seedAddress, network, flows, sourceMatches, reverseLeads, aliases, files, continuation),\n compactEvidence: compact,\n graphData: graph,\n files,\n continuation,\n addressMap: aliases.compactAddressMap(),\n }\n}\n","import { mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport type { ContentBlock } from '@modelcontextprotocol/sdk/types.js'\nimport type { InvestigatorConfig } from '../config/schema.js'\nimport { normalizeGraphPayload } from '../viz/graph-normalizer.js'\nimport { workspaceOutputPaths, type WorkspaceOutputPaths } from '../workspace/output-root.js'\n\nexport type ScamTopologyScope = 'history' | 'incident' | 'compare'\nexport type ScamTopologyGraphScope = 'history' | 'incident'\nexport type ScamTopologyScopeMembership = 'history_only' | 'incident_only' | 'overlap'\nexport type ScamTopologyActivityPolicy = 'global_incident' | 'node_relative'\nexport type ScamTopologyActivityPolicyMode = 'node_relative_only' | 'global_incident_only'\n\nexport interface ScamTopologyOptions {\n network: string\n victimAddress: string\n incidentTimestampMs: number\n maxHops?: number\n activityPolicyMode?: ScamTopologyActivityPolicyMode\n caseId?: string\n}\n\nexport type ScamTopologySeedRole = 'victim' | 'scammer'\n\nexport interface ScamTopologyLabelCandidate {\n address: string\n label: string\n address_type: 'SCAM'\n address_subtype: 'scam_seed' | 'laundering_intermediate' | 'exchange_deposit_candidate'\n trust_level: 'blacklisted' | 'candidate'\n risk_level: 'critical' | 'high'\n confidence_score: number\n promotion_status: 'promote_confirmed' | 'review_required'\n source: 'scam_topology'\n evidence: Array<Record<string, unknown>>\n}\n\nexport interface ScamTopologyScamLabel {\n address: string\n scam: true\n confidence: number\n source: 'scam_topology'\n source_victim_address: string\n source_incident_timestamp_ms: number\n}\n\nexport interface ScamTopologyCaseRole {\n address: string\n role:\n | ScamTopologySeedRole\n | 'laundering_intermediate'\n | 'exchange_deposit_candidate'\n | 'exchange_endpoint'\n | 'context_boundary'\n | 'continue_from_address'\n seed_address?: string\n seed_role?: ScamTopologySeedRole\n}\n\nexport type ScamTopologyEdgeRelation =\n | 'seed_outflow'\n | 'traversal_edge'\n | 'convergence_edge'\n | 'deposit_cluster_inflow'\n | 'terminal_exchange'\n | 'context_boundary'\n\nexport interface ScamTopologyTopologyEdge {\n relation: ScamTopologyEdgeRelation\n src: string\n dst: string\n hop: number\n graph_scope: ScamTopologyGraphScope\n topology_graph: 'archive_topology' | 'live_topology'\n activity_policy?: ScamTopologyActivityPolicy\n wave_index?: number\n expands_frontier?: boolean\n converges_to_seen_node?: boolean\n activity_threshold_timestamp?: number\n src_arrival_timestamp?: number\n dst_arrival_timestamp?: number\n scope_membership?: ScamTopologyScopeMembership\n graph_scopes?: ScamTopologyGraphScope[]\n seed_address?: string\n seed_role?: ScamTopologySeedRole\n amount_sum?: number\n amount_usd_sum?: number\n tx_count?: number\n first_seen_timestamp?: number\n last_seen_timestamp?: number\n first_tx_id?: string\n last_tx_id?: string\n src_labels: string[]\n dst_labels: string[]\n src_is_exchange: boolean\n dst_is_exchange: boolean\n}\n\nexport interface ScamTopologyInfrastructureFlow {\n relation: never\n src: string\n dst: string\n}\n\nexport interface ScamTopologyResult {\n summaryText: string\n structuredContent: {\n schema: 'chain-insights.result.v1'\n tool: 'scam_topology'\n facts: {\n network: string\n victim_address: string\n incident_timestamp_ms: number\n topology_graphs: Array<'archive_topology' | 'live_topology'>\n primary_activity_policy: ScamTopologyActivityPolicy\n activity_policy_mode: ScamTopologyActivityPolicyMode\n topology_edges: ScamTopologyTopologyEdge[]\n intermediaries: string[]\n terminal_points: Array<Record<string, unknown>>\n exchange_deposits: Array<Record<string, unknown>>\n investigation_hints: Array<Record<string, unknown>>\n scam_labels: ScamTopologyScamLabel[]\n label_candidates: ScamTopologyLabelCandidate[]\n case_roles: ScamTopologyCaseRole[]\n safety_decisions: Array<Record<string, unknown>>\n infrastructure_anchors: string[]\n infrastructure_flows: ScamTopologyInfrastructureFlow[]\n runs: Array<Record<string, unknown>>\n }\n hint: string\n }\n graphData: Record<string, unknown>\n}\n\ntype RemoteToolResult = {\n content?: ContentBlock[]\n isError?: boolean\n}\n\ntype ParsedGraphBatch = {\n facts?: {\n queries?: Array<{\n id?: string\n ok?: boolean\n results?: Array<Record<string, unknown>>\n error?: string\n }>\n }\n}\n\ntype Seed = {\n address: string\n role: ScamTopologySeedRole\n}\n\ntype FrontierEntry = {\n address: string\n seedAddress: string\n seedRole: ScamTopologySeedRole\n arrivalTimestamp?: number\n waveIndex: number\n}\n\ntype TraversalRun = {\n graphScope: ScamTopologyGraphScope\n topologyGraph: 'archive_topology' | 'live_topology'\n activityPolicy: ScamTopologyActivityPolicy\n edges: ScamTopologyTopologyEdge[]\n skippedQueryErrors: Array<Record<string, unknown>>\n}\n\nconst SCAM_TOPOLOGY_GRAPH_QUERY_TIMEOUT_SECONDS = 15\nconst SCAM_TOPOLOGY_GRAPH_BATCH_REQUEST_TIMEOUT_MS = 15 * 60 * 1000\nconst SCAM_TOPOLOGY_MAX_BATCH_QUERIES = 20\nconst SCAM_TOPOLOGY_ARCHIVE_BATCH_QUERIES = 1\nconst SCAM_TOPOLOGY_DEPOSIT_CLUSTER_LIMIT = 200\nconst SCAM_TOPOLOGY_DEFAULT_MAX_HOPS = 16\nconst SCAM_TOPOLOGY_MAX_HOPS = 64\nconst SCAM_TOPOLOGY_FRONTIER_LIMIT = 10\nconst SCAM_TOPOLOGY_MAX_FRONTIER_SOURCES_PER_HOP = 50\n\nfunction parseAddressList(value: string | string[] | undefined): string[] {\n const raw = Array.isArray(value) ? value.join(',') : value ?? ''\n return [...new Set(raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean))]\n}\n\nfunction stringArray(value: unknown): string[] {\n if (Array.isArray(value)) return value.map(String).map((entry) => entry.trim()).filter(Boolean)\n if (typeof value === 'string' && value.trim()) {\n const trimmed = value.trim()\n if (trimmed.startsWith('[')) {\n try {\n const parsed = JSON.parse(trimmed) as unknown\n if (Array.isArray(parsed)) return parsed.map(String).map((entry) => entry.trim()).filter(Boolean)\n } catch {\n // Fall back to comma splitting below.\n }\n }\n return trimmed.split(',').map((entry) => entry.trim()).filter(Boolean)\n }\n return []\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim() : undefined\n}\n\nfunction numberValue(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return undefined\n}\n\nfunction clampInt(value: number | undefined, fallback: number, min: number, max: number): number {\n if (!Number.isFinite(value)) return fallback\n return Math.max(min, Math.min(max, Math.trunc(value as number)))\n}\n\nfunction chunks<T>(values: T[], size: number): T[][] {\n const result: T[][] = []\n for (let index = 0; index < values.length; index += size) {\n result.push(values.slice(index, index + size))\n }\n return result\n}\n\nfunction sanitizeSegment(value: string): string {\n const sanitized = value.toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/(^-|-$)/g, '').slice(0, 80)\n return sanitized || 'scam-topology'\n}\n\nasync function ensureScamTopologyDirs(paths: WorkspaceOutputPaths): Promise<void> {\n await mkdir(paths.reportsRoot, { recursive: true, mode: 0o700 })\n await mkdir(paths.reportGraphsRoot, { recursive: true, mode: 0o700 })\n await mkdir(paths.reportTablesRoot, { recursive: true, mode: 0o700 })\n}\n\nfunction escapeCypherString(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')\n}\n\nfunction textFromToolResult(result: RemoteToolResult): string {\n return (result.content ?? [])\n .filter((item): item is Extract<ContentBlock, { type: 'text' }> => item.type === 'text')\n .map((item) => item.text)\n .join('\\n')\n}\n\nfunction parseGraphBatchResult(result: RemoteToolResult): ParsedGraphBatch {\n const text = textFromToolResult(result).trim()\n if (!text) throw new Error('graph_query_batch returned no text content')\n const parsed = JSON.parse(text) as ParsedGraphBatch\n if (!parsed.facts?.queries) throw new Error('graph_query_batch response did not include facts.queries')\n return parsed\n}\n\nasync function callGraphBatch(\n remoteClient: Client,\n network: string,\n queries: Array<{ id: string; query: string }>,\n): Promise<ParsedGraphBatch> {\n const result = await remoteClient.callTool(\n {\n name: 'graph_query_batch',\n arguments: {\n network,\n queries,\n per_query_timeout_seconds: SCAM_TOPOLOGY_GRAPH_QUERY_TIMEOUT_SECONDS,\n },\n },\n undefined,\n {\n timeout: SCAM_TOPOLOGY_GRAPH_BATCH_REQUEST_TIMEOUT_MS,\n maxTotalTimeout: SCAM_TOPOLOGY_GRAPH_BATCH_REQUEST_TIMEOUT_MS,\n },\n ) as RemoteToolResult\n if (result.isError) throw new Error(textFromToolResult(result) || 'graph_query_batch failed')\n return parseGraphBatchResult(result)\n}\n\nfunction graphForScope(graphScope: ScamTopologyGraphScope): 'archive_topology' | 'live_topology' {\n return graphScope === 'history' ? 'archive_topology' : 'live_topology'\n}\n\nfunction isExchangeFlag(value: unknown): boolean {\n if (value === true) return true\n if (value === false || value === null || value === undefined) return false\n if (typeof value === 'string') {\n const normalized = value.trim().toLowerCase()\n return normalized === 'true' || normalized === '1'\n }\n if (typeof value === 'number') return value === 1\n return false\n}\n\nfunction hasExchangeLabel(labels: string[]): boolean {\n return labels.some((label) => label.toLowerCase() === 'exchange' || label.toLowerCase().includes('exchange'))\n}\n\nfunction isExchangeEndpoint(labels: string[], isExchange: unknown, roles: string[]): boolean {\n return isExchangeFlag(isExchange) || hasExchangeLabel(labels) || roles.some((role) => role.toLowerCase().includes('exchange'))\n}\n\nfunction isGenericContextLabel(label: string): boolean {\n const normalized = label.trim().toLowerCase()\n return normalized === 'exchange' ||\n normalized === 'validator' ||\n /^miner subnet \\d+$/.test(normalized) ||\n /^subnet \\d+(?: owner)?$/.test(normalized)\n}\n\nfunction exchangeNamesFromLabels(labels: string[]): string[] {\n return [...new Set(labels\n .map((label) => label.trim().replace(/,\\s*exchange$/i, '').trim())\n .filter((label) => label.length > 0 && !isGenericContextLabel(label)))]\n}\n\nfunction addressPredicate(addresses: string[]): string {\n return addresses\n .map((address) => `src.address = \"${escapeCypherString(address)}\"`)\n .join(' OR ')\n}\n\nfunction traversalProjection(): string {\n return [\n 'src.address AS src',\n 'dst.address AS dst',\n 'src.labels AS src_labels',\n 'dst.labels AS dst_labels',\n 'src.is_exchange AS src_is_exchange',\n 'dst.is_exchange AS dst_is_exchange',\n 'r.amount_sum AS amount_sum',\n 'r.amount_usd_sum AS amount_usd_sum',\n 'r.tx_count AS tx_count',\n 'r.first_seen_timestamp AS first_seen_timestamp',\n 'r.last_seen_timestamp AS last_seen_timestamp',\n 'r.first_tx_id AS first_tx_id',\n 'r.last_tx_id AS last_tx_id',\n ].join(', ')\n}\n\nfunction frontierQuery(\n graphScope: ScamTopologyGraphScope,\n sourceAddress: string,\n hop: number,\n sourceIndex: number | undefined,\n perAddressLimit: number,\n minAmountSum: number | undefined,\n activityThresholdTimestamp: number | undefined,\n): { id: string; query: string } {\n const where = [\n 'src.address <> dst.address',\n ]\n if (minAmountSum !== undefined) where.push(`r.amount_sum >= ${minAmountSum}`)\n if (graphScope === 'incident' && activityThresholdTimestamp !== undefined) {\n where.push(`(r.first_seen_timestamp >= ${activityThresholdTimestamp} OR r.last_seen_timestamp >= ${activityThresholdTimestamp})`)\n }\n\n return {\n id: sourceIndex === undefined ? `${graphScope}_hop_${hop}` : `${graphScope}_hop_${hop}_source_${sourceIndex}`,\n query: [\n `USE ${graphForScope(graphScope)}`,\n `MATCH (src:Address {address: \"${escapeCypherString(sourceAddress)}\"})-[r:FLOWS_TO]->(dst:Address)`,\n `WHERE ${where.join(' AND ')}`,\n `RETURN ${traversalProjection()}`,\n 'ORDER BY r.amount_sum DESC',\n `LIMIT ${perAddressLimit}`,\n ].join(' '),\n }\n}\n\nfunction activityThresholdFor(\n policy: ScamTopologyActivityPolicy,\n incidentTimestampMs: number | undefined,\n entry: FrontierEntry,\n): number | undefined {\n if (policy === 'global_incident') return incidentTimestampMs\n return entry.arrivalTimestamp ?? incidentTimestampMs\n}\n\nfunction edgeArrivalTimestamp(edge: ScamTopologyTopologyEdge, threshold: number | undefined): number | undefined {\n if (threshold === undefined) return edge.first_seen_timestamp ?? edge.last_seen_timestamp\n if (edge.first_seen_timestamp !== undefined && edge.first_seen_timestamp >= threshold) return edge.first_seen_timestamp\n if (edge.last_seen_timestamp !== undefined && edge.last_seen_timestamp >= threshold) return threshold\n return edge.first_seen_timestamp ?? edge.last_seen_timestamp ?? threshold\n}\n\nfunction depositClusterQuery(\n graphScope: ScamTopologyGraphScope,\n depositAddress: string,\n index: number,\n minAmountSum: number | undefined,\n): { id: string; query: string } {\n const where = [\n 'src.address <> dst.address',\n 'src.is_exchange IS NULL',\n ]\n if (minAmountSum !== undefined) where.push(`r.amount_sum >= ${minAmountSum}`)\n\n return {\n id: `${graphScope}_deposit_cluster_${index}`,\n query: [\n `USE ${graphForScope(graphScope)}`,\n `MATCH (src:Address)-[r:FLOWS_TO]->(dst:Address {address: \"${escapeCypherString(depositAddress)}\"})`,\n `WHERE ${where.join(' AND ')}`,\n `RETURN ${traversalProjection()}`,\n 'ORDER BY r.amount_sum DESC',\n `LIMIT ${SCAM_TOPOLOGY_DEPOSIT_CLUSTER_LIMIT}`,\n ].join(' '),\n }\n}\n\nfunction edgeFromRow(\n row: Record<string, unknown>,\n graphScope: ScamTopologyGraphScope,\n hop: number,\n context: FrontierEntry,\n): ScamTopologyTopologyEdge | null {\n const src = stringValue(row['src']) ?? stringValue(row['from_address'])\n const dst = stringValue(row['dst']) ?? stringValue(row['to_address'])\n if (!src || !dst || src === dst) return null\n\n const srcLabels = stringArray(row['src_labels'])\n const dstLabels = stringArray(row['dst_labels'])\n const srcRoles = stringArray(row['src_roles'])\n const dstRoles = stringArray(row['dst_roles'])\n const srcIsExchange = isExchangeEndpoint(srcLabels, row['src_is_exchange'], srcRoles)\n const dstIsExchange = isExchangeEndpoint(dstLabels, row['dst_is_exchange'], dstRoles)\n const genericLabeledBoundary = dstLabels.length > 0 && !dstIsExchange\n const relation: ScamTopologyEdgeRelation = dstIsExchange\n ? 'terminal_exchange'\n : genericLabeledBoundary\n ? 'context_boundary'\n : hop === 1\n ? 'seed_outflow'\n : 'traversal_edge'\n\n return {\n relation,\n src,\n dst,\n hop,\n graph_scope: graphScope,\n topology_graph: graphForScope(graphScope),\n seed_address: context.seedAddress,\n seed_role: context.seedRole,\n amount_sum: numberValue(row['amount_sum']),\n amount_usd_sum: numberValue(row['amount_usd_sum']),\n tx_count: numberValue(row['tx_count']),\n first_seen_timestamp: numberValue(row['first_seen_timestamp']),\n last_seen_timestamp: numberValue(row['last_seen_timestamp']),\n first_tx_id: stringValue(row['first_tx_id']),\n last_tx_id: stringValue(row['last_tx_id']),\n src_labels: srcLabels,\n dst_labels: dstLabels,\n src_is_exchange: srcIsExchange,\n dst_is_exchange: dstIsExchange,\n }\n}\n\nfunction edgeKey(edge: Pick<ScamTopologyTopologyEdge, 'src' | 'dst' | 'graph_scope' | 'seed_address' | 'seed_role'>): string {\n return `${edge.graph_scope}\\u0000${edge.seed_role ?? ''}\\u0000${edge.seed_address ?? ''}\\u0000${edge.src}\\u0000${edge.dst}`\n}\n\nfunction mergedEdgeKey(edge: Pick<ScamTopologyTopologyEdge, 'src' | 'dst' | 'seed_address' | 'seed_role'>): string {\n return `${edge.seed_role ?? ''}\\u0000${edge.seed_address ?? ''}\\u0000${edge.src}\\u0000${edge.dst}`\n}\n\nfunction frontierKey(entry: FrontierEntry): string {\n return `${entry.seedRole}\\u0000${entry.seedAddress}\\u0000${entry.address}`\n}\n\nasync function runDirectedTraversal(\n remoteClient: Client,\n network: string,\n seeds: Seed[],\n graphScope: ScamTopologyGraphScope,\n activityPolicy: ScamTopologyActivityPolicy,\n maxHops: number,\n perAddressLimit: number,\n minAmountSum: number | undefined,\n incidentTimestampMs: number | undefined,\n): Promise<TraversalRun> {\n const edgesByKey = new Map<string, ScamTopologyTopologyEdge>()\n const skippedQueryErrors: Array<Record<string, unknown>> = []\n let frontier: FrontierEntry[] = seeds.map((seed) => ({\n address: seed.address,\n seedAddress: seed.address,\n seedRole: seed.role,\n arrivalTimestamp: incidentTimestampMs,\n waveIndex: 0,\n }))\n const visited = new Set(frontier.map(frontierKey))\n\n for (let hop = 1; hop <= maxHops && frontier.length > 0; hop += 1) {\n const frontierByAddress = new Map<string, FrontierEntry[]>()\n for (const entry of frontier) {\n const entries = frontierByAddress.get(entry.address) ?? []\n entries.push(entry)\n frontierByAddress.set(entry.address, entries)\n }\n const frontierAddresses = [...frontierByAddress.keys()]\n const queries = frontierAddresses.map((address, index) => {\n const entry = frontierByAddress.get(address)?.[0]\n return frontierQuery(\n graphScope,\n address,\n hop,\n frontierAddresses.length === 1 ? undefined : index + 1,\n perAddressLimit,\n minAmountSum,\n entry ? activityThresholdFor(activityPolicy, incidentTimestampMs, entry) : incidentTimestampMs,\n )\n })\n const nextByKey = new Map<string, FrontierEntry>()\n\n const maxBatchQueries = graphScope === 'history'\n ? SCAM_TOPOLOGY_ARCHIVE_BATCH_QUERIES\n : SCAM_TOPOLOGY_MAX_BATCH_QUERIES\n for (const queryChunk of chunks(queries, maxBatchQueries)) {\n let batch: ParsedGraphBatch\n try {\n batch = await callGraphBatch(remoteClient, network, queryChunk)\n } catch (err) {\n if (hop === 1) throw err\n for (const query of queryChunk) {\n skippedQueryErrors.push({\n id: query.id,\n hop,\n graph_scope: graphScope,\n error: (err as Error).message,\n })\n }\n continue\n }\n for (const queryResult of batch.facts?.queries ?? []) {\n if (queryResult.ok === false) {\n if (hop === 1) throw new Error(queryResult.error || `Query failed: ${queryResult.id}`)\n skippedQueryErrors.push({\n id: queryResult.id,\n hop,\n graph_scope: graphScope,\n error: queryResult.error || `Query failed: ${queryResult.id}`,\n })\n continue\n }\n for (const row of queryResult.results ?? []) {\n const src = stringValue(row['src']) ?? stringValue(row['from_address'])\n if (!src) continue\n const contexts = frontierByAddress.get(src) ?? []\n for (const context of contexts) {\n const baseEdge = edgeFromRow(row, graphScope, hop, context)\n if (!baseEdge || edgesByKey.has(edgeKey(baseEdge))) continue\n const threshold = activityThresholdFor(activityPolicy, incidentTimestampMs, context)\n const targetEntry: FrontierEntry = {\n address: baseEdge.dst,\n seedAddress: context.seedAddress,\n seedRole: context.seedRole,\n arrivalTimestamp: edgeArrivalTimestamp(baseEdge, threshold),\n waveIndex: hop,\n }\n const targetKey = frontierKey(targetEntry)\n const seenBefore = visited.has(targetKey)\n const terminal = baseEdge.relation === 'terminal_exchange' || baseEdge.relation === 'context_boundary'\n const expandsFrontier = !seenBefore && !terminal\n const edge: ScamTopologyTopologyEdge = {\n ...baseEdge,\n relation: seenBefore && baseEdge.relation === 'traversal_edge' ? 'convergence_edge' : baseEdge.relation,\n activity_policy: activityPolicy,\n wave_index: hop,\n expands_frontier: expandsFrontier,\n converges_to_seen_node: seenBefore,\n activity_threshold_timestamp: threshold,\n src_arrival_timestamp: context.arrivalTimestamp,\n dst_arrival_timestamp: targetEntry.arrivalTimestamp,\n }\n edgesByKey.set(edgeKey(edge), edge)\n\n if (!seenBefore) visited.add(targetKey)\n if (!expandsFrontier) continue\n const nextEntry: FrontierEntry = {\n address: edge.dst,\n seedAddress: context.seedAddress,\n seedRole: context.seedRole,\n arrivalTimestamp: edge.dst_arrival_timestamp,\n waveIndex: hop,\n }\n nextByKey.set(targetKey, nextEntry)\n }\n }\n }\n }\n\n frontier = [...nextByKey.values()].slice(0, SCAM_TOPOLOGY_MAX_FRONTIER_SOURCES_PER_HOP)\n }\n\n return {\n graphScope,\n topologyGraph: graphForScope(graphScope),\n activityPolicy,\n edges: [...edgesByKey.values()],\n skippedQueryErrors,\n }\n}\n\nasync function expandDepositClusters(\n remoteClient: Client,\n network: string,\n run: TraversalRun,\n minAmountSum: number | undefined,\n): Promise<TraversalRun> {\n const edgesByKey = new Map(run.edges.map((edge) => [edgeKey(edge), edge]))\n const terminalDepositsByKey = new Map<string, ScamTopologyTopologyEdge>()\n for (const edge of run.edges) {\n if (edge.relation !== 'terminal_exchange') continue\n const key = `${edge.seed_role ?? ''}\\u0000${edge.seed_address ?? ''}\\u0000${edge.src}`\n if (!terminalDepositsByKey.has(key)) terminalDepositsByKey.set(key, edge)\n }\n\n const terminalDeposits = [...terminalDepositsByKey.values()]\n if (terminalDeposits.length === 0) return run\n\n const queries = terminalDeposits.map((edge, index) => depositClusterQuery(\n run.graphScope,\n edge.src,\n index + 1,\n minAmountSum,\n ))\n const maxBatchQueries = run.graphScope === 'history'\n ? SCAM_TOPOLOGY_ARCHIVE_BATCH_QUERIES\n : SCAM_TOPOLOGY_MAX_BATCH_QUERIES\n\n for (const queryChunk of chunks(queries, maxBatchQueries)) {\n let batch: ParsedGraphBatch\n try {\n batch = await callGraphBatch(remoteClient, network, queryChunk)\n } catch (err) {\n for (const query of queryChunk) {\n run.skippedQueryErrors.push({\n id: query.id,\n graph_scope: run.graphScope,\n error: (err as Error).message,\n })\n }\n continue\n }\n for (const queryResult of batch.facts?.queries ?? []) {\n if (queryResult.ok === false) {\n run.skippedQueryErrors.push({\n id: queryResult.id,\n graph_scope: run.graphScope,\n error: queryResult.error || `Query failed: ${queryResult.id}`,\n })\n continue\n }\n const queryIndex = queries.findIndex((query) => query.id === queryResult.id)\n const terminalEdge = terminalDeposits[queryIndex]\n if (!terminalEdge) continue\n const context: FrontierEntry = {\n address: terminalEdge.src,\n seedAddress: terminalEdge.seed_address ?? terminalEdge.src,\n seedRole: terminalEdge.seed_role ?? 'victim',\n arrivalTimestamp: terminalEdge.src_arrival_timestamp ?? terminalEdge.first_seen_timestamp ?? terminalEdge.last_seen_timestamp,\n waveIndex: Math.max(0, terminalEdge.hop - 1),\n }\n for (const row of queryResult.results ?? []) {\n const edge = edgeFromRow(row, run.graphScope, Math.max(1, terminalEdge.hop - 1), context)\n if (!edge || edge.dst !== terminalEdge.src || edge.src === terminalEdge.dst) continue\n const clusterEdge: ScamTopologyTopologyEdge = {\n ...edge,\n relation: 'deposit_cluster_inflow',\n seed_address: terminalEdge.seed_address,\n seed_role: terminalEdge.seed_role,\n activity_policy: run.activityPolicy,\n wave_index: Math.max(1, terminalEdge.hop - 1),\n expands_frontier: false,\n converges_to_seen_node: true,\n activity_threshold_timestamp: terminalEdge.activity_threshold_timestamp,\n src_arrival_timestamp: edge.first_seen_timestamp ?? edge.last_seen_timestamp,\n dst_arrival_timestamp: terminalEdge.dst_arrival_timestamp,\n }\n if (!edgesByKey.has(edgeKey(clusterEdge))) {\n edgesByKey.set(edgeKey(clusterEdge), clusterEdge)\n }\n }\n }\n }\n\n return {\n ...run,\n edges: [...edgesByKey.values()],\n }\n}\n\nfunction mergeCompareRuns(history: TraversalRun, incident: TraversalRun): ScamTopologyTopologyEdge[] {\n const buckets = new Map<string, { history?: ScamTopologyTopologyEdge; incident?: ScamTopologyTopologyEdge }>()\n for (const edge of history.edges) {\n const bucket = buckets.get(mergedEdgeKey(edge)) ?? {}\n bucket.history = edge\n buckets.set(mergedEdgeKey(edge), bucket)\n }\n for (const edge of incident.edges) {\n const bucket = buckets.get(mergedEdgeKey(edge)) ?? {}\n bucket.incident = edge\n buckets.set(mergedEdgeKey(edge), bucket)\n }\n\n return [...buckets.values()].map((bucket) => {\n const base = bucket.incident ?? bucket.history\n if (!base) throw new Error('empty compare bucket')\n const graphScopes: ScamTopologyGraphScope[] = [\n ...(bucket.history ? ['history' as const] : []),\n ...(bucket.incident ? ['incident' as const] : []),\n ]\n const scopeMembership: ScamTopologyScopeMembership = bucket.history && bucket.incident\n ? 'overlap'\n : bucket.history\n ? 'history_only'\n : 'incident_only'\n const relation: ScamTopologyEdgeRelation = bucket.history?.relation === 'terminal_exchange' || bucket.incident?.relation === 'terminal_exchange'\n ? 'terminal_exchange'\n : bucket.history?.relation === 'context_boundary' || bucket.incident?.relation === 'context_boundary'\n ? 'context_boundary'\n : base.relation\n\n return {\n ...base,\n relation,\n scope_membership: scopeMembership,\n graph_scopes: graphScopes,\n }\n })\n}\n\nfunction candidateKey(candidate: Pick<ScamTopologyLabelCandidate, 'address' | 'address_subtype'>): string {\n return `${candidate.address}\\u0000${candidate.address_subtype}`\n}\n\nfunction mergeCandidate(\n candidates: Map<string, ScamTopologyLabelCandidate>,\n candidate: ScamTopologyLabelCandidate,\n): void {\n const key = candidateKey(candidate)\n const existing = candidates.get(key)\n if (!existing) {\n candidates.set(key, candidate)\n return\n }\n\n existing.confidence_score = Math.max(existing.confidence_score, candidate.confidence_score)\n existing.evidence.push(...candidate.evidence)\n if (candidate.promotion_status === 'promote_confirmed') {\n existing.promotion_status = 'promote_confirmed'\n existing.trust_level = 'blacklisted'\n existing.risk_level = 'critical'\n }\n}\n\nfunction labelForSubtype(subtype: ScamTopologyLabelCandidate['address_subtype']): string {\n switch (subtype) {\n case 'scam_seed':\n return 'Known scam seed'\n case 'laundering_intermediate':\n return 'Scam laundering intermediate'\n case 'exchange_deposit_candidate':\n return 'Scam exchange deposit candidate'\n }\n}\n\nfunction makeCandidate(\n address: string,\n subtype: ScamTopologyLabelCandidate['address_subtype'],\n evidence: Record<string, unknown>,\n confidence: number,\n promotionStatus: ScamTopologyLabelCandidate['promotion_status'],\n): ScamTopologyLabelCandidate {\n return {\n address,\n label: labelForSubtype(subtype),\n address_type: 'SCAM',\n address_subtype: subtype,\n trust_level: promotionStatus === 'promote_confirmed' ? 'blacklisted' : 'candidate',\n risk_level: promotionStatus === 'promote_confirmed' ? 'critical' : 'high',\n confidence_score: confidence,\n promotion_status: promotionStatus,\n source: 'scam_topology',\n evidence: [evidence],\n }\n}\n\nfunction addRole(rolesByAddress: Map<string, Set<string>>, address: string, role: string): void {\n if (!address) return\n const roles = rolesByAddress.get(address) ?? new Set<string>()\n roles.add(role)\n rolesByAddress.set(address, roles)\n}\n\nfunction pushCaseRole(caseRoles: ScamTopologyCaseRole[], role: ScamTopologyCaseRole): void {\n if (caseRoles.some((entry) => (\n entry.address === role.address &&\n entry.role === role.role &&\n entry.seed_address === role.seed_address &&\n entry.seed_role === role.seed_role\n ))) return\n caseRoles.push(role)\n}\n\nfunction pushSafetyDecision(safetyDecisions: Array<Record<string, unknown>>, decision: Record<string, unknown>): void {\n if (safetyDecisions.some((entry) => JSON.stringify(entry) === JSON.stringify(decision))) return\n safetyDecisions.push(decision)\n}\n\nfunction edgeEvidence(edge: ScamTopologyTopologyEdge, reason: string): Record<string, unknown> {\n return {\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n graph_scope: edge.graph_scope,\n scope_membership: edge.scope_membership,\n hop: edge.hop,\n src: edge.src,\n dst: edge.dst,\n amount_sum: edge.amount_sum,\n amount_usd_sum: edge.amount_usd_sum,\n tx_count: edge.tx_count,\n ...(edge.relation === 'terminal_exchange' ? {\n deposit_address: edge.src,\n exchange_address: edge.dst,\n exchange_names: exchangeNamesFromLabels(edge.dst_labels),\n exchange_labels: edge.dst_labels,\n } : {}),\n reason,\n }\n}\n\nfunction classifyTopology(\n seeds: Seed[],\n edges: ScamTopologyTopologyEdge[],\n): {\n labelCandidates: ScamTopologyLabelCandidate[]\n caseRoles: ScamTopologyCaseRole[]\n safetyDecisions: Array<Record<string, unknown>>\n rolesByAddress: Map<string, Set<string>>\n intermediaries: string[]\n terminalPoints: Array<Record<string, unknown>>\n exchangeDeposits: Array<Record<string, unknown>>\n investigationHints: Array<Record<string, unknown>>\n} {\n const candidates = new Map<string, ScamTopologyLabelCandidate>()\n const caseRoles: ScamTopologyCaseRole[] = []\n const safetyDecisions: Array<Record<string, unknown>> = []\n const rolesByAddress = new Map<string, Set<string>>()\n const seedAddresses = new Set(seeds.map((seed) => seed.address))\n const victimAddresses = new Set(seeds.filter((seed) => seed.role === 'victim').map((seed) => seed.address))\n const exchangeDepositAddresses = new Set(edges\n .filter((edge) => edge.relation === 'terminal_exchange')\n .map((edge) => edge.src)\n .filter((address) => !seedAddresses.has(address) && !victimAddresses.has(address)))\n const terminalPoints: Array<Record<string, unknown>> = []\n const exchangeDeposits: Array<Record<string, unknown>> = []\n const investigationHints: Array<Record<string, unknown>> = []\n\n for (const seed of seeds) {\n pushCaseRole(caseRoles, { address: seed.address, role: seed.role })\n addRole(rolesByAddress, seed.address, seed.role)\n if (seed.role === 'victim') {\n pushSafetyDecision(safetyDecisions, {\n address: seed.address,\n decision: 'do_not_label_victim_seed',\n reason: 'Victim/source addresses are protected case roles, not risky actors by default.',\n })\n } else {\n mergeCandidate(candidates, makeCandidate(\n seed.address,\n 'scam_seed',\n {\n seed_address: seed.address,\n seed_role: seed.role,\n reason: 'Operator supplied this address as a known scammer seed.',\n },\n 1,\n 'promote_confirmed',\n ))\n }\n }\n\n for (const edge of edges) {\n if (edge.relation === 'deposit_cluster_inflow') {\n if (seedAddresses.has(edge.src) || victimAddresses.has(edge.src) || exchangeDepositAddresses.has(edge.src)) continue\n if (edge.src_labels.length > 0) {\n pushCaseRole(caseRoles, {\n address: edge.src,\n role: 'continue_from_address',\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n })\n addRole(rolesByAddress, edge.src, 'continue_from_address')\n investigationHints.push({\n address: edge.src,\n hint_type: 'generic_labeled_cluster_member',\n labels: edge.src_labels,\n reason: 'Generic labels are preserved as context, but the address shares an exchange-deposit inflow cluster with the scam topology.',\n seed_address: edge.seed_address,\n })\n pushSafetyDecision(safetyDecisions, {\n address: edge.src,\n decision: 'context_only_generic_labeled_cluster_member',\n reason: 'Generic non-exchange labels stop automatic scam labeling; investigate manually if this context should continue.',\n labels: edge.src_labels,\n seed_address: edge.seed_address,\n })\n continue\n }\n pushCaseRole(caseRoles, {\n address: edge.src,\n role: 'laundering_intermediate',\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n })\n addRole(rolesByAddress, edge.src, 'laundering_intermediate')\n mergeCandidate(candidates, makeCandidate(\n edge.src,\n 'laundering_intermediate',\n edgeEvidence(edge, 'Address sends into an exchange-deposit cluster reached from a known scam topology seed.'),\n edge.seed_role === 'scammer' ? 0.78 : 0.64,\n 'review_required',\n ))\n continue\n }\n\n if (edge.relation === 'terminal_exchange') {\n const exchangeNames = exchangeNamesFromLabels(edge.dst_labels)\n const exchangeDeposit = {\n deposit_address: edge.src,\n exchange_address: edge.dst,\n exchange_names: exchangeNames,\n exchange_labels: edge.dst_labels,\n amount_sum: edge.amount_sum,\n amount_usd_sum: edge.amount_usd_sum,\n tx_count: edge.tx_count,\n hop: edge.hop,\n graph_scope: edge.graph_scope,\n topology_graph: edge.topology_graph,\n scope_membership: edge.scope_membership,\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n first_seen_timestamp: edge.first_seen_timestamp,\n last_seen_timestamp: edge.last_seen_timestamp,\n first_tx_id: edge.first_tx_id,\n last_tx_id: edge.last_tx_id,\n }\n pushCaseRole(caseRoles, {\n address: edge.dst,\n role: 'exchange_endpoint',\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n })\n addRole(rolesByAddress, edge.dst, 'exchange_endpoint')\n terminalPoints.push({\n address: edge.dst,\n terminal_type: 'exchange_endpoint',\n source_address: edge.src,\n deposit_address: edge.src,\n exchange_address: edge.dst,\n exchange_names: exchangeNames,\n exchange_labels: edge.dst_labels,\n seed_address: edge.seed_address,\n graph_scope: edge.graph_scope,\n topology_graph: edge.topology_graph,\n scope_membership: edge.scope_membership,\n })\n if (!exchangeDeposits.some((deposit) => (\n deposit.deposit_address === exchangeDeposit.deposit_address &&\n deposit.exchange_address === exchangeDeposit.exchange_address &&\n deposit.seed_address === exchangeDeposit.seed_address &&\n deposit.seed_role === exchangeDeposit.seed_role\n ))) {\n exchangeDeposits.push(exchangeDeposit)\n }\n pushSafetyDecision(safetyDecisions, {\n address: edge.dst,\n decision: 'do_not_label_exchange_endpoint',\n reason: 'Exchange endpoints are terminal service context, not scam label candidates.',\n seed_address: edge.seed_address,\n })\n\n if (!seedAddresses.has(edge.src) && !victimAddresses.has(edge.src)) {\n pushCaseRole(caseRoles, {\n address: edge.src,\n role: 'exchange_deposit_candidate',\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n })\n addRole(rolesByAddress, edge.src, 'exchange_deposit_candidate')\n mergeCandidate(candidates, makeCandidate(\n edge.src,\n 'exchange_deposit_candidate',\n edgeEvidence(edge, 'Address is the penultimate hop before an exchange endpoint.'),\n edge.seed_role === 'scammer' ? 0.8 : 0.68,\n 'review_required',\n ))\n }\n continue\n }\n\n if (edge.relation === 'context_boundary') {\n pushCaseRole(caseRoles, {\n address: edge.dst,\n role: 'context_boundary',\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n })\n addRole(rolesByAddress, edge.dst, 'context_boundary')\n terminalPoints.push({\n address: edge.dst,\n terminal_type: 'context_boundary',\n source_address: edge.src,\n labels: edge.dst_labels,\n seed_address: edge.seed_address,\n graph_scope: edge.graph_scope,\n scope_membership: edge.scope_membership,\n })\n investigationHints.push({\n address: edge.dst,\n hint_type: 'generic_labeled_context',\n labels: edge.dst_labels,\n reason: 'Non-exchange labels are context hints only and stop automatic scam traversal.',\n seed_address: edge.seed_address,\n })\n pushSafetyDecision(safetyDecisions, {\n address: edge.dst,\n decision: 'context_only_generic_labeled_node',\n reason: 'Generic non-exchange labels are not hard-coded scam infrastructure classes.',\n labels: edge.dst_labels,\n seed_address: edge.seed_address,\n })\n continue\n }\n\n if (seedAddresses.has(edge.dst) || victimAddresses.has(edge.dst) || exchangeDepositAddresses.has(edge.dst)) continue\n pushCaseRole(caseRoles, {\n address: edge.dst,\n role: 'laundering_intermediate',\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n })\n addRole(rolesByAddress, edge.dst, 'laundering_intermediate')\n mergeCandidate(candidates, makeCandidate(\n edge.dst,\n 'laundering_intermediate',\n edgeEvidence(edge, 'Address appears on an outward path from a known scam topology seed.'),\n edge.seed_role === 'scammer' ? 0.85 : 0.72,\n 'review_required',\n ))\n }\n\n const labelCandidates = [...candidates.values()]\n .sort((a, b) => b.confidence_score - a.confidence_score || a.address.localeCompare(b.address))\n return {\n labelCandidates,\n caseRoles,\n safetyDecisions,\n rolesByAddress,\n intermediaries: [...new Set(caseRoles\n .filter((role) => role.role === 'laundering_intermediate')\n .map((role) => role.address))],\n terminalPoints,\n exchangeDeposits,\n investigationHints,\n }\n}\n\nfunction mergeLabels(existing: unknown, next: string[]): string[] {\n return [...new Set([...stringArray(existing), ...next])]\n}\n\nfunction primaryFlowEdges(edges: ScamTopologyTopologyEdge[]): ScamTopologyTopologyEdge[] {\n return edges.filter((edge) => edge.relation !== 'deposit_cluster_inflow')\n}\n\nfunction depositClusterEdges(edges: ScamTopologyTopologyEdge[]): ScamTopologyTopologyEdge[] {\n return edges.filter((edge) => edge.relation === 'deposit_cluster_inflow')\n}\n\nfunction shortestPathFromSeed(seedAddress: string, targetAddress: string, edges: ScamTopologyTopologyEdge[]): string[] {\n if (seedAddress === targetAddress) return [seedAddress]\n const adjacency = new Map<string, string[]>()\n for (const edge of edges) {\n const destinations = adjacency.get(edge.src) ?? []\n destinations.push(edge.dst)\n adjacency.set(edge.src, destinations)\n }\n\n const queue = [seedAddress]\n const parent = new Map<string, string | null>([[seedAddress, null]])\n for (let index = 0; index < queue.length; index += 1) {\n const current = queue[index]!\n for (const next of adjacency.get(current) ?? []) {\n if (parent.has(next)) continue\n parent.set(next, current)\n if (next === targetAddress) {\n const path = [targetAddress]\n let cursor: string | null | undefined = current\n while (cursor) {\n path.push(cursor)\n cursor = parent.get(cursor)\n }\n return path.reverse()\n }\n queue.push(next)\n }\n }\n\n return [seedAddress, targetAddress]\n}\n\nfunction scamLabelsByAddress(facts: Record<string, unknown>): Map<string, ScamTopologyScamLabel> {\n const labels = Array.isArray(facts['scam_labels']) ? facts['scam_labels'] : []\n const result = new Map<string, ScamTopologyScamLabel>()\n for (const label of labels) {\n if (!label || typeof label !== 'object' || Array.isArray(label)) continue\n const record = label as Record<string, unknown>\n const address = stringValue(record['address'])\n const confidence = numberValue(record['confidence'])\n if (!address || confidence === undefined) continue\n result.set(address, {\n address,\n scam: true,\n confidence,\n source: 'scam_topology',\n source_victim_address: stringValue(record['source_victim_address']) ?? '',\n source_incident_timestamp_ms: numberValue(record['source_incident_timestamp_ms']) ?? 0,\n })\n }\n return result\n}\n\nfunction buildGraph(\n seeds: Seed[],\n edges: ScamTopologyTopologyEdge[],\n rolesByAddress: Map<string, Set<string>>,\n facts: Record<string, unknown>,\n): Record<string, unknown> {\n const nodesById = new Map<string, Record<string, unknown>>()\n const primaryEdges = primaryFlowEdges(edges)\n const clusterEdges = depositClusterEdges(edges)\n const scamLabels = scamLabelsByAddress(facts)\n\n for (const seed of seeds) {\n nodesById.set(seed.address, {\n id: seed.address,\n address: seed.address,\n node_type: 'address',\n roles: [...(rolesByAddress.get(seed.address) ?? new Set([seed.role]))],\n flow_in_usd: 0,\n flow_out_usd: 0,\n })\n }\n\n const mergeNode = (address: string, labels: string[], roles: string[] = []) => {\n const existing = nodesById.get(address) ?? {\n id: address,\n address,\n node_type: 'address',\n flow_in_usd: 0,\n flow_out_usd: 0,\n }\n const addressRoles = [...new Set([\n ...stringArray(existing['roles']),\n ...[...(rolesByAddress.get(address) ?? [])],\n ...roles,\n ])]\n const scamLabel = scamLabels.get(address)\n nodesById.set(address, {\n ...existing,\n labels: mergeLabels(existing['labels'], labels),\n roles: addressRoles,\n ...(scamLabel ? {\n scam: true,\n scam_confidence: scamLabel.confidence,\n scam_source: scamLabel.source,\n } : {}),\n })\n return nodesById.get(address)!\n }\n\n const addFlowTotals = (address: string, direction: 'in' | 'out', amount: number) => {\n const node = nodesById.get(address) ?? mergeNode(address, [])\n const key = direction === 'in' ? 'flow_in_usd' : 'flow_out_usd'\n const existing = numberValue(node[key]) ?? 0\n node[key] = existing + amount\n nodesById.set(address, node)\n }\n\n for (const edge of edges) {\n const src = mergeNode(edge.src, edge.src_labels, edge.relation === 'deposit_cluster_inflow' ? ['lead'] : [])\n const dstRoles = edge.relation === 'terminal_exchange'\n ? ['exchange']\n : edge.relation === 'context_boundary'\n ? ['context_boundary']\n : []\n const dst = mergeNode(edge.dst, edge.dst_labels, dstRoles)\n if (edge.src_is_exchange) src['is_exchange'] = true\n if (edge.dst_is_exchange) dst['is_exchange'] = true\n const amount = edge.amount_usd_sum ?? edge.amount_sum ?? 0\n addFlowTotals(edge.src, 'out', amount)\n addFlowTotals(edge.dst, 'in', amount)\n }\n\n const terminalEdges = primaryEdges.filter((edge) => edge.relation === 'terminal_exchange')\n const deposits = terminalEdges.map((edge) => ({\n address: edge.src,\n exchangeAddress: edge.dst,\n exchangeLabels: edge.dst_labels,\n exchangeNames: exchangeNamesFromLabels(edge.dst_labels),\n amount_sum: edge.amount_sum,\n amount_usd_sum: edge.amount_usd_sum,\n hops: edge.hop,\n path: shortestPathFromSeed(edge.seed_address ?? seeds[0]?.address ?? edge.src, edge.dst, primaryEdges),\n seed_role: edge.seed_role,\n seed_address: edge.seed_address,\n }))\n const reverseLeads = clusterEdges.map((edge) => ({\n address: edge.src,\n labels: edge.src_labels,\n deposit_address: edge.dst,\n amount_usd: edge.amount_usd_sum ?? edge.amount_sum,\n degree_in: undefined,\n degree_out: undefined,\n total_volume_usd: edge.amount_usd_sum,\n reason: 'deposit_cluster_inflow',\n seed_role: edge.seed_role,\n seed_address: edge.seed_address,\n first_seen_timestamp: edge.first_seen_timestamp,\n last_seen_timestamp: edge.last_seen_timestamp,\n tx_count: edge.tx_count,\n }))\n\n return normalizeGraphPayload({\n schema: 'chain-insights.graph.v1',\n nodes: [...nodesById.values()],\n edges: [\n ...primaryEdges.map((edge) => ({\n source: edge.src,\n target: edge.dst,\n edge_type: 'flows_to',\n relation: edge.relation,\n hop: edge.hop,\n wave_index: edge.wave_index,\n graph_scope: edge.graph_scope,\n topology_graph: edge.topology_graph,\n activity_policy: edge.activity_policy,\n scope_membership: edge.scope_membership,\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n usd_amount: edge.amount_usd_sum ?? edge.amount_sum,\n amount_sum: edge.amount_sum,\n amount_usd_sum: edge.amount_usd_sum,\n tx_count: edge.tx_count ?? 0,\n first_seen_timestamp: edge.first_seen_timestamp,\n last_seen_timestamp: edge.last_seen_timestamp,\n first_tx_id: edge.first_tx_id,\n last_tx_id: edge.last_tx_id,\n expands_frontier: edge.expands_frontier,\n converges_to_seen_node: edge.converges_to_seen_node,\n activity_threshold_timestamp: edge.activity_threshold_timestamp,\n src_arrival_timestamp: edge.src_arrival_timestamp,\n dst_arrival_timestamp: edge.dst_arrival_timestamp,\n terminal_exchange: edge.relation === 'terminal_exchange',\n context_boundary: edge.relation === 'context_boundary',\n })),\n ...reverseLeads.map((lead) => ({\n source: lead.address,\n target: lead.deposit_address,\n edge_type: 'flows_to',\n relation: 'deposit_cluster_inflow',\n usd_amount: lead.amount_usd ?? 0,\n amount_sum: lead.amount_usd ?? 0,\n tx_count: lead.tx_count ?? 0,\n direction: 'reverse_1hop_lead',\n })),\n ],\n flows: primaryEdges.map((edge) => ({\n hop: edge.hop,\n src: edge.src,\n dst: edge.dst,\n relation: edge.relation,\n graph_scope: edge.graph_scope,\n topology_graph: edge.topology_graph,\n activity_policy: edge.activity_policy,\n wave_index: edge.wave_index,\n scope_membership: edge.scope_membership,\n seed_address: edge.seed_address,\n seed_role: edge.seed_role,\n amount_sum: edge.amount_sum,\n amount_usd_sum: edge.amount_usd_sum,\n tx_count: edge.tx_count,\n first_seen_timestamp: edge.first_seen_timestamp,\n last_seen_timestamp: edge.last_seen_timestamp,\n first_tx_id: edge.first_tx_id,\n last_tx_id: edge.last_tx_id,\n expands_frontier: edge.expands_frontier,\n converges_to_seen_node: edge.converges_to_seen_node,\n terminal_exchange: edge.relation === 'terminal_exchange',\n context_boundary: edge.relation === 'context_boundary',\n })),\n deposits,\n source_matches: [],\n reverse_leads: reverseLeads,\n edge_anchors: [],\n metadata: {\n source: 'scam_topology',\n network: facts['network'],\n victim_address: facts['victim_address'],\n incident_timestamp_ms: facts['incident_timestamp_ms'],\n scam_label_count: Array.isArray(facts['scam_labels']) ? facts['scam_labels'].length : 0,\n label_candidate_count: Array.isArray(facts['label_candidates']) ? facts['label_candidates'].length : 0,\n topology_edge_count: edges.length,\n primary_flow_count: primaryEdges.length,\n reverse_lead_count: reverseLeads.length,\n primary_activity_policy: 'node_relative',\n generated_at: new Date().toISOString(),\n },\n })\n}\n\nfunction makeScamLabels(\n candidates: ScamTopologyLabelCandidate[],\n victimAddress: string,\n incidentTimestampMs: number,\n): ScamTopologyScamLabel[] {\n return candidates\n .filter((candidate) => candidate.address_subtype !== 'scam_seed')\n .map((candidate) => ({\n address: candidate.address,\n scam: true,\n confidence: candidate.confidence_score,\n source: 'scam_topology',\n source_victim_address: victimAddress,\n source_incident_timestamp_ms: incidentTimestampMs,\n }))\n}\n\nfunction summarize(\n network: string,\n victimAddress: string,\n incidentTimestampMs: number,\n candidates: ScamTopologyLabelCandidate[],\n scamLabels: ScamTopologyScamLabel[],\n safetyDecisions: Array<Record<string, unknown>>,\n topologyEdges: ScamTopologyTopologyEdge[],\n terminalPoints: Array<Record<string, unknown>>,\n): string {\n const review = candidates.filter((candidate) => candidate.promotion_status === 'review_required').length\n return [\n `Scam topology complete for ${network}`,\n '',\n 'Topology graph: live_topology',\n `Victim/source seed: ${victimAddress}`,\n `Incident timestamp ms: ${incidentTimestampMs}`,\n `Topology edges: ${topologyEdges.length}.`,\n `Terminal points: ${terminalPoints.length}.`,\n `Scam labels: ${scamLabels.length}.`,\n `Review candidates: ${candidates.length} (${review} review_required).`,\n `Safety decisions: ${safetyDecisions.length}.`,\n '',\n 'Policy: victims, exchange endpoints, and generic labeled context nodes are not automatic scam labels.',\n ].join('\\n')\n}\n\nfunction csvCell(value: unknown): string {\n if (value === undefined || value === null) return '\"\"'\n if (Array.isArray(value) || (typeof value === 'object' && value !== null)) {\n return JSON.stringify(JSON.stringify(value))\n }\n return JSON.stringify(String(value))\n}\n\nfunction labelCandidatesCsv(candidates: ScamTopologyLabelCandidate[]): string {\n const headers = [\n 'address',\n 'label',\n 'address_type',\n 'address_subtype',\n 'trust_level',\n 'risk_level',\n 'confidence_score',\n 'promotion_status',\n 'source',\n 'evidence_count',\n ]\n const rows = [headers.join(',')]\n for (const candidate of candidates) {\n rows.push([\n candidate.address,\n candidate.label,\n candidate.address_type,\n candidate.address_subtype,\n candidate.trust_level,\n candidate.risk_level,\n candidate.confidence_score,\n candidate.promotion_status,\n candidate.source,\n candidate.evidence.length,\n ].map(csvCell).join(','))\n }\n return rows.join('\\n') + '\\n'\n}\n\nfunction buildScamTopologyReport(\n facts: ScamTopologyResult['structuredContent']['facts'],\n files: Record<string, string>,\n): string {\n const lines = [\n `# Scam Topology: ${facts.victim_address}`,\n '',\n `Network: \\`${facts.network}\\``,\n `Incident timestamp ms: \\`${facts.incident_timestamp_ms}\\``,\n `Activity policy: \\`${facts.activity_policy_mode}\\``,\n `Graph: \\`${files.graph}\\``,\n `Label candidates CSV: \\`${files.labelCandidates}\\``,\n '',\n '## Summary',\n '',\n `- Topology edges: ${facts.topology_edges.length}`,\n `- Terminal points: ${facts.terminal_points.length}`,\n `- Exchange deposits: ${facts.exchange_deposits.length}`,\n `- Scam labels: ${facts.scam_labels.length}`,\n `- Review candidates: ${facts.label_candidates.length}`,\n `- Safety decisions: ${facts.safety_decisions.length}`,\n '',\n '## Exchange Deposits',\n '',\n '| Deposit | Exchange | Names | Hop | amount_sum | tx_count |',\n '|---|---|---|---:|---:|---:|',\n ...facts.exchange_deposits.map((entry) => {\n const deposit = stringValue(entry['deposit_address']) ?? ''\n const exchange = stringValue(entry['exchange_address']) ?? ''\n const names = stringArray(entry['exchange_names']).join(', ')\n return `| \\`${deposit}\\` | \\`${exchange}\\` | ${names || ''} | ${entry['hop'] ?? ''} | ${entry['amount_sum'] ?? ''} | ${entry['tx_count'] ?? ''} |`\n }),\n '',\n '## Label Candidates',\n '',\n '| Address | Subtype | Confidence | Status |',\n '|---|---|---:|---|',\n ...facts.label_candidates.map((candidate) =>\n `| \\`${candidate.address}\\` | ${candidate.address_subtype} | ${candidate.confidence_score} | ${candidate.promotion_status} |`\n ),\n '',\n ]\n return lines.join('\\n') + '\\n'\n}\n\nfunction scamTopologyCompactEvidence(\n facts: ScamTopologyResult['structuredContent']['facts'],\n): Record<string, unknown> {\n return {\n schema: 'chain-insights.scam_topology_evidence.v1',\n source: 'scam_topology',\n network: facts.network,\n victim_address: facts.victim_address,\n incident_timestamp_ms: facts.incident_timestamp_ms,\n topology_graphs: facts.topology_graphs,\n primary_activity_policy: facts.primary_activity_policy,\n activity_policy_mode: facts.activity_policy_mode,\n topology_edge_count: facts.topology_edges.length,\n terminal_points: facts.terminal_points,\n exchange_deposits: facts.exchange_deposits,\n scam_labels: facts.scam_labels,\n label_candidates: facts.label_candidates,\n safety_decisions: facts.safety_decisions,\n }\n}\n\nasync function writeScamTopologyCaseArtifacts(\n facts: ScamTopologyResult['structuredContent']['facts'],\n graphData: Record<string, unknown>,\n): Promise<Record<string, string>> {\n const paths = workspaceOutputPaths()\n await ensureScamTopologyDirs(paths)\n const slug = `${new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d{3}Z$/, 'Z')}_scam-topology_${sanitizeSegment(facts.victim_address.slice(0, 16))}`\n const compactEvidencePath = path.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`)\n const graphPath = path.join(paths.reportGraphsRoot, `${slug}.graph.json`)\n const graphHtmlPath = path.join(paths.reportsRoot, `${slug}.graph.html`)\n const labelCandidatesPath = path.join(paths.reportTablesRoot, `${slug}.label-candidates.csv`)\n const reportPath = path.join(paths.reportsRoot, `${slug}.scam-topology-report.md`)\n const { generateInlineGraphHtml } = await import('../viz/html-generator.js')\n const files = {\n compactEvidence: compactEvidencePath,\n graph: graphPath,\n graphHtml: graphHtmlPath,\n labelCandidates: labelCandidatesPath,\n report: reportPath,\n }\n\n await writeFile(compactEvidencePath, JSON.stringify(scamTopologyCompactEvidence(facts), null, 2) + '\\n', { mode: 0o600 })\n await writeFile(graphPath, JSON.stringify(graphData, null, 2) + '\\n', { mode: 0o600 })\n await writeFile(graphHtmlPath, generateInlineGraphHtml(graphData), { mode: 0o600 })\n await writeFile(labelCandidatesPath, labelCandidatesCsv(facts.label_candidates), { mode: 0o600 })\n await writeFile(reportPath, buildScamTopologyReport(facts, files), { mode: 0o600 })\n\n return files\n}\n\nfunction validateScope(value: unknown): ScamTopologyScope {\n const scope = value ?? 'incident'\n if (scope === 'history' || scope === 'incident' || scope === 'compare') return scope\n throw new Error('scope must be one of: history, incident, compare')\n}\n\nfunction validateNonNegativeNumber(value: number | undefined, name: string): number | undefined {\n if (value === undefined) return undefined\n if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be a non-negative number`)\n return value\n}\n\nfunction topologyGraphsForScope(scope: ScamTopologyScope): Array<'archive_topology' | 'live_topology'> {\n if (scope === 'history') return ['archive_topology']\n if (scope === 'incident') return ['live_topology']\n return ['archive_topology', 'live_topology']\n}\n\nfunction validateActivityPolicyMode(value: unknown): ScamTopologyActivityPolicyMode {\n if (value === undefined || value === null || value === '') return 'node_relative_only'\n if (value === 'node_relative_only' || value === 'global_incident_only') return value\n throw new Error('activity_policy must be one of: node_relative_only, global_incident_only')\n}\n\nfunction activityPolicyForMode(mode: ScamTopologyActivityPolicyMode): ScamTopologyActivityPolicy {\n return mode === 'global_incident_only' ? 'global_incident' : 'node_relative'\n}\n\nexport async function scamTopology(\n remoteClient: Client,\n config: Pick<InvestigatorConfig, 'dataDir' | 'serverPort'>,\n options: ScamTopologyOptions,\n): Promise<ScamTopologyResult> {\n void config\n const network = options.network.trim()\n const legacyOptions = options as ScamTopologyOptions & {\n victimAddresses?: string | string[]\n scammerAddresses?: string | string[]\n perAddressLimit?: number\n minAmountSum?: number\n scope?: ScamTopologyScope\n sinceTimestampMs?: number\n caseId?: string\n }\n const victimAddresses = parseAddressList(options.victimAddress ?? legacyOptions.victimAddresses)\n const scammerAddresses = parseAddressList(legacyOptions.scammerAddresses)\n const incidentTimestampMs = validateNonNegativeNumber(options.incidentTimestampMs, 'incident_timestamp_ms')\n const maxHops = clampInt(options.maxHops, SCAM_TOPOLOGY_DEFAULT_MAX_HOPS, 1, SCAM_TOPOLOGY_MAX_HOPS)\n const perAddressLimit = SCAM_TOPOLOGY_FRONTIER_LIMIT\n const minAmountSum = undefined\n const activityPolicyMode = validateActivityPolicyMode(options.activityPolicyMode)\n const primaryActivityPolicy = activityPolicyForMode(activityPolicyMode)\n const caseId = options.caseId ?? legacyOptions.caseId\n\n if (!network) throw new Error('network is required')\n if (legacyOptions.scope !== undefined) throw new Error('scope is no longer accepted; scam_topology always runs the victim incident topology')\n if (legacyOptions.sinceTimestampMs !== undefined) throw new Error('since_timestamp_ms is no longer accepted; use incident_timestamp_ms')\n if (legacyOptions.perAddressLimit !== undefined) throw new Error('per_address_limit is no longer accepted; scam_topology uses its internal bounded frontier')\n if (legacyOptions.minAmountSum !== undefined) throw new Error('min_amount_sum is no longer accepted; scam_topology does not amount-filter scam topology expansion')\n if (scammerAddresses.length > 0) throw new Error('scammer_addresses is no longer accepted; scam_topology starts from a victim incident')\n if (victimAddresses.length === 0) throw new Error('victim_address is required')\n if (victimAddresses.length !== 1) throw new Error('victim_address must contain exactly one address')\n if (incidentTimestampMs === undefined) throw new Error('incident_timestamp_ms is required')\n\n const victimAddress = victimAddresses[0]!\n const seeds: Seed[] = [\n { address: victimAddress, role: 'victim' as const },\n ]\n\n const primaryRun = await runDirectedTraversal(remoteClient, network, seeds, 'incident', primaryActivityPolicy, maxHops, perAddressLimit, minAmountSum, incidentTimestampMs)\n const primaryRunWithClusters = await expandDepositClusters(remoteClient, network, primaryRun, minAmountSum)\n const runs: TraversalRun[] = [primaryRunWithClusters]\n\n const topologyEdges = primaryRunWithClusters.edges\n\n const classification = classifyTopology(seeds, topologyEdges)\n const labelCandidates = classification.labelCandidates\n const scamLabels = makeScamLabels(labelCandidates, victimAddress, incidentTimestampMs)\n const facts = {\n network,\n victim_address: victimAddress,\n incident_timestamp_ms: incidentTimestampMs,\n topology_graphs: ['live_topology' as const],\n primary_activity_policy: primaryActivityPolicy,\n activity_policy_mode: activityPolicyMode,\n topology_edges: topologyEdges,\n intermediaries: classification.intermediaries,\n terminal_points: classification.terminalPoints,\n exchange_deposits: classification.exchangeDeposits,\n investigation_hints: classification.investigationHints,\n scam_labels: scamLabels,\n label_candidates: labelCandidates,\n case_roles: classification.caseRoles,\n safety_decisions: classification.safetyDecisions,\n infrastructure_anchors: [],\n infrastructure_flows: [],\n runs: runs.map((run) => ({\n graph_scope: run.graphScope,\n topology_graph: run.topologyGraph,\n activity_policy: run.activityPolicy,\n edge_count: run.edges.length,\n primary: run.activityPolicy === primaryActivityPolicy,\n max_hops: maxHops,\n frontier_limit: perAddressLimit,\n frontier_source_limit_per_hop: SCAM_TOPOLOGY_MAX_FRONTIER_SOURCES_PER_HOP,\n skipped_query_errors: run.skippedQueryErrors,\n })),\n }\n const graphData = buildGraph(seeds, topologyEdges, classification.rolesByAddress, facts)\n const summaryText = summarize(network, victimAddress, incidentTimestampMs, labelCandidates, scamLabels, classification.safetyDecisions, topologyEdges, classification.terminalPoints)\n\n if (caseId) {\n const files = await writeScamTopologyCaseArtifacts(facts, graphData)\n const { EvidenceStore } = await import('../cases/index.js')\n await EvidenceStore.append(caseId, {\n source: 'scam_topology',\n queryParams: [\n `network=${network}`,\n `victim_address=${victimAddress}`,\n `incident_timestamp_ms=${incidentTimestampMs}`,\n `max_hops=${maxHops}`,\n `activity_policy=${activityPolicyMode}`,\n ].filter(Boolean).join(' '),\n content: JSON.stringify({\n schema: 'chain-insights.evidence_pointer.v1',\n source: 'scam_topology',\n network,\n victim_address: victimAddress,\n incident_timestamp_ms: incidentTimestampMs,\n topology_graphs: facts.topology_graphs,\n primary_activity_policy: primaryActivityPolicy,\n activity_policy_mode: activityPolicyMode,\n files,\n facts: {\n topology_edges: topologyEdges.length,\n terminal_points: classification.terminalPoints.length,\n exchange_deposits: classification.exchangeDeposits.length,\n scam_labels: scamLabels.length,\n label_candidates: labelCandidates.length,\n safety_decisions: classification.safetyDecisions.length,\n },\n }, null, 2),\n })\n }\n\n return {\n summaryText,\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: 'scam_topology',\n facts,\n hint: 'Use scam_labels as ML-ready scam flags. Review label_candidates and safety_decisions before promoting addresses into core_address_labels.',\n },\n graphData,\n }\n}\n","import type { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport type { ContentBlock } from '@modelcontextprotocol/sdk/types.js'\nimport type { InvestigatorConfig } from '../config/schema.js'\nimport { runFundFlowProbe, type TraceFundsResult } from './trace-funds.js'\nimport { normalizeGraphPayload } from '../viz/graph-normalizer.js'\n\nexport { scamTopology, type ScamTopologyOptions, type ScamTopologyResult } from './scam-topology.js'\n\ntype RemoteToolResult = {\n content?: ContentBlock[]\n isError?: boolean\n}\n\ninterface ParsedGraphBatch {\n facts?: {\n queries?: Array<{\n id?: string\n ok?: boolean\n results?: Array<Record<string, unknown>>\n error?: string\n }>\n }\n}\n\ntype QueryFailure = {\n id: string\n error: string\n}\n\nconst GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 120\nconst GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS = 5 * 60 * 1000\n\nexport interface AddressRiskOptions {\n address: string\n network: string\n compareAddress?: string\n}\n\nexport interface TrackFundsOptions {\n trustedAddresses: string | string[]\n untrustedAddresses?: string | string[]\n network: string\n caseId?: string\n maxHops?: number\n perAddressLimit?: number\n minAmountSum?: number\n}\n\nfunction escapeCypherString(value: string): string {\n return value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"')\n}\n\nfunction textFromToolResult(result: RemoteToolResult): string {\n return (result.content ?? [])\n .filter((item): item is Extract<ContentBlock, { type: 'text' }> => item.type === 'text')\n .map((item) => item.text)\n .join('\\n')\n}\n\nfunction parseGraphBatchResult(result: RemoteToolResult): ParsedGraphBatch {\n const text = textFromToolResult(result).trim()\n if (!text) throw new Error('graph_query_batch returned no text content')\n const parsed = JSON.parse(text) as ParsedGraphBatch\n if (!parsed.facts?.queries) throw new Error('graph_query_batch response did not include facts.queries')\n return parsed\n}\n\nfunction topologyGraphQuery(query: string): string {\n const trimmed = query.trim()\n if (/^USE\\s+/i.test(trimmed)) return trimmed\n return `USE live_topology ${trimmed}`\n}\n\nfunction collectQueryFailure(failures: QueryFailure[], id: string, error: string | undefined): void {\n failures.push({ id, error: error || 'unknown error' })\n}\n\nfunction optionalResultsFor(batch: ParsedGraphBatch, id: string, failures: QueryFailure[]): Array<Record<string, unknown>> {\n const query = batch.facts?.queries?.find((entry) => entry.id === id)\n if (!query) return []\n if (query.ok === false) {\n collectQueryFailure(failures, id, query.error)\n return []\n }\n return query.results ?? []\n}\n\nfunction optionalResultsWithPrefix(batch: ParsedGraphBatch, prefix: string, failures: QueryFailure[]): Array<Record<string, unknown>> {\n return (batch.facts?.queries ?? [])\n .filter((entry) => entry.id?.startsWith(prefix))\n .flatMap((entry) => {\n if (entry.ok === false) {\n collectQueryFailure(failures, entry.id ?? prefix, entry.error)\n return []\n }\n return entry.results ?? []\n })\n}\n\nasync function callGraphBatch(\n remoteClient: Client,\n network: string,\n queries: Array<{ id: string; query: string }>,\n): Promise<ParsedGraphBatch> {\n const result = await remoteClient.callTool(\n {\n name: 'graph_query_batch',\n arguments: {\n network,\n queries: queries.map((query) => ({\n ...query,\n query: topologyGraphQuery(query.query),\n })),\n per_query_timeout_seconds: GRAPH_QUERY_BATCH_TIMEOUT_SECONDS,\n },\n },\n undefined,\n {\n timeout: GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS,\n maxTotalTimeout: GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS,\n },\n ) as RemoteToolResult\n if (result.isError) throw new Error(textFromToolResult(result) || 'graph_query_batch failed')\n return parseGraphBatchResult(result)\n}\n\nfunction parseAddressList(value: string | string[] | undefined): string[] {\n const raw = Array.isArray(value) ? value.join(',') : value ?? ''\n return raw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n}\n\nfunction graphArray(graphData: Record<string, unknown>, key: string): Array<Record<string, unknown>> {\n const value = graphData[key]\n return Array.isArray(value) ? value.filter((item): item is Record<string, unknown> => typeof item === 'object' && item !== null && !Array.isArray(item)) : []\n}\n\nfunction addressProfileQuery(address: string): { id: string; query: string } {\n return {\n id: 'address_profile',\n query: [\n `MATCH (a:Address {address: \"${escapeCypherString(address)}\"})`,\n 'RETURN a.address AS address, a.labels AS display_labels, a.labels AS system_labels, a.address_type AS address_type, a.address_subtypes AS address_subtypes, a.is_exchange AS is_exchange, a.confluence_score AS confluence_score, a.ml_risk_score AS ml_risk_score, a.ml_risk_level AS ml_risk_level, a.ml_top_drivers AS ml_top_drivers, a.ml_pattern_summary AS ml_pattern_summary, a.risk_score AS risk_score, a.risk_level AS risk_level, a.pattern_flags AS pattern_flags, a.ml_pagerank AS ml_pagerank, a.ml_betweenness AS ml_betweenness, a.ml_community_id AS ml_community_id',\n 'LIMIT 1',\n ].join(' '),\n }\n}\n\nfunction addressFeatureQuery(address: string): { id: string; query: string } {\n return {\n id: 'address_feature',\n query: [\n 'USE facts',\n `MATCH (a:Address {address: \"${escapeCypherString(address)}\"})-[:HAS_FEATURE]->(feature:AddressFeature)`,\n 'RETURN feature.degree_in AS degree_in, feature.degree_out AS degree_out, feature.degree_total AS degree_total, feature.tx_in_count AS tx_in_count, feature.tx_out_count AS tx_out_count, feature.tx_total_count AS tx_total_count, feature.total_volume_usd AS total_volume_usd, feature.total_in_usd AS total_in_usd, feature.total_out_usd AS total_out_usd, feature.net_flow_usd AS net_flow_usd, feature.first_activity_timestamp AS first_activity_timestamp, feature.last_activity_timestamp AS last_activity_timestamp, feature.activity_span_days AS activity_span_days, feature.active_days AS active_days',\n 'LIMIT 1',\n ].join(' '),\n }\n}\n\nfunction addressRiskScoreQuery(address: string): { id: string; query: string } {\n return {\n id: 'address_risk_score',\n query: [\n 'USE facts',\n `MATCH (a:Address {address: \"${escapeCypherString(address)}\"})-[:HAS_RISK_SCORE]->(risk:RiskScore)`,\n 'RETURN risk.risk_score AS risk_score, risk.window_days AS risk_window_days, risk.processing_date AS risk_processing_date, risk.shap_top_features AS shap_top_features',\n 'LIMIT 1',\n ].join(' '),\n }\n}\n\nfunction flowEdgeMap(variableName: string): string {\n return `{amount_sum: ${variableName}.amount_sum, amount_usd_sum: ${variableName}.amount_usd_sum, tx_count: ${variableName}.tx_count, first_tx_id: ${variableName}.first_tx_id, last_tx_id: ${variableName}.last_tx_id}`\n}\n\nfunction pathNodeMap(variableName: string): string {\n return `{address: ${variableName}.address, labels: ${variableName}.labels, system_labels: ${variableName}.labels, address_type: ${variableName}.address_type, address_subtypes: ${variableName}.address_subtypes}`\n}\n\nfunction exchangeOutflowQueries(address: string): Array<{ id: string; query: string }> {\n return Array.from({ length: 3 }, (_, index) => exchangeOutflowQueryAtDepth(address, index + 1))\n}\n\nfunction exchangeOutflowQueryAtDepth(address: string, depth: number): { id: string; query: string } {\n const intermediateVariables = Array.from({ length: Math.max(depth - 1, 0) }, (_, index) => `n${index + 1}`)\n const nodeVariables = ['a', ...intermediateVariables, 'exchange']\n const edgeVariables = Array.from({ length: depth }, (_, index) => `r${index + 1}`)\n const relationshipChain = edgeVariables.map((edgeVariable, index) => {\n const targetVariable = index === edgeVariables.length - 1 ? 'exchange' : intermediateVariables[index]!\n return `-[${edgeVariable}:FLOWS_TO]->(${targetVariable}:Address)`\n }).join('')\n const intermediatePredicates = intermediateVariables.map((nodeVariable) => `${nodeVariable}.is_exchange IS NULL`)\n const depositVariable = nodeVariables[nodeVariables.length - 2]!\n const terminalEdgeVariable = edgeVariables[edgeVariables.length - 1]!\n return {\n id: `exchange_outflows_${depth}`,\n query: [\n `MATCH (a:Address {address: \"${escapeCypherString(address)}\"})${relationshipChain}`,\n `WHERE a <> exchange AND exchange.is_exchange IS NOT NULL${intermediatePredicates.length > 0 ? ` AND ${intermediatePredicates.join(' AND ')}` : ''}`,\n `RETURN \"outflow\" AS direction, exchange.address AS exchange_address, exchange.labels AS exchange_display_labels, exchange.labels AS exchange_system_labels, exchange.address_type AS exchange_address_type, exchange.address_subtypes AS exchange_address_subtypes, ${depositVariable}.address AS deposit_address, ${depth} AS hops, ${terminalEdgeVariable}.amount_sum AS amount_sum, ${terminalEdgeVariable}.amount_usd_sum AS amount_usd_sum, ${terminalEdgeVariable}.tx_count AS tx_count, [${nodeVariables.map((nodeVariable) => `${nodeVariable}.address`).join(', ')}] AS addresses, [${nodeVariables.map(pathNodeMap).join(', ')}] AS path_nodes, [${edgeVariables.map(flowEdgeMap).join(', ')}] AS edge_props`,\n 'ORDER BY hops ASC',\n 'LIMIT 200',\n ].join(' '),\n }\n}\n\nfunction exchangeInflowQueries(address: string): Array<{ id: string; query: string }> {\n return Array.from({ length: 3 }, (_, index) => exchangeInflowQueryAtDepth(address, index + 1))\n}\n\nfunction exchangeInflowQueryAtDepth(address: string, depth: number): { id: string; query: string } {\n const intermediateVariables = Array.from({ length: Math.max(depth - 1, 0) }, (_, index) => `n${index + 1}`)\n const nodeVariables = ['exchange', ...intermediateVariables, 'a']\n const edgeVariables = Array.from({ length: depth }, (_, index) => `r${index + 1}`)\n const relationshipChain = edgeVariables.map((edgeVariable, index) => {\n const targetVariable = index === edgeVariables.length - 1 ? 'a' : intermediateVariables[index]!\n return `-[${edgeVariable}:FLOWS_TO]->(${targetVariable}:Address)`\n }).join('')\n const intermediatePredicates = intermediateVariables.map((nodeVariable) => `${nodeVariable}.is_exchange IS NULL`)\n const withdrawalVariable = nodeVariables[1]!\n const terminalEdgeVariable = edgeVariables[edgeVariables.length - 1]!\n return {\n id: `exchange_inflows_${depth}`,\n query: [\n `MATCH (exchange:Address)${relationshipChain}`,\n `WHERE a.address = \"${escapeCypherString(address)}\" AND a <> exchange AND exchange.is_exchange IS NOT NULL${intermediatePredicates.length > 0 ? ` AND ${intermediatePredicates.join(' AND ')}` : ''}`,\n `RETURN \"inflow\" AS direction, exchange.address AS exchange_address, exchange.labels AS exchange_display_labels, exchange.labels AS exchange_system_labels, exchange.address_type AS exchange_address_type, exchange.address_subtypes AS exchange_address_subtypes, ${withdrawalVariable}.address AS withdrawal_address, ${depth} AS hops, ${terminalEdgeVariable}.amount_sum AS amount_sum, ${terminalEdgeVariable}.amount_usd_sum AS amount_usd_sum, ${terminalEdgeVariable}.tx_count AS tx_count, [${nodeVariables.map((nodeVariable) => `${nodeVariable}.address`).join(', ')}] AS addresses, [${nodeVariables.map(pathNodeMap).join(', ')}] AS path_nodes, [${edgeVariables.map(flowEdgeMap).join(', ')}] AS edge_props`,\n 'ORDER BY hops ASC',\n 'LIMIT 200',\n ].join(' '),\n }\n}\n\nfunction connectionProbeQuery(address: string, compareAddress: string): { id: string; query: string } {\n return {\n id: 'connection_probe',\n query: [\n `MATCH (a:Address {address: \"${escapeCypherString(address)}\"})-[r:FLOWS_TO]-(b:Address {address: \"${escapeCypherString(compareAddress)}\"})`,\n 'RETURN [a.address, b.address] AS addresses, 1 AS hops',\n 'LIMIT 5',\n ].join(' '),\n }\n}\n\nfunction formatExchangeRows(rows: Array<Record<string, unknown>>): string[] {\n return rows.map((row) => {\n const direction = String(row['direction'] ?? 'flow')\n const exchange = String(row['exchange_address'] ?? '')\n const amount = row['amount_sum'] ?? row['amount_usd_sum'] ?? ''\n const hops = row['hops'] ?? ''\n return `- ${direction}: ${exchange} (${hops} hop(s), amount ${amount})`\n })\n}\n\nfunction numberValue(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n }\n return undefined\n}\n\nfunction firstNumber(...values: unknown[]): number | undefined {\n for (const value of values) {\n const parsed = numberValue(value)\n if (parsed !== undefined) return parsed\n }\n return undefined\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n for (const value of values) {\n if (typeof value === 'string' && value.trim()) return value.trim()\n }\n return undefined\n}\n\nfunction riskLevelFromScore(score: number): string {\n if (score >= 0.85) return 'critical'\n if (score >= 0.7) return 'high'\n if (score >= 0.4) return 'medium'\n return 'low'\n}\n\nfunction riskRecommendation(level: string): string {\n if (level === 'critical' || level === 'high') return 'Escalate for manual review.'\n if (level === 'medium') return 'Review exchange exposure and counterparties before clearing.'\n return 'No stored risk signal found; continue with normal monitoring.'\n}\n\nfunction riskDrivers(profile: Record<string, unknown>, exchangeRows: Array<Record<string, unknown>>): string[] {\n const drivers: string[] = []\n const storedDrivers = stringArrayValue(profile['ml_top_drivers'])\n if (storedDrivers?.length) drivers.push(...storedDrivers)\n\n const patternFlags = stringArrayValue(profile['pattern_flags'])\n if (patternFlags?.length) drivers.push(`Pattern flags: ${patternFlags.join(', ')}`)\n\n const outflowCount = exchangeRows.filter((row) => row['direction'] === 'outflow').length\n const inflowCount = exchangeRows.filter((row) => row['direction'] === 'inflow').length\n if (outflowCount > 0) drivers.push(`Forward bounded search reached ${outflowCount} exchange path(s).`)\n if (inflowCount > 0) drivers.push(`Backward bounded search found ${inflowCount} source exchange path(s).`)\n\n return [...new Set(drivers)]\n}\n\nfunction terminalEdgeProperties(row: Record<string, unknown>): Record<string, unknown> | undefined {\n const edgeProps = Array.isArray(row['edge_props']) ? row['edge_props'] as Array<Record<string, unknown>> : []\n return edgeProps[edgeProps.length - 1]\n}\n\nfunction enrichExchangeRows(rows: Array<Record<string, unknown>>): Array<Record<string, unknown>> {\n return rows.map((row) => {\n const terminal = terminalEdgeProperties(row)\n if (!terminal) return row\n return {\n ...row,\n amount_sum: row['amount_sum'] ?? terminal['amount_sum'],\n amount_usd_sum: row['amount_usd_sum'] ?? terminal['amount_usd_sum'],\n tx_count: row['tx_count'] ?? terminal['tx_count'],\n first_tx_id: row['first_tx_id'] ?? terminal['first_tx_id'],\n last_tx_id: row['last_tx_id'] ?? terminal['last_tx_id'],\n }\n })\n}\n\nfunction riskAssessment(profile: Record<string, unknown>, exchangeRows: Array<Record<string, unknown>>): Record<string, unknown> {\n const storedScore = firstNumber(profile['confluence_score'], profile['ml_risk_score'], profile['risk_score'])\n const score = storedScore ?? (exchangeRows.length > 0 ? 0.4 : 0)\n const level = firstString(profile['ml_risk_level'], profile['risk_level']) ?? riskLevelFromScore(score)\n const drivers = riskDrivers(profile, exchangeRows)\n return {\n level,\n score,\n confidence: storedScore !== undefined || firstString(profile['ml_risk_level'], profile['risk_level']) ? 'high' : exchangeRows.length > 0 ? 'medium' : 'low',\n recommendation: riskRecommendation(level),\n drivers,\n }\n}\n\nfunction formatRiskScore(score: unknown): string {\n const parsed = numberValue(score)\n if (parsed === undefined) return String(score ?? 'unknown')\n return Number.isInteger(parsed) ? parsed.toString() : parsed.toFixed(2)\n}\n\nfunction stringArrayValue(value: unknown): string[] | undefined {\n if (Array.isArray(value)) return value.map(String)\n if (typeof value === 'string' && value.trim()) return [value]\n return undefined\n}\n\nfunction restoreSystemLabels(graph: Record<string, unknown>, rawNodes: Array<Record<string, unknown>>): Record<string, unknown> {\n if (!Array.isArray(graph['nodes'])) return graph\n const labelsByAddress = new Map(rawNodes\n .map((node) => [typeof node['address'] === 'string' ? node['address'] : typeof node['id'] === 'string' ? node['id'] : '', stringArrayValue(node['system_labels'])] as const)\n .filter((entry): entry is readonly [string, string[]] => Boolean(entry[0]) && Array.isArray(entry[1]) && entry[1].length > 0))\n return {\n ...graph,\n nodes: graph['nodes'].map((node) => {\n if (typeof node !== 'object' || node === null || Array.isArray(node)) return node\n const record = node as Record<string, unknown>\n const address = typeof record['address'] === 'string' ? record['address'] : typeof record['id'] === 'string' ? record['id'] : ''\n const systemLabels = labelsByAddress.get(address)\n return systemLabels ? { ...record, system_labels: systemLabels } : record\n }),\n }\n}\n\nfunction buildRiskGraph(address: string, profile: Record<string, unknown>, rows: Array<Record<string, unknown>>, network: string): Record<string, unknown> {\n const nodes = new Map<string, Record<string, unknown>>()\n nodes.set(address, {\n id: address,\n address,\n node_type: 'address',\n labels: stringArrayValue(profile['display_labels']) ?? [],\n ...(stringArrayValue(profile['system_labels']) ? { system_labels: stringArrayValue(profile['system_labels']) } : {}),\n ...(typeof profile['address_type'] === 'string' ? { address_type: profile['address_type'] } : {}),\n ...(stringArrayValue(profile['address_subtypes']) ? { address_subtypes: stringArrayValue(profile['address_subtypes']) } : {}),\n roles: ['subject'],\n })\n const edges: Array<Record<string, unknown>> = []\n const mergeNode = (entry: string, metadata?: Record<string, unknown>) => {\n const existing = nodes.get(entry) ?? { id: entry, address: entry, node_type: 'address', labels: [] }\n const labels = stringArrayValue(metadata?.['labels']) ?? existing['labels']\n const systemLabels = stringArrayValue(metadata?.['system_labels']) ?? existing['system_labels']\n const addressType = typeof metadata?.['address_type'] === 'string' ? metadata['address_type'] : existing['address_type']\n const addressSubtypes = stringArrayValue(metadata?.['address_subtypes']) ?? existing['address_subtypes']\n nodes.set(entry, {\n ...existing,\n labels,\n ...(systemLabels ? { system_labels: systemLabels } : {}),\n ...(addressType ? { address_type: addressType } : {}),\n ...(addressSubtypes ? { address_subtypes: addressSubtypes } : {}),\n })\n }\n for (const row of rows) {\n const rawPath = Array.isArray(row['path']) ? row['path'] : row['addresses']\n const path = Array.isArray(rawPath) ? rawPath.map(String) : []\n const pathNodes = Array.isArray(row['path_nodes']) ? row['path_nodes'] as Array<Record<string, unknown>> : []\n for (let index = 0; index < path.length; index += 1) {\n const entry = path[index]!\n mergeNode(entry, pathNodes[index])\n }\n const exchange = typeof row['exchange_address'] === 'string' ? row['exchange_address'] : ''\n if (exchange) {\n const displayLabels = stringArrayValue(row['exchange_display_labels']) ?? []\n const systemLabels = stringArrayValue(row['exchange_system_labels']) ?? stringArrayValue(row['exchange_labels']) ?? []\n nodes.set(exchange, {\n id: exchange,\n address: exchange,\n node_type: 'address',\n labels: displayLabels,\n ...(systemLabels.length > 0 ? { system_labels: systemLabels } : {}),\n ...(typeof row['exchange_address_type'] === 'string' ? { address_type: row['exchange_address_type'] } : {}),\n ...(stringArrayValue(row['exchange_address_subtypes']) ? { address_subtypes: stringArrayValue(row['exchange_address_subtypes']) } : {}),\n roles: ['exchange'],\n })\n }\n for (let index = 0; index < path.length - 1; index += 1) {\n const edgeProps = Array.isArray(row['edge_props']) ? row['edge_props'] as Array<Record<string, unknown>> : []\n const edge = edgeProps[index] ?? row\n edges.push({\n source: path[index],\n target: path[index + 1],\n edge_type: 'flows_to',\n usd_amount: edge['amount_usd_sum'] ?? edge['amount_sum'] ?? 0,\n amount_sum: edge['amount_sum'] ?? 0,\n tx_count: edge['tx_count'] ?? 0,\n first_tx_id: edge['first_tx_id'],\n last_tx_id: edge['last_tx_id'],\n direction: row['direction'],\n })\n }\n }\n const rawNodes = [...nodes.values()]\n return restoreSystemLabels(normalizeGraphPayload({\n schema: 'chain-insights.graph.v1',\n nodes: rawNodes,\n edges,\n flows: [],\n edge_anchors: [],\n metadata: { address, network, generated_at: new Date().toISOString() },\n }), rawNodes)\n}\n\nexport async function addressRisk(remoteClient: Client, options: AddressRiskOptions): Promise<{\n summaryText: string\n structuredContent: Record<string, unknown>\n graphData: Record<string, unknown>\n}> {\n const address = options.address.trim()\n const network = options.network.trim()\n const compareAddress = options.compareAddress?.trim() ?? ''\n if (!address) throw new Error('address is required')\n if (!network) throw new Error('network is required')\n\n const queries = [\n addressProfileQuery(address),\n addressFeatureQuery(address),\n addressRiskScoreQuery(address),\n ...exchangeOutflowQueries(address),\n ...exchangeInflowQueries(address),\n ...(compareAddress ? [connectionProbeQuery(address, compareAddress)] : [{ id: 'connection_probe', query: 'MATCH (n:Address {address: \"__chain_insights_noop__\"}) RETURN n.address AS noop LIMIT 0' }]),\n ]\n const batch = await callGraphBatch(remoteClient, network, queries)\n const partialQueryFailures: QueryFailure[] = []\n const profile: Record<string, unknown> = {\n address,\n ...(optionalResultsFor(batch, 'address_profile', partialQueryFailures)[0] ?? {}),\n ...(optionalResultsFor(batch, 'address_feature', partialQueryFailures)[0] ?? {}),\n ...(optionalResultsFor(batch, 'address_risk_score', partialQueryFailures)[0] ?? {}),\n }\n const outflows = enrichExchangeRows(optionalResultsWithPrefix(batch, 'exchange_outflows_', partialQueryFailures))\n const inflows = enrichExchangeRows(optionalResultsWithPrefix(batch, 'exchange_inflows_', partialQueryFailures))\n const connections = compareAddress ? optionalResultsFor(batch, 'connection_probe', partialQueryFailures) : []\n const exchangeRows = [...outflows, ...inflows]\n const graphData = buildRiskGraph(address, profile, exchangeRows, network)\n const risk = riskAssessment(profile, exchangeRows)\n\n const lines = [\n `Address risk for ${network}:${address}`,\n '',\n `Risk: ${risk['level']} (${formatRiskScore(risk['score'])})`,\n `Confidence: ${risk['confidence']}`,\n `Recommendation: ${risk['recommendation']}`,\n `Graph degree: in ${profile['degree_in'] ?? 'unknown'}, out ${profile['degree_out'] ?? 'unknown'}.`,\n '',\n 'Exchange behavior',\n exchangeRows.length > 0 ? formatExchangeRows(exchangeRows).join('\\n') : '- No exchange inflow/outflow paths found in bounded search.',\n ]\n if (Array.isArray(risk['drivers']) && risk['drivers'].length > 0) {\n lines.push('', 'Risk drivers', risk['drivers'].map((driver) => `- ${driver}`).join('\\n'))\n }\n if (compareAddress) {\n lines.push('', `Connection compare target: ${compareAddress}`, connections.length > 0 ? `Connection paths found: ${connections.length}` : 'Connection paths found: 0')\n }\n if (partialQueryFailures.length > 0) {\n lines.push('', 'Partial query failures', partialQueryFailures.map((failure) => `- ${failure.id}: ${failure.error}`).join('\\n'))\n }\n\n return {\n summaryText: lines.join('\\n'),\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: 'address_risk',\n facts: {\n subject: { network, addresses: compareAddress ? [address, compareAddress] : [address] },\n risk,\n exchange_behavior: {\n outflows,\n inflows,\n },\n connection: compareAddress ? { compare_address: compareAddress, paths: connections } : undefined,\n partial_query_errors: partialQueryFailures.length > 0 ? partialQueryFailures : undefined,\n },\n },\n graphData,\n }\n}\n\nexport async function trackFunds(\n remoteClient: Client,\n config: Pick<InvestigatorConfig, 'dataDir' | 'serverPort'>,\n options: TrackFundsOptions,\n): Promise<{\n summaryText: string\n structuredContent: Record<string, unknown>\n graphData: Record<string, unknown>\n}> {\n const network = options.network.trim()\n const trusted = parseAddressList(options.trustedAddresses)\n const untrusted = parseAddressList(options.untrustedAddresses)\n if (!network) throw new Error('network is required')\n if (trusted.length < 1) throw new Error('trusted_addresses must contain at least 1 address')\n if (trusted.length > 5) throw new Error('trusted_addresses cannot exceed 5 addresses')\n if (untrusted.length > 5) throw new Error('untrusted_addresses cannot exceed 5 addresses')\n const overlap = trusted.filter((address) => untrusted.includes(address))\n if (overlap.length > 0) throw new Error(`Address(es) appear in both trusted and untrusted lists: ${overlap.join(', ')}`)\n\n const runs: Array<{ role: 'trusted' | 'untrusted'; address: string; result: TraceFundsResult }> = []\n for (const address of trusted) {\n runs.push({\n role: 'trusted',\n address,\n result: await runFundFlowProbe(remoteClient, config, {\n seedAddress: address,\n network,\n caseId: options.caseId,\n maxHops: options.maxHops,\n perAddressLimit: options.perAddressLimit,\n minAmountSum: options.minAmountSum,\n }),\n })\n }\n for (const address of untrusted) {\n runs.push({\n role: 'untrusted',\n address,\n result: await runFundFlowProbe(remoteClient, config, {\n seedAddress: address,\n network,\n caseId: options.caseId,\n maxHops: options.maxHops,\n perAddressLimit: options.perAddressLimit,\n minAmountSum: options.minAmountSum,\n }),\n })\n }\n\n const graphData = normalizeGraphPayload({\n schema: 'chain-insights.graph.v1',\n nodes: runs.flatMap((run) => Array.isArray(run.result.graphData.nodes) ? run.result.graphData.nodes : []),\n edges: runs.flatMap((run) => Array.isArray(run.result.graphData.edges) ? run.result.graphData.edges : []),\n flows: runs.flatMap((run) => Array.isArray(run.result.graphData.flows) ? run.result.graphData.flows : []),\n deposits: runs.flatMap((run) => graphArray(run.result.graphData, 'deposits').map((item) => ({ ...item, run_role: run.role, run_address: run.address }))),\n source_matches: runs.flatMap((run) => graphArray(run.result.graphData, 'source_matches').map((item) => ({ ...item, run_role: run.role, run_address: run.address }))),\n reverse_leads: runs.flatMap((run) => graphArray(run.result.graphData, 'reverse_leads').map((item) => ({ ...item, run_role: run.role, run_address: run.address }))),\n edge_anchors: [],\n metadata: { network, trusted_addresses: trusted, untrusted_addresses: untrusted, generated_at: new Date().toISOString() },\n })\n\n return {\n summaryText: [\n `Track funds complete for ${network}`,\n '',\n `Trusted addresses: ${trusted.join(', ')}`,\n `Untrusted addresses: ${untrusted.join(', ') || 'none'}`,\n '',\n ...runs.map((run) => `## ${run.role}: ${run.address}\\n${run.result.summaryText}`),\n ].join('\\n'),\n structuredContent: {\n schema: 'chain-insights.result.v1',\n tool: 'track_funds',\n facts: {\n network,\n trusted_addresses: trusted,\n untrusted_addresses: untrusted,\n runs: runs.map((run) => ({\n role: run.role,\n address: run.address,\n files: run.result.files,\n continuation: run.result.continuation,\n address_map: run.result.addressMap,\n })),\n },\n },\n graphData,\n }\n}\n"],"mappings":"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"}