kimiflare 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +548 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -653,7 +653,8 @@ async function logTurnDebug(ctx) {
|
|
|
653
653
|
toolTotalReducedBytes: toolTotalReduced,
|
|
654
654
|
toolSavingsPct: toolTotalRaw > 0 ? Math.round((toolTotalRaw - toolTotalReduced) / toolTotalRaw * 100) : 0,
|
|
655
655
|
cacheDiagnostics,
|
|
656
|
-
compaction: ctx.compaction
|
|
656
|
+
compaction: ctx.compaction,
|
|
657
|
+
shadowStrip: ctx.shadowStrip
|
|
657
658
|
});
|
|
658
659
|
}
|
|
659
660
|
var LOG_VERSION;
|
|
@@ -665,6 +666,53 @@ var init_cost_debug = __esm({
|
|
|
665
666
|
}
|
|
666
667
|
});
|
|
667
668
|
|
|
669
|
+
// src/agent/strip-reasoning.ts
|
|
670
|
+
function stripHistoricalReasoning(messages, opts2 = {}) {
|
|
671
|
+
const keepLast = opts2.keepLast ?? DEFAULT_KEEP_LAST;
|
|
672
|
+
const assistantIndices = [];
|
|
673
|
+
for (let i = 0; i < messages.length; i++) {
|
|
674
|
+
if (messages[i].role === "assistant") {
|
|
675
|
+
assistantIndices.push(i);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const preservedSet = keepLast === 0 ? /* @__PURE__ */ new Set() : new Set(assistantIndices.slice(-keepLast));
|
|
679
|
+
return messages.map((m, idx) => {
|
|
680
|
+
if (m.role !== "assistant") return m;
|
|
681
|
+
if (preservedSet.has(idx)) return m;
|
|
682
|
+
const next = { ...m };
|
|
683
|
+
delete next.reasoning_content;
|
|
684
|
+
if (next.tool_calls && next.tool_calls.length > 0) {
|
|
685
|
+
if (typeof next.content === "string") {
|
|
686
|
+
next.content = "";
|
|
687
|
+
} else if (Array.isArray(next.content)) {
|
|
688
|
+
next.content = next.content.map(
|
|
689
|
+
(part) => part.type === "text" ? { ...part, text: "" } : part
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
return next;
|
|
693
|
+
}
|
|
694
|
+
const textLen = typeof next.content === "string" ? next.content.length : Array.isArray(next.content) ? next.content.filter((p) => p.type === "text").reduce((sum, p) => sum + p.text.length, 0) : 0;
|
|
695
|
+
if (textLen <= SUBSTANTIVE_TEXT_THRESHOLD) {
|
|
696
|
+
if (typeof next.content === "string") {
|
|
697
|
+
next.content = "";
|
|
698
|
+
} else if (Array.isArray(next.content)) {
|
|
699
|
+
next.content = next.content.map(
|
|
700
|
+
(part) => part.type === "text" ? { ...part, text: "" } : part
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return next;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
var DEFAULT_KEEP_LAST, SUBSTANTIVE_TEXT_THRESHOLD;
|
|
708
|
+
var init_strip_reasoning = __esm({
|
|
709
|
+
"src/agent/strip-reasoning.ts"() {
|
|
710
|
+
"use strict";
|
|
711
|
+
DEFAULT_KEEP_LAST = 1;
|
|
712
|
+
SUBSTANTIVE_TEXT_THRESHOLD = 200;
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
|
|
668
716
|
// src/agent/loop.ts
|
|
669
717
|
async function runAgentTurn(opts2) {
|
|
670
718
|
const max = opts2.maxToolIterations ?? 50;
|
|
@@ -679,11 +727,44 @@ async function runAgentTurn(opts2) {
|
|
|
679
727
|
let content = "";
|
|
680
728
|
let reasoning = "";
|
|
681
729
|
opts2.callbacks.onAssistantStart?.();
|
|
730
|
+
const stripReasoning = process.env.KIMIFLARE_STRIP_REASONING === "1";
|
|
731
|
+
const shadowStrip = process.env.KIMIFLARE_SHADOW_STRIP === "1";
|
|
732
|
+
const keepLastRaw = process.env.KIMIFLARE_REASONING_KEEP_LAST;
|
|
733
|
+
const keepLast = keepLastRaw ? parseInt(keepLastRaw, 10) : 1;
|
|
734
|
+
let apiMessages = opts2.messages;
|
|
735
|
+
let shadowStripMetrics;
|
|
736
|
+
if (stripReasoning || shadowStrip) {
|
|
737
|
+
const stripped = stripHistoricalReasoning(opts2.messages, {
|
|
738
|
+
keepLast: Number.isNaN(keepLast) ? 1 : keepLast
|
|
739
|
+
});
|
|
740
|
+
if (shadowStrip) {
|
|
741
|
+
const originalSections = analyzePrompt(opts2.messages);
|
|
742
|
+
const strippedSections = analyzePrompt(stripped);
|
|
743
|
+
const originalApproxTokens = originalSections.reduce(
|
|
744
|
+
(sum, s) => sum + s.approxTokens,
|
|
745
|
+
0
|
|
746
|
+
);
|
|
747
|
+
const strippedApproxTokens = strippedSections.reduce(
|
|
748
|
+
(sum, s) => sum + s.approxTokens,
|
|
749
|
+
0
|
|
750
|
+
);
|
|
751
|
+
shadowStripMetrics = {
|
|
752
|
+
originalApproxTokens,
|
|
753
|
+
strippedApproxTokens,
|
|
754
|
+
savingsPct: originalApproxTokens > 0 ? Math.round(
|
|
755
|
+
(originalApproxTokens - strippedApproxTokens) / originalApproxTokens * 100
|
|
756
|
+
) : 0
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
if (stripReasoning) {
|
|
760
|
+
apiMessages = stripped;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
682
763
|
const events = runKimi({
|
|
683
764
|
accountId: opts2.accountId,
|
|
684
765
|
apiToken: opts2.apiToken,
|
|
685
766
|
model: opts2.model,
|
|
686
|
-
messages:
|
|
767
|
+
messages: apiMessages,
|
|
687
768
|
tools: toolDefs,
|
|
688
769
|
signal: opts2.signal,
|
|
689
770
|
temperature: opts2.temperature,
|
|
@@ -750,7 +831,8 @@ async function runAgentTurn(opts2) {
|
|
|
750
831
|
messages: opts2.messages,
|
|
751
832
|
previousMessages,
|
|
752
833
|
toolResults,
|
|
753
|
-
usage: lastUsage
|
|
834
|
+
usage: lastUsage,
|
|
835
|
+
shadowStrip: shadowStripMetrics
|
|
754
836
|
});
|
|
755
837
|
}
|
|
756
838
|
return;
|
|
@@ -778,7 +860,8 @@ async function runAgentTurn(opts2) {
|
|
|
778
860
|
messages: opts2.messages,
|
|
779
861
|
previousMessages,
|
|
780
862
|
toolResults,
|
|
781
|
-
usage: lastUsage
|
|
863
|
+
usage: lastUsage,
|
|
864
|
+
shadowStrip: shadowStripMetrics
|
|
782
865
|
});
|
|
783
866
|
}
|
|
784
867
|
}
|
|
@@ -800,6 +883,7 @@ var init_loop = __esm({
|
|
|
800
883
|
init_registry();
|
|
801
884
|
init_messages();
|
|
802
885
|
init_cost_debug();
|
|
886
|
+
init_strip_reasoning();
|
|
803
887
|
}
|
|
804
888
|
});
|
|
805
889
|
|
|
@@ -872,22 +956,18 @@ function isReadOnlyBash(command) {
|
|
|
872
956
|
return false;
|
|
873
957
|
}
|
|
874
958
|
}
|
|
875
|
-
const argCheck = COMMANDS_NEEDING_ARG_CHECK[cmd];
|
|
876
|
-
if (argCheck) {
|
|
877
|
-
return argCheck(args);
|
|
878
|
-
}
|
|
879
959
|
return READONLY_COMMANDS.has(cmd);
|
|
880
960
|
}
|
|
881
961
|
function systemPromptForMode(m) {
|
|
882
962
|
if (m === "plan") {
|
|
883
|
-
return "\n\nPLAN MODE is active. The user wants you to investigate and produce a plan WITHOUT making any changes. Do not call write, edit, or mutating bash commands. You may use read-only bash commands (e.g., git log, git diff, ls, cat) along with read/glob/grep/web-fetch. At the end, present a concise plan (bullets, files to change, approach). The user will review and then exit plan mode to execute.";
|
|
963
|
+
return "\n\nPLAN MODE is active. The user wants you to investigate and produce a plan WITHOUT making any changes. Do not call write, edit, or mutating bash commands. You may use read-only bash commands (e.g., git log, git diff, ls, cat, grep) along with read/glob/grep/web-fetch. Scripting interpreters (node, python3, ruby, perl, awk) and build/package tools (npm, cargo, go, tsc, jest, etc.) are blocked in plan mode. At the end, present a concise plan (bullets, files to change, approach). The user will review and then exit plan mode to execute.";
|
|
884
964
|
}
|
|
885
965
|
if (m === "auto") {
|
|
886
966
|
return "\n\nAUTO MODE is active. The user has opted into autonomous execution \u2014 every tool call will be auto-approved. Work efficiently, but do not take irreversible destructive actions (rm -rf, git push --force, dropping tables, etc.) without pausing to describe them in chat first. Prefer smaller reversible steps.";
|
|
887
967
|
}
|
|
888
968
|
return "";
|
|
889
969
|
}
|
|
890
|
-
var MODES, MUTATING_TOOLS, DANGEROUS_PATTERNS, GIT_READONLY_SUBCOMMANDS, READONLY_COMMANDS
|
|
970
|
+
var MODES, MUTATING_TOOLS, DANGEROUS_PATTERNS, GIT_READONLY_SUBCOMMANDS, READONLY_COMMANDS;
|
|
891
971
|
var init_mode = __esm({
|
|
892
972
|
"src/mode.ts"() {
|
|
893
973
|
"use strict";
|
|
@@ -958,16 +1038,8 @@ var init_mode = __esm({
|
|
|
958
1038
|
"id",
|
|
959
1039
|
"whoami",
|
|
960
1040
|
"groups",
|
|
961
|
-
// Dev tools (version/info only)
|
|
962
|
-
"node",
|
|
963
|
-
"npx",
|
|
964
|
-
"python3",
|
|
965
|
-
"ruby",
|
|
966
|
-
"perl",
|
|
967
1041
|
// Utilities
|
|
968
1042
|
"jq",
|
|
969
|
-
"yq",
|
|
970
|
-
"awk",
|
|
971
1043
|
"cut",
|
|
972
1044
|
"tr",
|
|
973
1045
|
"base64",
|
|
@@ -990,32 +1062,6 @@ var init_mode = __esm({
|
|
|
990
1062
|
"ss",
|
|
991
1063
|
"lsof"
|
|
992
1064
|
]);
|
|
993
|
-
COMMANDS_NEEDING_ARG_CHECK = {
|
|
994
|
-
find: (args) => !args.some((a) => a === "-delete" || a === "-exec"),
|
|
995
|
-
sed: (args) => !args.some((a) => a === "-i" || a.startsWith("-i")),
|
|
996
|
-
tar: (args) => args[0] === "-tf" || args[0] === "--list",
|
|
997
|
-
unzip: (args) => args[0] === "-l",
|
|
998
|
-
curl: (args) => !args.some((a) => a === "-o" || a === "-O" || a === "-d" || a === "--data" || a.startsWith("-X")),
|
|
999
|
-
wget: (args) => !args.some((a) => a === "-O" || a === "--output-document" || a.startsWith("--post")),
|
|
1000
|
-
npm: (args) => ["list", "view", "config"].includes(args[0] ?? "") && !(args[0] === "config" && args[1] && !args[1].startsWith("get") && args[1] !== "list"),
|
|
1001
|
-
tsc: (args) => args.every(
|
|
1002
|
-
(a) => ["--noEmit", "--version", "--showConfig", "--help", "-h", "--init"].includes(a)
|
|
1003
|
-
),
|
|
1004
|
-
eslint: (args) => args.every(
|
|
1005
|
-
(a) => ["--version", "--print-config", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
1006
|
-
),
|
|
1007
|
-
prettier: (args) => args.every(
|
|
1008
|
-
(a) => ["--version", "--check", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
1009
|
-
),
|
|
1010
|
-
jest: (args) => args.every(
|
|
1011
|
-
(a) => ["--version", "--listTests", "--showConfig", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
1012
|
-
),
|
|
1013
|
-
vitest: (args) => args.every(
|
|
1014
|
-
(a) => ["--version", "--help", "-h"].includes(a) || !a.startsWith("-")
|
|
1015
|
-
),
|
|
1016
|
-
go: (args) => ["version", "env", "list", "mod"].includes(args[0] ?? "") && !(args[0] === "mod" && args[1] && !["graph", "download", "why", "verify"].includes(args[1])),
|
|
1017
|
-
cargo: (args) => ["--version", "-V", "check", "test", "metadata"].includes(args[0] ?? "") && !(args[0] === "test" && args.includes("--no-run") === false)
|
|
1018
|
-
};
|
|
1019
1065
|
}
|
|
1020
1066
|
});
|
|
1021
1067
|
|
|
@@ -1049,7 +1095,12 @@ How to work:
|
|
|
1049
1095
|
- You have a 262k-token context window. Read as much of a file as needed rather than guessing.
|
|
1050
1096
|
- If a request is ambiguous, ask one focused question instead of making large assumptions.
|
|
1051
1097
|
- When you finish a task, stop. Do not add a closing summary.
|
|
1052
|
-
- When creating git commits, you must include \`Co-authored-by: kimiflare <kimiflare@proton.me>\` in the commit message so kimiflare is credited as a contributor. The bash tool will also auto-append this trailer when it detects git commit-creating commands
|
|
1098
|
+
- When creating git commits, you must include \`Co-authored-by: kimiflare <kimiflare@proton.me>\` in the commit message so kimiflare is credited as a contributor. The bash tool will also auto-append this trailer when it detects git commit-creating commands.
|
|
1099
|
+
|
|
1100
|
+
Tool output reduction:
|
|
1101
|
+
- Large tool outputs (grep, read, bash, web_fetch) are reduced to compact summaries by default to preserve context window.
|
|
1102
|
+
- When you see "[output reduced]" with an artifact ID, you can call \`expand_artifact\` with that ID to retrieve the full raw output if you need more detail.
|
|
1103
|
+
- You can also re-run the original tool with more targeted parameters (e.g. read with offset/limit, grep with output_mode="files") instead of expanding.`;
|
|
1053
1104
|
}
|
|
1054
1105
|
function buildSessionPrefix(opts2) {
|
|
1055
1106
|
const now2 = opts2.now ?? /* @__PURE__ */ new Date();
|
|
@@ -1103,11 +1154,6 @@ function resolvePath(cwd, input) {
|
|
|
1103
1154
|
}
|
|
1104
1155
|
return isAbsolute(input) ? input : resolve(cwd, input);
|
|
1105
1156
|
}
|
|
1106
|
-
function truncate(s, n) {
|
|
1107
|
-
if (s.length <= n) return s;
|
|
1108
|
-
return s.slice(0, n) + `
|
|
1109
|
-
... [truncated, ${s.length - n} chars omitted]`;
|
|
1110
|
-
}
|
|
1111
1157
|
function collapsePath(input, cwd, maxLen = 40) {
|
|
1112
1158
|
if (!input) return input;
|
|
1113
1159
|
let abs;
|
|
@@ -1144,7 +1190,7 @@ var init_read = __esm({
|
|
|
1144
1190
|
MAX_BYTES = 2 * 1024 * 1024;
|
|
1145
1191
|
readTool = {
|
|
1146
1192
|
name: "read",
|
|
1147
|
-
description: "Read a text file from the local filesystem. Supports optional line offset/limit. Refuses files larger than 2MB. Returns contents with 1-indexed line numbers prefixed, cat -n style.",
|
|
1193
|
+
description: "Read a text file from the local filesystem. Supports optional line offset/limit. Refuses files larger than 2MB. Returns contents with 1-indexed line numbers prefixed, cat -n style. When reading a full file without offset/limit, the output is reduced to a compact outline (imports, exports, signatures, preview) by default; use expand_artifact to retrieve the full content or specify offset/limit for a targeted slice.",
|
|
1148
1194
|
parameters: {
|
|
1149
1195
|
type: "object",
|
|
1150
1196
|
properties: {
|
|
@@ -1340,26 +1386,23 @@ ${stdout.trimEnd()}`);
|
|
|
1340
1386
|
${stderr.trimEnd()}`);
|
|
1341
1387
|
if (!stdout && !stderr) parts.push("(no output)");
|
|
1342
1388
|
const raw = parts.join("\n");
|
|
1343
|
-
const reduced = truncate(raw, OUTPUT_CAP);
|
|
1344
1389
|
resolve2({
|
|
1345
|
-
content:
|
|
1390
|
+
content: raw,
|
|
1346
1391
|
rawBytes: Buffer.byteLength(raw, "utf8"),
|
|
1347
|
-
reducedBytes: Buffer.byteLength(
|
|
1392
|
+
reducedBytes: Buffer.byteLength(raw, "utf8")
|
|
1348
1393
|
});
|
|
1349
1394
|
});
|
|
1350
1395
|
});
|
|
1351
1396
|
}
|
|
1352
|
-
var DEFAULT_TIMEOUT, MAX_TIMEOUT,
|
|
1397
|
+
var DEFAULT_TIMEOUT, MAX_TIMEOUT, bashTool;
|
|
1353
1398
|
var init_bash = __esm({
|
|
1354
1399
|
"src/tools/bash.ts"() {
|
|
1355
1400
|
"use strict";
|
|
1356
|
-
init_paths();
|
|
1357
1401
|
DEFAULT_TIMEOUT = 12e4;
|
|
1358
1402
|
MAX_TIMEOUT = 6e5;
|
|
1359
|
-
OUTPUT_CAP = 3e4;
|
|
1360
1403
|
bashTool = {
|
|
1361
1404
|
name: "bash",
|
|
1362
|
-
description: "Run a shell command via `bash -lc`. Prompts the user for permission before executing. stdout and stderr are captured
|
|
1405
|
+
description: "Run a shell command via `bash -lc`. Prompts the user for permission before executing. stdout and stderr are captured and combined. Large outputs are reduced to a compact summary by default; use expand_artifact to retrieve the full log.",
|
|
1363
1406
|
parameters: {
|
|
1364
1407
|
type: "object",
|
|
1365
1408
|
properties: {
|
|
@@ -1444,11 +1487,10 @@ async function runRipgrep(args, root, mode) {
|
|
|
1444
1487
|
const { stdout } = await pExecFile("rg", rgArgs, { maxBuffer: 10 * 1024 * 1024 });
|
|
1445
1488
|
const trimmed = stdout.trim();
|
|
1446
1489
|
if (!trimmed) return { content: "(no matches)", rawBytes: 0, reducedBytes: 0 };
|
|
1447
|
-
const reduced = truncate(trimmed, 3e4);
|
|
1448
1490
|
return {
|
|
1449
|
-
content:
|
|
1491
|
+
content: trimmed,
|
|
1450
1492
|
rawBytes: Buffer.byteLength(trimmed, "utf8"),
|
|
1451
|
-
reducedBytes: Buffer.byteLength(
|
|
1493
|
+
reducedBytes: Buffer.byteLength(trimmed, "utf8")
|
|
1452
1494
|
};
|
|
1453
1495
|
} catch (e) {
|
|
1454
1496
|
const err = e;
|
|
@@ -1487,11 +1529,10 @@ async function runJsFallback(args, root, mode) {
|
|
|
1487
1529
|
}
|
|
1488
1530
|
if (!out.length) return { content: "(no matches)", rawBytes: 0, reducedBytes: 0 };
|
|
1489
1531
|
const raw = out.join("\n");
|
|
1490
|
-
const reduced = truncate(raw, 3e4);
|
|
1491
1532
|
return {
|
|
1492
|
-
content:
|
|
1533
|
+
content: raw,
|
|
1493
1534
|
rawBytes: Buffer.byteLength(raw, "utf8"),
|
|
1494
|
-
reducedBytes: Buffer.byteLength(
|
|
1535
|
+
reducedBytes: Buffer.byteLength(raw, "utf8")
|
|
1495
1536
|
};
|
|
1496
1537
|
}
|
|
1497
1538
|
var pExecFile, cachedHasRg, grepTool;
|
|
@@ -1534,17 +1575,15 @@ var init_grep = __esm({
|
|
|
1534
1575
|
|
|
1535
1576
|
// src/tools/web-fetch.ts
|
|
1536
1577
|
import TurndownService from "turndown";
|
|
1537
|
-
var MAX_BYTES2,
|
|
1578
|
+
var MAX_BYTES2, TIMEOUT_MS, webFetchTool;
|
|
1538
1579
|
var init_web_fetch = __esm({
|
|
1539
1580
|
"src/tools/web-fetch.ts"() {
|
|
1540
1581
|
"use strict";
|
|
1541
|
-
init_paths();
|
|
1542
1582
|
MAX_BYTES2 = 1 * 1024 * 1024;
|
|
1543
|
-
MAX_OUTPUT = 1e5;
|
|
1544
1583
|
TIMEOUT_MS = 2e4;
|
|
1545
1584
|
webFetchTool = {
|
|
1546
1585
|
name: "web_fetch",
|
|
1547
|
-
description: "Fetch a URL over HTTPS and return its content. HTML pages are converted to markdown.
|
|
1586
|
+
description: "Fetch a URL over HTTPS and return its content. HTML pages are converted to markdown. Large pages are reduced to a summary by default; use expand_artifact to retrieve the full content.",
|
|
1548
1587
|
parameters: {
|
|
1549
1588
|
type: "object",
|
|
1550
1589
|
properties: {
|
|
@@ -1578,11 +1617,10 @@ ${td.turndown(bounded)}`;
|
|
|
1578
1617
|
|
|
1579
1618
|
${bounded}`;
|
|
1580
1619
|
}
|
|
1581
|
-
const reduced = truncate(raw, MAX_OUTPUT);
|
|
1582
1620
|
return {
|
|
1583
|
-
content:
|
|
1621
|
+
content: raw,
|
|
1584
1622
|
rawBytes: Buffer.byteLength(raw, "utf8"),
|
|
1585
|
-
reducedBytes: Buffer.byteLength(
|
|
1623
|
+
reducedBytes: Buffer.byteLength(raw, "utf8")
|
|
1586
1624
|
};
|
|
1587
1625
|
} finally {
|
|
1588
1626
|
clearTimeout(timer);
|
|
@@ -1674,6 +1712,423 @@ var init_tasks = __esm({
|
|
|
1674
1712
|
}
|
|
1675
1713
|
});
|
|
1676
1714
|
|
|
1715
|
+
// src/tools/artifact-store.ts
|
|
1716
|
+
var ToolArtifactStore;
|
|
1717
|
+
var init_artifact_store = __esm({
|
|
1718
|
+
"src/tools/artifact-store.ts"() {
|
|
1719
|
+
"use strict";
|
|
1720
|
+
ToolArtifactStore = class {
|
|
1721
|
+
artifacts = /* @__PURE__ */ new Map();
|
|
1722
|
+
nextId = 0;
|
|
1723
|
+
maxArtifacts;
|
|
1724
|
+
maxTotalChars;
|
|
1725
|
+
constructor(opts2) {
|
|
1726
|
+
this.maxArtifacts = opts2?.maxArtifacts ?? 500;
|
|
1727
|
+
this.maxTotalChars = opts2?.maxTotalChars ?? 2e6;
|
|
1728
|
+
}
|
|
1729
|
+
/** Store raw content and return a stable artifact ID. */
|
|
1730
|
+
store(raw) {
|
|
1731
|
+
const id = `art_${++this.nextId}`;
|
|
1732
|
+
while (this.totalChars() + raw.length > this.maxTotalChars && this.artifacts.size > 0) {
|
|
1733
|
+
this.evictOldest();
|
|
1734
|
+
}
|
|
1735
|
+
while (this.artifacts.size >= this.maxArtifacts && this.artifacts.size > 0) {
|
|
1736
|
+
this.evictOldest();
|
|
1737
|
+
}
|
|
1738
|
+
this.artifacts.set(id, raw);
|
|
1739
|
+
return id;
|
|
1740
|
+
}
|
|
1741
|
+
retrieve(id) {
|
|
1742
|
+
return this.artifacts.get(id);
|
|
1743
|
+
}
|
|
1744
|
+
has(id) {
|
|
1745
|
+
return this.artifacts.has(id);
|
|
1746
|
+
}
|
|
1747
|
+
clear() {
|
|
1748
|
+
this.artifacts.clear();
|
|
1749
|
+
this.nextId = 0;
|
|
1750
|
+
}
|
|
1751
|
+
size() {
|
|
1752
|
+
return this.artifacts.size;
|
|
1753
|
+
}
|
|
1754
|
+
totalChars() {
|
|
1755
|
+
let sum = 0;
|
|
1756
|
+
for (const raw of this.artifacts.values()) {
|
|
1757
|
+
sum += raw.length;
|
|
1758
|
+
}
|
|
1759
|
+
return sum;
|
|
1760
|
+
}
|
|
1761
|
+
evictOldest() {
|
|
1762
|
+
const first = this.artifacts.keys().next().value;
|
|
1763
|
+
if (first !== void 0) {
|
|
1764
|
+
this.artifacts.delete(first);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
|
|
1771
|
+
// src/tools/reducer.ts
|
|
1772
|
+
function reduceToolOutput(toolName, raw, args, store, config = DEFAULT_REDUCER_CONFIG) {
|
|
1773
|
+
const rawBytes = Buffer.byteLength(raw, "utf8");
|
|
1774
|
+
const artifactId = store.store(raw);
|
|
1775
|
+
if (!config.enabled) {
|
|
1776
|
+
return { content: raw, rawBytes, reducedBytes: rawBytes, artifactId };
|
|
1777
|
+
}
|
|
1778
|
+
let reduced;
|
|
1779
|
+
let wasReduced = false;
|
|
1780
|
+
let hint;
|
|
1781
|
+
switch (toolName) {
|
|
1782
|
+
case "grep": {
|
|
1783
|
+
const r = reduceGrep(raw, args, config.grep);
|
|
1784
|
+
reduced = r.body;
|
|
1785
|
+
wasReduced = r.wasReduced;
|
|
1786
|
+
hint = r.hint;
|
|
1787
|
+
break;
|
|
1788
|
+
}
|
|
1789
|
+
case "read": {
|
|
1790
|
+
const r = reduceRead(raw, args, config.read);
|
|
1791
|
+
reduced = r.body;
|
|
1792
|
+
wasReduced = r.wasReduced;
|
|
1793
|
+
hint = r.hint;
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
case "bash": {
|
|
1797
|
+
const r = reduceBash(raw, args, config.bash);
|
|
1798
|
+
reduced = r.body;
|
|
1799
|
+
wasReduced = r.wasReduced;
|
|
1800
|
+
hint = r.hint;
|
|
1801
|
+
break;
|
|
1802
|
+
}
|
|
1803
|
+
case "web_fetch": {
|
|
1804
|
+
const r = reduceWebFetch(raw, args, config.webFetch);
|
|
1805
|
+
reduced = r.body;
|
|
1806
|
+
wasReduced = r.wasReduced;
|
|
1807
|
+
hint = r.hint;
|
|
1808
|
+
break;
|
|
1809
|
+
}
|
|
1810
|
+
default:
|
|
1811
|
+
reduced = raw;
|
|
1812
|
+
break;
|
|
1813
|
+
}
|
|
1814
|
+
if (!wasReduced) {
|
|
1815
|
+
return { content: reduced, rawBytes, reducedBytes: rawBytes, artifactId };
|
|
1816
|
+
}
|
|
1817
|
+
const footer = `[output reduced \u2014 full raw stored as artifact ${artifactId}]`;
|
|
1818
|
+
const content = hint ? `${reduced}
|
|
1819
|
+
${footer}
|
|
1820
|
+
${hint}` : `${reduced}
|
|
1821
|
+
${footer}`;
|
|
1822
|
+
const reducedBytes = Buffer.byteLength(content, "utf8");
|
|
1823
|
+
return { content, rawBytes, reducedBytes, artifactId };
|
|
1824
|
+
}
|
|
1825
|
+
function parseGrepLines(raw) {
|
|
1826
|
+
const matches = [];
|
|
1827
|
+
for (const line of raw.split("\n")) {
|
|
1828
|
+
const trimmed = line.trim();
|
|
1829
|
+
if (!trimmed) continue;
|
|
1830
|
+
const m = trimmed.match(/^(.+?):(\d+)?:(.*)$/);
|
|
1831
|
+
if (m) {
|
|
1832
|
+
matches.push({ file: m[1], line: m[2] ? parseInt(m[2], 10) : 0, text: m[3] });
|
|
1833
|
+
} else {
|
|
1834
|
+
matches.push({ file: trimmed, line: 0, text: "" });
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
return matches;
|
|
1838
|
+
}
|
|
1839
|
+
function reduceGrep(raw, args, cfg) {
|
|
1840
|
+
const isFilesMode = args.output_mode === "files";
|
|
1841
|
+
const matches = parseGrepLines(raw);
|
|
1842
|
+
if (matches.length === 0) {
|
|
1843
|
+
return { body: raw, wasReduced: false };
|
|
1844
|
+
}
|
|
1845
|
+
if (isFilesMode) {
|
|
1846
|
+
const files = [...new Set(matches.map((m) => m.file))];
|
|
1847
|
+
const lines2 = [`${files.length} file(s) matched:`, ...files];
|
|
1848
|
+
return {
|
|
1849
|
+
body: lines2.join("\n"),
|
|
1850
|
+
wasReduced: true,
|
|
1851
|
+
hint: 'Re-run with output_mode="content" for match details.'
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1855
|
+
for (const m of matches) {
|
|
1856
|
+
const list = byFile.get(m.file) ?? [];
|
|
1857
|
+
list.push(m);
|
|
1858
|
+
byFile.set(m.file, list);
|
|
1859
|
+
}
|
|
1860
|
+
const lines = [];
|
|
1861
|
+
let totalShown = 0;
|
|
1862
|
+
const totalHits = matches.length;
|
|
1863
|
+
const fileCount = byFile.size;
|
|
1864
|
+
lines.push(`Matched ${fileCount} file(s) (${totalHits} total hits):`);
|
|
1865
|
+
for (const [file, hits] of byFile) {
|
|
1866
|
+
if (totalShown >= cfg.maxTotalLines) break;
|
|
1867
|
+
lines.push(` ${file}: ${hits.length} hit(s)`);
|
|
1868
|
+
const toShow = Math.min(hits.length, cfg.maxMatchesPerFile);
|
|
1869
|
+
for (let i = 0; i < toShow; i++) {
|
|
1870
|
+
const h = hits[i];
|
|
1871
|
+
const text = h.text.length > cfg.maxLineLength ? h.text.slice(0, cfg.maxLineLength) + "\u2026" : h.text;
|
|
1872
|
+
const prefix = h.line > 0 ? ` ${h.line}:` : " ";
|
|
1873
|
+
lines.push(`${prefix}${text}`);
|
|
1874
|
+
totalShown++;
|
|
1875
|
+
if (totalShown >= cfg.maxTotalLines) break;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if (totalShown < totalHits) {
|
|
1879
|
+
lines.push(` \u2026 (${totalHits - totalShown} more hits omitted)`);
|
|
1880
|
+
}
|
|
1881
|
+
return {
|
|
1882
|
+
body: lines.join("\n"),
|
|
1883
|
+
wasReduced: totalHits > totalShown || fileCount > 1,
|
|
1884
|
+
hint: 'Use expand_artifact for full matches, or re-run with output_mode="files" for paths only.'
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
function reduceRead(raw, args, cfg) {
|
|
1888
|
+
const hasSlice = typeof args.offset === "number" || typeof args.limit === "number";
|
|
1889
|
+
if (hasSlice) {
|
|
1890
|
+
const lines = raw.split("\n");
|
|
1891
|
+
if (lines.length > cfg.maxSliceLines) {
|
|
1892
|
+
const kept = lines.slice(0, cfg.maxSliceLines).join("\n");
|
|
1893
|
+
return {
|
|
1894
|
+
body: kept,
|
|
1895
|
+
wasReduced: true,
|
|
1896
|
+
hint: `\u2026 (${lines.length - cfg.maxSliceLines} more lines omitted)`
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
return { body: raw, wasReduced: false };
|
|
1900
|
+
}
|
|
1901
|
+
const allLines = raw.split("\n");
|
|
1902
|
+
const totalLines = allLines.length;
|
|
1903
|
+
const cleanLines = allLines.map((l) => l.replace(/^\s*\d+\t/, ""));
|
|
1904
|
+
const imports = [];
|
|
1905
|
+
const exports = [];
|
|
1906
|
+
const functions = [];
|
|
1907
|
+
const classes = [];
|
|
1908
|
+
for (let i = 0; i < cleanLines.length; i++) {
|
|
1909
|
+
const line = cleanLines[i];
|
|
1910
|
+
const lineNum = i + 1;
|
|
1911
|
+
if (/^import\s+/.test(line)) {
|
|
1912
|
+
imports.push(`${lineNum}: ${line.trim()}`);
|
|
1913
|
+
} else if (/^(?:export\s+)?class\s+\w+/.test(line)) {
|
|
1914
|
+
classes.push(`${lineNum}: ${line.trim()}`);
|
|
1915
|
+
} else if (/^export\s+/.test(line)) {
|
|
1916
|
+
exports.push(`${lineNum}: ${line.trim()}`);
|
|
1917
|
+
} else if (/^(?:async\s+)?function\s+\w+/.test(line)) {
|
|
1918
|
+
functions.push(`${lineNum}: ${line.trim()}`);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
const parts = [];
|
|
1922
|
+
parts.push(`File: ${totalLines} lines total`);
|
|
1923
|
+
if (imports.length > 0) {
|
|
1924
|
+
parts.push(`
|
|
1925
|
+
Imports (${imports.length}):`);
|
|
1926
|
+
parts.push(...imports.slice(0, Math.floor(cfg.maxOutlineLines / 4)));
|
|
1927
|
+
}
|
|
1928
|
+
if (exports.length > 0) {
|
|
1929
|
+
parts.push(`
|
|
1930
|
+
Exports (${exports.length}):`);
|
|
1931
|
+
parts.push(...exports.slice(0, Math.floor(cfg.maxOutlineLines / 4)));
|
|
1932
|
+
}
|
|
1933
|
+
if (functions.length > 0) {
|
|
1934
|
+
parts.push(`
|
|
1935
|
+
Functions (${functions.length}):`);
|
|
1936
|
+
parts.push(...functions.slice(0, Math.floor(cfg.maxOutlineLines / 4)));
|
|
1937
|
+
}
|
|
1938
|
+
if (classes.length > 0) {
|
|
1939
|
+
parts.push(`
|
|
1940
|
+
Classes (${classes.length}):`);
|
|
1941
|
+
parts.push(...classes.slice(0, Math.floor(cfg.maxOutlineLines / 4)));
|
|
1942
|
+
}
|
|
1943
|
+
const previewCount = Math.min(cfg.maxPreviewLines, totalLines);
|
|
1944
|
+
parts.push(`
|
|
1945
|
+
Preview (lines 1\u2013${previewCount}):`);
|
|
1946
|
+
parts.push(...allLines.slice(0, previewCount));
|
|
1947
|
+
return {
|
|
1948
|
+
body: parts.join("\n"),
|
|
1949
|
+
wasReduced: true,
|
|
1950
|
+
hint: "Use expand_artifact for full file, or read with offset/limit for a specific slice."
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
function reduceBash(raw, _args, cfg) {
|
|
1954
|
+
const lines = raw.split("\n");
|
|
1955
|
+
if (lines.length <= cfg.maxTotalLines) {
|
|
1956
|
+
return { body: raw, wasReduced: false };
|
|
1957
|
+
}
|
|
1958
|
+
let header = "";
|
|
1959
|
+
let bodyStart = 0;
|
|
1960
|
+
if (lines[0]?.startsWith("exit=") || lines[0]?.startsWith("(timed out")) {
|
|
1961
|
+
header = lines[0];
|
|
1962
|
+
bodyStart = 1;
|
|
1963
|
+
}
|
|
1964
|
+
const body = lines.slice(bodyStart);
|
|
1965
|
+
const isFailure = header.includes("exit=1") || raw.includes("Error:") || raw.includes("error:") || raw.includes("FAIL") || raw.includes("failed");
|
|
1966
|
+
const out = [header];
|
|
1967
|
+
if (isFailure) {
|
|
1968
|
+
const errorIndices = [];
|
|
1969
|
+
for (let i = 0; i < body.length; i++) {
|
|
1970
|
+
const line = body[i];
|
|
1971
|
+
if (/\bError\b/i.test(line) || /\berror\b/i.test(line) || /\bFAIL\b/i.test(line) || /\bfailed\b/i.test(line) || /^\s+at\s+/.test(line) || /\s+Error:\s+/.test(line)) {
|
|
1972
|
+
for (let j = Math.max(0, i - 2); j <= Math.min(body.length - 1, i + 2); j++) {
|
|
1973
|
+
if (!errorIndices.includes(j)) errorIndices.push(j);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
errorIndices.sort((a, b) => a - b);
|
|
1978
|
+
const cappedError = errorIndices.slice(0, cfg.maxErrorBlockLines);
|
|
1979
|
+
if (cappedError.length > 0) {
|
|
1980
|
+
out.push("--- error block ---");
|
|
1981
|
+
for (const idx of cappedError) {
|
|
1982
|
+
out.push(body[idx]);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
const testNames = [];
|
|
1986
|
+
for (const line of body) {
|
|
1987
|
+
const m = line.match(/(?:✗|✕|×|FAIL)\s+(.+)/) || line.match(/failing\s*\d*\s*:?\s*(.+)/i) || line.match(/Test\s+\w+\s+failed/i);
|
|
1988
|
+
if (m && m[1]) {
|
|
1989
|
+
const name = m[1].trim().slice(0, 120);
|
|
1990
|
+
if (!testNames.includes(name)) testNames.push(name);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
if (testNames.length > 0) {
|
|
1994
|
+
out.push("--- failing tests ---");
|
|
1995
|
+
out.push(...testNames.slice(0, 10));
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
const trailing = body.slice(-cfg.maxTrailingLines);
|
|
1999
|
+
out.push("--- last lines ---");
|
|
2000
|
+
out.push(...trailing);
|
|
2001
|
+
let result = out.join("\n");
|
|
2002
|
+
if (cfg.dedupeConsecutiveLines) {
|
|
2003
|
+
result = dedupeConsecutive(result);
|
|
2004
|
+
}
|
|
2005
|
+
return {
|
|
2006
|
+
body: result,
|
|
2007
|
+
wasReduced: true,
|
|
2008
|
+
hint: "Use expand_artifact for full output."
|
|
2009
|
+
};
|
|
2010
|
+
}
|
|
2011
|
+
function dedupeConsecutive(text) {
|
|
2012
|
+
const lines = text.split("\n");
|
|
2013
|
+
const out = [];
|
|
2014
|
+
let repeatCount = 1;
|
|
2015
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2016
|
+
const line = lines[i];
|
|
2017
|
+
const next = lines[i + 1];
|
|
2018
|
+
if (next !== void 0 && next === line) {
|
|
2019
|
+
repeatCount++;
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
if (repeatCount > 2) {
|
|
2023
|
+
out.push(line);
|
|
2024
|
+
out.push(`\u2026 (${repeatCount - 1} identical lines omitted)`);
|
|
2025
|
+
} else {
|
|
2026
|
+
for (let j = 0; j < repeatCount; j++) {
|
|
2027
|
+
out.push(line);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
repeatCount = 1;
|
|
2031
|
+
}
|
|
2032
|
+
return out.join("\n");
|
|
2033
|
+
}
|
|
2034
|
+
function reduceWebFetch(raw, args, cfg) {
|
|
2035
|
+
const url = typeof args.url === "string" ? args.url : "(unknown URL)";
|
|
2036
|
+
const titleMatch = raw.match(/^#\s+(.+)$/m);
|
|
2037
|
+
const title = titleMatch ? titleMatch[1].trim() : "(no title)";
|
|
2038
|
+
const parts = [];
|
|
2039
|
+
parts.push(`Title: ${title}`);
|
|
2040
|
+
parts.push(`URL: ${url}`);
|
|
2041
|
+
const headings = raw.match(/^#{1,3}\s+.+$/gm) ?? [];
|
|
2042
|
+
if (headings.length > 0) {
|
|
2043
|
+
parts.push("\nSections:");
|
|
2044
|
+
for (const h of headings.slice(0, 10)) {
|
|
2045
|
+
parts.push(` ${h}`);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
const bodyStart = raw.indexOf("\n\n");
|
|
2049
|
+
const body = bodyStart > 0 ? raw.slice(bodyStart + 2) : raw;
|
|
2050
|
+
const excerpt = body.slice(0, cfg.maxChars).trim();
|
|
2051
|
+
if (excerpt) {
|
|
2052
|
+
parts.push(`
|
|
2053
|
+
Excerpt (${excerpt.length} chars):`);
|
|
2054
|
+
parts.push(excerpt);
|
|
2055
|
+
}
|
|
2056
|
+
if (body.length > cfg.maxChars) {
|
|
2057
|
+
parts.push(`
|
|
2058
|
+
\u2026 (${body.length - cfg.maxChars} more chars omitted)`);
|
|
2059
|
+
}
|
|
2060
|
+
return {
|
|
2061
|
+
body: parts.join("\n"),
|
|
2062
|
+
wasReduced: true,
|
|
2063
|
+
hint: "Use expand_artifact for full page content."
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
var DEFAULT_REDUCER_CONFIG;
|
|
2067
|
+
var init_reducer = __esm({
|
|
2068
|
+
"src/tools/reducer.ts"() {
|
|
2069
|
+
"use strict";
|
|
2070
|
+
DEFAULT_REDUCER_CONFIG = {
|
|
2071
|
+
enabled: true,
|
|
2072
|
+
grep: {
|
|
2073
|
+
maxTotalLines: 50,
|
|
2074
|
+
maxMatchesPerFile: 3,
|
|
2075
|
+
maxLineLength: 200,
|
|
2076
|
+
maxOutputChars: 3e3
|
|
2077
|
+
},
|
|
2078
|
+
read: {
|
|
2079
|
+
maxOutlineLines: 60,
|
|
2080
|
+
maxSliceLines: 200,
|
|
2081
|
+
maxPreviewLines: 30,
|
|
2082
|
+
maxOutputChars: 4e3
|
|
2083
|
+
},
|
|
2084
|
+
bash: {
|
|
2085
|
+
maxTotalLines: 40,
|
|
2086
|
+
maxErrorBlockLines: 20,
|
|
2087
|
+
maxTrailingLines: 20,
|
|
2088
|
+
maxOutputChars: 4e3,
|
|
2089
|
+
dedupeConsecutiveLines: true
|
|
2090
|
+
},
|
|
2091
|
+
webFetch: {
|
|
2092
|
+
maxChars: 2e3,
|
|
2093
|
+
maxHeadingChars: 500
|
|
2094
|
+
}
|
|
2095
|
+
};
|
|
2096
|
+
}
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
// src/tools/expand-artifact.ts
|
|
2100
|
+
function makeExpandArtifactTool(store) {
|
|
2101
|
+
return {
|
|
2102
|
+
name: "expand_artifact",
|
|
2103
|
+
description: "Retrieve the full raw content of a previously reduced tool output by its artifact ID. Use this when the compact summary is insufficient and you need the complete original output.",
|
|
2104
|
+
parameters: {
|
|
2105
|
+
type: "object",
|
|
2106
|
+
properties: {
|
|
2107
|
+
artifact_id: {
|
|
2108
|
+
type: "string",
|
|
2109
|
+
description: "The artifact ID from a reduced tool output footer, e.g. art_42."
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2112
|
+
required: ["artifact_id"],
|
|
2113
|
+
additionalProperties: false
|
|
2114
|
+
},
|
|
2115
|
+
needsPermission: false,
|
|
2116
|
+
render: (args) => ({ title: `expand ${args.artifact_id}` }),
|
|
2117
|
+
run: async (args) => {
|
|
2118
|
+
const raw = store.retrieve(args.artifact_id);
|
|
2119
|
+
if (!raw) {
|
|
2120
|
+
return `Artifact "${args.artifact_id}" not found. It may have been evicted from memory. Re-run the original tool to regenerate the output.`;
|
|
2121
|
+
}
|
|
2122
|
+
return raw;
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
var init_expand_artifact = __esm({
|
|
2127
|
+
"src/tools/expand-artifact.ts"() {
|
|
2128
|
+
"use strict";
|
|
2129
|
+
}
|
|
2130
|
+
});
|
|
2131
|
+
|
|
1677
2132
|
// src/tools/executor.ts
|
|
1678
2133
|
function normalizeToolOutput(result) {
|
|
1679
2134
|
if (typeof result === "string") {
|
|
@@ -1697,6 +2152,9 @@ var init_executor = __esm({
|
|
|
1697
2152
|
init_grep();
|
|
1698
2153
|
init_web_fetch();
|
|
1699
2154
|
init_tasks();
|
|
2155
|
+
init_artifact_store();
|
|
2156
|
+
init_reducer();
|
|
2157
|
+
init_expand_artifact();
|
|
1700
2158
|
ALL_TOOLS = [
|
|
1701
2159
|
readTool,
|
|
1702
2160
|
writeTool,
|
|
@@ -1710,8 +2168,11 @@ var init_executor = __esm({
|
|
|
1710
2168
|
ToolExecutor = class {
|
|
1711
2169
|
sessionAllowed = /* @__PURE__ */ new Set();
|
|
1712
2170
|
tools;
|
|
2171
|
+
artifactStore;
|
|
1713
2172
|
constructor(tools = ALL_TOOLS) {
|
|
1714
2173
|
this.tools = new Map(tools.map((t) => [t.name, t]));
|
|
2174
|
+
this.artifactStore = new ToolArtifactStore();
|
|
2175
|
+
this.tools.set("expand_artifact", makeExpandArtifactTool(this.artifactStore));
|
|
1715
2176
|
}
|
|
1716
2177
|
list() {
|
|
1717
2178
|
return [...this.tools.values()];
|
|
@@ -1725,6 +2186,9 @@ var init_executor = __esm({
|
|
|
1725
2186
|
clearSessionPermissions() {
|
|
1726
2187
|
this.sessionAllowed.clear();
|
|
1727
2188
|
}
|
|
2189
|
+
clearArtifacts() {
|
|
2190
|
+
this.artifactStore.clear();
|
|
2191
|
+
}
|
|
1728
2192
|
async run(call, askPermission, ctx) {
|
|
1729
2193
|
const tool = this.tools.get(call.name);
|
|
1730
2194
|
if (!tool) {
|
|
@@ -1764,13 +2228,21 @@ var init_executor = __esm({
|
|
|
1764
2228
|
try {
|
|
1765
2229
|
const result = await tool.run(args, ctx);
|
|
1766
2230
|
const normalized = normalizeToolOutput(result);
|
|
2231
|
+
const reduced = reduceToolOutput(
|
|
2232
|
+
call.name,
|
|
2233
|
+
normalized.content,
|
|
2234
|
+
args,
|
|
2235
|
+
this.artifactStore,
|
|
2236
|
+
DEFAULT_REDUCER_CONFIG
|
|
2237
|
+
);
|
|
1767
2238
|
return {
|
|
1768
2239
|
tool_call_id: call.id,
|
|
1769
2240
|
name: call.name,
|
|
1770
|
-
content:
|
|
2241
|
+
content: reduced.content,
|
|
1771
2242
|
ok: true,
|
|
1772
|
-
rawBytes:
|
|
1773
|
-
reducedBytes:
|
|
2243
|
+
rawBytes: reduced.rawBytes,
|
|
2244
|
+
reducedBytes: reduced.reducedBytes,
|
|
2245
|
+
artifactId: reduced.artifactId
|
|
1774
2246
|
};
|
|
1775
2247
|
} catch (e) {
|
|
1776
2248
|
const msg = `Error running ${call.name}: ${e.message ?? String(e)}`;
|
|
@@ -2921,7 +3393,7 @@ function buildRightParts(usage, contextLimit) {
|
|
|
2921
3393
|
`in ${usage.prompt_tokens}${cached ? ` (${cached} cached)` : ""}`,
|
|
2922
3394
|
`out ${usage.completion_tokens}`,
|
|
2923
3395
|
`ctx ${pct}%`,
|
|
2924
|
-
|
|
3396
|
+
`$${cost.total.toFixed(5)}`
|
|
2925
3397
|
];
|
|
2926
3398
|
}
|
|
2927
3399
|
function shortModel(m) {
|
|
@@ -5282,6 +5754,7 @@ function App({ initialCfg, initialUpdateResult }) {
|
|
|
5282
5754
|
sessionIdRef.current = null;
|
|
5283
5755
|
sessionStateRef.current = emptySessionState();
|
|
5284
5756
|
artifactStoreRef.current = new ArtifactStore();
|
|
5757
|
+
executorRef.current.clearArtifacts();
|
|
5285
5758
|
setEvents([]);
|
|
5286
5759
|
setUsage(null);
|
|
5287
5760
|
setTasks([]);
|