bashkit 0.3.0 → 0.3.2
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/README.md +2 -0
- package/dist/cloudflare/index.d.ts +20 -0
- package/dist/cloudflare/index.js +1251 -0
- package/dist/durable/durable-session.d.ts +220 -0
- package/dist/durable/index.d.ts +41 -0
- package/dist/durable/index.js +159 -0
- package/dist/durable/schema.d.ts +51 -0
- package/dist/durable/types.d.ts +208 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +646 -113
- package/dist/react/index.d.ts +42 -0
- package/dist/react/index.js +10 -0
- package/dist/react/types.d.ts +333 -0
- package/dist/react/use-agent.d.ts +33 -0
- package/dist/react/use-durable-chat.d.ts +39 -0
- package/dist/sandbox/ripgrep.d.ts +11 -0
- package/dist/tools/task.d.ts +6 -4
- package/dist/utils/debug.d.ts +83 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/workflow.d.ts +52 -0
- package/dist/workflow.js +1051 -0
- package/package.json +1 -1
- package/dist/tools/web-constants.d.ts +0 -5
package/dist/index.js
CHANGED
|
@@ -65,8 +65,25 @@ function createLazySingleton(factory) {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// src/sandbox/ripgrep.ts
|
|
69
|
+
async function getBundledRgPath() {
|
|
70
|
+
try {
|
|
71
|
+
const { rgPath } = await import("@vscode/ripgrep");
|
|
72
|
+
return rgPath;
|
|
73
|
+
} catch {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getBundledRgPathSync() {
|
|
78
|
+
try {
|
|
79
|
+
const { rgPath } = __require("@vscode/ripgrep");
|
|
80
|
+
return rgPath;
|
|
81
|
+
} catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
68
86
|
// src/sandbox/ensure-tools.ts
|
|
69
|
-
import { rgPath as bundledRgPath } from "@vscode/ripgrep";
|
|
70
87
|
var RIPGREP_VERSION = "14.1.0";
|
|
71
88
|
var ARCH_MAP = {
|
|
72
89
|
x86_64: "x86_64-unknown-linux-musl",
|
|
@@ -74,10 +91,13 @@ var ARCH_MAP = {
|
|
|
74
91
|
arm64: "aarch64-unknown-linux-gnu"
|
|
75
92
|
};
|
|
76
93
|
async function ensureSandboxTools(sandbox) {
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
sandbox.
|
|
80
|
-
|
|
94
|
+
const bundledRgPath = await getBundledRgPath();
|
|
95
|
+
if (bundledRgPath) {
|
|
96
|
+
const bundledCheck = await sandbox.exec(`test -x "${bundledRgPath}" && echo found`);
|
|
97
|
+
if (bundledCheck.stdout.includes("found")) {
|
|
98
|
+
sandbox.rgPath = bundledRgPath;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
81
101
|
}
|
|
82
102
|
const tmpCheck = await sandbox.exec("test -x /tmp/rg && echo found");
|
|
83
103
|
if (tmpCheck.stdout.includes("found")) {
|
|
@@ -229,9 +249,9 @@ async function createE2BSandbox(config = {}) {
|
|
|
229
249
|
}
|
|
230
250
|
// src/sandbox/local.ts
|
|
231
251
|
import { existsSync, mkdirSync } from "node:fs";
|
|
232
|
-
import { rgPath as bundledRgPath2 } from "@vscode/ripgrep";
|
|
233
252
|
function createLocalSandbox(config = {}) {
|
|
234
253
|
const workingDirectory = config.cwd || "/tmp";
|
|
254
|
+
const rgPath = getBundledRgPathSync();
|
|
235
255
|
if (!existsSync(workingDirectory)) {
|
|
236
256
|
mkdirSync(workingDirectory, { recursive: true });
|
|
237
257
|
}
|
|
@@ -271,7 +291,7 @@ function createLocalSandbox(config = {}) {
|
|
|
271
291
|
};
|
|
272
292
|
return {
|
|
273
293
|
exec,
|
|
274
|
-
rgPath
|
|
294
|
+
rgPath,
|
|
275
295
|
async readFile(path) {
|
|
276
296
|
const fullPath = path.startsWith("/") ? path : `${workingDirectory}/${path}`;
|
|
277
297
|
const file = Bun.file(fullPath);
|
|
@@ -595,6 +615,184 @@ var DEFAULT_CONFIG = {
|
|
|
595
615
|
// src/tools/ask-user.ts
|
|
596
616
|
import { tool, zodSchema } from "ai";
|
|
597
617
|
import { z } from "zod";
|
|
618
|
+
|
|
619
|
+
// src/utils/debug.ts
|
|
620
|
+
import { appendFileSync } from "node:fs";
|
|
621
|
+
var state = {
|
|
622
|
+
mode: "off",
|
|
623
|
+
logs: [],
|
|
624
|
+
counters: new Map,
|
|
625
|
+
parentStack: []
|
|
626
|
+
};
|
|
627
|
+
var MAX_STRING_LENGTH = 1000;
|
|
628
|
+
var MAX_ARRAY_ITEMS = 10;
|
|
629
|
+
function initDebugMode() {
|
|
630
|
+
const envValue = process.env.BASHKIT_DEBUG;
|
|
631
|
+
if (!envValue) {
|
|
632
|
+
state.mode = "off";
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
if (envValue === "1" || envValue === "stderr") {
|
|
636
|
+
state.mode = "stderr";
|
|
637
|
+
} else if (envValue === "json") {
|
|
638
|
+
state.mode = "json";
|
|
639
|
+
} else if (envValue === "memory") {
|
|
640
|
+
state.mode = "memory";
|
|
641
|
+
} else if (envValue.startsWith("file:")) {
|
|
642
|
+
state.mode = "file";
|
|
643
|
+
state.filePath = envValue.slice(5);
|
|
644
|
+
} else {
|
|
645
|
+
state.mode = "stderr";
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
initDebugMode();
|
|
649
|
+
function isDebugEnabled() {
|
|
650
|
+
return state.mode !== "off";
|
|
651
|
+
}
|
|
652
|
+
function generateId(tool) {
|
|
653
|
+
const count = (state.counters.get(tool) || 0) + 1;
|
|
654
|
+
state.counters.set(tool, count);
|
|
655
|
+
return `${tool}-${count}`;
|
|
656
|
+
}
|
|
657
|
+
function truncateString(str) {
|
|
658
|
+
if (str.length <= MAX_STRING_LENGTH)
|
|
659
|
+
return str;
|
|
660
|
+
return `${str.slice(0, MAX_STRING_LENGTH)}... [truncated, ${str.length - MAX_STRING_LENGTH} more chars]`;
|
|
661
|
+
}
|
|
662
|
+
function summarize(data, depth = 0) {
|
|
663
|
+
if (depth > 5)
|
|
664
|
+
return "[nested object]";
|
|
665
|
+
if (data === null || data === undefined)
|
|
666
|
+
return data;
|
|
667
|
+
if (typeof data === "string") {
|
|
668
|
+
return truncateString(data);
|
|
669
|
+
}
|
|
670
|
+
if (typeof data === "number" || typeof data === "boolean") {
|
|
671
|
+
return data;
|
|
672
|
+
}
|
|
673
|
+
if (Array.isArray(data)) {
|
|
674
|
+
const truncated = data.length > MAX_ARRAY_ITEMS;
|
|
675
|
+
const items = data.slice(0, MAX_ARRAY_ITEMS).map((item) => summarize(item, depth + 1));
|
|
676
|
+
if (truncated) {
|
|
677
|
+
return [...items, `[${data.length - MAX_ARRAY_ITEMS} more items]`];
|
|
678
|
+
}
|
|
679
|
+
return items;
|
|
680
|
+
}
|
|
681
|
+
if (typeof data === "object") {
|
|
682
|
+
const result = {};
|
|
683
|
+
for (const [key, value] of Object.entries(data)) {
|
|
684
|
+
result[key] = summarize(value, depth + 1);
|
|
685
|
+
}
|
|
686
|
+
return result;
|
|
687
|
+
}
|
|
688
|
+
return String(data);
|
|
689
|
+
}
|
|
690
|
+
function emitEvent(event) {
|
|
691
|
+
if (state.mode === "off")
|
|
692
|
+
return;
|
|
693
|
+
switch (state.mode) {
|
|
694
|
+
case "memory":
|
|
695
|
+
state.logs.push(event);
|
|
696
|
+
break;
|
|
697
|
+
case "json":
|
|
698
|
+
process.stderr.write(`${JSON.stringify(event)}
|
|
699
|
+
`);
|
|
700
|
+
break;
|
|
701
|
+
case "file":
|
|
702
|
+
if (state.filePath) {
|
|
703
|
+
appendFileSync(state.filePath, `${JSON.stringify(event)}
|
|
704
|
+
`);
|
|
705
|
+
}
|
|
706
|
+
break;
|
|
707
|
+
case "stderr":
|
|
708
|
+
default:
|
|
709
|
+
formatHumanReadable(event);
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function formatHumanReadable(event) {
|
|
714
|
+
const indent = " ".repeat(state.parentStack.length);
|
|
715
|
+
if (event.event === "start") {
|
|
716
|
+
const inputSummary = event.input ? Object.entries(event.input).map(([k, v]) => `${k}=${JSON.stringify(v)}`).slice(0, 3).join(" ") : "";
|
|
717
|
+
process.stderr.write(`${indent}[bashkit:${event.tool}] → ${inputSummary}
|
|
718
|
+
`);
|
|
719
|
+
} else if (event.event === "end") {
|
|
720
|
+
const summaryStr = event.summary ? Object.entries(event.summary).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ") : "";
|
|
721
|
+
process.stderr.write(`${indent}[bashkit:${event.tool}] ← ${event.duration_ms}ms ${summaryStr}
|
|
722
|
+
`);
|
|
723
|
+
} else if (event.event === "error") {
|
|
724
|
+
process.stderr.write(`${indent}[bashkit:${event.tool}] ✗ ${event.error}
|
|
725
|
+
`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function debugStart(tool, input) {
|
|
729
|
+
if (state.mode === "off")
|
|
730
|
+
return "";
|
|
731
|
+
const id = generateId(tool);
|
|
732
|
+
const parent = state.parentStack.length > 0 ? state.parentStack[state.parentStack.length - 1] : undefined;
|
|
733
|
+
const event = {
|
|
734
|
+
id,
|
|
735
|
+
ts: Date.now(),
|
|
736
|
+
tool,
|
|
737
|
+
event: "start",
|
|
738
|
+
input: input ? summarize(input) : undefined,
|
|
739
|
+
parent
|
|
740
|
+
};
|
|
741
|
+
emitEvent(event);
|
|
742
|
+
return id;
|
|
743
|
+
}
|
|
744
|
+
function debugEnd(id, tool, options) {
|
|
745
|
+
if (state.mode === "off" || !id)
|
|
746
|
+
return;
|
|
747
|
+
const event = {
|
|
748
|
+
id,
|
|
749
|
+
ts: Date.now(),
|
|
750
|
+
tool,
|
|
751
|
+
event: "end",
|
|
752
|
+
output: options.output ? summarize(options.output) : undefined,
|
|
753
|
+
summary: options.summary,
|
|
754
|
+
duration_ms: options.duration_ms
|
|
755
|
+
};
|
|
756
|
+
emitEvent(event);
|
|
757
|
+
}
|
|
758
|
+
function debugError(id, tool, error) {
|
|
759
|
+
if (state.mode === "off" || !id)
|
|
760
|
+
return;
|
|
761
|
+
const event = {
|
|
762
|
+
id,
|
|
763
|
+
ts: Date.now(),
|
|
764
|
+
tool,
|
|
765
|
+
event: "error",
|
|
766
|
+
error: error instanceof Error ? error.message : error
|
|
767
|
+
};
|
|
768
|
+
emitEvent(event);
|
|
769
|
+
}
|
|
770
|
+
function pushParent(id) {
|
|
771
|
+
if (state.mode === "off" || !id)
|
|
772
|
+
return;
|
|
773
|
+
state.parentStack.push(id);
|
|
774
|
+
}
|
|
775
|
+
function popParent() {
|
|
776
|
+
if (state.mode === "off")
|
|
777
|
+
return;
|
|
778
|
+
state.parentStack.pop();
|
|
779
|
+
}
|
|
780
|
+
function getDebugLogs() {
|
|
781
|
+
return [...state.logs];
|
|
782
|
+
}
|
|
783
|
+
function clearDebugLogs() {
|
|
784
|
+
state.logs = [];
|
|
785
|
+
state.counters.clear();
|
|
786
|
+
state.parentStack = [];
|
|
787
|
+
}
|
|
788
|
+
function reinitDebugMode() {
|
|
789
|
+
state.logs = [];
|
|
790
|
+
state.counters.clear();
|
|
791
|
+
state.parentStack = [];
|
|
792
|
+
initDebugMode();
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// src/tools/ask-user.ts
|
|
598
796
|
var questionOptionSchema = z.object({
|
|
599
797
|
label: z.string().describe("The display text for this option. Should be concise (1-5 words). Add '(Recommended)' suffix for suggested options."),
|
|
600
798
|
description: z.string().optional().describe("Explanation of what this option means or its implications.")
|
|
@@ -644,22 +842,46 @@ function createAskUserTool(config) {
|
|
|
644
842
|
description: ASK_USER_DESCRIPTION,
|
|
645
843
|
inputSchema: zodSchema(askUserInputSchema),
|
|
646
844
|
execute: async (input) => {
|
|
845
|
+
const startTime = performance.now();
|
|
846
|
+
const debugId = isDebugEnabled() ? debugStart("ask-user", {
|
|
847
|
+
hasQuestion: !!input.question,
|
|
848
|
+
questionCount: input.questions?.length ?? 0,
|
|
849
|
+
question: input.question ? input.question.length > 100 ? `${input.question.slice(0, 100)}...` : input.question : undefined
|
|
850
|
+
}) : "";
|
|
647
851
|
try {
|
|
648
852
|
if (!input.question && !input.questions) {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
853
|
+
const error2 = "Either 'question' or 'questions' must be provided";
|
|
854
|
+
if (debugId)
|
|
855
|
+
debugError(debugId, "ask-user", error2);
|
|
856
|
+
return { error: error2 };
|
|
652
857
|
}
|
|
653
858
|
if (input.questions && input.questions.length > 0) {
|
|
654
859
|
if (normalizedConfig.onStructuredQuestions) {
|
|
655
860
|
const answers = await normalizedConfig.onStructuredQuestions(input.questions);
|
|
656
861
|
const firstKey = Object.keys(answers)[0];
|
|
657
862
|
const firstAnswer = answers[firstKey];
|
|
863
|
+
const durationMs2 = Math.round(performance.now() - startTime);
|
|
864
|
+
if (debugId) {
|
|
865
|
+
debugEnd(debugId, "ask-user", {
|
|
866
|
+
summary: {
|
|
867
|
+
type: "structured",
|
|
868
|
+
answerCount: Object.keys(answers).length
|
|
869
|
+
},
|
|
870
|
+
duration_ms: durationMs2
|
|
871
|
+
});
|
|
872
|
+
}
|
|
658
873
|
return {
|
|
659
874
|
answer: Array.isArray(firstAnswer) ? firstAnswer.join(", ") : firstAnswer,
|
|
660
875
|
answers
|
|
661
876
|
};
|
|
662
877
|
}
|
|
878
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
879
|
+
if (debugId) {
|
|
880
|
+
debugEnd(debugId, "ask-user", {
|
|
881
|
+
summary: { type: "structured", awaiting: true },
|
|
882
|
+
duration_ms: durationMs
|
|
883
|
+
});
|
|
884
|
+
}
|
|
663
885
|
return {
|
|
664
886
|
questions: input.questions,
|
|
665
887
|
awaiting_response: true
|
|
@@ -668,18 +890,36 @@ function createAskUserTool(config) {
|
|
|
668
890
|
if (input.question) {
|
|
669
891
|
if (normalizedConfig.onQuestion) {
|
|
670
892
|
const answer = await normalizedConfig.onQuestion(input.question);
|
|
893
|
+
const durationMs2 = Math.round(performance.now() - startTime);
|
|
894
|
+
if (debugId) {
|
|
895
|
+
debugEnd(debugId, "ask-user", {
|
|
896
|
+
summary: { type: "simple", hasAnswer: true },
|
|
897
|
+
duration_ms: durationMs2
|
|
898
|
+
});
|
|
899
|
+
}
|
|
671
900
|
return { answer };
|
|
672
901
|
}
|
|
902
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
903
|
+
if (debugId) {
|
|
904
|
+
debugEnd(debugId, "ask-user", {
|
|
905
|
+
summary: { type: "simple", awaiting: true },
|
|
906
|
+
duration_ms: durationMs
|
|
907
|
+
});
|
|
908
|
+
}
|
|
673
909
|
return {
|
|
674
910
|
question: input.question,
|
|
675
911
|
awaiting_response: true
|
|
676
912
|
};
|
|
677
913
|
}
|
|
678
|
-
|
|
914
|
+
const error = "No question provided";
|
|
915
|
+
if (debugId)
|
|
916
|
+
debugError(debugId, "ask-user", error);
|
|
917
|
+
return { error };
|
|
679
918
|
} catch (error) {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
919
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
920
|
+
if (debugId)
|
|
921
|
+
debugError(debugId, "ask-user", errorMessage);
|
|
922
|
+
return { error: errorMessage };
|
|
683
923
|
}
|
|
684
924
|
}
|
|
685
925
|
});
|
|
@@ -743,12 +983,18 @@ function createBashTool(sandbox, config) {
|
|
|
743
983
|
description: _description,
|
|
744
984
|
run_in_background: _run_in_background
|
|
745
985
|
}) => {
|
|
986
|
+
const startTime = performance.now();
|
|
987
|
+
const debugId = isDebugEnabled() ? debugStart("bash", {
|
|
988
|
+
command: command.length > 200 ? `${command.slice(0, 200)}...` : command,
|
|
989
|
+
timeout
|
|
990
|
+
}) : "";
|
|
746
991
|
if (config?.blockedCommands) {
|
|
747
992
|
for (const blocked of config.blockedCommands) {
|
|
748
993
|
if (command.includes(blocked)) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
994
|
+
const error = `Command blocked: contains '${blocked}'`;
|
|
995
|
+
if (debugId)
|
|
996
|
+
debugError(debugId, "bash", error);
|
|
997
|
+
return { error };
|
|
752
998
|
}
|
|
753
999
|
}
|
|
754
1000
|
}
|
|
@@ -767,6 +1013,18 @@ function createBashTool(sandbox, config) {
|
|
|
767
1013
|
stderr = stderr.slice(0, maxOutputLength) + `
|
|
768
1014
|
[output truncated, ${stderr.length - maxOutputLength} chars omitted]`;
|
|
769
1015
|
}
|
|
1016
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1017
|
+
if (debugId) {
|
|
1018
|
+
debugEnd(debugId, "bash", {
|
|
1019
|
+
summary: {
|
|
1020
|
+
exitCode: result.exitCode,
|
|
1021
|
+
stdoutLen: result.stdout.length,
|
|
1022
|
+
stderrLen: result.stderr.length,
|
|
1023
|
+
interrupted: result.interrupted
|
|
1024
|
+
},
|
|
1025
|
+
duration_ms: durationMs
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
770
1028
|
return {
|
|
771
1029
|
stdout,
|
|
772
1030
|
stderr,
|
|
@@ -775,9 +1033,10 @@ function createBashTool(sandbox, config) {
|
|
|
775
1033
|
duration_ms: result.durationMs
|
|
776
1034
|
};
|
|
777
1035
|
} catch (error) {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1036
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1037
|
+
if (debugId)
|
|
1038
|
+
debugError(debugId, "bash", errorMessage);
|
|
1039
|
+
return { error: errorMessage };
|
|
781
1040
|
}
|
|
782
1041
|
}
|
|
783
1042
|
});
|
|
@@ -822,29 +1081,48 @@ function createEditTool(sandbox, config) {
|
|
|
822
1081
|
new_string,
|
|
823
1082
|
replace_all = false
|
|
824
1083
|
}) => {
|
|
1084
|
+
const startTime = performance.now();
|
|
1085
|
+
const debugId = isDebugEnabled() ? debugStart("edit", {
|
|
1086
|
+
file_path,
|
|
1087
|
+
old_string: old_string.length > 100 ? `${old_string.slice(0, 100)}...` : old_string,
|
|
1088
|
+
replace_all
|
|
1089
|
+
}) : "";
|
|
825
1090
|
if (old_string === new_string) {
|
|
826
|
-
|
|
1091
|
+
const error = "old_string and new_string must be different";
|
|
1092
|
+
if (debugId)
|
|
1093
|
+
debugError(debugId, "edit", error);
|
|
1094
|
+
return { error };
|
|
827
1095
|
}
|
|
828
1096
|
if (config?.allowedPaths) {
|
|
829
1097
|
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
830
1098
|
if (!isAllowed) {
|
|
831
|
-
|
|
1099
|
+
const error = `Path not allowed: ${file_path}`;
|
|
1100
|
+
if (debugId)
|
|
1101
|
+
debugError(debugId, "edit", error);
|
|
1102
|
+
return { error };
|
|
832
1103
|
}
|
|
833
1104
|
}
|
|
834
1105
|
try {
|
|
835
1106
|
const exists = await sandbox.fileExists(file_path);
|
|
836
1107
|
if (!exists) {
|
|
837
|
-
|
|
1108
|
+
const error = `File not found: ${file_path}`;
|
|
1109
|
+
if (debugId)
|
|
1110
|
+
debugError(debugId, "edit", error);
|
|
1111
|
+
return { error };
|
|
838
1112
|
}
|
|
839
1113
|
const content = await sandbox.readFile(file_path);
|
|
840
1114
|
const occurrences = content.split(old_string).length - 1;
|
|
841
1115
|
if (occurrences === 0) {
|
|
842
|
-
|
|
1116
|
+
const error = `String not found in file: "${old_string}"`;
|
|
1117
|
+
if (debugId)
|
|
1118
|
+
debugError(debugId, "edit", error);
|
|
1119
|
+
return { error };
|
|
843
1120
|
}
|
|
844
1121
|
if (!replace_all && occurrences > 1) {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1122
|
+
const error = `String appears ${occurrences} times in file. Use replace_all=true to replace all, or provide a more unique string.`;
|
|
1123
|
+
if (debugId)
|
|
1124
|
+
debugError(debugId, "edit", error);
|
|
1125
|
+
return { error };
|
|
848
1126
|
}
|
|
849
1127
|
let newContent;
|
|
850
1128
|
let replacements;
|
|
@@ -856,15 +1134,23 @@ function createEditTool(sandbox, config) {
|
|
|
856
1134
|
replacements = 1;
|
|
857
1135
|
}
|
|
858
1136
|
await sandbox.writeFile(file_path, newContent);
|
|
1137
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1138
|
+
if (debugId) {
|
|
1139
|
+
debugEnd(debugId, "edit", {
|
|
1140
|
+
summary: { replacements },
|
|
1141
|
+
duration_ms: durationMs
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
859
1144
|
return {
|
|
860
1145
|
message: `Successfully edited ${file_path}`,
|
|
861
1146
|
file_path,
|
|
862
1147
|
replacements
|
|
863
1148
|
};
|
|
864
1149
|
} catch (error) {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1150
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1151
|
+
if (debugId)
|
|
1152
|
+
debugError(debugId, "edit", errorMessage);
|
|
1153
|
+
return { error: errorMessage };
|
|
868
1154
|
}
|
|
869
1155
|
}
|
|
870
1156
|
});
|
|
@@ -931,33 +1217,44 @@ In plan mode, you'll:
|
|
|
931
1217
|
- This tool REQUIRES user approval - they must consent to entering plan mode
|
|
932
1218
|
- If unsure whether to use it, err on the side of planning - it's better to get alignment upfront than to redo work
|
|
933
1219
|
- Users appreciate being consulted before significant changes`;
|
|
934
|
-
function createEnterPlanModeTool(
|
|
1220
|
+
function createEnterPlanModeTool(state2, onEnter) {
|
|
935
1221
|
return tool4({
|
|
936
1222
|
description: ENTER_PLAN_MODE_DESCRIPTION,
|
|
937
1223
|
inputSchema: zodSchema4(enterPlanModeInputSchema),
|
|
938
1224
|
execute: async ({
|
|
939
1225
|
reason
|
|
940
1226
|
}) => {
|
|
1227
|
+
const startTime = performance.now();
|
|
1228
|
+
const debugId = isDebugEnabled() ? debugStart("enter-plan-mode", { reason }) : "";
|
|
941
1229
|
try {
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1230
|
+
if (state2.isActive) {
|
|
1231
|
+
const error = "Already in planning mode. Use ExitPlanMode to exit.";
|
|
1232
|
+
if (debugId)
|
|
1233
|
+
debugError(debugId, "enter-plan-mode", error);
|
|
1234
|
+
return { error };
|
|
946
1235
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1236
|
+
state2.isActive = true;
|
|
1237
|
+
state2.enteredAt = new Date;
|
|
1238
|
+
state2.reason = reason;
|
|
950
1239
|
if (onEnter) {
|
|
951
1240
|
await onEnter(reason);
|
|
952
1241
|
}
|
|
1242
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1243
|
+
if (debugId) {
|
|
1244
|
+
debugEnd(debugId, "enter-plan-mode", {
|
|
1245
|
+
summary: { mode: "planning" },
|
|
1246
|
+
duration_ms: durationMs
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
953
1249
|
return {
|
|
954
1250
|
message: `Entered planning mode: ${reason}. Use Read, Grep, and Glob to explore. Call ExitPlanMode when ready with a plan.`,
|
|
955
1251
|
mode: "planning"
|
|
956
1252
|
};
|
|
957
1253
|
} catch (error) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1254
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1255
|
+
if (debugId)
|
|
1256
|
+
debugError(debugId, "enter-plan-mode", errorMessage);
|
|
1257
|
+
return { error: errorMessage };
|
|
961
1258
|
}
|
|
962
1259
|
}
|
|
963
1260
|
});
|
|
@@ -997,19 +1294,32 @@ function createExitPlanModeTool(onPlanSubmit) {
|
|
|
997
1294
|
execute: async ({
|
|
998
1295
|
plan
|
|
999
1296
|
}) => {
|
|
1297
|
+
const startTime = performance.now();
|
|
1298
|
+
const debugId = isDebugEnabled() ? debugStart("exit-plan-mode", {
|
|
1299
|
+
planLength: plan.length,
|
|
1300
|
+
planPreview: plan.length > 200 ? `${plan.slice(0, 200)}...` : plan
|
|
1301
|
+
}) : "";
|
|
1000
1302
|
try {
|
|
1001
1303
|
let approved;
|
|
1002
1304
|
if (onPlanSubmit) {
|
|
1003
1305
|
approved = await onPlanSubmit(plan);
|
|
1004
1306
|
}
|
|
1307
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1308
|
+
if (debugId) {
|
|
1309
|
+
debugEnd(debugId, "exit-plan-mode", {
|
|
1310
|
+
summary: { approved },
|
|
1311
|
+
duration_ms: durationMs
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1005
1314
|
return {
|
|
1006
1315
|
message: approved ? "Plan approved, proceeding with execution" : "Plan submitted for review",
|
|
1007
1316
|
approved
|
|
1008
1317
|
};
|
|
1009
1318
|
} catch (error) {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1319
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1320
|
+
if (debugId)
|
|
1321
|
+
debugError(debugId, "exit-plan-mode", errorMessage);
|
|
1322
|
+
return { error: errorMessage };
|
|
1013
1323
|
}
|
|
1014
1324
|
}
|
|
1015
1325
|
});
|
|
@@ -1042,28 +1352,47 @@ function createGlobTool(sandbox, config) {
|
|
|
1042
1352
|
path
|
|
1043
1353
|
}) => {
|
|
1044
1354
|
const searchPath = path || ".";
|
|
1355
|
+
const startTime = performance.now();
|
|
1356
|
+
const debugId = isDebugEnabled() ? debugStart("glob", { pattern, path: searchPath }) : "";
|
|
1045
1357
|
if (config?.allowedPaths) {
|
|
1046
1358
|
const isAllowed = config.allowedPaths.some((allowed) => searchPath.startsWith(allowed));
|
|
1047
1359
|
if (!isAllowed) {
|
|
1048
|
-
|
|
1360
|
+
const error = `Path not allowed: ${searchPath}`;
|
|
1361
|
+
if (debugId)
|
|
1362
|
+
debugError(debugId, "glob", error);
|
|
1363
|
+
return { error };
|
|
1049
1364
|
}
|
|
1050
1365
|
}
|
|
1051
1366
|
try {
|
|
1052
|
-
const
|
|
1367
|
+
const findFlag = pattern.includes("/") ? "-path" : "-name";
|
|
1368
|
+
const findPattern = pattern.includes("/") && !pattern.startsWith("*") ? `*/${pattern}` : pattern;
|
|
1369
|
+
const result = await sandbox.exec(`find ${searchPath} -type f ${findFlag} "${findPattern}" 2>/dev/null | head -1000`, { timeout: config?.timeout });
|
|
1053
1370
|
if (result.exitCode !== 0 && result.stderr) {
|
|
1054
|
-
|
|
1371
|
+
const error = result.stderr;
|
|
1372
|
+
if (debugId)
|
|
1373
|
+
debugError(debugId, "glob", error);
|
|
1374
|
+
return { error };
|
|
1055
1375
|
}
|
|
1056
1376
|
const matches = result.stdout.split(`
|
|
1057
1377
|
`).filter(Boolean).map((p) => p.trim());
|
|
1378
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1379
|
+
if (debugId) {
|
|
1380
|
+
debugEnd(debugId, "glob", {
|
|
1381
|
+
summary: { count: matches.length },
|
|
1382
|
+
output: matches.slice(0, 10),
|
|
1383
|
+
duration_ms: durationMs
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1058
1386
|
return {
|
|
1059
1387
|
matches,
|
|
1060
1388
|
count: matches.length,
|
|
1061
1389
|
search_path: searchPath
|
|
1062
1390
|
};
|
|
1063
1391
|
} catch (error) {
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1392
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1393
|
+
if (debugId)
|
|
1394
|
+
debugError(debugId, "glob", errorMessage);
|
|
1395
|
+
return { error: errorMessage };
|
|
1067
1396
|
}
|
|
1068
1397
|
}
|
|
1069
1398
|
});
|
|
@@ -1130,17 +1459,31 @@ function createGrepTool(sandbox, config) {
|
|
|
1130
1459
|
multiline
|
|
1131
1460
|
} = input;
|
|
1132
1461
|
const searchPath = path || ".";
|
|
1462
|
+
const startTime = performance.now();
|
|
1463
|
+
const debugId = isDebugEnabled() ? debugStart("grep", {
|
|
1464
|
+
pattern,
|
|
1465
|
+
path: searchPath,
|
|
1466
|
+
output_mode,
|
|
1467
|
+
glob,
|
|
1468
|
+
type,
|
|
1469
|
+
caseInsensitive,
|
|
1470
|
+
multiline
|
|
1471
|
+
}) : "";
|
|
1133
1472
|
if (config?.allowedPaths) {
|
|
1134
1473
|
const isAllowed = config.allowedPaths.some((allowed) => searchPath.startsWith(allowed));
|
|
1135
1474
|
if (!isAllowed) {
|
|
1136
|
-
|
|
1475
|
+
const error = `Path not allowed: ${searchPath}`;
|
|
1476
|
+
if (debugId)
|
|
1477
|
+
debugError(debugId, "grep", error);
|
|
1478
|
+
return { error };
|
|
1137
1479
|
}
|
|
1138
1480
|
}
|
|
1139
1481
|
try {
|
|
1140
1482
|
if (!sandbox.rgPath) {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1483
|
+
const error = "Ripgrep not available. Call ensureSandboxTools(sandbox) before using Grep with remote sandboxes.";
|
|
1484
|
+
if (debugId)
|
|
1485
|
+
debugError(debugId, "grep", error);
|
|
1486
|
+
return { error };
|
|
1144
1487
|
}
|
|
1145
1488
|
const cmd = buildRipgrepCommand({
|
|
1146
1489
|
rgPath: sandbox.rgPath,
|
|
@@ -1156,17 +1499,48 @@ function createGrepTool(sandbox, config) {
|
|
|
1156
1499
|
multiline
|
|
1157
1500
|
});
|
|
1158
1501
|
const result = await sandbox.exec(cmd, { timeout: config?.timeout });
|
|
1502
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1503
|
+
let output;
|
|
1159
1504
|
if (output_mode === "files_with_matches") {
|
|
1160
|
-
|
|
1505
|
+
output = parseFilesOutput(result.stdout);
|
|
1506
|
+
if (debugId) {
|
|
1507
|
+
debugEnd(debugId, "grep", {
|
|
1508
|
+
summary: {
|
|
1509
|
+
fileCount: output.count,
|
|
1510
|
+
exitCode: result.exitCode
|
|
1511
|
+
},
|
|
1512
|
+
duration_ms: durationMs
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1161
1515
|
} else if (output_mode === "count") {
|
|
1162
|
-
|
|
1516
|
+
output = parseCountOutput(result.stdout);
|
|
1517
|
+
if (debugId) {
|
|
1518
|
+
debugEnd(debugId, "grep", {
|
|
1519
|
+
summary: {
|
|
1520
|
+
total: output.total,
|
|
1521
|
+
exitCode: result.exitCode
|
|
1522
|
+
},
|
|
1523
|
+
duration_ms: durationMs
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1163
1526
|
} else {
|
|
1164
|
-
|
|
1527
|
+
output = parseContentOutput(result.stdout, head_limit, offset);
|
|
1528
|
+
if (debugId) {
|
|
1529
|
+
debugEnd(debugId, "grep", {
|
|
1530
|
+
summary: {
|
|
1531
|
+
matchCount: output.total_matches,
|
|
1532
|
+
exitCode: result.exitCode
|
|
1533
|
+
},
|
|
1534
|
+
duration_ms: durationMs
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1165
1537
|
}
|
|
1538
|
+
return output;
|
|
1166
1539
|
} catch (error) {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1540
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1541
|
+
if (debugId)
|
|
1542
|
+
debugError(debugId, "grep", errorMessage);
|
|
1543
|
+
return { error: errorMessage };
|
|
1170
1544
|
}
|
|
1171
1545
|
}
|
|
1172
1546
|
});
|
|
@@ -1357,20 +1731,35 @@ function createReadTool(sandbox, config) {
|
|
|
1357
1731
|
offset,
|
|
1358
1732
|
limit
|
|
1359
1733
|
}) => {
|
|
1734
|
+
const startTime = performance.now();
|
|
1735
|
+
const debugId = isDebugEnabled() ? debugStart("read", { file_path, offset, limit }) : "";
|
|
1360
1736
|
if (config?.allowedPaths) {
|
|
1361
1737
|
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
1362
1738
|
if (!isAllowed) {
|
|
1363
|
-
|
|
1739
|
+
const error = `Path not allowed: ${file_path}`;
|
|
1740
|
+
if (debugId)
|
|
1741
|
+
debugError(debugId, "read", error);
|
|
1742
|
+
return { error };
|
|
1364
1743
|
}
|
|
1365
1744
|
}
|
|
1366
1745
|
try {
|
|
1367
1746
|
const exists = await sandbox.fileExists(file_path);
|
|
1368
1747
|
if (!exists) {
|
|
1369
|
-
|
|
1748
|
+
const error = `Path not found: ${file_path}`;
|
|
1749
|
+
if (debugId)
|
|
1750
|
+
debugError(debugId, "read", error);
|
|
1751
|
+
return { error };
|
|
1370
1752
|
}
|
|
1371
1753
|
const isDir = await sandbox.isDirectory(file_path);
|
|
1372
1754
|
if (isDir) {
|
|
1373
1755
|
const entries = await sandbox.readDir(file_path);
|
|
1756
|
+
const durationMs2 = Math.round(performance.now() - startTime);
|
|
1757
|
+
if (debugId) {
|
|
1758
|
+
debugEnd(debugId, "read", {
|
|
1759
|
+
summary: { type: "directory", count: entries.length },
|
|
1760
|
+
duration_ms: durationMs2
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1374
1763
|
return {
|
|
1375
1764
|
type: "directory",
|
|
1376
1765
|
entries,
|
|
@@ -1396,9 +1785,10 @@ function createReadTool(sandbox, config) {
|
|
|
1396
1785
|
"dylib"
|
|
1397
1786
|
];
|
|
1398
1787
|
if (binaryExtensions.includes(ext || "")) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1788
|
+
const error = `Cannot read binary file: ${file_path} (file exists, ${content.length} bytes). Use appropriate tools to process ${ext?.toUpperCase()} files (e.g., Python scripts for PDFs).`;
|
|
1789
|
+
if (debugId)
|
|
1790
|
+
debugError(debugId, "read", error);
|
|
1791
|
+
return { error };
|
|
1402
1792
|
}
|
|
1403
1793
|
}
|
|
1404
1794
|
const allLines = content.split(`
|
|
@@ -1406,9 +1796,10 @@ function createReadTool(sandbox, config) {
|
|
|
1406
1796
|
const totalLines = allLines.length;
|
|
1407
1797
|
const maxLinesWithoutLimit = config?.maxFileSize || 500;
|
|
1408
1798
|
if (!limit && totalLines > maxLinesWithoutLimit) {
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1799
|
+
const error = `File is large (${totalLines} lines). Use 'offset' and 'limit' to read in chunks. Example: offset=1, limit=100 for first 100 lines.`;
|
|
1800
|
+
if (debugId)
|
|
1801
|
+
debugError(debugId, "read", error);
|
|
1802
|
+
return { error };
|
|
1412
1803
|
}
|
|
1413
1804
|
const startLine = offset ? offset - 1 : 0;
|
|
1414
1805
|
const endLine = limit ? startLine + limit : allLines.length;
|
|
@@ -1417,6 +1808,18 @@ function createReadTool(sandbox, config) {
|
|
|
1417
1808
|
line_number: startLine + i + 1,
|
|
1418
1809
|
content: line
|
|
1419
1810
|
}));
|
|
1811
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1812
|
+
if (debugId) {
|
|
1813
|
+
debugEnd(debugId, "read", {
|
|
1814
|
+
summary: {
|
|
1815
|
+
type: "text",
|
|
1816
|
+
totalLines,
|
|
1817
|
+
returnedLines: lines.length,
|
|
1818
|
+
bytes: content.length
|
|
1819
|
+
},
|
|
1820
|
+
duration_ms: durationMs
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1420
1823
|
return {
|
|
1421
1824
|
type: "text",
|
|
1422
1825
|
content: selectedLines.join(`
|
|
@@ -1425,9 +1828,10 @@ function createReadTool(sandbox, config) {
|
|
|
1425
1828
|
total_lines: totalLines
|
|
1426
1829
|
};
|
|
1427
1830
|
} catch (error) {
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1831
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1832
|
+
if (debugId)
|
|
1833
|
+
debugError(debugId, "read", errorMessage);
|
|
1834
|
+
return { error: errorMessage };
|
|
1431
1835
|
}
|
|
1432
1836
|
}
|
|
1433
1837
|
});
|
|
@@ -1461,22 +1865,29 @@ function createSkillTool(config) {
|
|
|
1461
1865
|
execute: async ({
|
|
1462
1866
|
name
|
|
1463
1867
|
}) => {
|
|
1868
|
+
const startTime = performance.now();
|
|
1869
|
+
const debugId = isDebugEnabled() ? debugStart("skill", { name, availableSkills: Object.keys(skills) }) : "";
|
|
1464
1870
|
try {
|
|
1465
1871
|
const skill = skills[name];
|
|
1466
1872
|
if (!skill) {
|
|
1467
1873
|
const available = Object.keys(skills);
|
|
1468
1874
|
if (available.length === 0) {
|
|
1469
|
-
|
|
1875
|
+
const error2 = "No skills are available.";
|
|
1876
|
+
if (debugId)
|
|
1877
|
+
debugError(debugId, "skill", error2);
|
|
1878
|
+
return { error: error2 };
|
|
1470
1879
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1880
|
+
const error = `Skill '${name}' not found. Available skills: ${available.join(", ")}`;
|
|
1881
|
+
if (debugId)
|
|
1882
|
+
debugError(debugId, "skill", error);
|
|
1883
|
+
return { error };
|
|
1474
1884
|
}
|
|
1475
1885
|
let instructions;
|
|
1476
1886
|
if (!sandbox) {
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1887
|
+
const error = `Cannot load skill '${name}': no sandbox provided to read ${skill.path}`;
|
|
1888
|
+
if (debugId)
|
|
1889
|
+
debugError(debugId, "skill", error);
|
|
1890
|
+
return { error };
|
|
1480
1891
|
}
|
|
1481
1892
|
const content = await sandbox.readFile(skill.path);
|
|
1482
1893
|
const frontmatterEnd = content.indexOf(`
|
|
@@ -1489,6 +1900,17 @@ function createSkillTool(config) {
|
|
|
1489
1900
|
if (onActivate) {
|
|
1490
1901
|
await onActivate(skill, instructions);
|
|
1491
1902
|
}
|
|
1903
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1904
|
+
if (debugId) {
|
|
1905
|
+
debugEnd(debugId, "skill", {
|
|
1906
|
+
summary: {
|
|
1907
|
+
skillName: skill.name,
|
|
1908
|
+
instructionLength: instructions.length,
|
|
1909
|
+
hasAllowedTools: !!skill.allowedTools
|
|
1910
|
+
},
|
|
1911
|
+
duration_ms: durationMs
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1492
1914
|
return {
|
|
1493
1915
|
name: skill.name,
|
|
1494
1916
|
instructions,
|
|
@@ -1496,9 +1918,10 @@ function createSkillTool(config) {
|
|
|
1496
1918
|
message: skill.allowedTools ? `Skill '${name}' activated. Restricted to tools: ${skill.allowedTools.join(", ")}` : `Skill '${name}' activated. Follow the instructions below.`
|
|
1497
1919
|
};
|
|
1498
1920
|
} catch (error) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1921
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1922
|
+
if (debugId)
|
|
1923
|
+
debugError(debugId, "skill", errorMessage);
|
|
1924
|
+
return { error: errorMessage };
|
|
1502
1925
|
}
|
|
1503
1926
|
}
|
|
1504
1927
|
});
|
|
@@ -1593,6 +2016,11 @@ function createWebFetchTool(config) {
|
|
|
1593
2016
|
providerOptions,
|
|
1594
2017
|
execute: async (input) => {
|
|
1595
2018
|
const { url, prompt } = input;
|
|
2019
|
+
const startTime = performance.now();
|
|
2020
|
+
const debugId = isDebugEnabled() ? debugStart("web-fetch", {
|
|
2021
|
+
url,
|
|
2022
|
+
prompt: prompt.length > 200 ? `${prompt.slice(0, 200)}...` : prompt
|
|
2023
|
+
}) : "";
|
|
1596
2024
|
try {
|
|
1597
2025
|
const { content, finalUrl } = await fetchContent(url, apiKey, provider);
|
|
1598
2026
|
const result = await generateText({
|
|
@@ -1603,6 +2031,16 @@ Content from ${url}:
|
|
|
1603
2031
|
|
|
1604
2032
|
${content}`
|
|
1605
2033
|
});
|
|
2034
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
2035
|
+
if (debugId) {
|
|
2036
|
+
debugEnd(debugId, "web-fetch", {
|
|
2037
|
+
summary: {
|
|
2038
|
+
contentLength: content.length,
|
|
2039
|
+
responseLength: result.text.length
|
|
2040
|
+
},
|
|
2041
|
+
duration_ms: durationMs
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
1606
2044
|
return {
|
|
1607
2045
|
response: result.text,
|
|
1608
2046
|
url,
|
|
@@ -1612,15 +2050,18 @@ ${content}`
|
|
|
1612
2050
|
if (error && typeof error === "object" && "status" in error) {
|
|
1613
2051
|
const statusCode = error.status;
|
|
1614
2052
|
const message = error.message || "API request failed";
|
|
2053
|
+
if (debugId)
|
|
2054
|
+
debugError(debugId, "web-fetch", `${message} (status: ${statusCode})`);
|
|
1615
2055
|
return {
|
|
1616
2056
|
error: message,
|
|
1617
2057
|
status_code: statusCode,
|
|
1618
2058
|
retryable: RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
1619
2059
|
};
|
|
1620
2060
|
}
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
2061
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2062
|
+
if (debugId)
|
|
2063
|
+
debugError(debugId, "web-fetch", errorMessage);
|
|
2064
|
+
return { error: errorMessage };
|
|
1624
2065
|
}
|
|
1625
2066
|
}
|
|
1626
2067
|
});
|
|
@@ -1716,12 +2157,22 @@ function createWebSearchTool(config) {
|
|
|
1716
2157
|
providerOptions,
|
|
1717
2158
|
execute: async (input) => {
|
|
1718
2159
|
const { query, allowed_domains, blocked_domains } = input;
|
|
2160
|
+
const startTime = performance.now();
|
|
2161
|
+
const debugId = isDebugEnabled() ? debugStart("web-search", { query, allowed_domains, blocked_domains }) : "";
|
|
1719
2162
|
try {
|
|
1720
2163
|
const results = await searchContent(apiKey, provider, {
|
|
1721
2164
|
query,
|
|
1722
2165
|
allowedDomains: allowed_domains,
|
|
1723
2166
|
blockedDomains: blocked_domains
|
|
1724
2167
|
});
|
|
2168
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
2169
|
+
if (debugId) {
|
|
2170
|
+
debugEnd(debugId, "web-search", {
|
|
2171
|
+
summary: { resultCount: results.length },
|
|
2172
|
+
output: results.slice(0, 5).map((r) => ({ title: r.title, url: r.url })),
|
|
2173
|
+
duration_ms: durationMs
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
1725
2176
|
return {
|
|
1726
2177
|
results,
|
|
1727
2178
|
total_results: results.length,
|
|
@@ -1731,15 +2182,18 @@ function createWebSearchTool(config) {
|
|
|
1731
2182
|
if (error && typeof error === "object" && "status" in error) {
|
|
1732
2183
|
const statusCode = error.status;
|
|
1733
2184
|
const message = error.message || "API request failed";
|
|
2185
|
+
if (debugId)
|
|
2186
|
+
debugError(debugId, "web-search", `${message} (status: ${statusCode})`);
|
|
1734
2187
|
return {
|
|
1735
2188
|
error: message,
|
|
1736
2189
|
status_code: statusCode,
|
|
1737
2190
|
retryable: RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
1738
2191
|
};
|
|
1739
2192
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
2193
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2194
|
+
if (debugId)
|
|
2195
|
+
debugError(debugId, "web-search", errorMessage);
|
|
2196
|
+
return { error: errorMessage };
|
|
1743
2197
|
}
|
|
1744
2198
|
}
|
|
1745
2199
|
});
|
|
@@ -1775,29 +2229,43 @@ function createWriteTool(sandbox, config) {
|
|
|
1775
2229
|
file_path,
|
|
1776
2230
|
content
|
|
1777
2231
|
}) => {
|
|
2232
|
+
const startTime = performance.now();
|
|
1778
2233
|
const byteLength = Buffer.byteLength(content, "utf-8");
|
|
2234
|
+
const debugId = isDebugEnabled() ? debugStart("write", { file_path, contentLength: byteLength }) : "";
|
|
1779
2235
|
if (config?.maxFileSize && byteLength > config.maxFileSize) {
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2236
|
+
const error = `File content exceeds maximum size of ${config.maxFileSize} bytes (got ${byteLength})`;
|
|
2237
|
+
if (debugId)
|
|
2238
|
+
debugError(debugId, "write", error);
|
|
2239
|
+
return { error };
|
|
1783
2240
|
}
|
|
1784
2241
|
if (config?.allowedPaths) {
|
|
1785
2242
|
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
1786
2243
|
if (!isAllowed) {
|
|
1787
|
-
|
|
2244
|
+
const error = `Path not allowed: ${file_path}`;
|
|
2245
|
+
if (debugId)
|
|
2246
|
+
debugError(debugId, "write", error);
|
|
2247
|
+
return { error };
|
|
1788
2248
|
}
|
|
1789
2249
|
}
|
|
1790
2250
|
try {
|
|
1791
2251
|
await sandbox.writeFile(file_path, content);
|
|
2252
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
2253
|
+
if (debugId) {
|
|
2254
|
+
debugEnd(debugId, "write", {
|
|
2255
|
+
summary: { bytes_written: byteLength },
|
|
2256
|
+
duration_ms: durationMs
|
|
2257
|
+
});
|
|
2258
|
+
}
|
|
1792
2259
|
return {
|
|
1793
2260
|
message: `Successfully wrote to ${file_path}`,
|
|
1794
2261
|
bytes_written: byteLength,
|
|
1795
2262
|
file_path
|
|
1796
2263
|
};
|
|
1797
2264
|
} catch (error) {
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
2265
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2266
|
+
if (debugId)
|
|
2267
|
+
debugError(debugId, "write", errorMessage);
|
|
2268
|
+
return { error: errorMessage };
|
|
1801
2269
|
}
|
|
1802
2270
|
}
|
|
1803
2271
|
});
|
|
@@ -1844,16 +2312,22 @@ var eventCounter = 0;
|
|
|
1844
2312
|
function generateEventId() {
|
|
1845
2313
|
return `subagent-${Date.now()}-${++eventCounter}`;
|
|
1846
2314
|
}
|
|
1847
|
-
function filterTools(allTools, allowedTools) {
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
2315
|
+
function filterTools(allTools, allowedTools, additionalTools) {
|
|
2316
|
+
let result;
|
|
2317
|
+
if (allowedTools) {
|
|
2318
|
+
result = {};
|
|
2319
|
+
for (const name of allowedTools) {
|
|
2320
|
+
if (allTools[name]) {
|
|
2321
|
+
result[name] = allTools[name];
|
|
2322
|
+
}
|
|
1854
2323
|
}
|
|
2324
|
+
} else {
|
|
2325
|
+
result = allTools;
|
|
1855
2326
|
}
|
|
1856
|
-
|
|
2327
|
+
if (additionalTools) {
|
|
2328
|
+
result = { ...result, ...additionalTools };
|
|
2329
|
+
}
|
|
2330
|
+
return result;
|
|
1857
2331
|
}
|
|
1858
2332
|
function createTaskTool(config) {
|
|
1859
2333
|
const {
|
|
@@ -1875,10 +2349,20 @@ function createTaskTool(config) {
|
|
|
1875
2349
|
tools: customTools
|
|
1876
2350
|
}) => {
|
|
1877
2351
|
const startTime = performance.now();
|
|
2352
|
+
const typeConfig = subagentTypes[subagent_type] || {};
|
|
2353
|
+
const debugId = isDebugEnabled() ? debugStart("task", {
|
|
2354
|
+
subagent_type,
|
|
2355
|
+
description,
|
|
2356
|
+
tools: [
|
|
2357
|
+
...customTools ?? typeConfig.tools ?? Object.keys(allTools),
|
|
2358
|
+
...Object.keys(typeConfig.additionalTools ?? {})
|
|
2359
|
+
]
|
|
2360
|
+
}) : "";
|
|
2361
|
+
if (debugId)
|
|
2362
|
+
pushParent(debugId);
|
|
1878
2363
|
try {
|
|
1879
|
-
const typeConfig = subagentTypes[subagent_type] || {};
|
|
1880
2364
|
const model = typeConfig.model || defaultModel;
|
|
1881
|
-
const tools = filterTools(allTools, customTools ?? typeConfig.tools);
|
|
2365
|
+
const tools = filterTools(allTools, customTools ?? typeConfig.tools, typeConfig.additionalTools);
|
|
1882
2366
|
const systemPrompt = system_prompt ?? typeConfig.systemPrompt;
|
|
1883
2367
|
const commonOptions = {
|
|
1884
2368
|
model,
|
|
@@ -1949,6 +2433,19 @@ function createTaskTool(config) {
|
|
|
1949
2433
|
}
|
|
1950
2434
|
});
|
|
1951
2435
|
const durationMs2 = Math.round(performance.now() - startTime);
|
|
2436
|
+
if (debugId) {
|
|
2437
|
+
popParent();
|
|
2438
|
+
debugEnd(debugId, "task", {
|
|
2439
|
+
summary: {
|
|
2440
|
+
tokens: {
|
|
2441
|
+
input: usage2.inputTokens,
|
|
2442
|
+
output: usage2.outputTokens
|
|
2443
|
+
},
|
|
2444
|
+
steps: response.messages?.length
|
|
2445
|
+
},
|
|
2446
|
+
duration_ms: durationMs2
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
1952
2449
|
return {
|
|
1953
2450
|
result: text,
|
|
1954
2451
|
usage: usage2.inputTokens !== undefined && usage2.outputTokens !== undefined ? {
|
|
@@ -1976,6 +2473,19 @@ function createTaskTool(config) {
|
|
|
1976
2473
|
input_tokens: result.usage.inputTokens,
|
|
1977
2474
|
output_tokens: result.usage.outputTokens
|
|
1978
2475
|
} : undefined;
|
|
2476
|
+
if (debugId) {
|
|
2477
|
+
popParent();
|
|
2478
|
+
debugEnd(debugId, "task", {
|
|
2479
|
+
summary: {
|
|
2480
|
+
tokens: {
|
|
2481
|
+
input: result.usage.inputTokens,
|
|
2482
|
+
output: result.usage.outputTokens
|
|
2483
|
+
},
|
|
2484
|
+
steps: result.steps?.length
|
|
2485
|
+
},
|
|
2486
|
+
duration_ms: durationMs
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
1979
2489
|
return {
|
|
1980
2490
|
result: result.text,
|
|
1981
2491
|
usage,
|
|
@@ -1985,6 +2495,10 @@ function createTaskTool(config) {
|
|
|
1985
2495
|
};
|
|
1986
2496
|
} catch (error) {
|
|
1987
2497
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2498
|
+
if (debugId) {
|
|
2499
|
+
popParent();
|
|
2500
|
+
debugError(debugId, "task", errorMessage);
|
|
2501
|
+
}
|
|
1988
2502
|
return { error: errorMessage };
|
|
1989
2503
|
}
|
|
1990
2504
|
}
|
|
@@ -2032,15 +2546,22 @@ var TODO_WRITE_DESCRIPTION = `Use this tool to create and manage a structured ta
|
|
|
2032
2546
|
- Keep exactly ONE task in_progress at any time
|
|
2033
2547
|
- ONLY mark completed when FULLY accomplished
|
|
2034
2548
|
- If blocked/errors, keep in_progress and create new task for the blocker`;
|
|
2035
|
-
function createTodoWriteTool(
|
|
2549
|
+
function createTodoWriteTool(state2, onUpdate) {
|
|
2036
2550
|
return tool14({
|
|
2037
2551
|
description: TODO_WRITE_DESCRIPTION,
|
|
2038
2552
|
inputSchema: zodSchema14(todoWriteInputSchema),
|
|
2039
2553
|
execute: async ({
|
|
2040
2554
|
todos
|
|
2041
2555
|
}) => {
|
|
2556
|
+
const startTime = performance.now();
|
|
2557
|
+
const debugId = isDebugEnabled() ? debugStart("todo-write", {
|
|
2558
|
+
todoCount: todos.length,
|
|
2559
|
+
pending: todos.filter((t) => t.status === "pending").length,
|
|
2560
|
+
in_progress: todos.filter((t) => t.status === "in_progress").length,
|
|
2561
|
+
completed: todos.filter((t) => t.status === "completed").length
|
|
2562
|
+
}) : "";
|
|
2042
2563
|
try {
|
|
2043
|
-
|
|
2564
|
+
state2.todos = todos;
|
|
2044
2565
|
if (onUpdate) {
|
|
2045
2566
|
onUpdate(todos);
|
|
2046
2567
|
}
|
|
@@ -2050,14 +2571,22 @@ function createTodoWriteTool(state, onUpdate) {
|
|
|
2050
2571
|
in_progress: todos.filter((t) => t.status === "in_progress").length,
|
|
2051
2572
|
completed: todos.filter((t) => t.status === "completed").length
|
|
2052
2573
|
};
|
|
2574
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
2575
|
+
if (debugId) {
|
|
2576
|
+
debugEnd(debugId, "todo-write", {
|
|
2577
|
+
summary: stats,
|
|
2578
|
+
duration_ms: durationMs
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2053
2581
|
return {
|
|
2054
2582
|
message: "Todo list updated successfully",
|
|
2055
2583
|
stats
|
|
2056
2584
|
};
|
|
2057
2585
|
} catch (error) {
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2586
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2587
|
+
if (debugId)
|
|
2588
|
+
debugError(debugId, "todo-write", errorMessage);
|
|
2589
|
+
return { error: errorMessage };
|
|
2061
2590
|
}
|
|
2062
2591
|
}
|
|
2063
2592
|
});
|
|
@@ -2306,20 +2835,20 @@ function pruneMessagesByTokens(messages, config) {
|
|
|
2306
2835
|
}
|
|
2307
2836
|
|
|
2308
2837
|
// src/utils/compact-conversation.ts
|
|
2309
|
-
async function compactConversation(messages, config,
|
|
2838
|
+
async function compactConversation(messages, config, state2 = { conversationSummary: "" }) {
|
|
2310
2839
|
const currentTokens = estimateMessagesTokens(messages);
|
|
2311
2840
|
const threshold = config.compactionThreshold ?? 0.85;
|
|
2312
2841
|
const limit = config.maxTokens * threshold;
|
|
2313
2842
|
if (currentTokens < limit) {
|
|
2314
|
-
return { messages, state, didCompact: false };
|
|
2843
|
+
return { messages, state: state2, didCompact: false };
|
|
2315
2844
|
}
|
|
2316
2845
|
const protectCount = config.protectRecentMessages ?? 10;
|
|
2317
2846
|
const recentMessages = messages.slice(-protectCount);
|
|
2318
2847
|
const oldMessages = messages.slice(0, -protectCount);
|
|
2319
2848
|
if (oldMessages.length === 0) {
|
|
2320
|
-
return { messages, state, didCompact: false };
|
|
2849
|
+
return { messages, state: state2, didCompact: false };
|
|
2321
2850
|
}
|
|
2322
|
-
const newSummary = await summarizeMessages(oldMessages, config.summarizerModel, config.taskContext,
|
|
2851
|
+
const newSummary = await summarizeMessages(oldMessages, config.summarizerModel, config.taskContext, state2.conversationSummary);
|
|
2323
2852
|
const compactedMessages = [
|
|
2324
2853
|
{
|
|
2325
2854
|
role: "user",
|
|
@@ -2854,10 +3383,13 @@ async function createDirectory(sandbox, path) {
|
|
|
2854
3383
|
export {
|
|
2855
3384
|
skillsToXml,
|
|
2856
3385
|
setupAgentEnvironment,
|
|
3386
|
+
reinitDebugMode,
|
|
2857
3387
|
pruneMessagesByTokens,
|
|
2858
3388
|
parseSkillMetadata,
|
|
2859
3389
|
loadSkillBundles,
|
|
2860
3390
|
loadSkillBundle,
|
|
3391
|
+
isDebugEnabled,
|
|
3392
|
+
getDebugLogs,
|
|
2861
3393
|
getContextStatus,
|
|
2862
3394
|
fetchSkills,
|
|
2863
3395
|
fetchSkill,
|
|
@@ -2889,6 +3421,7 @@ export {
|
|
|
2889
3421
|
contextNeedsCompaction,
|
|
2890
3422
|
contextNeedsAttention,
|
|
2891
3423
|
compactConversation,
|
|
3424
|
+
clearDebugLogs,
|
|
2892
3425
|
cached,
|
|
2893
3426
|
anthropicPromptCacheMiddlewareV2,
|
|
2894
3427
|
anthropicPromptCacheMiddleware,
|