chain-insights 0.3.6 → 0.3.9

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 (36) hide show
  1. package/README.md +18 -13
  2. package/dist/{capabilities-BC3Y5EOi.mjs → capabilities-BCvkTkIu.mjs} +3 -6
  3. package/dist/capabilities-BCvkTkIu.mjs.map +1 -0
  4. package/dist/{capabilities-D5PSx9Hj.cjs → capabilities-DOa6EFO-.cjs} +2 -5
  5. package/dist/cli.cjs +41 -14
  6. package/dist/cli.mjs +41 -14
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{client-D4JE7fFF.mjs → client-BgmHjBHQ.mjs} +15 -7
  9. package/dist/client-BgmHjBHQ.mjs.map +1 -0
  10. package/dist/{client-Db6IV1tv.cjs → client-Y_zqKqJT.cjs} +19 -5
  11. package/dist/index.cjs +1 -1
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts.map +1 -1
  14. package/dist/index.mjs +1 -1
  15. package/dist/mcp-proxy.cjs +442 -408
  16. package/dist/mcp-proxy.d.cts +3 -1
  17. package/dist/mcp-proxy.d.cts.map +1 -1
  18. package/dist/mcp-proxy.d.mts +3 -1
  19. package/dist/mcp-proxy.d.mts.map +1 -1
  20. package/dist/mcp-proxy.mjs +442 -409
  21. package/dist/mcp-proxy.mjs.map +1 -1
  22. package/dist/{public-tools-xfVNz9NE.cjs → public-tools-BY3PTw6x.cjs} +59 -31
  23. package/dist/{public-tools-CyUZEz9B.mjs → public-tools-CvlZcysd.mjs} +60 -32
  24. package/dist/public-tools-CvlZcysd.mjs.map +1 -0
  25. package/dist/{runner-DWuSy1Se.mjs → runner-CVnjpqc-.mjs} +2 -2
  26. package/dist/{runner-DWuSy1Se.mjs.map → runner-CVnjpqc-.mjs.map} +1 -1
  27. package/dist/{runner-CVo41fjz.cjs → runner-bLy0pTr_.cjs} +1 -1
  28. package/dist/update-BJoXYucO.cjs +145 -0
  29. package/dist/update-CJUfGCxs.mjs +145 -0
  30. package/dist/update-CJUfGCxs.mjs.map +1 -0
  31. package/docs/graph-tools.md +8 -8
  32. package/docs/mcp-proxy.md +14 -14
  33. package/package.json +1 -1
  34. package/dist/capabilities-BC3Y5EOi.mjs.map +0 -1
  35. package/dist/client-D4JE7fFF.mjs.map +0 -1
  36. package/dist/public-tools-CyUZEz9B.mjs.map +0 -1
@@ -45,7 +45,7 @@ var AliasTracker = class {
45
45
  return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b, void 0, { numeric: true })));
46
46
  }
47
47
  };
48
- const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 = 120;
48
+ const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 = 10;
49
49
  const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS$1 = 300 * 1e3;
