chain-insights 0.65.0 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
3
- const require_print_result = require("./print-result-BEA_PyNz.cjs");
3
+ const require_print_result = require("./print-result-qVhhuSdo.cjs");
4
4
  const require_client = require("./client-Ypw9BNbn.cjs");
5
- const require_tool_visibility = require("./tool-visibility-CDe-ft0L.cjs");
5
+ const require_tool_visibility = require("./tool-visibility-B4RAFMaB.cjs");
6
6
  const require_usage_status = require("./usage-status-BdwwgYsu.cjs");
7
7
  const require_subscription_status = require("./subscription-status-ru99UCPZ.cjs");
8
8
  const require_capabilities = require("./capabilities-DGavTSoW.cjs");
@@ -101,17 +101,17 @@ function rateLimitedResult(toolName, err) {
101
101
  const BUSY_CODE = "chain_busy";
102
102
  const BUSY_WAIT_MS = 1100;
103
103
  const defaultSleep = (ms) => new Promise((done) => setTimeout(done, ms));
104
- function isRecord$4(value) {
104
+ function isRecord$3(value) {
105
105
  return typeof value === "object" && value !== null && !Array.isArray(value);
106
106
  }
107
107
  function membersOf(result) {
108
- const facts = isRecord$4(result.structuredContent) ? result.structuredContent.facts : void 0;
109
- const queries = isRecord$4(facts) ? facts.queries : void 0;
110
- return Array.isArray(queries) && queries.every(isRecord$4) ? queries : null;
108
+ const facts = isRecord$3(result.structuredContent) ? result.structuredContent.facts : void 0;
109
+ const queries = isRecord$3(facts) ? facts.queries : void 0;
110
+ return Array.isArray(queries) && queries.every(isRecord$3) ? queries : null;
111
111
  }
112
112
  function isBusyMember(member) {
113
113
  const detail = member.error_detail;
114
- return isRecord$4(detail) && detail.code === BUSY_CODE;
114
+ return isRecord$3(detail) && detail.code === BUSY_CODE;
115
115
  }
116
116
  /** The ids of the members the server refused as chain_busy. */
117
117
  function busyMemberIds(result) {
@@ -142,7 +142,7 @@ function mergeBatchAnswers(first, again) {
142
142
  const merged = members.map((m) => isBusyMember(m) && byId.has(String(m.id)) ? byId.get(String(m.id)) : m);
143
143
  const structured = first.structuredContent;
144
144
  const facts = structured.facts;
145
- const batch = isRecord$4(facts.batch) ? recount(facts.batch, merged) : facts.batch;
145
+ const batch = isRecord$3(facts.batch) ? recount(facts.batch, merged) : facts.batch;
146
146
  const nextStructured = {
147
147
  ...structured,
148
148
  facts: {
@@ -186,7 +186,7 @@ async function withBusyMemberRetry(args, call, sleep = defaultSleep) {
186
186
  function isBusyResult(result) {
187
187
  if (!result.isError) return false;
188
188
  const first = Array.isArray(result.content) ? result.content[0] : void 0;
189
- const text = isRecord$4(first) && typeof first.text === "string" ? first.text : "";
189
+ const text = isRecord$3(first) && typeof first.text === "string" ? first.text : "";
190
190
  return /^(Query execution failed: )?chain_busy\b/.test(text.trim());
191
191
  }
192
192
  /**
@@ -333,7 +333,7 @@ function send(res, status, contentType, body) {
333
333
  function sendJson(res, status, value) {
334
334
  send(res, status, "application/json; charset=utf-8", JSON.stringify(value));
335
335
  }
336
- function isRecord$3(value) {
336
+ function isRecord$2(value) {
337
337
  return !!value && typeof value === "object" && !Array.isArray(value);
338
338
  }
339
339
  var BodyTooLarge = class extends Error {};
@@ -424,8 +424,8 @@ async function startViewServer(options) {
424
424
  sendJson(res, 400, { error: "invalid JSON" });
425
425
  return;
426
426
  }
427
- const args = isRecord$3(body) ? body.arguments ?? {} : void 0;
428
- if (!isRecord$3(args)) {
427
+ const args = isRecord$2(body) ? body.arguments ?? {} : void 0;
428
+ if (!isRecord$2(args)) {
429
429
  sendJson(res, 400, { error: "arguments must be an object" });
430
430
  return;
431
431
  }
@@ -606,168 +606,6 @@ async function connectRemote(options) {
606
606
  };
607
607
  }
608
608
  //#endregion
609
- //#region src/mcp/balance-text.ts
610
- /**
611
- * An ETH text twin beside every native balance column of a chain answer.
612
- *
613
- * `USE chain` serves `balance` as wei in decimal text, exact and 16 to 22
614
- * digits long. A host without a shell (Claude Desktop, 2026-10-21) printed
615
- * `10003752747799400` to the user as the balance. A model that divides by
616
- * 10^18 by head misplaces the point, so the proxy writes `<column>_eth`
617
- * next to each one, the same way `<column>_utc` sits beside a timestamp.
618
- */
619
- const BALANCE_KEY = /(^|_)balance$/;
620
- const WEI_TEXT = /^[0-9]{1,78}$/;
621
- const WEI_PER_ETH = 10n ** 18n;
622
- /** `0.0100037527477994` for `10003752747799400` wei: exact, no exponent, no trailing zeros. */
623
- function ethText(wei) {
624
- const value = BigInt(wei);
625
- const whole = value / WEI_PER_ETH;
626
- const frac = (value % WEI_PER_ETH).toString().padStart(18, "0").replace(/0+$/, "");
627
- return frac === "" ? whole.toString() : `${whole}.${frac}`;
628
- }
629
- function isWeiText(value) {
630
- return typeof value === "string" && WEI_TEXT.test(value);
631
- }
632
- /**
633
- * One row with a `<key>_eth` string after every wei balance column that has
634
- * no twin yet. A column the model itself named `*_eth` (an alias such as
635
- * `a.balance_eth AS held_eth`, see stripEthProperties) arrives as wei and is
636
- * rendered as ETH text in place: the model gets the column it asked for.
637
- */
638
- function addBalanceText(row) {
639
- const out = {};
640
- for (const [key, value] of Object.entries(row)) {
641
- out[key] = key.endsWith("_eth") && isWeiText(value) ? ethText(value) : value;
642
- if (BALANCE_KEY.test(key) && isWeiText(value) && !(`${key}_eth` in row)) out[`${key}_eth`] = ethText(value);
643
- }
644
- return out;
645
- }
646
- /**
647
- * The `_eth` twin is added by the proxy; it is no chain property. A query
648
- * that names `a.balance_eth` is rewritten to `a.balance`, so the row comes
649
- * back and the twin is written beside it. An alias after AS stays as written.
650
- */
651
- function stripEthProperties(query) {
652
- return query.replace(/\.((?:[A-Za-z_][A-Za-z0-9_]*_)?balance)_eth\b/g, ".$1");
653
- }
654
- //#endregion
655
- //#region src/mcp/timestamp-text.ts
656
- /**
657
- * A UTC text twin beside every timestamp column of a graph answer.
658
- *
659
- * The graph serves integer milliseconds. A model that converts them by head
660
- * gets the time of day wrong about one time in three (loop 3, 2026-10-10:
661
- * four of twelve answers about one block named 00:15, 09:35, 06:35 or 09:15
662
- * for 23:55:43 UTC). Hosts without a shell (Claude Desktop) have no other
663
- * way to convert, so the proxy writes `<column>_utc` next to each one.
664
- */
665
- const TIMESTAMP_KEY = /(_timestamp|_time|_at|_seen|_active)$/;
666
- const MIN_MS = 0xe8d4a51000;
667
- const MAX_MS = 4e12;
668
- function isRecord$2(value) {
669
- return typeof value === "object" && value !== null && !Array.isArray(value);
670
- }
671
- /** `2026-08-05 23:55:43 UTC` for epoch milliseconds. */
672
- function utcText(ms) {
673
- return `${new Date(ms).toISOString().slice(0, 19).replace("T", " ")} UTC`;
674
- }
675
- function isEpochMs(value) {
676
- return typeof value === "number" && Number.isInteger(value) && value >= MIN_MS && value <= MAX_MS;
677
- }
678
- /**
679
- * One row with a `<key>_utc` string after every epoch-millisecond timestamp
680
- * column. A column the model itself named `*_utc` (its query asked for the
681
- * twin as a property, see stripUtcProperties) arrives as milliseconds and is
682
- * rendered as UTC text in place: the model gets the column it asked for.
683
- */
684
- function addTimestampText(row) {
685
- const out = {};
686
- for (const [key, value] of Object.entries(row)) {
687
- out[key] = key.endsWith("_utc") && isEpochMs(value) ? utcText(value) : value;
688
- if (TIMESTAMP_KEY.test(key) && isEpochMs(value) && !(`${key}_utc` in row)) out[`${key}_utc`] = utcText(value);
689
- }
690
- return out;
691
- }
692
- /**
693
- * The `_utc` twin is added to a reply by the proxy; it is no graph property.
694
- * Thirteen of 48 loop-5 rounds wrote `f.first_seen_timestamp_utc` into a
695
- * query anyway: topology answers null, a SWAP read is refused. The property
696
- * reference is rewritten to its millisecond column, `x.<column>_utc` to
697
- * `x.<column>`; an alias after AS is not a property and stays as written, so
698
- * `f.first_seen_timestamp_utc AS first_seen_utc` yields a `first_seen_utc`
699
- * column that addTimestampText renders as text.
700
- */
701
- function stripUtcProperties(query) {
702
- return stripEthProperties(query.replace(/\.([A-Za-z_][A-Za-z0-9_]*?)_utc\b/g, ".$1"));
703
- }
704
- /** graph_query and graph_query_batch arguments with stripUtcProperties applied to every query. */
705
- function withoutUtcProperties(toolName, args) {
706
- if (toolName === "graph_query" && typeof args.query === "string") return {
707
- ...args,
708
- query: stripUtcProperties(args.query)
709
- };
710
- if (toolName === "graph_query_batch" && Array.isArray(args.queries)) return {
711
- ...args,
712
- queries: args.queries.map((entry) => isRecord$2(entry) && typeof entry.query === "string" ? {
713
- ...entry,
714
- query: stripUtcProperties(entry.query)
715
- } : entry)
716
- };
717
- return args;
718
- }
719
- function mapResults(holder) {
720
- if (!isRecord$2(holder) || !Array.isArray(holder.results)) return holder;
721
- return {
722
- ...holder,
723
- results: holder.results.map((row) => isRecord$2(row) ? addBalanceText(addTimestampText(row)) : row)
724
- };
725
- }
726
- /**
727
- * The structured content of graph_query (facts.query.results) or
728
- * graph_query_batch (facts.queries[].results) with the UTC twins and the
729
- * ETH twins (balance-text.ts) added.
730
- * Anything else comes back as it was.
731
- */
732
- function withTimestampText(structuredContent) {
733
- if (!isRecord$2(structuredContent) || !isRecord$2(structuredContent.facts)) return structuredContent;
734
- const facts = structuredContent.facts;
735
- if (!isRecord$2(facts.query) && !Array.isArray(facts.queries)) return structuredContent;
736
- const next = { ...facts };
737
- if (isRecord$2(facts.query)) next.query = mapResults(facts.query);
738
- if (Array.isArray(facts.queries)) next.queries = facts.queries.map(mapResults);
739
- return {
740
- ...structuredContent,
741
- facts: next
742
- };
743
- }
744
- /**
745
- * A tool result whose structured content and single JSON text block both carry
746
- * the UTC twins. A text block that is not the JSON of the structured content
747
- * is left alone.
748
- */
749
- function withTimestampTextResult(result) {
750
- const structured = withTimestampText(result.structuredContent);
751
- if (structured === result.structuredContent) return result;
752
- const content = result.content ?? [];
753
- const only = content.length === 1 && content[0]?.type === "text" ? content[0] : null;
754
- let parsed = void 0;
755
- try {
756
- parsed = only?.text ? JSON.parse(only.text) : void 0;
757
- } catch {
758
- parsed = void 0;
759
- }
760
- const textIsStructured = parsed !== void 0 && JSON.stringify(parsed) === JSON.stringify(result.structuredContent);
761
- return {
762
- ...result,
763
- structuredContent: structured,
764
- content: textIsStructured ? [{
765
- ...only,
766
- text: JSON.stringify(structured)
767
- }] : content
768
- };
769
- }
770
- //#endregion
771
609
  //#region src/mcp/answer-hint.ts
772
610
  /**
773
611
  * The two answer rules that still slip in long answers, carried on every
@@ -1346,7 +1184,7 @@ function normalizeRemoteToolResult(toolName, result) {
1346
1184
  _meta: sanitizeRemoteMeta(result._meta),
1347
1185
  isError: result.isError
1348
1186
  };
1349
- return toolName === "graph_query" || toolName === "graph_query_batch" ? withTimestampTextResult(withAnswerHintResult(normalized)) : normalized;
1187
+ return toolName === "graph_query" || toolName === "graph_query_batch" ? require_print_result.withTimestampTextResult(withAnswerHintResult(normalized)) : normalized;
1350
1188
  }
1351
1189
  function cleanNetworkCapabilities(value) {
1352
1190
  const structuredContent = isRecord(value) ? value.structuredContent : void 0;
@@ -1853,7 +1691,7 @@ async function createProxy() {
1853
1691
  }],
1854
1692
  isError: true
1855
1693
  };
1856
- const normalizedArgs = withoutUtcProperties(tool.name, normalizeRemoteToolArguments(tool.name, args));
1694
+ const normalizedArgs = require_print_result.withoutUtcProperties(tool.name, normalizeRemoteToolArguments(tool.name, args));
1857
1695
  const validationError = validateKnownPublicToolArguments(tool.name, normalizedArgs);
1858
1696
  if (validationError) return {
1859
1697
  content: [{
@@ -1,6 +1,6 @@
1
- import { a as PACKAGE_VERSION } from "./print-result-k9kXVR2c.mjs";
1
+ import { a as withTimestampTextResult, c as PACKAGE_VERSION, o as withoutUtcProperties } from "./print-result-CJ8d7lmz.mjs";
2
2
  import { t as PaymentRequiredError } from "./client-gsLi528K.mjs";
3
- import { i as isAppOnlyTool, n as PUBLIC_MCP_TOOL_ALLOWED_ARGS, r as PUBLIC_MCP_TOOL_REQUIRED_ARGS, t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-P9wETx-d.mjs";
3
+ import { i as isAppOnlyTool, n as PUBLIC_MCP_TOOL_ALLOWED_ARGS, r as PUBLIC_MCP_TOOL_REQUIRED_ARGS, t as HIDDEN_REMOTE_TOOL_NAMES } from "./tool-visibility-kclgGKL5.mjs";
4
4
  import { t as primitiveBackendUsageStatus } from "./usage-status-CXOnrq-s.mjs";
5
5
  import { n as unavailableSubscriptionStatus } from "./subscription-status-DA9DCFrC.mjs";
6
6
  import { r as mirrorGraphNetworkCapabilities } from "./capabilities-BBVDIECk.mjs";
@@ -97,17 +97,17 @@ function rateLimitedResult(toolName, err) {
97
97
  const BUSY_CODE = "chain_busy";
98
98
  const BUSY_WAIT_MS = 1100;
99
99
  const defaultSleep = (ms) => new Promise((done) => setTimeout(done, ms));
100
- function isRecord$4(value) {
100
+ function isRecord$3(value) {
101
101
  return typeof value === "object" && value !== null && !Array.isArray(value);
102
102
  }
103
103
  function membersOf(result) {
104
- const facts = isRecord$4(result.structuredContent) ? result.structuredContent.facts : void 0;
105
- const queries = isRecord$4(facts) ? facts.queries : void 0;
106
- return Array.isArray(queries) && queries.every(isRecord$4) ? queries : null;
104
+ const facts = isRecord$3(result.structuredContent) ? result.structuredContent.facts : void 0;
105
+ const queries = isRecord$3(facts) ? facts.queries : void 0;
106
+ return Array.isArray(queries) && queries.every(isRecord$3) ? queries : null;
107
107
  }
108
108
  function isBusyMember(member) {
109
109
  const detail = member.error_detail;
110
- return isRecord$4(detail) && detail.code === BUSY_CODE;
110
+ return isRecord$3(detail) && detail.code === BUSY_CODE;
111
111
  }
112
112
  /** The ids of the members the server refused as chain_busy. */
113
113
  function busyMemberIds(result) {
@@ -138,7 +138,7 @@ function mergeBatchAnswers(first, again) {
138
138
  const merged = members.map((m) => isBusyMember(m) && byId.has(String(m.id)) ? byId.get(String(m.id)) : m);
139
139
  const structured = first.structuredContent;
140
140
  const facts = structured.facts;
141
- const batch = isRecord$4(facts.batch) ? recount(facts.batch, merged) : facts.batch;
141
+ const batch = isRecord$3(facts.batch) ? recount(facts.batch, merged) : facts.batch;
142
142
  const nextStructured = {
143
143
  ...structured,
144
144
  facts: {
@@ -182,7 +182,7 @@ async function withBusyMemberRetry(args, call, sleep = defaultSleep) {
182
182
  function isBusyResult(result) {
183
183
  if (!result.isError) return false;
184
184
  const first = Array.isArray(result.content) ? result.content[0] : void 0;
185
- const text = isRecord$4(first) && typeof first.text === "string" ? first.text : "";
185
+ const text = isRecord$3(first) && typeof first.text === "string" ? first.text : "";
186
186
  return /^(Query execution failed: )?chain_busy\b/.test(text.trim());
187
187
  }
188
188
  /**
@@ -329,7 +329,7 @@ function send(res, status, contentType, body) {
329
329
  function sendJson(res, status, value) {
330
330
  send(res, status, "application/json; charset=utf-8", JSON.stringify(value));
331
331
  }
332
- function isRecord$3(value) {
332
+ function isRecord$2(value) {
333
333
  return !!value && typeof value === "object" && !Array.isArray(value);
334
334
  }
335
335
  var BodyTooLarge = class extends Error {};
@@ -420,8 +420,8 @@ async function startViewServer(options) {
420
420
  sendJson(res, 400, { error: "invalid JSON" });
421
421
  return;
422
422
  }
423
- const args = isRecord$3(body) ? body.arguments ?? {} : void 0;
424
- if (!isRecord$3(args)) {
423
+ const args = isRecord$2(body) ? body.arguments ?? {} : void 0;
424
+ if (!isRecord$2(args)) {
425
425
  sendJson(res, 400, { error: "arguments must be an object" });
426
426
  return;
427
427
  }
@@ -602,168 +602,6 @@ async function connectRemote(options) {
602
602
  };
603
603
  }
604
604
  //#endregion
605
- //#region src/mcp/balance-text.ts
606
- /**
607
- * An ETH text twin beside every native balance column of a chain answer.
608
- *
609
- * `USE chain` serves `balance` as wei in decimal text, exact and 16 to 22
610
- * digits long. A host without a shell (Claude Desktop, 2026-10-21) printed
611
- * `10003752747799400` to the user as the balance. A model that divides by
612
- * 10^18 by head misplaces the point, so the proxy writes `<column>_eth`
613
- * next to each one, the same way `<column>_utc` sits beside a timestamp.
614
- */
615
- const BALANCE_KEY = /(^|_)balance$/;
616
- const WEI_TEXT = /^[0-9]{1,78}$/;
617
- const WEI_PER_ETH = 10n ** 18n;
618
- /** `0.0100037527477994` for `10003752747799400` wei: exact, no exponent, no trailing zeros. */
619
- function ethText(wei) {
620
- const value = BigInt(wei);
621
- const whole = value / WEI_PER_ETH;
622
- const frac = (value % WEI_PER_ETH).toString().padStart(18, "0").replace(/0+$/, "");
623
- return frac === "" ? whole.toString() : `${whole}.${frac}`;
624
- }
625
- function isWeiText(value) {
626
- return typeof value === "string" && WEI_TEXT.test(value);
627
- }
628
- /**
629
- * One row with a `<key>_eth` string after every wei balance column that has
630
- * no twin yet. A column the model itself named `*_eth` (an alias such as
631
- * `a.balance_eth AS held_eth`, see stripEthProperties) arrives as wei and is
632
- * rendered as ETH text in place: the model gets the column it asked for.
633
- */
634
- function addBalanceText(row) {
635
- const out = {};
636
- for (const [key, value] of Object.entries(row)) {
637
- out[key] = key.endsWith("_eth") && isWeiText(value) ? ethText(value) : value;
638
- if (BALANCE_KEY.test(key) && isWeiText(value) && !(`${key}_eth` in row)) out[`${key}_eth`] = ethText(value);
639
- }
640
- return out;
641
- }
642
- /**
643
- * The `_eth` twin is added by the proxy; it is no chain property. A query
644
- * that names `a.balance_eth` is rewritten to `a.balance`, so the row comes
645
- * back and the twin is written beside it. An alias after AS stays as written.
646
- */
647
- function stripEthProperties(query) {
648
- return query.replace(/\.((?:[A-Za-z_][A-Za-z0-9_]*_)?balance)_eth\b/g, ".$1");
649
- }
650
- //#endregion
651
- //#region src/mcp/timestamp-text.ts
652
- /**
653
- * A UTC text twin beside every timestamp column of a graph answer.
654
- *
655
- * The graph serves integer milliseconds. A model that converts them by head
656
- * gets the time of day wrong about one time in three (loop 3, 2026-10-10:
657
- * four of twelve answers about one block named 00:15, 09:35, 06:35 or 09:15
658
- * for 23:55:43 UTC). Hosts without a shell (Claude Desktop) have no other
659
- * way to convert, so the proxy writes `<column>_utc` next to each one.
660
- */
661
- const TIMESTAMP_KEY = /(_timestamp|_time|_at|_seen|_active)$/;
662
- const MIN_MS = 0xe8d4a51000;
663
- const MAX_MS = 4e12;
664
- function isRecord$2(value) {
665
- return typeof value === "object" && value !== null && !Array.isArray(value);
666
- }
667
- /** `2026-08-05 23:55:43 UTC` for epoch milliseconds. */
668
- function utcText(ms) {
669
- return `${new Date(ms).toISOString().slice(0, 19).replace("T", " ")} UTC`;
670
- }
671
- function isEpochMs(value) {
672
- return typeof value === "number" && Number.isInteger(value) && value >= MIN_MS && value <= MAX_MS;
673
- }
674
- /**
675
- * One row with a `<key>_utc` string after every epoch-millisecond timestamp
676
- * column. A column the model itself named `*_utc` (its query asked for the
677
- * twin as a property, see stripUtcProperties) arrives as milliseconds and is
678
- * rendered as UTC text in place: the model gets the column it asked for.
679
- */
680
- function addTimestampText(row) {
681
- const out = {};
682
- for (const [key, value] of Object.entries(row)) {
683
- out[key] = key.endsWith("_utc") && isEpochMs(value) ? utcText(value) : value;
684
- if (TIMESTAMP_KEY.test(key) && isEpochMs(value) && !(`${key}_utc` in row)) out[`${key}_utc`] = utcText(value);
685
- }
686
- return out;
687
- }
688
- /**
689
- * The `_utc` twin is added to a reply by the proxy; it is no graph property.
690
- * Thirteen of 48 loop-5 rounds wrote `f.first_seen_timestamp_utc` into a
691
- * query anyway: topology answers null, a SWAP read is refused. The property
692
- * reference is rewritten to its millisecond column, `x.<column>_utc` to
693
- * `x.<column>`; an alias after AS is not a property and stays as written, so
694
- * `f.first_seen_timestamp_utc AS first_seen_utc` yields a `first_seen_utc`
695
- * column that addTimestampText renders as text.
696
- */
697
- function stripUtcProperties(query) {
698
- return stripEthProperties(query.replace(/\.([A-Za-z_][A-Za-z0-9_]*?)_utc\b/g, ".$1"));
699
- }
700
- /** graph_query and graph_query_batch arguments with stripUtcProperties applied to every query. */
701
- function withoutUtcProperties(toolName, args) {
702
- if (toolName === "graph_query" && typeof args.query === "string") return {
703
- ...args,
704
- query: stripUtcProperties(args.query)
705
- };
706
- if (toolName === "graph_query_batch" && Array.isArray(args.queries)) return {
707
- ...args,
708
- queries: args.queries.map((entry) => isRecord$2(entry) && typeof entry.query === "string" ? {
709
- ...entry,
710
- query: stripUtcProperties(entry.query)
711
- } : entry)
712
- };
713
- return args;
714
- }
715
- function mapResults(holder) {
716
- if (!isRecord$2(holder) || !Array.isArray(holder.results)) return holder;
717
- return {
718
- ...holder,
719
- results: holder.results.map((row) => isRecord$2(row) ? addBalanceText(addTimestampText(row)) : row)
720
- };
721
- }
722
- /**
723
- * The structured content of graph_query (facts.query.results) or
724
- * graph_query_batch (facts.queries[].results) with the UTC twins and the
725
- * ETH twins (balance-text.ts) added.
726
- * Anything else comes back as it was.
727
- */
728
- function withTimestampText(structuredContent) {
729
- if (!isRecord$2(structuredContent) || !isRecord$2(structuredContent.facts)) return structuredContent;
730
- const facts = structuredContent.facts;
731
- if (!isRecord$2(facts.query) && !Array.isArray(facts.queries)) return structuredContent;
732
- const next = { ...facts };
733
- if (isRecord$2(facts.query)) next.query = mapResults(facts.query);
734
- if (Array.isArray(facts.queries)) next.queries = facts.queries.map(mapResults);
735
- return {
736
- ...structuredContent,
737
- facts: next
738
- };
739
- }
740
- /**
741
- * A tool result whose structured content and single JSON text block both carry
742
- * the UTC twins. A text block that is not the JSON of the structured content
743
- * is left alone.
744
- */
745
- function withTimestampTextResult(result) {
746
- const structured = withTimestampText(result.structuredContent);
747
- if (structured === result.structuredContent) return result;
748
- const content = result.content ?? [];
749
- const only = content.length === 1 && content[0]?.type === "text" ? content[0] : null;
750
- let parsed = void 0;
751
- try {
752
- parsed = only?.text ? JSON.parse(only.text) : void 0;
753
- } catch {
754
- parsed = void 0;
755
- }
756
- const textIsStructured = parsed !== void 0 && JSON.stringify(parsed) === JSON.stringify(result.structuredContent);
757
- return {
758
- ...result,
759
- structuredContent: structured,
760
- content: textIsStructured ? [{
761
- ...only,
762
- text: JSON.stringify(structured)
763
- }] : content
764
- };
765
- }
766
- //#endregion
767
605
  //#region src/mcp/answer-hint.ts
768
606
  /**
769
607
  * The two answer rules that still slip in long answers, carried on every