chain-insights 0.64.0 → 0.65.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +9 -7
- package/dist/cli.mjs +9 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/mcp-proxy.cjs +14 -129
- package/dist/mcp-proxy.mjs +12 -127
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/print-result--j99yoCV.cjs +396 -0
- package/dist/print-result-CZ2cH30Z.mjs +351 -0
- package/dist/print-result-CZ2cH30Z.mjs.map +1 -0
- package/dist/{tool-visibility-CDe-ft0L.cjs → tool-visibility-BppZZD77.cjs} +1 -1
- package/dist/{tool-visibility-P9wETx-d.mjs → tool-visibility-C5IgMQuu.mjs} +2 -2
- package/dist/{tool-visibility-P9wETx-d.mjs.map → tool-visibility-C5IgMQuu.mjs.map} +1 -1
- package/dist/{transport-error-S0za4TCe.mjs → transport-error--eFoC7tP.mjs} +2 -2
- package/dist/{transport-error-S0za4TCe.mjs.map → transport-error--eFoC7tP.mjs.map} +1 -1
- package/dist/{transport-error-CenkuT7T.cjs → transport-error-YlHlPEWJ.cjs} +1 -1
- package/dist/{update-Rn1RSqrb.cjs → update-CdMN-5BW.cjs} +1 -1
- package/dist/{update-7TewxG_2.mjs → update-DRZfepx8.mjs} +2 -2
- package/dist/{update-7TewxG_2.mjs.map → update-DRZfepx8.mjs.map} +1 -1
- package/package.json +1 -1
- package/skills/chain-insights-cypher/SKILL.md +6 -5
- package/dist/print-result-BEA_PyNz.cjs +0 -178
- package/dist/print-result-k9kXVR2c.mjs +0 -151
- package/dist/print-result-k9kXVR2c.mjs.map +0 -1
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -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
|
|
3
|
+
const require_print_result = require("./print-result--j99yoCV.cjs");
|
|
4
4
|
const require_client = require("./client-Ypw9BNbn.cjs");
|
|
5
|
-
const require_tool_visibility = require("./tool-visibility-
|
|
5
|
+
const require_tool_visibility = require("./tool-visibility-BppZZD77.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$
|
|
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$
|
|
109
|
-
const queries = isRecord$
|
|
110
|
-
return Array.isArray(queries) && queries.every(isRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
428
|
-
if (!isRecord$
|
|
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,121 +606,6 @@ async function connectRemote(options) {
|
|
|
606
606
|
};
|
|
607
607
|
}
|
|
608
608
|
//#endregion
|
|
609
|
-
//#region src/mcp/timestamp-text.ts
|
|
610
|
-
/**
|
|
611
|
-
* A UTC text twin beside every timestamp column of a graph answer.
|
|
612
|
-
*
|
|
613
|
-
* The graph serves integer milliseconds. A model that converts them by head
|
|
614
|
-
* gets the time of day wrong about one time in three (loop 3, 2026-10-10:
|
|
615
|
-
* four of twelve answers about one block named 00:15, 09:35, 06:35 or 09:15
|
|
616
|
-
* for 23:55:43 UTC). Hosts without a shell (Claude Desktop) have no other
|
|
617
|
-
* way to convert, so the proxy writes `<column>_utc` next to each one.
|
|
618
|
-
*/
|
|
619
|
-
const TIMESTAMP_KEY = /(_timestamp|_time|_at|_seen|_active)$/;
|
|
620
|
-
const MIN_MS = 0xe8d4a51000;
|
|
621
|
-
const MAX_MS = 4e12;
|
|
622
|
-
function isRecord$2(value) {
|
|
623
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
624
|
-
}
|
|
625
|
-
/** `2026-08-05 23:55:43 UTC` for epoch milliseconds. */
|
|
626
|
-
function utcText(ms) {
|
|
627
|
-
return `${new Date(ms).toISOString().slice(0, 19).replace("T", " ")} UTC`;
|
|
628
|
-
}
|
|
629
|
-
function isEpochMs(value) {
|
|
630
|
-
return typeof value === "number" && Number.isInteger(value) && value >= MIN_MS && value <= MAX_MS;
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* One row with a `<key>_utc` string after every epoch-millisecond timestamp
|
|
634
|
-
* column. A column the model itself named `*_utc` (its query asked for the
|
|
635
|
-
* twin as a property, see stripUtcProperties) arrives as milliseconds and is
|
|
636
|
-
* rendered as UTC text in place: the model gets the column it asked for.
|
|
637
|
-
*/
|
|
638
|
-
function addTimestampText(row) {
|
|
639
|
-
const out = {};
|
|
640
|
-
for (const [key, value] of Object.entries(row)) {
|
|
641
|
-
out[key] = key.endsWith("_utc") && isEpochMs(value) ? utcText(value) : value;
|
|
642
|
-
if (TIMESTAMP_KEY.test(key) && isEpochMs(value) && !(`${key}_utc` in row)) out[`${key}_utc`] = utcText(value);
|
|
643
|
-
}
|
|
644
|
-
return out;
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* The `_utc` twin is added to a reply by the proxy; it is no graph property.
|
|
648
|
-
* Thirteen of 48 loop-5 rounds wrote `f.first_seen_timestamp_utc` into a
|
|
649
|
-
* query anyway: topology answers null, a SWAP read is refused. The property
|
|
650
|
-
* reference is rewritten to its millisecond column, `x.<column>_utc` to
|
|
651
|
-
* `x.<column>`; an alias after AS is not a property and stays as written, so
|
|
652
|
-
* `f.first_seen_timestamp_utc AS first_seen_utc` yields a `first_seen_utc`
|
|
653
|
-
* column that addTimestampText renders as text.
|
|
654
|
-
*/
|
|
655
|
-
function stripUtcProperties(query) {
|
|
656
|
-
return query.replace(/\.([A-Za-z_][A-Za-z0-9_]*?)_utc\b/g, ".$1");
|
|
657
|
-
}
|
|
658
|
-
/** graph_query and graph_query_batch arguments with stripUtcProperties applied to every query. */
|
|
659
|
-
function withoutUtcProperties(toolName, args) {
|
|
660
|
-
if (toolName === "graph_query" && typeof args.query === "string") return {
|
|
661
|
-
...args,
|
|
662
|
-
query: stripUtcProperties(args.query)
|
|
663
|
-
};
|
|
664
|
-
if (toolName === "graph_query_batch" && Array.isArray(args.queries)) return {
|
|
665
|
-
...args,
|
|
666
|
-
queries: args.queries.map((entry) => isRecord$2(entry) && typeof entry.query === "string" ? {
|
|
667
|
-
...entry,
|
|
668
|
-
query: stripUtcProperties(entry.query)
|
|
669
|
-
} : entry)
|
|
670
|
-
};
|
|
671
|
-
return args;
|
|
672
|
-
}
|
|
673
|
-
function mapResults(holder) {
|
|
674
|
-
if (!isRecord$2(holder) || !Array.isArray(holder.results)) return holder;
|
|
675
|
-
return {
|
|
676
|
-
...holder,
|
|
677
|
-
results: holder.results.map((row) => isRecord$2(row) ? addTimestampText(row) : row)
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
/**
|
|
681
|
-
* The structured content of graph_query (facts.query.results) or
|
|
682
|
-
* graph_query_batch (facts.queries[].results) with the UTC twins added.
|
|
683
|
-
* Anything else comes back as it was.
|
|
684
|
-
*/
|
|
685
|
-
function withTimestampText(structuredContent) {
|
|
686
|
-
if (!isRecord$2(structuredContent) || !isRecord$2(structuredContent.facts)) return structuredContent;
|
|
687
|
-
const facts = structuredContent.facts;
|
|
688
|
-
if (!isRecord$2(facts.query) && !Array.isArray(facts.queries)) return structuredContent;
|
|
689
|
-
const next = { ...facts };
|
|
690
|
-
if (isRecord$2(facts.query)) next.query = mapResults(facts.query);
|
|
691
|
-
if (Array.isArray(facts.queries)) next.queries = facts.queries.map(mapResults);
|
|
692
|
-
return {
|
|
693
|
-
...structuredContent,
|
|
694
|
-
facts: next
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
/**
|
|
698
|
-
* A tool result whose structured content and single JSON text block both carry
|
|
699
|
-
* the UTC twins. A text block that is not the JSON of the structured content
|
|
700
|
-
* is left alone.
|
|
701
|
-
*/
|
|
702
|
-
function withTimestampTextResult(result) {
|
|
703
|
-
const structured = withTimestampText(result.structuredContent);
|
|
704
|
-
if (structured === result.structuredContent) return result;
|
|
705
|
-
const content = result.content ?? [];
|
|
706
|
-
const only = content.length === 1 && content[0]?.type === "text" ? content[0] : null;
|
|
707
|
-
let parsed = void 0;
|
|
708
|
-
try {
|
|
709
|
-
parsed = only?.text ? JSON.parse(only.text) : void 0;
|
|
710
|
-
} catch {
|
|
711
|
-
parsed = void 0;
|
|
712
|
-
}
|
|
713
|
-
const textIsStructured = parsed !== void 0 && JSON.stringify(parsed) === JSON.stringify(result.structuredContent);
|
|
714
|
-
return {
|
|
715
|
-
...result,
|
|
716
|
-
structuredContent: structured,
|
|
717
|
-
content: textIsStructured ? [{
|
|
718
|
-
...only,
|
|
719
|
-
text: JSON.stringify(structured)
|
|
720
|
-
}] : content
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
//#endregion
|
|
724
609
|
//#region src/mcp/answer-hint.ts
|
|
725
610
|
/**
|
|
726
611
|
* The two answer rules that still slip in long answers, carried on every
|
|
@@ -1299,7 +1184,7 @@ function normalizeRemoteToolResult(toolName, result) {
|
|
|
1299
1184
|
_meta: sanitizeRemoteMeta(result._meta),
|
|
1300
1185
|
isError: result.isError
|
|
1301
1186
|
};
|
|
1302
|
-
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;
|
|
1303
1188
|
}
|
|
1304
1189
|
function cleanNetworkCapabilities(value) {
|
|
1305
1190
|
const structuredContent = isRecord(value) ? value.structuredContent : void 0;
|
|
@@ -1806,7 +1691,7 @@ async function createProxy() {
|
|
|
1806
1691
|
}],
|
|
1807
1692
|
isError: true
|
|
1808
1693
|
};
|
|
1809
|
-
const normalizedArgs = withoutUtcProperties(tool.name, normalizeRemoteToolArguments(tool.name, args));
|
|
1694
|
+
const normalizedArgs = require_print_result.withoutUtcProperties(tool.name, normalizeRemoteToolArguments(tool.name, args));
|
|
1810
1695
|
const validationError = validateKnownPublicToolArguments(tool.name, normalizedArgs);
|
|
1811
1696
|
if (validationError) return {
|
|
1812
1697
|
content: [{
|
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as PACKAGE_VERSION } from "./print-result-
|
|
1
|
+
import { a as withTimestampTextResult, c as PACKAGE_VERSION, o as withoutUtcProperties } from "./print-result-CZ2cH30Z.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-
|
|
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-C5IgMQuu.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$
|
|
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$
|
|
105
|
-
const queries = isRecord$
|
|
106
|
-
return Array.isArray(queries) && queries.every(isRecord$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
424
|
-
if (!isRecord$
|
|
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,121 +602,6 @@ async function connectRemote(options) {
|
|
|
602
602
|
};
|
|
603
603
|
}
|
|
604
604
|
//#endregion
|
|
605
|
-
//#region src/mcp/timestamp-text.ts
|
|
606
|
-
/**
|
|
607
|
-
* A UTC text twin beside every timestamp column of a graph answer.
|
|
608
|
-
*
|
|
609
|
-
* The graph serves integer milliseconds. A model that converts them by head
|
|
610
|
-
* gets the time of day wrong about one time in three (loop 3, 2026-10-10:
|
|
611
|
-
* four of twelve answers about one block named 00:15, 09:35, 06:35 or 09:15
|
|
612
|
-
* for 23:55:43 UTC). Hosts without a shell (Claude Desktop) have no other
|
|
613
|
-
* way to convert, so the proxy writes `<column>_utc` next to each one.
|
|
614
|
-
*/
|
|
615
|
-
const TIMESTAMP_KEY = /(_timestamp|_time|_at|_seen|_active)$/;
|
|
616
|
-
const MIN_MS = 0xe8d4a51000;
|
|
617
|
-
const MAX_MS = 4e12;
|
|
618
|
-
function isRecord$2(value) {
|
|
619
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
620
|
-
}
|
|
621
|
-
/** `2026-08-05 23:55:43 UTC` for epoch milliseconds. */
|
|
622
|
-
function utcText(ms) {
|
|
623
|
-
return `${new Date(ms).toISOString().slice(0, 19).replace("T", " ")} UTC`;
|
|
624
|
-
}
|
|
625
|
-
function isEpochMs(value) {
|
|
626
|
-
return typeof value === "number" && Number.isInteger(value) && value >= MIN_MS && value <= MAX_MS;
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
|
-
* One row with a `<key>_utc` string after every epoch-millisecond timestamp
|
|
630
|
-
* column. A column the model itself named `*_utc` (its query asked for the
|
|
631
|
-
* twin as a property, see stripUtcProperties) arrives as milliseconds and is
|
|
632
|
-
* rendered as UTC text in place: the model gets the column it asked for.
|
|
633
|
-
*/
|
|
634
|
-
function addTimestampText(row) {
|
|
635
|
-
const out = {};
|
|
636
|
-
for (const [key, value] of Object.entries(row)) {
|
|
637
|
-
out[key] = key.endsWith("_utc") && isEpochMs(value) ? utcText(value) : value;
|
|
638
|
-
if (TIMESTAMP_KEY.test(key) && isEpochMs(value) && !(`${key}_utc` in row)) out[`${key}_utc`] = utcText(value);
|
|
639
|
-
}
|
|
640
|
-
return out;
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* The `_utc` twin is added to a reply by the proxy; it is no graph property.
|
|
644
|
-
* Thirteen of 48 loop-5 rounds wrote `f.first_seen_timestamp_utc` into a
|
|
645
|
-
* query anyway: topology answers null, a SWAP read is refused. The property
|
|
646
|
-
* reference is rewritten to its millisecond column, `x.<column>_utc` to
|
|
647
|
-
* `x.<column>`; an alias after AS is not a property and stays as written, so
|
|
648
|
-
* `f.first_seen_timestamp_utc AS first_seen_utc` yields a `first_seen_utc`
|
|
649
|
-
* column that addTimestampText renders as text.
|
|
650
|
-
*/
|
|
651
|
-
function stripUtcProperties(query) {
|
|
652
|
-
return query.replace(/\.([A-Za-z_][A-Za-z0-9_]*?)_utc\b/g, ".$1");
|
|
653
|
-
}
|
|
654
|
-
/** graph_query and graph_query_batch arguments with stripUtcProperties applied to every query. */
|
|
655
|
-
function withoutUtcProperties(toolName, args) {
|
|
656
|
-
if (toolName === "graph_query" && typeof args.query === "string") return {
|
|
657
|
-
...args,
|
|
658
|
-
query: stripUtcProperties(args.query)
|
|
659
|
-
};
|
|
660
|
-
if (toolName === "graph_query_batch" && Array.isArray(args.queries)) return {
|
|
661
|
-
...args,
|
|
662
|
-
queries: args.queries.map((entry) => isRecord$2(entry) && typeof entry.query === "string" ? {
|
|
663
|
-
...entry,
|
|
664
|
-
query: stripUtcProperties(entry.query)
|
|
665
|
-
} : entry)
|
|
666
|
-
};
|
|
667
|
-
return args;
|
|
668
|
-
}
|
|
669
|
-
function mapResults(holder) {
|
|
670
|
-
if (!isRecord$2(holder) || !Array.isArray(holder.results)) return holder;
|
|
671
|
-
return {
|
|
672
|
-
...holder,
|
|
673
|
-
results: holder.results.map((row) => isRecord$2(row) ? addTimestampText(row) : row)
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* The structured content of graph_query (facts.query.results) or
|
|
678
|
-
* graph_query_batch (facts.queries[].results) with the UTC twins added.
|
|
679
|
-
* Anything else comes back as it was.
|
|
680
|
-
*/
|
|
681
|
-
function withTimestampText(structuredContent) {
|
|
682
|
-
if (!isRecord$2(structuredContent) || !isRecord$2(structuredContent.facts)) return structuredContent;
|
|
683
|
-
const facts = structuredContent.facts;
|
|
684
|
-
if (!isRecord$2(facts.query) && !Array.isArray(facts.queries)) return structuredContent;
|
|
685
|
-
const next = { ...facts };
|
|
686
|
-
if (isRecord$2(facts.query)) next.query = mapResults(facts.query);
|
|
687
|
-
if (Array.isArray(facts.queries)) next.queries = facts.queries.map(mapResults);
|
|
688
|
-
return {
|
|
689
|
-
...structuredContent,
|
|
690
|
-
facts: next
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* A tool result whose structured content and single JSON text block both carry
|
|
695
|
-
* the UTC twins. A text block that is not the JSON of the structured content
|
|
696
|
-
* is left alone.
|
|
697
|
-
*/
|
|
698
|
-
function withTimestampTextResult(result) {
|
|
699
|
-
const structured = withTimestampText(result.structuredContent);
|
|
700
|
-
if (structured === result.structuredContent) return result;
|
|
701
|
-
const content = result.content ?? [];
|
|
702
|
-
const only = content.length === 1 && content[0]?.type === "text" ? content[0] : null;
|
|
703
|
-
let parsed = void 0;
|
|
704
|
-
try {
|
|
705
|
-
parsed = only?.text ? JSON.parse(only.text) : void 0;
|
|
706
|
-
} catch {
|
|
707
|
-
parsed = void 0;
|
|
708
|
-
}
|
|
709
|
-
const textIsStructured = parsed !== void 0 && JSON.stringify(parsed) === JSON.stringify(result.structuredContent);
|
|
710
|
-
return {
|
|
711
|
-
...result,
|
|
712
|
-
structuredContent: structured,
|
|
713
|
-
content: textIsStructured ? [{
|
|
714
|
-
...only,
|
|
715
|
-
text: JSON.stringify(structured)
|
|
716
|
-
}] : content
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
//#endregion
|
|
720
605
|
//#region src/mcp/answer-hint.ts
|
|
721
606
|
/**
|
|
722
607
|
* The two answer rules that still slip in long answers, carried on every
|