50
50
  const SCHEMA_QUERY_SET = [
51
51
  {
@@ -822,6 +822,7 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
822
822
  for (const flow of flows) byHop.set(flow.hop, (byHop.get(flow.hop) ?? 0) + 1);
823
823
  const depositCount = continuation.depositAddresses.length;
824
824
  const exchangeCount = continuation.exchangeAddresses.length;
825
+ const hasFiles = Object.values(files).some((value) => value.length > 0);
825
826
  return [
826
827
  `Trace complete for ${network}:${seedAddress}`,
827
828
  "",
@@ -830,14 +831,16 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
830
831
  `Exchange endpoints reached: ${exchangeCount}. Deposit candidate address(es): ${depositCount}.`,
831
832
  `Traceback source path(s): ${sourceMatches.length}. Reverse 1-hop lead(s): ${reverseLeads.length}.`,
832
833
  "",
833
- "Files written:",
834
- `- schema: ${files.schema}`,
835
- `- compact evidence JSON: ${files.compactEvidence}`,
836
- `- graph JSON: ${files.graph}`,
837
- `- graph HTML: ${files.graphHtml}`,
838
- `- table CSV: ${files.table}`,
839
- `- table HTML: ${files.tableHtml}`,
840
- `- report: ${files.report}`,
834
+ hasFiles ? [
835
+ "Files written:",
836
+ `- schema: ${files.schema}`,
837
+ `- compact evidence JSON: ${files.compactEvidence}`,
838
+ `- graph JSON: ${files.graph}`,
839
+ `- graph HTML: ${files.graphHtml}`,
840
+ `- table CSV: ${files.table}`,
841
+ `- table HTML: ${files.tableHtml}`,
842
+ `- report: ${files.report}`
843
+ ].join("\n") : "Files written: disabled by stateless proxy mode.",
841
844
  "",
842
845
  `Continuation hint: ${continuation.hint}`,
843
846
  continuation.depositAddresses.length > 0 ? `Deposit candidates: ${continuation.depositAddresses.map((address) => aliases.alias(address) ?? address).join(", ")}` : "Deposit candidates: none reached in this bounded trace.",
@@ -853,9 +856,18 @@ async function runFundFlowProbe(remoteClient, _config, options) {
853
856
  const perAddressLimit = clampInt$2(options.perAddressLimit, 5, 1, 10);
854
857
  const minAmountSum = Math.max(0, options.minAmountSum ?? 0);
855
858
  const evidenceSource = options.evidenceSource ?? "track_funds";
856
- const paths = require_output_root.workspaceOutputPaths();
857
- await ensureDirs(paths);
858
- const schemaResult = await loadOrCaptureTopologySchema(remoteClient, paths, network);
859
+ const writeArtifacts = options.writeArtifacts !== false;
860
+ if (!writeArtifacts && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
861
+ const paths = writeArtifacts ? require_output_root.workspaceOutputPaths() : void 0;
862
+ if (paths) await ensureDirs(paths);
863
+ const schemaResult = paths ? await loadOrCaptureTopologySchema(remoteClient, paths, network) : {
864
+ schema: {
865
+ schema: "chain-insights.runtime_graph_schema.v1",
866
+ network,
867
+ source: "stateless_proxy_mode"
868
+ },
869
+ filePath: "stateless://runtime-schema-not-written"
870
+ };
859
871
  const { flows, deposits, sourceMatches, reverseLeads } = await collectProbeTrace(remoteClient, {
860
872
  seedAddress,
861
873
  network,
@@ -868,19 +880,21 @@ async function runFundFlowProbe(remoteClient, _config, options) {
868
880
  const slug = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}_${sanitizeSegment(seedAddress.slice(0, 16))}`;
869
881
  const compact = probeEvidence(seedAddress, network, schemaResult.filePath, aliases, flows, deposits, sourceMatches, reverseLeads, evidenceSource);
870
882
  const graph = buildGraph(seedAddress, network, flows, deposits, sourceMatches, reverseLeads);
871
- const compactPath = node_path.default.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`);
872
- const graphPath = node_path.default.join(paths.reportGraphsRoot, `${slug}.graph.json`);
873
- const graphHtmlPath = node_path.default.join(paths.reportsRoot, `${slug}.graph.html`);
874
- const tablePath = node_path.default.join(paths.reportTablesRoot, `${slug}.flows.csv`);
875
- const tableHtmlPath = node_path.default.join(paths.reportsRoot, `${slug}.table.html`);
876
- const reportPath = node_path.default.join(paths.reportsRoot, `${slug}.trace-report.md`);
877
- const { generateInlineGraphHtml } = await Promise.resolve().then(() => require("./html-generator-Bx3UcLTB.cjs")).then((n) => n.html_generator_exports);
878
- await (0, node_fs_promises.writeFile)(compactPath, JSON.stringify(compact, null, 2) + "\n", { mode: 384 });
879
- await (0, node_fs_promises.writeFile)(graphPath, JSON.stringify(graph, null, 2) + "\n", { mode: 384 });
880
- await (0, node_fs_promises.writeFile)(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 384 });
881
- await (0, node_fs_promises.writeFile)(tablePath, tableCsv(flows), { mode: 384 });
882
- await (0, node_fs_promises.writeFile)(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 384 });
883
- await (0, node_fs_promises.writeFile)(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 384 });
883
+ const compactPath = paths ? node_path.default.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`) : "";
884
+ const graphPath = paths ? node_path.default.join(paths.reportGraphsRoot, `${slug}.graph.json`) : "";
885
+ const graphHtmlPath = paths ? node_path.default.join(paths.reportsRoot, `${slug}.graph.html`) : "";
886
+ const tablePath = paths ? node_path.default.join(paths.reportTablesRoot, `${slug}.flows.csv`) : "";
887
+ const tableHtmlPath = paths ? node_path.default.join(paths.reportsRoot, `${slug}.table.html`) : "";
888
+ const reportPath = paths ? node_path.default.join(paths.reportsRoot, `${slug}.trace-report.md`) : "";
889
+ if (paths) {
890
+ const { generateInlineGraphHtml } = await Promise.resolve().then(() => require("./html-generator-Bx3UcLTB.cjs")).then((n) => n.html_generator_exports);
891
+ await (0, node_fs_promises.writeFile)(compactPath, JSON.stringify(compact, null, 2) + "\n", { mode: 384 });
892
+ await (0, node_fs_promises.writeFile)(graphPath, JSON.stringify(graph, null, 2) + "\n", { mode: 384 });
893
+ await (0, node_fs_promises.writeFile)(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 384 });
894
+ await (0, node_fs_promises.writeFile)(tablePath, tableCsv(flows), { mode: 384 });
895
+ await (0, node_fs_promises.writeFile)(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 384 });
896
+ await (0, node_fs_promises.writeFile)(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 384 });
897
+ }
884
898
  if (options.caseId) {
885
899
  const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-Bz_9XKEw.cjs")).then((n) => n.cases_exports);
886
900
  await EvidenceStore.append(options.caseId, {
@@ -939,7 +953,7 @@ async function runFundFlowProbe(remoteClient, _config, options) {
939
953
  }
940
954
  //#endregion
941
955
  //#region src/investigation/stake-insights.ts
942
- const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS = 120;
956
+ const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS = 10;
943
957
  const STAKE_INSIGHTS_REQUEST_TIMEOUT_MS = 300 * 1e3;
944
958
  function escapeCypherString$1(value) {
945
959
  return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
@@ -1299,7 +1313,8 @@ function summaryLines(network, subject, rows, totals, failures) {
1299
1313
  async function stakeInsights(remoteClient, options) {
1300
1314
  const { network, subject, depth } = validateOptions(options);
1301
1315
  const { live, archive, failures, evidence } = collectRelationships(await callGraphBatch$1(remoteClient, network, [stakeRelationshipQuery("live_topology", subject, options, depth), stakeRelationshipQuery("archive_topology", subject, options, depth)]));
1302
- if (live.length === 0 && archive.length === 0 && failures.length > 0) throw new Error(`Stake insights unavailable: ${failures.map((failure) => `${failure.id}: ${failure.error}`).join("; ")}`);
1316
+ const successfulQueryCount = evidence.filter((entry) => entry["ok"] === true).length;
1317
+ if (live.length === 0 && archive.length === 0 && failures.length > 0 && successfulQueryCount === 0) throw new Error(`Stake insights unavailable: ${failures.map((failure) => `${failure.id}: ${failure.error}`).join("; ")}`);
1303
1318
  const rows = live.length > 0 ? live : archive;
1304
1319
  const totals = stakeTotals(rows);
1305
1320
  const facts = {
@@ -1334,7 +1349,7 @@ async function stakeInsights(remoteClient, options) {
1334
1349
  }
1335
1350
  //#endregion
1336
1351
  //#region src/investigation/public-tools.ts
1337
- const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 120;
1352
+ const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 10;
1338
1353
  const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS = 300 * 1e3;
1339
1354
  function escapeCypherString(value) {
1340
1355
  return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
@@ -1815,6 +1830,10 @@ function normalizeTraceGraphData(runs, network) {
1815
1830
  }
1816
1831
  function traceArtifactPointersFromRun(run) {
1817
1832
  if (!run) return {};
1833
+ if (!Object.values(run.files).some((value) => value.length > 0)) return {
1834
+ artifacts_written: false,
1835
+ artifact_mode: "stateless"
1836
+ };
1818
1837
  return {
1819
1838
  graph_json: run.files.graph,
1820
1839
  graph_html: run.files.graphHtml,
@@ -1824,6 +1843,12 @@ function traceArtifactPointersFromRun(run) {
1824
1843
  report_md: run.files.report
1825
1844
  };
1826
1845
  }
1846
+ function statelessArtifacts() {
1847
+ return {
1848
+ artifacts_written: false,
1849
+ artifact_mode: "stateless"
1850
+ };
1851
+ }
1827
1852
  function artifactEvidence(artifacts) {
1828
1853
  return Object.entries(artifacts).filter((entry) => typeof entry[1] === "string" && entry[1].length > 0).map(([kind, filePath]) => ({
1829
1854
  evidence_type: "artifact_pointer",
@@ -2028,7 +2053,8 @@ async function traceVictimFunds(remoteClient, config, options) {
2028
2053
  perAddressLimit: options.perAddressLimit,
2029
2054
  minAmountSum: options.minAmountSum,
2030
2055
  includeDepositTraceback: false,
2031
- evidenceSource: "trace_victim_funds"
2056
+ evidenceSource: "trace_victim_funds",
2057
+ writeArtifacts: options.writeArtifacts
2032
2058
  })
2033
2059
  });
2034
2060
  return traceResultFromFundRuns("trace_victim_funds", "victim", network, runs, {
@@ -2056,7 +2082,8 @@ async function traceSuspectFunds(remoteClient, config, options) {
2056
2082
  perAddressLimit: options.perAddressLimit,
2057
2083
  minAmountSum: options.minAmountSum,
2058
2084
  includeDepositTraceback: false,
2059
- evidenceSource: "trace_suspect_funds"
2085
+ evidenceSource: "trace_suspect_funds",
2086
+ writeArtifacts: options.writeArtifacts
2060
2087
  })
2061
2088
  });
2062
2089
  return traceResultFromFundRuns("trace_suspect_funds", "suspect", network, runs, {
@@ -2201,6 +2228,7 @@ async function traceDepositSources(remoteClient, _config, options) {
2201
2228
  if (!network) throw new Error("network is required");
2202
2229
  if (deposits.length < 1) throw new Error("deposit_addresses must contain at least 1 address");
2203
2230
  if (deposits.length > 5) throw new Error("deposit_addresses cannot exceed 5 addresses");
2231
+ if (options.writeArtifacts === false && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
2204
2232
  const maxHops = clampInt(options.maxHops, 2, 1, 5);
2205
2233
  const batch = await callGraphBatch(remoteClient, network, Array.from({ length: maxHops }, (_, index) => reverseDepositSourceQueryAtDepth(deposits, index + 1)));
2206
2234
  const failures = [];
@@ -2318,7 +2346,7 @@ async function traceDepositSources(remoteClient, _config, options) {
2318
2346
  `Reverse path(s): ${paths.length}`,
2319
2347
  `Shared upstream convergence: ${convergence.length}`
2320
2348
  ].join("\n");
2321
- const artifacts = await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
2349
+ const artifacts = options.writeArtifacts === false ? statelessArtifacts() : await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
2322
2350
  const evidence = artifactEvidence(artifacts);
2323
2351
  if (options.caseId) {
2324
2352
  const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-Bz_9XKEw.cjs")).then((n) => n.cases_exports);
@@ -43,7 +43,7 @@ var AliasTracker = class {
43
43
  return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b, void 0, { numeric: true })));
44
44
  }
45
45
  };
46
- const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 = 120;
46
+ const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 = 10;
47
47
  const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS$1 = 300 * 1e3;
48
48
  const SCHEMA_QUERY_SET = [
49
49
  {
@@ -820,6 +820,7 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
820
820
  for (const flow of flows) byHop.set(flow.hop, (byHop.get(flow.hop) ?? 0) + 1);
821
821
  const depositCount = continuation.depositAddresses.length;
822
822
  const exchangeCount = continuation.exchangeAddresses.length;
823
+ const hasFiles = Object.values(files).some((value) => value.length > 0);
823
824
  return [
824
825
  `Trace complete for ${network}:${seedAddress}`,
825
826
  "",
@@ -828,14 +829,16 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
828
829
  `Exchange endpoints reached: ${exchangeCount}. Deposit candidate address(es): ${depositCount}.`,
829
830
  `Traceback source path(s): ${sourceMatches.length}. Reverse 1-hop lead(s): ${reverseLeads.length}.`,
830
831
  "",
831
- "Files written:",
832
- `- schema: ${files.schema}`,
833
- `- compact evidence JSON: ${files.compactEvidence}`,
834
- `- graph JSON: ${files.graph}`,
835
- `- graph HTML: ${files.graphHtml}`,
836
- `- table CSV: ${files.table}`,
837
- `- table HTML: ${files.tableHtml}`,
838
- `- report: ${files.report}`,
832
+ hasFiles ? [
833
+ "Files written:",
834
+ `- schema: ${files.schema}`,
835
+ `- compact evidence JSON: ${files.compactEvidence}`,
836
+ `- graph JSON: ${files.graph}`,
837
+ `- graph HTML: ${files.graphHtml}`,
838
+ `- table CSV: ${files.table}`,
839
+ `- table HTML: ${files.tableHtml}`,
840
+ `- report: ${files.report}`
841
+ ].join("\n") : "Files written: disabled by stateless proxy mode.",
839
842
  "",
840
843
  `Continuation hint: ${continuation.hint}`,
841
844
  continuation.depositAddresses.length > 0 ? `Deposit candidates: ${continuation.depositAddresses.map((address) => aliases.alias(address) ?? address).join(", ")}` : "Deposit candidates: none reached in this bounded trace.",
@@ -851,9 +854,18 @@ async function runFundFlowProbe(remoteClient, _config, options) {
851
854
  const perAddressLimit = clampInt$2(options.perAddressLimit, 5, 1, 10);
852
855
  const minAmountSum = Math.max(0, options.minAmountSum ?? 0);
853
856
  const evidenceSource = options.evidenceSource ?? "track_funds";
854
- const paths = workspaceOutputPaths();
855
- await ensureDirs(paths);
856
- const schemaResult = await loadOrCaptureTopologySchema(remoteClient, paths, network);
857
+ const writeArtifacts = options.writeArtifacts !== false;
858
+ if (!writeArtifacts && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
859
+ const paths = writeArtifacts ? workspaceOutputPaths() : void 0;
860
+ if (paths) await ensureDirs(paths);
861
+ const schemaResult = paths ? await loadOrCaptureTopologySchema(remoteClient, paths, network) : {
862
+ schema: {
863
+ schema: "chain-insights.runtime_graph_schema.v1",
864
+ network,
865
+ source: "stateless_proxy_mode"
866
+ },
867
+ filePath: "stateless://runtime-schema-not-written"
868
+ };
857
869
  const { flows, deposits, sourceMatches, reverseLeads } = await collectProbeTrace(remoteClient, {
858
870
  seedAddress,
859
871
  network,
@@ -866,19 +878,21 @@ async function runFundFlowProbe(remoteClient, _config, options) {
866
878
  const slug = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}_${sanitizeSegment(seedAddress.slice(0, 16))}`;
867
879
  const compact = probeEvidence(seedAddress, network, schemaResult.filePath, aliases, flows, deposits, sourceMatches, reverseLeads, evidenceSource);
868
880
  const graph = buildGraph(seedAddress, network, flows, deposits, sourceMatches, reverseLeads);
869
- const compactPath = path.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`);
870
- const graphPath = path.join(paths.reportGraphsRoot, `${slug}.graph.json`);
871
- const graphHtmlPath = path.join(paths.reportsRoot, `${slug}.graph.html`);
872
- const tablePath = path.join(paths.reportTablesRoot, `${slug}.flows.csv`);
873
- const tableHtmlPath = path.join(paths.reportsRoot, `${slug}.table.html`);
874
- const reportPath = path.join(paths.reportsRoot, `${slug}.trace-report.md`);
875
- const { generateInlineGraphHtml } = await import("./html-generator-AowOmzyi.mjs").then((n) => n.n);
876
- await writeFile(compactPath, JSON.stringify(compact, null, 2) + "\n", { mode: 384 });
877
- await writeFile(graphPath, JSON.stringify(graph, null, 2) + "\n", { mode: 384 });
878
- await writeFile(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 384 });
879
- await writeFile(tablePath, tableCsv(flows), { mode: 384 });
880
- await writeFile(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 384 });
881
- await writeFile(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 384 });
881
+ const compactPath = paths ? path.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`) : "";
882
+ const graphPath = paths ? path.join(paths.reportGraphsRoot, `${slug}.graph.json`) : "";
883
+ const graphHtmlPath = paths ? path.join(paths.reportsRoot, `${slug}.graph.html`) : "";
884
+ const tablePath = paths ? path.join(paths.reportTablesRoot, `${slug}.flows.csv`) : "";
885
+ const tableHtmlPath = paths ? path.join(paths.reportsRoot, `${slug}.table.html`) : "";
886
+ const reportPath = paths ? path.join(paths.reportsRoot, `${slug}.trace-report.md`) : "";
887
+ if (paths) {
888
+ const { generateInlineGraphHtml } = await import("./html-generator-AowOmzyi.mjs").then((n) => n.n);
889
+ await writeFile(compactPath, JSON.stringify(compact, null, 2) + "\n", { mode: 384 });
890
+ await writeFile(graphPath, JSON.stringify(graph, null, 2) + "\n", { mode: 384 });
891
+ await writeFile(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 384 });
892
+ await writeFile(tablePath, tableCsv(flows), { mode: 384 });
893
+ await writeFile(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 384 });
894
+ await writeFile(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 384 });
895
+ }
882
896
  if (options.caseId) {
883
897
  const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
884
898
  await EvidenceStore.append(options.caseId, {
@@ -937,7 +951,7 @@ async function runFundFlowProbe(remoteClient, _config, options) {
937
951
  }
938
952
  //#endregion
939
953
  //#region src/investigation/stake-insights.ts
940
- const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS = 120;
954
+ const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS = 10;
941
955
  const STAKE_INSIGHTS_REQUEST_TIMEOUT_MS = 300 * 1e3;
942
956
  function escapeCypherString$1(value) {
943
957
  return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
@@ -1297,7 +1311,8 @@ function summaryLines(network, subject, rows, totals, failures) {
1297
1311
  async function stakeInsights(remoteClient, options) {
1298
1312
  const { network, subject, depth } = validateOptions(options);
1299
1313
  const { live, archive, failures, evidence } = collectRelationships(await callGraphBatch$1(remoteClient, network, [stakeRelationshipQuery("live_topology", subject, options, depth), stakeRelationshipQuery("archive_topology", subject, options, depth)]));
1300
- if (live.length === 0 && archive.length === 0 && failures.length > 0) throw new Error(`Stake insights unavailable: ${failures.map((failure) => `${failure.id}: ${failure.error}`).join("; ")}`);
1314
+ const successfulQueryCount = evidence.filter((entry) => entry["ok"] === true).length;
1315
+ if (live.length === 0 && archive.length === 0 && failures.length > 0 && successfulQueryCount === 0) throw new Error(`Stake insights unavailable: ${failures.map((failure) => `${failure.id}: ${failure.error}`).join("; ")}`);
1301
1316
  const rows = live.length > 0 ? live : archive;
1302
1317
  const totals = stakeTotals(rows);
1303
1318
  const facts = {
@@ -1332,7 +1347,7 @@ async function stakeInsights(remoteClient, options) {
1332
1347
  }
1333
1348
  //#endregion
1334
1349
  //#region src/investigation/public-tools.ts
1335
- const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 120;
1350
+ const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 10;
1336
1351
  const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS = 300 * 1e3;
1337
1352
  function escapeCypherString(value) {
1338
1353
  return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
@@ -1813,6 +1828,10 @@ function normalizeTraceGraphData(runs, network) {
1813
1828
  }
1814
1829
  function traceArtifactPointersFromRun(run) {
1815
1830
  if (!run) return {};
1831
+ if (!Object.values(run.files).some((value) => value.length > 0)) return {
1832
+ artifacts_written: false,
1833
+ artifact_mode: "stateless"
1834
+ };
1816
1835
  return {
1817
1836
  graph_json: run.files.graph,
1818
1837
  graph_html: run.files.graphHtml,
@@ -1822,6 +1841,12 @@ function traceArtifactPointersFromRun(run) {
1822
1841
  report_md: run.files.report
1823
1842
  };
1824
1843
  }
1844
+ function statelessArtifacts() {
1845
+ return {
1846
+ artifacts_written: false,
1847
+ artifact_mode: "stateless"
1848
+ };
1849
+ }
1825
1850
  function artifactEvidence(artifacts) {
1826
1851
  return Object.entries(artifacts).filter((entry) => typeof entry[1] === "string" && entry[1].length > 0).map(([kind, filePath]) => ({
1827
1852
  evidence_type: "artifact_pointer",
@@ -2026,7 +2051,8 @@ async function traceVictimFunds(remoteClient, config, options) {
2026
2051
  perAddressLimit: options.perAddressLimit,
2027
2052
  minAmountSum: options.minAmountSum,
2028
2053
  includeDepositTraceback: false,
2029
- evidenceSource: "trace_victim_funds"
2054
+ evidenceSource: "trace_victim_funds",
2055
+ writeArtifacts: options.writeArtifacts
2030
2056
  })
2031
2057
  });
2032
2058
  return traceResultFromFundRuns("trace_victim_funds", "victim", network, runs, {
@@ -2054,7 +2080,8 @@ async function traceSuspectFunds(remoteClient, config, options) {
2054
2080
  perAddressLimit: options.perAddressLimit,
2055
2081
  minAmountSum: options.minAmountSum,
2056
2082
  includeDepositTraceback: false,
2057
- evidenceSource: "trace_suspect_funds"
2083
+ evidenceSource: "trace_suspect_funds",
2084
+ writeArtifacts: options.writeArtifacts
2058
2085
  })
2059
2086
  });
2060
2087
  return traceResultFromFundRuns("trace_suspect_funds", "suspect", network, runs, {
@@ -2199,6 +2226,7 @@ async function traceDepositSources(remoteClient, _config, options) {
2199
2226
  if (!network) throw new Error("network is required");
2200
2227
  if (deposits.length < 1) throw new Error("deposit_addresses must contain at least 1 address");
2201
2228
  if (deposits.length > 5) throw new Error("deposit_addresses cannot exceed 5 addresses");
2229
+ if (options.writeArtifacts === false && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
2202
2230
  const maxHops = clampInt(options.maxHops, 2, 1, 5);
2203
2231
  const batch = await callGraphBatch(remoteClient, network, Array.from({ length: maxHops }, (_, index) => reverseDepositSourceQueryAtDepth(deposits, index + 1)));
2204
2232
  const failures = [];
@@ -2316,7 +2344,7 @@ async function traceDepositSources(remoteClient, _config, options) {
2316
2344
  `Reverse path(s): ${paths.length}`,
2317
2345
  `Shared upstream convergence: ${convergence.length}`
2318
2346
  ].join("\n");
2319
- const artifacts = await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
2347
+ const artifacts = options.writeArtifacts === false ? statelessArtifacts() : await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
2320
2348
  const evidence = artifactEvidence(artifacts);
2321
2349
  if (options.caseId) {
2322
2350
  const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
@@ -2391,4 +2419,4 @@ async function traceDepositSources(remoteClient, _config, options) {
2391
2419
  //#endregion
2392
2420
  export { addressRisk, stakeInsights, traceDepositSources, traceSuspectFunds, traceVictimFunds };
2393
2421
 
2394
- //# sourceMappingURL=public-tools-CyUZEz9B.mjs.map
2422
+ //# sourceMappingURL=public-tools-CvlZcysd.mjs.map