bashkit 0.3.1 → 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/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 +619 -106
- 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/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.js +827 -234
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -615,6 +615,184 @@ var DEFAULT_CONFIG = {
|
|
|
615
615
|
// src/tools/ask-user.ts
|
|
616
616
|
import { tool, zodSchema } from "ai";
|
|
617
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
|
|
618
796
|
var questionOptionSchema = z.object({
|
|
619
797
|
label: z.string().describe("The display text for this option. Should be concise (1-5 words). Add '(Recommended)' suffix for suggested options."),
|
|
620
798
|
description: z.string().optional().describe("Explanation of what this option means or its implications.")
|
|
@@ -664,22 +842,46 @@ function createAskUserTool(config) {
|
|
|
664
842
|
description: ASK_USER_DESCRIPTION,
|
|
665
843
|
inputSchema: zodSchema(askUserInputSchema),
|
|
666
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
|
+
}) : "";
|
|
667
851
|
try {
|
|
668
852
|
if (!input.question && !input.questions) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
853
|
+
const error2 = "Either 'question' or 'questions' must be provided";
|
|
854
|
+
if (debugId)
|
|
855
|
+
debugError(debugId, "ask-user", error2);
|
|
856
|
+
return { error: error2 };
|
|
672
857
|
}
|
|
673
858
|
if (input.questions && input.questions.length > 0) {
|
|
674
859
|
if (normalizedConfig.onStructuredQuestions) {
|
|
675
860
|
const answers = await normalizedConfig.onStructuredQuestions(input.questions);
|
|
676
861
|
const firstKey = Object.keys(answers)[0];
|
|
677
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
|
+
}
|
|
678
873
|
return {
|
|
679
874
|
answer: Array.isArray(firstAnswer) ? firstAnswer.join(", ") : firstAnswer,
|
|
680
875
|
answers
|
|
681
876
|
};
|
|
682
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
|
+
}
|
|
683
885
|
return {
|
|
684
886
|
questions: input.questions,
|
|
685
887
|
awaiting_response: true
|
|
@@ -688,18 +890,36 @@ function createAskUserTool(config) {
|
|
|
688
890
|
if (input.question) {
|
|
689
891
|
if (normalizedConfig.onQuestion) {
|
|
690
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
|
+
}
|
|
691
900
|
return { answer };
|
|
692
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
|
+
}
|
|
693
909
|
return {
|
|
694
910
|
question: input.question,
|
|
695
911
|
awaiting_response: true
|
|
696
912
|
};
|
|
697
913
|
}
|
|
698
|
-
|
|
914
|
+
const error = "No question provided";
|
|
915
|
+
if (debugId)
|
|
916
|
+
debugError(debugId, "ask-user", error);
|
|
917
|
+
return { error };
|
|
699
918
|
} catch (error) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
919
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
920
|
+
if (debugId)
|
|
921
|
+
debugError(debugId, "ask-user", errorMessage);
|
|
922
|
+
return { error: errorMessage };
|
|
703
923
|
}
|
|
704
924
|
}
|
|
705
925
|
});
|
|
@@ -763,12 +983,18 @@ function createBashTool(sandbox, config) {
|
|
|
763
983
|
description: _description,
|
|
764
984
|
run_in_background: _run_in_background
|
|
765
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
|
+
}) : "";
|
|
766
991
|
if (config?.blockedCommands) {
|
|
767
992
|
for (const blocked of config.blockedCommands) {
|
|
768
993
|
if (command.includes(blocked)) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
994
|
+
const error = `Command blocked: contains '${blocked}'`;
|
|
995
|
+
if (debugId)
|
|
996
|
+
debugError(debugId, "bash", error);
|
|
997
|
+
return { error };
|
|
772
998
|
}
|
|
773
999
|
}
|
|
774
1000
|
}
|
|
@@ -787,6 +1013,18 @@ function createBashTool(sandbox, config) {
|
|
|
787
1013
|
stderr = stderr.slice(0, maxOutputLength) + `
|
|
788
1014
|
[output truncated, ${stderr.length - maxOutputLength} chars omitted]`;
|
|
789
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
|
+
}
|
|
790
1028
|
return {
|
|
791
1029
|
stdout,
|
|
792
1030
|
stderr,
|
|
@@ -795,9 +1033,10 @@ function createBashTool(sandbox, config) {
|
|
|
795
1033
|
duration_ms: result.durationMs
|
|
796
1034
|
};
|
|
797
1035
|
} catch (error) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1036
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1037
|
+
if (debugId)
|
|
1038
|
+
debugError(debugId, "bash", errorMessage);
|
|
1039
|
+
return { error: errorMessage };
|
|
801
1040
|
}
|
|
802
1041
|
}
|
|
803
1042
|
});
|
|
@@ -842,29 +1081,48 @@ function createEditTool(sandbox, config) {
|
|
|
842
1081
|
new_string,
|
|
843
1082
|
replace_all = false
|
|
844
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
|
+
}) : "";
|
|
845
1090
|
if (old_string === new_string) {
|
|
846
|
-
|
|
1091
|
+
const error = "old_string and new_string must be different";
|
|
1092
|
+
if (debugId)
|
|
1093
|
+
debugError(debugId, "edit", error);
|
|
1094
|
+
return { error };
|
|
847
1095
|
}
|
|
848
1096
|
if (config?.allowedPaths) {
|
|
849
1097
|
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
850
1098
|
if (!isAllowed) {
|
|
851
|
-
|
|
1099
|
+
const error = `Path not allowed: ${file_path}`;
|
|
1100
|
+
if (debugId)
|
|
1101
|
+
debugError(debugId, "edit", error);
|
|
1102
|
+
return { error };
|
|
852
1103
|
}
|
|
853
1104
|
}
|
|
854
1105
|
try {
|
|
855
1106
|
const exists = await sandbox.fileExists(file_path);
|
|
856
1107
|
if (!exists) {
|
|
857
|
-
|
|
1108
|
+
const error = `File not found: ${file_path}`;
|
|
1109
|
+
if (debugId)
|
|
1110
|
+
debugError(debugId, "edit", error);
|
|
1111
|
+
return { error };
|
|
858
1112
|
}
|
|
859
1113
|
const content = await sandbox.readFile(file_path);
|
|
860
1114
|
const occurrences = content.split(old_string).length - 1;
|
|
861
1115
|
if (occurrences === 0) {
|
|
862
|
-
|
|
1116
|
+
const error = `String not found in file: "${old_string}"`;
|
|
1117
|
+
if (debugId)
|
|
1118
|
+
debugError(debugId, "edit", error);
|
|
1119
|
+
return { error };
|
|
863
1120
|
}
|
|
864
1121
|
if (!replace_all && occurrences > 1) {
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
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 };
|
|
868
1126
|
}
|
|
869
1127
|
let newContent;
|
|
870
1128
|
let replacements;
|
|
@@ -876,15 +1134,23 @@ function createEditTool(sandbox, config) {
|
|
|
876
1134
|
replacements = 1;
|
|
877
1135
|
}
|
|
878
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
|
+
}
|
|
879
1144
|
return {
|
|
880
1145
|
message: `Successfully edited ${file_path}`,
|
|
881
1146
|
file_path,
|
|
882
1147
|
replacements
|
|
883
1148
|
};
|
|
884
1149
|
} catch (error) {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1150
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1151
|
+
if (debugId)
|
|
1152
|
+
debugError(debugId, "edit", errorMessage);
|
|
1153
|
+
return { error: errorMessage };
|
|
888
1154
|
}
|
|
889
1155
|
}
|
|
890
1156
|
});
|
|
@@ -951,33 +1217,44 @@ In plan mode, you'll:
|
|
|
951
1217
|
- This tool REQUIRES user approval - they must consent to entering plan mode
|
|
952
1218
|
- If unsure whether to use it, err on the side of planning - it's better to get alignment upfront than to redo work
|
|
953
1219
|
- Users appreciate being consulted before significant changes`;
|
|
954
|
-
function createEnterPlanModeTool(
|
|
1220
|
+
function createEnterPlanModeTool(state2, onEnter) {
|
|
955
1221
|
return tool4({
|
|
956
1222
|
description: ENTER_PLAN_MODE_DESCRIPTION,
|
|
957
1223
|
inputSchema: zodSchema4(enterPlanModeInputSchema),
|
|
958
1224
|
execute: async ({
|
|
959
1225
|
reason
|
|
960
1226
|
}) => {
|
|
1227
|
+
const startTime = performance.now();
|
|
1228
|
+
const debugId = isDebugEnabled() ? debugStart("enter-plan-mode", { reason }) : "";
|
|
961
1229
|
try {
|
|
962
|
-
if (
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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 };
|
|
966
1235
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1236
|
+
state2.isActive = true;
|
|
1237
|
+
state2.enteredAt = new Date;
|
|
1238
|
+
state2.reason = reason;
|
|
970
1239
|
if (onEnter) {
|
|
971
1240
|
await onEnter(reason);
|
|
972
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
|
+
}
|
|
973
1249
|
return {
|
|
974
1250
|
message: `Entered planning mode: ${reason}. Use Read, Grep, and Glob to explore. Call ExitPlanMode when ready with a plan.`,
|
|
975
1251
|
mode: "planning"
|
|
976
1252
|
};
|
|
977
1253
|
} catch (error) {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1254
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1255
|
+
if (debugId)
|
|
1256
|
+
debugError(debugId, "enter-plan-mode", errorMessage);
|
|
1257
|
+
return { error: errorMessage };
|
|
981
1258
|
}
|
|
982
1259
|
}
|
|
983
1260
|
});
|
|
@@ -1017,19 +1294,32 @@ function createExitPlanModeTool(onPlanSubmit) {
|
|
|
1017
1294
|
execute: async ({
|
|
1018
1295
|
plan
|
|
1019
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
|
+
}) : "";
|
|
1020
1302
|
try {
|
|
1021
1303
|
let approved;
|
|
1022
1304
|
if (onPlanSubmit) {
|
|
1023
1305
|
approved = await onPlanSubmit(plan);
|
|
1024
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
|
+
}
|
|
1025
1314
|
return {
|
|
1026
1315
|
message: approved ? "Plan approved, proceeding with execution" : "Plan submitted for review",
|
|
1027
1316
|
approved
|
|
1028
1317
|
};
|
|
1029
1318
|
} catch (error) {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1319
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1320
|
+
if (debugId)
|
|
1321
|
+
debugError(debugId, "exit-plan-mode", errorMessage);
|
|
1322
|
+
return { error: errorMessage };
|
|
1033
1323
|
}
|
|
1034
1324
|
}
|
|
1035
1325
|
});
|
|
@@ -1062,28 +1352,47 @@ function createGlobTool(sandbox, config) {
|
|
|
1062
1352
|
path
|
|
1063
1353
|
}) => {
|
|
1064
1354
|
const searchPath = path || ".";
|
|
1355
|
+
const startTime = performance.now();
|
|
1356
|
+
const debugId = isDebugEnabled() ? debugStart("glob", { pattern, path: searchPath }) : "";
|
|
1065
1357
|
if (config?.allowedPaths) {
|
|
1066
1358
|
const isAllowed = config.allowedPaths.some((allowed) => searchPath.startsWith(allowed));
|
|
1067
1359
|
if (!isAllowed) {
|
|
1068
|
-
|
|
1360
|
+
const error = `Path not allowed: ${searchPath}`;
|
|
1361
|
+
if (debugId)
|
|
1362
|
+
debugError(debugId, "glob", error);
|
|
1363
|
+
return { error };
|
|
1069
1364
|
}
|
|
1070
1365
|
}
|
|
1071
1366
|
try {
|
|
1072
|
-
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 });
|
|
1073
1370
|
if (result.exitCode !== 0 && result.stderr) {
|
|
1074
|
-
|
|
1371
|
+
const error = result.stderr;
|
|
1372
|
+
if (debugId)
|
|
1373
|
+
debugError(debugId, "glob", error);
|
|
1374
|
+
return { error };
|
|
1075
1375
|
}
|
|
1076
1376
|
const matches = result.stdout.split(`
|
|
1077
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
|
+
}
|
|
1078
1386
|
return {
|
|
1079
1387
|
matches,
|
|
1080
1388
|
count: matches.length,
|
|
1081
1389
|
search_path: searchPath
|
|
1082
1390
|
};
|
|
1083
1391
|
} catch (error) {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1392
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1393
|
+
if (debugId)
|
|
1394
|
+
debugError(debugId, "glob", errorMessage);
|
|
1395
|
+
return { error: errorMessage };
|
|
1087
1396
|
}
|
|
1088
1397
|
}
|
|
1089
1398
|
});
|
|
@@ -1150,17 +1459,31 @@ function createGrepTool(sandbox, config) {
|
|
|
1150
1459
|
multiline
|
|
1151
1460
|
} = input;
|
|
1152
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
|
+
}) : "";
|
|
1153
1472
|
if (config?.allowedPaths) {
|
|
1154
1473
|
const isAllowed = config.allowedPaths.some((allowed) => searchPath.startsWith(allowed));
|
|
1155
1474
|
if (!isAllowed) {
|
|
1156
|
-
|
|
1475
|
+
const error = `Path not allowed: ${searchPath}`;
|
|
1476
|
+
if (debugId)
|
|
1477
|
+
debugError(debugId, "grep", error);
|
|
1478
|
+
return { error };
|
|
1157
1479
|
}
|
|
1158
1480
|
}
|
|
1159
1481
|
try {
|
|
1160
1482
|
if (!sandbox.rgPath) {
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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 };
|
|
1164
1487
|
}
|
|
1165
1488
|
const cmd = buildRipgrepCommand({
|
|
1166
1489
|
rgPath: sandbox.rgPath,
|
|
@@ -1176,17 +1499,48 @@ function createGrepTool(sandbox, config) {
|
|
|
1176
1499
|
multiline
|
|
1177
1500
|
});
|
|
1178
1501
|
const result = await sandbox.exec(cmd, { timeout: config?.timeout });
|
|
1502
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
1503
|
+
let output;
|
|
1179
1504
|
if (output_mode === "files_with_matches") {
|
|
1180
|
-
|
|
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
|
+
}
|
|
1181
1515
|
} else if (output_mode === "count") {
|
|
1182
|
-
|
|
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
|
+
}
|
|
1183
1526
|
} else {
|
|
1184
|
-
|
|
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
|
+
}
|
|
1185
1537
|
}
|
|
1538
|
+
return output;
|
|
1186
1539
|
} catch (error) {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1540
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1541
|
+
if (debugId)
|
|
1542
|
+
debugError(debugId, "grep", errorMessage);
|
|
1543
|
+
return { error: errorMessage };
|
|
1190
1544
|
}
|
|
1191
1545
|
}
|
|
1192
1546
|
});
|
|
@@ -1377,20 +1731,35 @@ function createReadTool(sandbox, config) {
|
|
|
1377
1731
|
offset,
|
|
1378
1732
|
limit
|
|
1379
1733
|
}) => {
|
|
1734
|
+
const startTime = performance.now();
|
|
1735
|
+
const debugId = isDebugEnabled() ? debugStart("read", { file_path, offset, limit }) : "";
|
|
1380
1736
|
if (config?.allowedPaths) {
|
|
1381
1737
|
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
1382
1738
|
if (!isAllowed) {
|
|
1383
|
-
|
|
1739
|
+
const error = `Path not allowed: ${file_path}`;
|
|
1740
|
+
if (debugId)
|
|
1741
|
+
debugError(debugId, "read", error);
|
|
1742
|
+
return { error };
|
|
1384
1743
|
}
|
|
1385
1744
|
}
|
|
1386
1745
|
try {
|
|
1387
1746
|
const exists = await sandbox.fileExists(file_path);
|
|
1388
1747
|
if (!exists) {
|
|
1389
|
-
|
|
1748
|
+
const error = `Path not found: ${file_path}`;
|
|
1749
|
+
if (debugId)
|
|
1750
|
+
debugError(debugId, "read", error);
|
|
1751
|
+
return { error };
|
|
1390
1752
|
}
|
|
1391
1753
|
const isDir = await sandbox.isDirectory(file_path);
|
|
1392
1754
|
if (isDir) {
|
|
1393
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
|
+
}
|
|
1394
1763
|
return {
|
|
1395
1764
|
type: "directory",
|
|
1396
1765
|
entries,
|
|
@@ -1416,9 +1785,10 @@ function createReadTool(sandbox, config) {
|
|
|
1416
1785
|
"dylib"
|
|
1417
1786
|
];
|
|
1418
1787
|
if (binaryExtensions.includes(ext || "")) {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
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 };
|
|
1422
1792
|
}
|
|
1423
1793
|
}
|
|
1424
1794
|
const allLines = content.split(`
|
|
@@ -1426,9 +1796,10 @@ function createReadTool(sandbox, config) {
|
|
|
1426
1796
|
const totalLines = allLines.length;
|
|
1427
1797
|
const maxLinesWithoutLimit = config?.maxFileSize || 500;
|
|
1428
1798
|
if (!limit && totalLines > maxLinesWithoutLimit) {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
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 };
|
|
1432
1803
|
}
|
|
1433
1804
|
const startLine = offset ? offset - 1 : 0;
|
|
1434
1805
|
const endLine = limit ? startLine + limit : allLines.length;
|
|
@@ -1437,6 +1808,18 @@ function createReadTool(sandbox, config) {
|
|
|
1437
1808
|
line_number: startLine + i + 1,
|
|
1438
1809
|
content: line
|
|
1439
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
|
+
}
|
|
1440
1823
|
return {
|
|
1441
1824
|
type: "text",
|
|
1442
1825
|
content: selectedLines.join(`
|
|
@@ -1445,9 +1828,10 @@ function createReadTool(sandbox, config) {
|
|
|
1445
1828
|
total_lines: totalLines
|
|
1446
1829
|
};
|
|
1447
1830
|
} catch (error) {
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1831
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1832
|
+
if (debugId)
|
|
1833
|
+
debugError(debugId, "read", errorMessage);
|
|
1834
|
+
return { error: errorMessage };
|
|
1451
1835
|
}
|
|
1452
1836
|
}
|
|
1453
1837
|
});
|
|
@@ -1481,22 +1865,29 @@ function createSkillTool(config) {
|
|
|
1481
1865
|
execute: async ({
|
|
1482
1866
|
name
|
|
1483
1867
|
}) => {
|
|
1868
|
+
const startTime = performance.now();
|
|
1869
|
+
const debugId = isDebugEnabled() ? debugStart("skill", { name, availableSkills: Object.keys(skills) }) : "";
|
|
1484
1870
|
try {
|
|
1485
1871
|
const skill = skills[name];
|
|
1486
1872
|
if (!skill) {
|
|
1487
1873
|
const available = Object.keys(skills);
|
|
1488
1874
|
if (available.length === 0) {
|
|
1489
|
-
|
|
1875
|
+
const error2 = "No skills are available.";
|
|
1876
|
+
if (debugId)
|
|
1877
|
+
debugError(debugId, "skill", error2);
|
|
1878
|
+
return { error: error2 };
|
|
1490
1879
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1880
|
+
const error = `Skill '${name}' not found. Available skills: ${available.join(", ")}`;
|
|
1881
|
+
if (debugId)
|
|
1882
|
+
debugError(debugId, "skill", error);
|
|
1883
|
+
return { error };
|
|
1494
1884
|
}
|
|
1495
1885
|
let instructions;
|
|
1496
1886
|
if (!sandbox) {
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
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 };
|
|
1500
1891
|
}
|
|
1501
1892
|
const content = await sandbox.readFile(skill.path);
|
|
1502
1893
|
const frontmatterEnd = content.indexOf(`
|
|
@@ -1509,6 +1900,17 @@ function createSkillTool(config) {
|
|
|
1509
1900
|
if (onActivate) {
|
|
1510
1901
|
await onActivate(skill, instructions);
|
|
1511
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
|
+
}
|
|
1512
1914
|
return {
|
|
1513
1915
|
name: skill.name,
|
|
1514
1916
|
instructions,
|
|
@@ -1516,9 +1918,10 @@ function createSkillTool(config) {
|
|
|
1516
1918
|
message: skill.allowedTools ? `Skill '${name}' activated. Restricted to tools: ${skill.allowedTools.join(", ")}` : `Skill '${name}' activated. Follow the instructions below.`
|
|
1517
1919
|
};
|
|
1518
1920
|
} catch (error) {
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1921
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1922
|
+
if (debugId)
|
|
1923
|
+
debugError(debugId, "skill", errorMessage);
|
|
1924
|
+
return { error: errorMessage };
|
|
1522
1925
|
}
|
|
1523
1926
|
}
|
|
1524
1927
|
});
|
|
@@ -1613,6 +2016,11 @@ function createWebFetchTool(config) {
|
|
|
1613
2016
|
providerOptions,
|
|
1614
2017
|
execute: async (input) => {
|
|
1615
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
|
+
}) : "";
|
|
1616
2024
|
try {
|
|
1617
2025
|
const { content, finalUrl } = await fetchContent(url, apiKey, provider);
|
|
1618
2026
|
const result = await generateText({
|
|
@@ -1623,6 +2031,16 @@ Content from ${url}:
|
|
|
1623
2031
|
|
|
1624
2032
|
${content}`
|
|
1625
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
|
+
}
|
|
1626
2044
|
return {
|
|
1627
2045
|
response: result.text,
|
|
1628
2046
|
url,
|
|
@@ -1632,15 +2050,18 @@ ${content}`
|
|
|
1632
2050
|
if (error && typeof error === "object" && "status" in error) {
|
|
1633
2051
|
const statusCode = error.status;
|
|
1634
2052
|
const message = error.message || "API request failed";
|
|
2053
|
+
if (debugId)
|
|
2054
|
+
debugError(debugId, "web-fetch", `${message} (status: ${statusCode})`);
|
|
1635
2055
|
return {
|
|
1636
2056
|
error: message,
|
|
1637
2057
|
status_code: statusCode,
|
|
1638
2058
|
retryable: RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
1639
2059
|
};
|
|
1640
2060
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
2061
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2062
|
+
if (debugId)
|
|
2063
|
+
debugError(debugId, "web-fetch", errorMessage);
|
|
2064
|
+
return { error: errorMessage };
|
|
1644
2065
|
}
|
|
1645
2066
|
}
|
|
1646
2067
|
});
|
|
@@ -1736,12 +2157,22 @@ function createWebSearchTool(config) {
|
|
|
1736
2157
|
providerOptions,
|
|
1737
2158
|
execute: async (input) => {
|
|
1738
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 }) : "";
|
|
1739
2162
|
try {
|
|
1740
2163
|
const results = await searchContent(apiKey, provider, {
|
|
1741
2164
|
query,
|
|
1742
2165
|
allowedDomains: allowed_domains,
|
|
1743
2166
|
blockedDomains: blocked_domains
|
|
1744
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
|
+
}
|
|
1745
2176
|
return {
|
|
1746
2177
|
results,
|
|
1747
2178
|
total_results: results.length,
|
|
@@ -1751,15 +2182,18 @@ function createWebSearchTool(config) {
|
|
|
1751
2182
|
if (error && typeof error === "object" && "status" in error) {
|
|
1752
2183
|
const statusCode = error.status;
|
|
1753
2184
|
const message = error.message || "API request failed";
|
|
2185
|
+
if (debugId)
|
|
2186
|
+
debugError(debugId, "web-search", `${message} (status: ${statusCode})`);
|
|
1754
2187
|
return {
|
|
1755
2188
|
error: message,
|
|
1756
2189
|
status_code: statusCode,
|
|
1757
2190
|
retryable: RETRYABLE_STATUS_CODES.includes(statusCode)
|
|
1758
2191
|
};
|
|
1759
2192
|
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
2193
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2194
|
+
if (debugId)
|
|
2195
|
+
debugError(debugId, "web-search", errorMessage);
|
|
2196
|
+
return { error: errorMessage };
|
|
1763
2197
|
}
|
|
1764
2198
|
}
|
|
1765
2199
|
});
|
|
@@ -1795,29 +2229,43 @@ function createWriteTool(sandbox, config) {
|
|
|
1795
2229
|
file_path,
|
|
1796
2230
|
content
|
|
1797
2231
|
}) => {
|
|
2232
|
+
const startTime = performance.now();
|
|
1798
2233
|
const byteLength = Buffer.byteLength(content, "utf-8");
|
|
2234
|
+
const debugId = isDebugEnabled() ? debugStart("write", { file_path, contentLength: byteLength }) : "";
|
|
1799
2235
|
if (config?.maxFileSize && byteLength > config.maxFileSize) {
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
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 };
|
|
1803
2240
|
}
|
|
1804
2241
|
if (config?.allowedPaths) {
|
|
1805
2242
|
const isAllowed = config.allowedPaths.some((allowed) => file_path.startsWith(allowed));
|
|
1806
2243
|
if (!isAllowed) {
|
|
1807
|
-
|
|
2244
|
+
const error = `Path not allowed: ${file_path}`;
|
|
2245
|
+
if (debugId)
|
|
2246
|
+
debugError(debugId, "write", error);
|
|
2247
|
+
return { error };
|
|
1808
2248
|
}
|
|
1809
2249
|
}
|
|
1810
2250
|
try {
|
|
1811
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
|
+
}
|
|
1812
2259
|
return {
|
|
1813
2260
|
message: `Successfully wrote to ${file_path}`,
|
|
1814
2261
|
bytes_written: byteLength,
|
|
1815
2262
|
file_path
|
|
1816
2263
|
};
|
|
1817
2264
|
} catch (error) {
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2265
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2266
|
+
if (debugId)
|
|
2267
|
+
debugError(debugId, "write", errorMessage);
|
|
2268
|
+
return { error: errorMessage };
|
|
1821
2269
|
}
|
|
1822
2270
|
}
|
|
1823
2271
|
});
|
|
@@ -1864,16 +2312,22 @@ var eventCounter = 0;
|
|
|
1864
2312
|
function generateEventId() {
|
|
1865
2313
|
return `subagent-${Date.now()}-${++eventCounter}`;
|
|
1866
2314
|
}
|
|
1867
|
-
function filterTools(allTools, allowedTools) {
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
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
|
+
}
|
|
1874
2323
|
}
|
|
2324
|
+
} else {
|
|
2325
|
+
result = allTools;
|
|
2326
|
+
}
|
|
2327
|
+
if (additionalTools) {
|
|
2328
|
+
result = { ...result, ...additionalTools };
|
|
1875
2329
|
}
|
|
1876
|
-
return
|
|
2330
|
+
return result;
|
|
1877
2331
|
}
|
|
1878
2332
|
function createTaskTool(config) {
|
|
1879
2333
|
const {
|
|
@@ -1895,10 +2349,20 @@ function createTaskTool(config) {
|
|
|
1895
2349
|
tools: customTools
|
|
1896
2350
|
}) => {
|
|
1897
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);
|
|
1898
2363
|
try {
|
|
1899
|
-
const typeConfig = subagentTypes[subagent_type] || {};
|
|
1900
2364
|
const model = typeConfig.model || defaultModel;
|
|
1901
|
-
const tools = filterTools(allTools, customTools ?? typeConfig.tools);
|
|
2365
|
+
const tools = filterTools(allTools, customTools ?? typeConfig.tools, typeConfig.additionalTools);
|
|
1902
2366
|
const systemPrompt = system_prompt ?? typeConfig.systemPrompt;
|
|
1903
2367
|
const commonOptions = {
|
|
1904
2368
|
model,
|
|
@@ -1969,6 +2433,19 @@ function createTaskTool(config) {
|
|
|
1969
2433
|
}
|
|
1970
2434
|
});
|
|
1971
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
|
+
}
|
|
1972
2449
|
return {
|
|
1973
2450
|
result: text,
|
|
1974
2451
|
usage: usage2.inputTokens !== undefined && usage2.outputTokens !== undefined ? {
|
|
@@ -1996,6 +2473,19 @@ function createTaskTool(config) {
|
|
|
1996
2473
|
input_tokens: result.usage.inputTokens,
|
|
1997
2474
|
output_tokens: result.usage.outputTokens
|
|
1998
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
|
+
}
|
|
1999
2489
|
return {
|
|
2000
2490
|
result: result.text,
|
|
2001
2491
|
usage,
|
|
@@ -2005,6 +2495,10 @@ function createTaskTool(config) {
|
|
|
2005
2495
|
};
|
|
2006
2496
|
} catch (error) {
|
|
2007
2497
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2498
|
+
if (debugId) {
|
|
2499
|
+
popParent();
|
|
2500
|
+
debugError(debugId, "task", errorMessage);
|
|
2501
|
+
}
|
|
2008
2502
|
return { error: errorMessage };
|
|
2009
2503
|
}
|
|
2010
2504
|
}
|
|
@@ -2052,15 +2546,22 @@ var TODO_WRITE_DESCRIPTION = `Use this tool to create and manage a structured ta
|
|
|
2052
2546
|
- Keep exactly ONE task in_progress at any time
|
|
2053
2547
|
- ONLY mark completed when FULLY accomplished
|
|
2054
2548
|
- If blocked/errors, keep in_progress and create new task for the blocker`;
|
|
2055
|
-
function createTodoWriteTool(
|
|
2549
|
+
function createTodoWriteTool(state2, onUpdate) {
|
|
2056
2550
|
return tool14({
|
|
2057
2551
|
description: TODO_WRITE_DESCRIPTION,
|
|
2058
2552
|
inputSchema: zodSchema14(todoWriteInputSchema),
|
|
2059
2553
|
execute: async ({
|
|
2060
2554
|
todos
|
|
2061
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
|
+
}) : "";
|
|
2062
2563
|
try {
|
|
2063
|
-
|
|
2564
|
+
state2.todos = todos;
|
|
2064
2565
|
if (onUpdate) {
|
|
2065
2566
|
onUpdate(todos);
|
|
2066
2567
|
}
|
|
@@ -2070,14 +2571,22 @@ function createTodoWriteTool(state, onUpdate) {
|
|
|
2070
2571
|
in_progress: todos.filter((t) => t.status === "in_progress").length,
|
|
2071
2572
|
completed: todos.filter((t) => t.status === "completed").length
|
|
2072
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
|
+
}
|
|
2073
2581
|
return {
|
|
2074
2582
|
message: "Todo list updated successfully",
|
|
2075
2583
|
stats
|
|
2076
2584
|
};
|
|
2077
2585
|
} catch (error) {
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2586
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2587
|
+
if (debugId)
|
|
2588
|
+
debugError(debugId, "todo-write", errorMessage);
|
|
2589
|
+
return { error: errorMessage };
|
|
2081
2590
|
}
|
|
2082
2591
|
}
|
|
2083
2592
|
});
|
|
@@ -2326,20 +2835,20 @@ function pruneMessagesByTokens(messages, config) {
|
|
|
2326
2835
|
}
|
|
2327
2836
|
|
|
2328
2837
|
// src/utils/compact-conversation.ts
|
|
2329
|
-
async function compactConversation(messages, config,
|
|
2838
|
+
async function compactConversation(messages, config, state2 = { conversationSummary: "" }) {
|
|
2330
2839
|
const currentTokens = estimateMessagesTokens(messages);
|
|
2331
2840
|
const threshold = config.compactionThreshold ?? 0.85;
|
|
2332
2841
|
const limit = config.maxTokens * threshold;
|
|
2333
2842
|
if (currentTokens < limit) {
|
|
2334
|
-
return { messages, state, didCompact: false };
|
|
2843
|
+
return { messages, state: state2, didCompact: false };
|
|
2335
2844
|
}
|
|
2336
2845
|
const protectCount = config.protectRecentMessages ?? 10;
|
|
2337
2846
|
const recentMessages = messages.slice(-protectCount);
|
|
2338
2847
|
const oldMessages = messages.slice(0, -protectCount);
|
|
2339
2848
|
if (oldMessages.length === 0) {
|
|
2340
|
-
return { messages, state, didCompact: false };
|
|
2849
|
+
return { messages, state: state2, didCompact: false };
|
|
2341
2850
|
}
|
|
2342
|
-
const newSummary = await summarizeMessages(oldMessages, config.summarizerModel, config.taskContext,
|
|
2851
|
+
const newSummary = await summarizeMessages(oldMessages, config.summarizerModel, config.taskContext, state2.conversationSummary);
|
|
2343
2852
|
const compactedMessages = [
|
|
2344
2853
|
{
|
|
2345
2854
|
role: "user",
|
|
@@ -2874,10 +3383,13 @@ async function createDirectory(sandbox, path) {
|
|
|
2874
3383
|
export {
|
|
2875
3384
|
skillsToXml,
|
|
2876
3385
|
setupAgentEnvironment,
|
|
3386
|
+
reinitDebugMode,
|
|
2877
3387
|
pruneMessagesByTokens,
|
|
2878
3388
|
parseSkillMetadata,
|
|
2879
3389
|
loadSkillBundles,
|
|
2880
3390
|
loadSkillBundle,
|
|
3391
|
+
isDebugEnabled,
|
|
3392
|
+
getDebugLogs,
|
|
2881
3393
|
getContextStatus,
|
|
2882
3394
|
fetchSkills,
|
|
2883
3395
|
fetchSkill,
|
|
@@ -2909,6 +3421,7 @@ export {
|
|
|
2909
3421
|
contextNeedsCompaction,
|
|
2910
3422
|
contextNeedsAttention,
|
|
2911
3423
|
compactConversation,
|
|
3424
|
+
clearDebugLogs,
|
|
2912
3425
|
cached,
|
|
2913
3426
|
anthropicPromptCacheMiddlewareV2,
|
|
2914
3427
|
anthropicPromptCacheMiddleware,
|