@axiom-lattice/core 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1442 -1411
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1452 -1424
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -10
package/dist/index.mjs
CHANGED
|
@@ -601,245 +601,6 @@ registerToolLattice(
|
|
|
601
601
|
}
|
|
602
602
|
);
|
|
603
603
|
|
|
604
|
-
// src/tool_lattice/write_todos/index.ts
|
|
605
|
-
import z3 from "zod";
|
|
606
|
-
import { ToolMessage } from "@langchain/core/messages";
|
|
607
|
-
import { Command } from "@langchain/langgraph";
|
|
608
|
-
var WRITE_TODOS_DESCRIPTION = `Use this tool to create and manage a structured task list for your current work session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user. It also helps the user understand the progress of the task and overall progress of their requests.
|
|
609
|
-
|
|
610
|
-
When to Use This Tool
|
|
611
|
-
Use this tool proactively in these scenarios:
|
|
612
|
-
|
|
613
|
-
Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
614
|
-
Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
615
|
-
User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
616
|
-
User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
617
|
-
After receiving new instructions - Immediately capture user requirements as todos
|
|
618
|
-
When you start working on a task - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time
|
|
619
|
-
After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
620
|
-
|
|
621
|
-
When NOT to Use This Tool
|
|
622
|
-
Skip using this tool when:
|
|
623
|
-
|
|
624
|
-
There is only a single, straightforward task
|
|
625
|
-
The task is trivial and tracking it provides no organizational benefit
|
|
626
|
-
The task can be completed in less than 3 trivial steps
|
|
627
|
-
The task is purely conversational or informational
|
|
628
|
-
|
|
629
|
-
NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.`;
|
|
630
|
-
registerToolLattice(
|
|
631
|
-
"write_todos",
|
|
632
|
-
{
|
|
633
|
-
name: "write_todos",
|
|
634
|
-
description: WRITE_TODOS_DESCRIPTION,
|
|
635
|
-
needUserApprove: false,
|
|
636
|
-
schema: z3.object({
|
|
637
|
-
todos: z3.array(
|
|
638
|
-
z3.object({
|
|
639
|
-
content: z3.string().describe("Content of the todo item"),
|
|
640
|
-
status: z3.enum(["pending", "in_progress", "completed"]).describe("Status of the todo")
|
|
641
|
-
})
|
|
642
|
-
).describe("List of todo items to update")
|
|
643
|
-
})
|
|
644
|
-
},
|
|
645
|
-
((input, config) => {
|
|
646
|
-
return new Command({
|
|
647
|
-
update: {
|
|
648
|
-
todos: input.todos,
|
|
649
|
-
messages: [
|
|
650
|
-
new ToolMessage({
|
|
651
|
-
content: genUIMarkdown("todo_list", input.todos),
|
|
652
|
-
tool_call_id: config.toolCall?.id
|
|
653
|
-
})
|
|
654
|
-
]
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
})
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
// src/tool_lattice/read_file/index.ts
|
|
661
|
-
import z4 from "zod";
|
|
662
|
-
import { getCurrentTaskInput } from "@langchain/langgraph";
|
|
663
|
-
var READ_FILE_DESCRIPTION = `Reads a file from the local filesystem. You can access any file directly by using this tool. Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
|
|
664
|
-
Usage:
|
|
665
|
-
|
|
666
|
-
The file_path parameter must be an absolute path, not a relative path
|
|
667
|
-
By default, it reads up to 2000 lines starting from the beginning of the file
|
|
668
|
-
You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
|
|
669
|
-
Any lines longer than 2000 characters will be truncated
|
|
670
|
-
Results are returned using cat -n format, with line numbers starting at 1
|
|
671
|
-
You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.
|
|
672
|
-
If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`;
|
|
673
|
-
registerToolLattice(
|
|
674
|
-
"read_file",
|
|
675
|
-
{
|
|
676
|
-
name: "read_file",
|
|
677
|
-
description: READ_FILE_DESCRIPTION,
|
|
678
|
-
needUserApprove: false,
|
|
679
|
-
schema: z4.object({
|
|
680
|
-
file_path: z4.string().describe("Absolute path to the file to read"),
|
|
681
|
-
offset: z4.number().optional().default(0).describe("Line offset to start reading from"),
|
|
682
|
-
limit: z4.number().optional().default(2e3).describe("Maximum number of lines to read")
|
|
683
|
-
})
|
|
684
|
-
},
|
|
685
|
-
((input) => {
|
|
686
|
-
const state = getCurrentTaskInput();
|
|
687
|
-
const mockFilesystem = state.files || {};
|
|
688
|
-
const { file_path, offset = 0, limit = 2e3 } = input;
|
|
689
|
-
if (!(file_path in mockFilesystem)) {
|
|
690
|
-
return `Error: File '${file_path}' not found`;
|
|
691
|
-
}
|
|
692
|
-
const content = mockFilesystem[file_path];
|
|
693
|
-
if (!content || content.trim() === "") {
|
|
694
|
-
return "System reminder: File exists but has empty contents";
|
|
695
|
-
}
|
|
696
|
-
const lines = content.split("\n");
|
|
697
|
-
const startIdx = offset;
|
|
698
|
-
const endIdx = Math.min(startIdx + limit, lines.length);
|
|
699
|
-
if (startIdx >= lines.length) {
|
|
700
|
-
return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
|
|
701
|
-
}
|
|
702
|
-
const resultLines = [];
|
|
703
|
-
for (let i = startIdx; i < endIdx; i++) {
|
|
704
|
-
let lineContent = lines[i];
|
|
705
|
-
if (lineContent.length > 2e3) {
|
|
706
|
-
lineContent = lineContent.substring(0, 2e3);
|
|
707
|
-
}
|
|
708
|
-
const lineNumber = i + 1;
|
|
709
|
-
resultLines.push(`${lineNumber.toString().padStart(6)} ${lineContent}`);
|
|
710
|
-
}
|
|
711
|
-
return resultLines.join("\n");
|
|
712
|
-
})
|
|
713
|
-
);
|
|
714
|
-
|
|
715
|
-
// src/tool_lattice/write_file/index.ts
|
|
716
|
-
import z5 from "zod";
|
|
717
|
-
import { ToolMessage as ToolMessage2 } from "@langchain/core/messages";
|
|
718
|
-
import { Command as Command2, getCurrentTaskInput as getCurrentTaskInput2 } from "@langchain/langgraph";
|
|
719
|
-
registerToolLattice(
|
|
720
|
-
"write_file",
|
|
721
|
-
{
|
|
722
|
-
name: "write_file",
|
|
723
|
-
description: "Write content to a file in the mock filesystem",
|
|
724
|
-
needUserApprove: false,
|
|
725
|
-
schema: z5.object({
|
|
726
|
-
file_path: z5.string().describe("Absolute path to the file to write"),
|
|
727
|
-
content: z5.string().describe("Content to write to the file")
|
|
728
|
-
})
|
|
729
|
-
},
|
|
730
|
-
((input, config) => {
|
|
731
|
-
const state = getCurrentTaskInput2();
|
|
732
|
-
const files = { ...state.files || {} };
|
|
733
|
-
files[input.file_path] = input.content;
|
|
734
|
-
return new Command2({
|
|
735
|
-
update: {
|
|
736
|
-
files,
|
|
737
|
-
messages: [
|
|
738
|
-
new ToolMessage2({
|
|
739
|
-
content: `Updated file ${input.file_path}`,
|
|
740
|
-
tool_call_id: config.toolCall?.id
|
|
741
|
-
})
|
|
742
|
-
]
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
})
|
|
746
|
-
);
|
|
747
|
-
|
|
748
|
-
// src/tool_lattice/edit_file/index.ts
|
|
749
|
-
import z6 from "zod";
|
|
750
|
-
import { ToolMessage as ToolMessage3 } from "@langchain/core/messages";
|
|
751
|
-
import { Command as Command3, getCurrentTaskInput as getCurrentTaskInput3 } from "@langchain/langgraph";
|
|
752
|
-
var EDIT_FILE_DESCRIPTION = `Performs exact string replacements in files.
|
|
753
|
-
Usage:
|
|
754
|
-
|
|
755
|
-
You must use your Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.
|
|
756
|
-
When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.
|
|
757
|
-
ALWAYS prefer editing existing files. NEVER write new files unless explicitly required.
|
|
758
|
-
Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
|
|
759
|
-
The edit will FAIL if old_string is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all to change every instance of old_string.
|
|
760
|
-
Use replace_all for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`;
|
|
761
|
-
registerToolLattice(
|
|
762
|
-
"edit_file",
|
|
763
|
-
{
|
|
764
|
-
name: "edit_file",
|
|
765
|
-
description: EDIT_FILE_DESCRIPTION,
|
|
766
|
-
needUserApprove: false,
|
|
767
|
-
schema: z6.object({
|
|
768
|
-
file_path: z6.string().describe("Absolute path to the file to edit"),
|
|
769
|
-
old_string: z6.string().describe("String to be replaced (must match exactly)"),
|
|
770
|
-
new_string: z6.string().describe("String to replace with"),
|
|
771
|
-
replace_all: z6.boolean().optional().default(false).describe("Whether to replace all occurrences")
|
|
772
|
-
})
|
|
773
|
-
},
|
|
774
|
-
((input, config) => {
|
|
775
|
-
const state = getCurrentTaskInput3();
|
|
776
|
-
const mockFilesystem = { ...state.files || {} };
|
|
777
|
-
const { file_path, old_string, new_string, replace_all = false } = input;
|
|
778
|
-
if (!(file_path in mockFilesystem)) {
|
|
779
|
-
return `Error: File '${file_path}' not found`;
|
|
780
|
-
}
|
|
781
|
-
const content = mockFilesystem[file_path];
|
|
782
|
-
if (!content.includes(old_string)) {
|
|
783
|
-
return `Error: String not found in file: '${old_string}'`;
|
|
784
|
-
}
|
|
785
|
-
if (!replace_all) {
|
|
786
|
-
const escapedOldString = old_string.replace(
|
|
787
|
-
/[.*+?^${}()|[\]\\]/g,
|
|
788
|
-
"\\$&"
|
|
789
|
-
);
|
|
790
|
-
const occurrences = (content.match(new RegExp(escapedOldString, "g")) || []).length;
|
|
791
|
-
if (occurrences > 1) {
|
|
792
|
-
return `Error: String '${old_string}' appears ${occurrences} times in file. Use replace_all=True to replace all instances, or provide a more specific string with surrounding context.`;
|
|
793
|
-
} else if (occurrences === 0) {
|
|
794
|
-
return `Error: String not found in file: '${old_string}'`;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
let newContent;
|
|
798
|
-
if (replace_all) {
|
|
799
|
-
const escapedOldString = old_string.replace(
|
|
800
|
-
/[.*+?^${}()|[\]\\]/g,
|
|
801
|
-
"\\$&"
|
|
802
|
-
);
|
|
803
|
-
newContent = content.replace(
|
|
804
|
-
new RegExp(escapedOldString, "g"),
|
|
805
|
-
new_string
|
|
806
|
-
);
|
|
807
|
-
} else {
|
|
808
|
-
newContent = content.replace(old_string, new_string);
|
|
809
|
-
}
|
|
810
|
-
mockFilesystem[file_path] = newContent;
|
|
811
|
-
return new Command3({
|
|
812
|
-
update: {
|
|
813
|
-
files: mockFilesystem,
|
|
814
|
-
messages: [
|
|
815
|
-
new ToolMessage3({
|
|
816
|
-
content: `Updated file ${file_path}`,
|
|
817
|
-
tool_call_id: config.toolCall?.id
|
|
818
|
-
})
|
|
819
|
-
]
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
|
-
})
|
|
823
|
-
);
|
|
824
|
-
|
|
825
|
-
// src/tool_lattice/ls/index.ts
|
|
826
|
-
import z7 from "zod";
|
|
827
|
-
import { getCurrentTaskInput as getCurrentTaskInput4 } from "@langchain/langgraph";
|
|
828
|
-
registerToolLattice(
|
|
829
|
-
"ls",
|
|
830
|
-
{
|
|
831
|
-
name: "ls",
|
|
832
|
-
description: "List all files in the mock filesystem",
|
|
833
|
-
needUserApprove: false,
|
|
834
|
-
schema: z7.object({})
|
|
835
|
-
},
|
|
836
|
-
(() => {
|
|
837
|
-
const state = getCurrentTaskInput4();
|
|
838
|
-
const files = state.files || {};
|
|
839
|
-
return Object.keys(files);
|
|
840
|
-
})
|
|
841
|
-
);
|
|
842
|
-
|
|
843
604
|
// src/agent_lattice/types.ts
|
|
844
605
|
import {
|
|
845
606
|
AgentType,
|
|
@@ -856,9 +617,6 @@ import {
|
|
|
856
617
|
getSubAgentsFromConfig
|
|
857
618
|
} from "@axiom-lattice/protocols";
|
|
858
619
|
|
|
859
|
-
// src/agent_lattice/builders/ReActAgentGraphBuilder.ts
|
|
860
|
-
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
|
861
|
-
|
|
862
620
|
// src/memory_lattice/DefaultMemorySaver.ts
|
|
863
621
|
import { MemorySaver } from "@langchain/langgraph";
|
|
864
622
|
|
|
@@ -942,17 +700,12 @@ registerCheckpointSaver("default", memory);
|
|
|
942
700
|
// src/agent_lattice/builders/state.ts
|
|
943
701
|
import "@langchain/langgraph/zod";
|
|
944
702
|
import { MessagesZodState } from "@langchain/langgraph";
|
|
945
|
-
import { z as z8 } from "zod";
|
|
946
|
-
var ReActAgentState = MessagesZodState.extend({
|
|
947
|
-
files: z8.object({
|
|
948
|
-
final_output: z8.record(z8.any())
|
|
949
|
-
})
|
|
950
|
-
});
|
|
951
703
|
var createReactAgentSchema = (schema) => {
|
|
952
704
|
return schema ? MessagesZodState.extend(schema.shape) : void 0;
|
|
953
705
|
};
|
|
954
706
|
|
|
955
707
|
// src/agent_lattice/builders/ReActAgentGraphBuilder.ts
|
|
708
|
+
import { createAgent } from "langchain";
|
|
956
709
|
var ReActAgentGraphBuilder = class {
|
|
957
710
|
/**
|
|
958
711
|
* 构建ReAct Agent Graph
|
|
@@ -966,1230 +719,1490 @@ var ReActAgentGraphBuilder = class {
|
|
|
966
719
|
const tool5 = getToolClient(t.key);
|
|
967
720
|
return tool5;
|
|
968
721
|
}).filter((tool5) => tool5 !== void 0);
|
|
969
|
-
const
|
|
970
|
-
return
|
|
971
|
-
|
|
722
|
+
const stateSchema2 = createReactAgentSchema(params.stateSchema);
|
|
723
|
+
return createAgent({
|
|
724
|
+
model: params.model,
|
|
972
725
|
tools,
|
|
973
|
-
|
|
726
|
+
systemPrompt: params.prompt,
|
|
974
727
|
name: agentLattice.config.name,
|
|
975
728
|
checkpointer: getCheckpointSaver("default"),
|
|
976
|
-
stateSchema
|
|
729
|
+
stateSchema: stateSchema2
|
|
977
730
|
});
|
|
978
731
|
}
|
|
979
732
|
};
|
|
980
733
|
|
|
981
|
-
// src/
|
|
982
|
-
import { tool as tool2 } from "@langchain/core/tools";
|
|
983
|
-
import { ToolMessage as ToolMessage4 } from "@langchain/core/messages";
|
|
734
|
+
// src/deep_agent_new/agent.ts
|
|
984
735
|
import {
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
// src/
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
998
|
-
Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
999
|
-
User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
1000
|
-
User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
1001
|
-
After receiving new instructions - Immediately capture user requirements as todos
|
|
1002
|
-
When you start working on a task - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time
|
|
1003
|
-
After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
1004
|
-
When NOT to Use This Tool
|
|
1005
|
-
Skip using this tool when:
|
|
1006
|
-
|
|
1007
|
-
There is only a single, straightforward task
|
|
1008
|
-
The task is trivial and tracking it provides no organizational benefit
|
|
1009
|
-
The task can be completed in less than 3 trivial steps
|
|
1010
|
-
The task is purely conversational or informational
|
|
1011
|
-
NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.
|
|
1012
|
-
|
|
1013
|
-
Examples of When to Use the Todo List
|
|
1014
|
-
<example>
|
|
1015
|
-
User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!
|
|
1016
|
-
Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
|
|
1017
|
-
*Creates todo list with the following items:*
|
|
1018
|
-
1. Create dark mode toggle component in Settings page
|
|
1019
|
-
2. Add dark mode state management (context/store)
|
|
1020
|
-
3. Implement CSS-in-JS styles for dark theme
|
|
1021
|
-
4. Update existing components to support theme switching
|
|
1022
|
-
5. Run tests and build process, addressing any failures or errors that occur
|
|
1023
|
-
*Begins working on the first task*
|
|
1024
|
-
<reasoning>
|
|
1025
|
-
The assistant used the todo list because:
|
|
1026
|
-
1. Adding dark mode is a multi-step feature requiring UI, state management, and styling changes
|
|
1027
|
-
2. The user explicitly requested tests and build be run afterward
|
|
1028
|
-
3. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
|
|
1029
|
-
</reasoning>
|
|
1030
|
-
</example>
|
|
1031
|
-
<example>
|
|
1032
|
-
User: Help me plan a comprehensive marketing campaign for our new product launch, including social media, email marketing, and press releases.
|
|
1033
|
-
Assistant: I'll help you plan a comprehensive marketing campaign for your product launch. Let me create a todo list to organize all the components.
|
|
1034
|
-
*Creates todo list with the following items:*
|
|
1035
|
-
1. Define target audience and key messaging
|
|
1036
|
-
2. Create social media content calendar and posting schedule
|
|
1037
|
-
3. Design email marketing sequence and automation flows
|
|
1038
|
-
4. Draft press release and identify media contacts
|
|
1039
|
-
5. Set up tracking and analytics for campaign performance
|
|
1040
|
-
6. Create visual assets and brand materials
|
|
1041
|
-
7. Coordinate launch timeline and deliverables
|
|
1042
|
-
*Begins working on the first task*
|
|
1043
|
-
<reasoning>
|
|
1044
|
-
The assistant used the todo list because:
|
|
1045
|
-
1. Marketing campaign planning involves multiple distinct channels and activities
|
|
1046
|
-
2. Each component requires careful coordination and planning
|
|
1047
|
-
3. The systematic approach ensures all aspects of the launch are covered
|
|
1048
|
-
4. Progress tracking helps maintain timeline and deliverables
|
|
1049
|
-
</reasoning>
|
|
1050
|
-
</example>
|
|
1051
|
-
<example>
|
|
1052
|
-
User: Help me rename the function getCwd to getCurrentWorkingDirectory across my project
|
|
1053
|
-
Assistant: Let me first search through your codebase to find all occurrences of 'getCwd'.
|
|
1054
|
-
*Uses search tools to locate all instances of getCwd in the codebase*
|
|
1055
|
-
Assistant: I've found 15 instances of 'getCwd' across 8 different files. Let me create a todo list to track these changes.
|
|
1056
|
-
*Creates todo list with specific items for each file that needs updating*
|
|
1057
|
-
<reasoning>
|
|
1058
|
-
The assistant used the todo list because:
|
|
1059
|
-
1. First, the assistant searched to understand the scope of the task
|
|
1060
|
-
2. Upon finding multiple occurrences across different files, it determined this was a complex task with multiple steps
|
|
1061
|
-
3. The todo list helps ensure every instance is tracked and updated systematically
|
|
1062
|
-
4. This approach prevents missing any occurrences and maintains consistency
|
|
1063
|
-
</reasoning>
|
|
1064
|
-
</example>
|
|
1065
|
-
<example>
|
|
1066
|
-
User: I need help organizing my home office: decluttering the desk, setting up a filing system, organizing digital files, and creating a daily workflow.
|
|
1067
|
-
Assistant: I'll help you organize your home office systematically. Let me create a todo list to track each area of organization.
|
|
1068
|
-
*Creates todo list with items like: 1) Sort and declutter desk items, 2) Set up physical filing system for documents, 3) Organize digital files and folders, 4) Create daily workflow and productivity systems, 5) Establish maintenance routines*
|
|
1069
|
-
Let's start with decluttering your desk space to create a clean foundation for the other organizational systems.
|
|
1070
|
-
<reasoning>
|
|
1071
|
-
The assistant used the todo list because:
|
|
1072
|
-
1. The user provided multiple distinct organizational tasks
|
|
1073
|
-
2. Each area requires different approaches and careful planning
|
|
1074
|
-
3. Home organization benefits from systematic, step-by-step completion
|
|
1075
|
-
4. Progress tracking ensures all areas are addressed thoroughly
|
|
1076
|
-
</reasoning>
|
|
1077
|
-
</example>
|
|
1078
|
-
Examples of When NOT to Use the Todo List
|
|
1079
|
-
<example>
|
|
1080
|
-
User: How do I print 'Hello World' in Python?
|
|
1081
|
-
Assistant: In Python, you can print "Hello World" with this simple code:
|
|
1082
|
-
|
|
1083
|
-
\`\`\`python
|
|
1084
|
-
print("Hello World")
|
|
1085
|
-
\`\`\`
|
|
1086
|
-
|
|
1087
|
-
This will output the text "Hello World" to the console when executed.
|
|
1088
|
-
<reasoning>
|
|
1089
|
-
The assistant did not use the todo list because this is a single, trivial task that can be completed in one step. There's no need to track multiple tasks or steps for such a straightforward request.
|
|
1090
|
-
</reasoning>
|
|
1091
|
-
</example>
|
|
1092
|
-
<example>
|
|
1093
|
-
User: What's the best way to water houseplants?
|
|
1094
|
-
Assistant: The best way to water houseplants depends on the specific plant type, but here are some general guidelines:
|
|
1095
|
-
|
|
1096
|
-
Check soil moisture by inserting your finger about 1-2 inches into the soil
|
|
1097
|
-
Water thoroughly when the top inch feels dry, allowing water to drain from the bottom
|
|
1098
|
-
Use room temperature water and water in the morning when possible
|
|
1099
|
-
Avoid overwatering, which is more harmful than underwatering for most plants
|
|
1100
|
-
|
|
1101
|
-
Different plants have different needs, so it's important to research your specific varieties for optimal care.
|
|
1102
|
-
|
|
1103
|
-
<reasoning>
|
|
1104
|
-
The assistant did not use the todo list because this is an informational request with no actual tasks to complete. The user is simply asking for advice and explanation, not for the assistant to perform multiple steps or activities.
|
|
1105
|
-
</reasoning>
|
|
1106
|
-
</example>
|
|
1107
|
-
<example>
|
|
1108
|
-
User: Can you add a comment to the calculateTotal function to explain what it does?
|
|
1109
|
-
Assistant: Sure, let me add a comment to the calculateTotal function to explain what it does.
|
|
1110
|
-
* Uses the Edit tool to add a comment to the calculateTotal function *
|
|
1111
|
-
<reasoning>
|
|
1112
|
-
The assistant did not use the todo list because this is a single, straightforward task confined to one location. Adding a comment doesn't require tracking multiple steps or systematic organization.
|
|
1113
|
-
</reasoning>
|
|
1114
|
-
</example>
|
|
1115
|
-
<example>
|
|
1116
|
-
User: What time is it in Tokyo right now?
|
|
1117
|
-
Assistant: I'll check the current time in Tokyo for you.
|
|
1118
|
-
|
|
1119
|
-
*Searches for current time in Tokyo*
|
|
1120
|
-
|
|
1121
|
-
The current time in Tokyo, Japan is [current time]. Tokyo is in the Japan Standard Time (JST) zone, which is UTC+9.
|
|
1122
|
-
|
|
1123
|
-
<reasoning>
|
|
1124
|
-
The assistant did not use the todo list because this is a single information lookup with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward request.
|
|
1125
|
-
</reasoning>
|
|
1126
|
-
</example>
|
|
1127
|
-
Task States and Management
|
|
1128
|
-
Task States: Use these states to track progress:
|
|
1129
|
-
|
|
1130
|
-
pending: Task not yet started
|
|
1131
|
-
in_progress: Currently working on (limit to ONE task at a time)
|
|
1132
|
-
completed: Task finished successfully
|
|
1133
|
-
Task Management:
|
|
1134
|
-
|
|
1135
|
-
Update task status in real-time as you work
|
|
1136
|
-
Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
|
|
1137
|
-
Only have ONE task in_progress at any time
|
|
1138
|
-
Complete current tasks before starting new ones
|
|
1139
|
-
Remove tasks that are no longer relevant from the list entirely
|
|
1140
|
-
Task Completion Requirements:
|
|
1141
|
-
|
|
1142
|
-
ONLY mark a task as completed when you have FULLY accomplished it
|
|
1143
|
-
If you encounter errors, blockers, or cannot finish, keep the task as in_progress
|
|
1144
|
-
When blocked, create a new task describing what needs to be resolved
|
|
1145
|
-
Never mark a task as completed if:
|
|
1146
|
-
There are unresolved issues or errors
|
|
1147
|
-
Work is partial or incomplete
|
|
1148
|
-
You encountered blockers that prevent completion
|
|
1149
|
-
You couldn't find necessary resources or dependencies
|
|
1150
|
-
Quality standards haven't been met
|
|
1151
|
-
Task Breakdown:
|
|
1152
|
-
|
|
1153
|
-
Create specific, actionable items
|
|
1154
|
-
Break complex tasks into smaller, manageable steps
|
|
1155
|
-
Use clear, descriptive task names
|
|
1156
|
-
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.`;
|
|
1157
|
-
var TASK_DESCRIPTION_PREFIX = `Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
1158
|
-
|
|
1159
|
-
Available agent types and the tools they have access to:
|
|
1160
|
-
|
|
1161
|
-
general-purpose: General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)
|
|
1162
|
-
{other_agents}
|
|
1163
|
-
`;
|
|
1164
|
-
var TASK_DESCRIPTION_SUFFIX = `When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
|
|
1165
|
-
|
|
1166
|
-
When to use the Agent tool:
|
|
1167
|
-
|
|
1168
|
-
When you are instructed to execute custom slash commands. Use the Agent tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description="Check the file", prompt="/check-file path/to/file.py")
|
|
1169
|
-
When NOT to use the Agent tool:
|
|
1170
|
-
|
|
1171
|
-
If you want to read a specific file path, use the Read or Glob tool instead of the Agent tool, to find the match more quickly
|
|
1172
|
-
If you are searching for a specific term or definition within a known location, use the Glob tool instead, to find the match more quickly
|
|
1173
|
-
If you are searching for content within a specific file or set of 2-3 files, use the Read tool instead of the Agent tool, to find the match more quickly
|
|
1174
|
-
Other tasks that are not related to the agent descriptions above
|
|
1175
|
-
Usage notes:
|
|
1176
|
-
|
|
1177
|
-
Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
|
|
1178
|
-
When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
|
|
1179
|
-
Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
|
|
1180
|
-
The agent's outputs should generally be trusted
|
|
1181
|
-
Clearly tell the agent whether you expect it to create content, perform analysis, or just do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
|
|
1182
|
-
If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
|
|
1183
|
-
Example usage:
|
|
1184
|
-
|
|
1185
|
-
<example_agent_descriptions>
|
|
1186
|
-
"content-reviewer": use this agent after you are done creating significant content or documents
|
|
1187
|
-
"greeting-responder": use this agent when to respond to user greetings with a friendly joke
|
|
1188
|
-
"research-analyst": use this agent to conduct thorough research on complex topics
|
|
1189
|
-
</example_agent_description>
|
|
736
|
+
createAgent as createAgent3,
|
|
737
|
+
humanInTheLoopMiddleware as humanInTheLoopMiddleware2,
|
|
738
|
+
anthropicPromptCachingMiddleware,
|
|
739
|
+
summarizationMiddleware
|
|
740
|
+
} from "langchain";
|
|
741
|
+
|
|
742
|
+
// src/deep_agent_new/middleware/fs.ts
|
|
743
|
+
import { createMiddleware, tool as tool2, ToolMessage } from "langchain";
|
|
744
|
+
import { Command, isCommand, getCurrentTaskInput } from "@langchain/langgraph";
|
|
745
|
+
import { z as z3 } from "zod/v3";
|
|
746
|
+
import { withLangGraph } from "@langchain/langgraph/zod";
|
|
1190
747
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
function
|
|
1198
|
-
|
|
1199
|
-
for (let i = 2; i * i <= n; i++) {
|
|
1200
|
-
if (n % i === 0) return false
|
|
1201
|
-
}
|
|
1202
|
-
return true
|
|
748
|
+
// src/deep_agent_new/backends/utils.ts
|
|
749
|
+
import micromatch from "micromatch";
|
|
750
|
+
import { basename } from "path";
|
|
751
|
+
var EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents";
|
|
752
|
+
var MAX_LINE_LENGTH = 1e4;
|
|
753
|
+
var LINE_NUMBER_WIDTH = 6;
|
|
754
|
+
function sanitizeToolCallId(toolCallId) {
|
|
755
|
+
return toolCallId.replace(/\./g, "_").replace(/\//g, "_").replace(/\\/g, "_");
|
|
1203
756
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
</example>
|
|
1211
|
-
<example>
|
|
1212
|
-
user: "Can you help me research the environmental impact of different renewable energy sources and create a comprehensive report?"
|
|
1213
|
-
<commentary>
|
|
1214
|
-
This is a complex research task that would benefit from using the research-analyst agent to conduct thorough analysis
|
|
1215
|
-
</commentary>
|
|
1216
|
-
assistant: I'll help you research the environmental impact of renewable energy sources. Let me use the research-analyst agent to conduct comprehensive research on this topic.
|
|
1217
|
-
assistant: Uses the Task tool to launch with the research-analyst agent, providing detailed instructions about what research to conduct and what format the report should take
|
|
1218
|
-
</example>
|
|
1219
|
-
<example>
|
|
1220
|
-
user: "Hello"
|
|
1221
|
-
<commentary>
|
|
1222
|
-
Since the user is greeting, use the greeting-responder agent to respond with a friendly joke
|
|
1223
|
-
</commentary>
|
|
1224
|
-
assistant: "I'm going to use the Task tool to launch with the greeting-responder agent"
|
|
1225
|
-
</example>`;
|
|
1226
|
-
var EDIT_DESCRIPTION = `Performs exact string replacements in files.
|
|
1227
|
-
Usage:
|
|
1228
|
-
|
|
1229
|
-
You must use your Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.
|
|
1230
|
-
When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.
|
|
1231
|
-
ALWAYS prefer editing existing files. NEVER write new files unless explicitly required.
|
|
1232
|
-
Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
|
|
1233
|
-
The edit will FAIL if old_string is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all to change every instance of old_string.
|
|
1234
|
-
Use replace_all for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`;
|
|
1235
|
-
var TOOL_DESCRIPTION = `Reads a file from the local filesystem. You can access any file directly by using this tool. Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
|
|
1236
|
-
Usage:
|
|
1237
|
-
|
|
1238
|
-
The file_path parameter must be an absolute path, not a relative path
|
|
1239
|
-
By default, it reads up to 2000 lines starting from the beginning of the file
|
|
1240
|
-
You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
|
|
1241
|
-
Any lines longer than 2000 characters will be truncated
|
|
1242
|
-
Results are returned using cat -n format, with line numbers starting at 1
|
|
1243
|
-
You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.
|
|
1244
|
-
If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`;
|
|
1245
|
-
|
|
1246
|
-
// src/deep_agent/subAgent.ts
|
|
1247
|
-
import {
|
|
1248
|
-
getToolsFromConfig as getToolsFromConfig2,
|
|
1249
|
-
hasTools as hasTools2
|
|
1250
|
-
} from "@axiom-lattice/protocols";
|
|
1251
|
-
var BUILTIN_TOOL_NAMES = [
|
|
1252
|
-
"write_todos",
|
|
1253
|
-
"read_file",
|
|
1254
|
-
"write_file",
|
|
1255
|
-
"edit_file",
|
|
1256
|
-
"ls"
|
|
1257
|
-
];
|
|
1258
|
-
function getBuiltinTools() {
|
|
1259
|
-
const tools = {};
|
|
1260
|
-
for (const name of BUILTIN_TOOL_NAMES) {
|
|
1261
|
-
try {
|
|
1262
|
-
tools[name] = getToolClient(name);
|
|
1263
|
-
} catch {
|
|
757
|
+
function formatContentWithLineNumbers(content, startLine = 1) {
|
|
758
|
+
let lines;
|
|
759
|
+
if (typeof content === "string") {
|
|
760
|
+
lines = content.split("\n");
|
|
761
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
762
|
+
lines = lines.slice(0, -1);
|
|
1264
763
|
}
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if (!model) {
|
|
1277
|
-
throw new Error("Model not found");
|
|
1278
|
-
}
|
|
1279
|
-
const allTools = { ...getBuiltinTools(), ...tools };
|
|
1280
|
-
const agentsMap = /* @__PURE__ */ new Map();
|
|
1281
|
-
for (const subagent of subagents) {
|
|
1282
|
-
let reactAgent;
|
|
1283
|
-
const agentLattice = getAgentLattice(subagent.key);
|
|
1284
|
-
if (agentLattice) {
|
|
1285
|
-
reactAgent = agentLattice.client;
|
|
764
|
+
} else {
|
|
765
|
+
lines = content;
|
|
766
|
+
}
|
|
767
|
+
const resultLines = [];
|
|
768
|
+
for (let i = 0; i < lines.length; i++) {
|
|
769
|
+
const line = lines[i];
|
|
770
|
+
const lineNum = i + startLine;
|
|
771
|
+
if (line.length <= MAX_LINE_LENGTH) {
|
|
772
|
+
resultLines.push(
|
|
773
|
+
`${lineNum.toString().padStart(LINE_NUMBER_WIDTH)} ${line}`
|
|
774
|
+
);
|
|
1286
775
|
} else {
|
|
1287
|
-
const
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
}
|
|
776
|
+
const numChunks = Math.ceil(line.length / MAX_LINE_LENGTH);
|
|
777
|
+
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
778
|
+
const start = chunkIdx * MAX_LINE_LENGTH;
|
|
779
|
+
const end = Math.min(start + MAX_LINE_LENGTH, line.length);
|
|
780
|
+
const chunk = line.substring(start, end);
|
|
781
|
+
if (chunkIdx === 0) {
|
|
782
|
+
resultLines.push(
|
|
783
|
+
`${lineNum.toString().padStart(LINE_NUMBER_WIDTH)} ${chunk}`
|
|
784
|
+
);
|
|
785
|
+
} else {
|
|
786
|
+
const continuationMarker = `${lineNum}.${chunkIdx}`;
|
|
787
|
+
resultLines.push(
|
|
788
|
+
`${continuationMarker.padStart(LINE_NUMBER_WIDTH)} ${chunk}`
|
|
789
|
+
);
|
|
1298
790
|
}
|
|
1299
|
-
} else {
|
|
1300
|
-
subagentTools.push(...Object.values(allTools));
|
|
1301
791
|
}
|
|
1302
|
-
reactAgent = createAgentClientFromAgentLattice({ config: subagent });
|
|
1303
792
|
}
|
|
1304
|
-
agentsMap.set(subagent.name, reactAgent);
|
|
1305
793
|
}
|
|
1306
|
-
return
|
|
1307
|
-
async (input, config) => {
|
|
1308
|
-
const { description, subagent_type } = input;
|
|
1309
|
-
const reactAgent = agentsMap.get(subagent_type);
|
|
1310
|
-
if (!reactAgent) {
|
|
1311
|
-
return `Error: Agent '${subagent_type}' not found. Available agents: ${Array.from(
|
|
1312
|
-
agentsMap.keys()
|
|
1313
|
-
).join(", ")}`;
|
|
1314
|
-
}
|
|
1315
|
-
try {
|
|
1316
|
-
const currentState = getCurrentTaskInput5();
|
|
1317
|
-
const modifiedState = {
|
|
1318
|
-
...currentState,
|
|
1319
|
-
messages: [
|
|
1320
|
-
{
|
|
1321
|
-
role: "user",
|
|
1322
|
-
content: description
|
|
1323
|
-
}
|
|
1324
|
-
]
|
|
1325
|
-
};
|
|
1326
|
-
const result = await reactAgent.invoke(modifiedState, config);
|
|
1327
|
-
return new Command4({
|
|
1328
|
-
update: {
|
|
1329
|
-
files: result.files || {},
|
|
1330
|
-
messages: [
|
|
1331
|
-
new ToolMessage4({
|
|
1332
|
-
content: result.messages?.slice(-1)[0]?.content || "Task completed",
|
|
1333
|
-
tool_call_id: config.toolCall?.id
|
|
1334
|
-
})
|
|
1335
|
-
]
|
|
1336
|
-
}
|
|
1337
|
-
});
|
|
1338
|
-
} catch (error) {
|
|
1339
|
-
if (error instanceof GraphInterrupt) {
|
|
1340
|
-
throw error;
|
|
1341
|
-
}
|
|
1342
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1343
|
-
return new Command4({
|
|
1344
|
-
update: {
|
|
1345
|
-
messages: [
|
|
1346
|
-
new ToolMessage4({
|
|
1347
|
-
content: `Error executing task '${description}' with agent '${subagent_type}': ${errorMessage}`,
|
|
1348
|
-
tool_call_id: config.toolCall?.id
|
|
1349
|
-
})
|
|
1350
|
-
]
|
|
1351
|
-
}
|
|
1352
|
-
});
|
|
1353
|
-
}
|
|
1354
|
-
},
|
|
1355
|
-
{
|
|
1356
|
-
name: "task",
|
|
1357
|
-
description: TASK_DESCRIPTION_PREFIX.replace(
|
|
1358
|
-
"{other_agents}",
|
|
1359
|
-
subagents.map((a) => `- ${a.name}: ${a.description}`).join("\n")
|
|
1360
|
-
) + TASK_DESCRIPTION_SUFFIX,
|
|
1361
|
-
schema: z9.object({
|
|
1362
|
-
description: z9.string().describe("The task to execute with the selected agent"),
|
|
1363
|
-
subagent_type: z9.string().describe(
|
|
1364
|
-
`Name of the agent to use. Available: ${subagents.map((a) => a.name).join(", ")}`
|
|
1365
|
-
)
|
|
1366
|
-
})
|
|
1367
|
-
}
|
|
1368
|
-
);
|
|
794
|
+
return resultLines.join("\n");
|
|
1369
795
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
import { ToolMessage as ToolMessage5 } from "@langchain/core/messages";
|
|
1374
|
-
import { Command as Command5, getCurrentTaskInput as getCurrentTaskInput6 } from "@langchain/langgraph";
|
|
1375
|
-
import { z as z10 } from "zod";
|
|
1376
|
-
var writeTodos = tool3(
|
|
1377
|
-
(input, config) => {
|
|
1378
|
-
return new Command5({
|
|
1379
|
-
update: {
|
|
1380
|
-
todos: input.todos,
|
|
1381
|
-
messages: [
|
|
1382
|
-
new ToolMessage5({
|
|
1383
|
-
content: genUIMarkdown("todo_list", input.todos),
|
|
1384
|
-
tool_call_id: config.toolCall?.id
|
|
1385
|
-
})
|
|
1386
|
-
]
|
|
1387
|
-
}
|
|
1388
|
-
});
|
|
1389
|
-
},
|
|
1390
|
-
{
|
|
1391
|
-
name: "write_todos",
|
|
1392
|
-
description: WRITE_TODOS_DESCRIPTION2,
|
|
1393
|
-
schema: z10.object({
|
|
1394
|
-
todos: z10.array(
|
|
1395
|
-
z10.object({
|
|
1396
|
-
content: z10.string().describe("Content of the todo item"),
|
|
1397
|
-
status: z10.enum(["pending", "in_progress", "completed"]).describe("Status of the todo")
|
|
1398
|
-
})
|
|
1399
|
-
).describe("List of todo items to update")
|
|
1400
|
-
})
|
|
796
|
+
function checkEmptyContent(content) {
|
|
797
|
+
if (!content || content.trim() === "") {
|
|
798
|
+
return EMPTY_CONTENT_WARNING;
|
|
1401
799
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
function fileDataToString(fileData) {
|
|
803
|
+
return fileData.content.join("\n");
|
|
804
|
+
}
|
|
805
|
+
function createFileData(content, createdAt) {
|
|
806
|
+
const lines = typeof content === "string" ? content.split("\n") : content;
|
|
807
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
808
|
+
return {
|
|
809
|
+
content: lines,
|
|
810
|
+
created_at: createdAt || now,
|
|
811
|
+
modified_at: now
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function updateFileData(fileData, content) {
|
|
815
|
+
const lines = typeof content === "string" ? content.split("\n") : content;
|
|
816
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
817
|
+
return {
|
|
818
|
+
content: lines,
|
|
819
|
+
created_at: fileData.created_at,
|
|
820
|
+
modified_at: now
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
function formatReadResponse(fileData, offset, limit) {
|
|
824
|
+
const content = fileDataToString(fileData);
|
|
825
|
+
const emptyMsg = checkEmptyContent(content);
|
|
826
|
+
if (emptyMsg) {
|
|
827
|
+
return emptyMsg;
|
|
828
|
+
}
|
|
829
|
+
const lines = content.split("\n");
|
|
830
|
+
const startIdx = offset;
|
|
831
|
+
const endIdx = Math.min(startIdx + limit, lines.length);
|
|
832
|
+
if (startIdx >= lines.length) {
|
|
833
|
+
return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
|
|
834
|
+
}
|
|
835
|
+
const selectedLines = lines.slice(startIdx, endIdx);
|
|
836
|
+
return formatContentWithLineNumbers(selectedLines, startIdx + 1);
|
|
837
|
+
}
|
|
838
|
+
function performStringReplacement(content, oldString, newString, replaceAll) {
|
|
839
|
+
const occurrences = content.split(oldString).length - 1;
|
|
840
|
+
if (occurrences === 0) {
|
|
841
|
+
return `Error: String not found in file: '${oldString}'`;
|
|
1413
842
|
}
|
|
1414
|
-
)
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
843
|
+
if (occurrences > 1 && !replaceAll) {
|
|
844
|
+
return `Error: String '${oldString}' appears ${occurrences} times in file. Use replace_all=True to replace all instances, or provide a more specific string with surrounding context.`;
|
|
845
|
+
}
|
|
846
|
+
const newContent = content.split(oldString).join(newString);
|
|
847
|
+
return [newContent, occurrences];
|
|
848
|
+
}
|
|
849
|
+
function validatePath(path) {
|
|
850
|
+
const pathStr = path || "/";
|
|
851
|
+
if (!pathStr || pathStr.trim() === "") {
|
|
852
|
+
throw new Error("Path cannot be empty");
|
|
853
|
+
}
|
|
854
|
+
let normalized = pathStr.startsWith("/") ? pathStr : "/" + pathStr;
|
|
855
|
+
if (!normalized.endsWith("/")) {
|
|
856
|
+
normalized += "/";
|
|
857
|
+
}
|
|
858
|
+
return normalized;
|
|
859
|
+
}
|
|
860
|
+
function globSearchFiles(files, pattern, path = "/") {
|
|
861
|
+
let normalizedPath;
|
|
862
|
+
try {
|
|
863
|
+
normalizedPath = validatePath(path);
|
|
864
|
+
} catch {
|
|
865
|
+
return "No files found";
|
|
866
|
+
}
|
|
867
|
+
const filtered = Object.fromEntries(
|
|
868
|
+
Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath))
|
|
869
|
+
);
|
|
870
|
+
const effectivePattern = pattern;
|
|
871
|
+
const matches = [];
|
|
872
|
+
for (const [filePath, fileData] of Object.entries(filtered)) {
|
|
873
|
+
let relative = filePath.substring(normalizedPath.length);
|
|
874
|
+
if (relative.startsWith("/")) {
|
|
875
|
+
relative = relative.substring(1);
|
|
876
|
+
}
|
|
877
|
+
if (!relative) {
|
|
878
|
+
const parts = filePath.split("/");
|
|
879
|
+
relative = parts[parts.length - 1] || "";
|
|
880
|
+
}
|
|
881
|
+
if (micromatch.isMatch(relative, effectivePattern, {
|
|
882
|
+
dot: true,
|
|
883
|
+
nobrace: false
|
|
884
|
+
})) {
|
|
885
|
+
matches.push([filePath, fileData.modified_at]);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
matches.sort((a, b) => b[1].localeCompare(a[1]));
|
|
889
|
+
if (matches.length === 0) {
|
|
890
|
+
return "No files found";
|
|
891
|
+
}
|
|
892
|
+
return matches.map(([fp]) => fp).join("\n");
|
|
893
|
+
}
|
|
894
|
+
function grepMatchesFromFiles(files, pattern, path = null, glob = null) {
|
|
895
|
+
let regex;
|
|
896
|
+
try {
|
|
897
|
+
regex = new RegExp(pattern);
|
|
898
|
+
} catch (e) {
|
|
899
|
+
return `Invalid regex pattern: ${e.message}`;
|
|
900
|
+
}
|
|
901
|
+
let normalizedPath;
|
|
902
|
+
try {
|
|
903
|
+
normalizedPath = validatePath(path);
|
|
904
|
+
} catch {
|
|
905
|
+
return [];
|
|
906
|
+
}
|
|
907
|
+
let filtered = Object.fromEntries(
|
|
908
|
+
Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath))
|
|
909
|
+
);
|
|
910
|
+
if (glob) {
|
|
911
|
+
filtered = Object.fromEntries(
|
|
912
|
+
Object.entries(filtered).filter(
|
|
913
|
+
([fp]) => micromatch.isMatch(basename(fp), glob, { dot: true, nobrace: false })
|
|
914
|
+
)
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
const matches = [];
|
|
918
|
+
for (const [filePath, fileData] of Object.entries(filtered)) {
|
|
919
|
+
for (let i = 0; i < fileData.content.length; i++) {
|
|
920
|
+
const line = fileData.content[i];
|
|
921
|
+
const lineNum = i + 1;
|
|
922
|
+
if (regex.test(line)) {
|
|
923
|
+
matches.push({ path: filePath, line: lineNum, text: line });
|
|
1438
924
|
}
|
|
1439
|
-
const lineNumber = i + 1;
|
|
1440
|
-
resultLines.push(`${lineNumber.toString().padStart(6)} ${lineContent}`);
|
|
1441
925
|
}
|
|
1442
|
-
return resultLines.join("\n");
|
|
1443
|
-
},
|
|
1444
|
-
{
|
|
1445
|
-
name: "read_file",
|
|
1446
|
-
description: TOOL_DESCRIPTION,
|
|
1447
|
-
schema: z10.object({
|
|
1448
|
-
file_path: z10.string().describe("Absolute path to the file to read"),
|
|
1449
|
-
offset: z10.number().optional().default(0).describe("Line offset to start reading from"),
|
|
1450
|
-
limit: z10.number().optional().default(2e3).describe("Maximum number of lines to read")
|
|
1451
|
-
})
|
|
1452
926
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
927
|
+
return matches;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// src/deep_agent_new/backends/state.ts
|
|
931
|
+
var StateBackend = class {
|
|
932
|
+
constructor(stateAndStore) {
|
|
933
|
+
this.stateAndStore = stateAndStore;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Get files from current state.
|
|
937
|
+
*/
|
|
938
|
+
getFiles() {
|
|
939
|
+
return this.stateAndStore.state.files || {};
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* List files and directories in the specified directory (non-recursive).
|
|
943
|
+
*
|
|
944
|
+
* @param path - Absolute path to directory
|
|
945
|
+
* @returns List of FileInfo objects for files and directories directly in the directory.
|
|
946
|
+
* Directories have a trailing / in their path and is_dir=true.
|
|
947
|
+
*/
|
|
948
|
+
lsInfo(path) {
|
|
949
|
+
const files = this.getFiles();
|
|
950
|
+
const infos = [];
|
|
951
|
+
const subdirs = /* @__PURE__ */ new Set();
|
|
952
|
+
const normalizedPath = path.endsWith("/") ? path : path + "/";
|
|
953
|
+
for (const [k, fd] of Object.entries(files)) {
|
|
954
|
+
if (!k.startsWith(normalizedPath)) {
|
|
955
|
+
continue;
|
|
1468
956
|
}
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
957
|
+
const relative = k.substring(normalizedPath.length);
|
|
958
|
+
if (relative.includes("/")) {
|
|
959
|
+
const subdirName = relative.split("/")[0];
|
|
960
|
+
subdirs.add(normalizedPath + subdirName + "/");
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
const size = fd.content.join("\n").length;
|
|
964
|
+
infos.push({
|
|
965
|
+
path: k,
|
|
966
|
+
is_dir: false,
|
|
967
|
+
size,
|
|
968
|
+
modified_at: fd.modified_at
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
for (const subdir of Array.from(subdirs).sort()) {
|
|
972
|
+
infos.push({
|
|
973
|
+
path: subdir,
|
|
974
|
+
is_dir: true,
|
|
975
|
+
size: 0,
|
|
976
|
+
modified_at: ""
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
infos.sort((a, b) => a.path.localeCompare(b.path));
|
|
980
|
+
return infos;
|
|
1478
981
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
const
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
982
|
+
/**
|
|
983
|
+
* Read file content with line numbers.
|
|
984
|
+
*
|
|
985
|
+
* @param filePath - Absolute file path
|
|
986
|
+
* @param offset - Line offset to start reading from (0-indexed)
|
|
987
|
+
* @param limit - Maximum number of lines to read
|
|
988
|
+
* @returns Formatted file content with line numbers, or error message
|
|
989
|
+
*/
|
|
990
|
+
read(filePath, offset = 0, limit = 2e3) {
|
|
991
|
+
const files = this.getFiles();
|
|
992
|
+
const fileData = files[filePath];
|
|
993
|
+
if (!fileData) {
|
|
994
|
+
return `Error: File '${filePath}' not found`;
|
|
995
|
+
}
|
|
996
|
+
return formatReadResponse(fileData, offset, limit);
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Read file content as raw FileData.
|
|
1000
|
+
*
|
|
1001
|
+
* @param filePath - Absolute file path
|
|
1002
|
+
* @returns Raw file content as FileData
|
|
1003
|
+
*/
|
|
1004
|
+
readRaw(filePath) {
|
|
1005
|
+
const files = this.getFiles();
|
|
1006
|
+
const fileData = files[filePath];
|
|
1007
|
+
if (!fileData) throw new Error(`File '${filePath}' not found`);
|
|
1008
|
+
return fileData;
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Create a new file with content.
|
|
1012
|
+
* Returns WriteResult with filesUpdate to update LangGraph state.
|
|
1013
|
+
*/
|
|
1014
|
+
write(filePath, content) {
|
|
1015
|
+
const files = this.getFiles();
|
|
1016
|
+
if (filePath in files) {
|
|
1017
|
+
return {
|
|
1018
|
+
error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
const newFileData = createFileData(content);
|
|
1022
|
+
return {
|
|
1023
|
+
path: filePath,
|
|
1024
|
+
filesUpdate: { [filePath]: newFileData }
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Edit a file by replacing string occurrences.
|
|
1029
|
+
* Returns EditResult with filesUpdate and occurrences.
|
|
1030
|
+
*/
|
|
1031
|
+
edit(filePath, oldString, newString, replaceAll = false) {
|
|
1032
|
+
const files = this.getFiles();
|
|
1033
|
+
const fileData = files[filePath];
|
|
1034
|
+
if (!fileData) {
|
|
1035
|
+
return { error: `Error: File '${filePath}' not found` };
|
|
1036
|
+
}
|
|
1037
|
+
const content = fileDataToString(fileData);
|
|
1038
|
+
const result = performStringReplacement(
|
|
1039
|
+
content,
|
|
1040
|
+
oldString,
|
|
1041
|
+
newString,
|
|
1042
|
+
replaceAll
|
|
1043
|
+
);
|
|
1044
|
+
if (typeof result === "string") {
|
|
1045
|
+
return { error: result };
|
|
1046
|
+
}
|
|
1047
|
+
const [newContent, occurrences] = result;
|
|
1048
|
+
const newFileData = updateFileData(fileData, newContent);
|
|
1049
|
+
return {
|
|
1050
|
+
path: filePath,
|
|
1051
|
+
filesUpdate: { [filePath]: newFileData },
|
|
1052
|
+
occurrences
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Structured search results or error string for invalid input.
|
|
1057
|
+
*/
|
|
1058
|
+
grepRaw(pattern, path = "/", glob = null) {
|
|
1059
|
+
const files = this.getFiles();
|
|
1060
|
+
return grepMatchesFromFiles(files, pattern, path, glob);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Structured glob matching returning FileInfo objects.
|
|
1064
|
+
*/
|
|
1065
|
+
globInfo(pattern, path = "/") {
|
|
1066
|
+
const files = this.getFiles();
|
|
1067
|
+
const result = globSearchFiles(files, pattern, path);
|
|
1068
|
+
if (result === "No files found") {
|
|
1069
|
+
return [];
|
|
1070
|
+
}
|
|
1071
|
+
const paths = result.split("\n");
|
|
1072
|
+
const infos = [];
|
|
1073
|
+
for (const p of paths) {
|
|
1074
|
+
const fd = files[p];
|
|
1075
|
+
const size = fd ? fd.content.join("\n").length : 0;
|
|
1076
|
+
infos.push({
|
|
1077
|
+
path: p,
|
|
1078
|
+
is_dir: false,
|
|
1079
|
+
size,
|
|
1080
|
+
modified_at: fd?.modified_at || ""
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
return infos;
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
// src/deep_agent_new/middleware/fs.ts
|
|
1088
|
+
var FileDataSchema = z3.object({
|
|
1089
|
+
content: z3.array(z3.string()),
|
|
1090
|
+
created_at: z3.string(),
|
|
1091
|
+
modified_at: z3.string()
|
|
1092
|
+
});
|
|
1093
|
+
function fileDataReducer(left, right) {
|
|
1094
|
+
if (left === void 0) {
|
|
1095
|
+
const result2 = {};
|
|
1096
|
+
for (const [key, value] of Object.entries(right)) {
|
|
1097
|
+
if (value !== null) {
|
|
1098
|
+
result2[key] = value;
|
|
1502
1099
|
}
|
|
1503
1100
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
newContent = content.replace(
|
|
1511
|
-
new RegExp(escapedOldString, "g"),
|
|
1512
|
-
new_string
|
|
1513
|
-
);
|
|
1101
|
+
return result2;
|
|
1102
|
+
}
|
|
1103
|
+
const result = { ...left };
|
|
1104
|
+
for (const [key, value] of Object.entries(right)) {
|
|
1105
|
+
if (value === null) {
|
|
1106
|
+
delete result[key];
|
|
1514
1107
|
} else {
|
|
1515
|
-
|
|
1108
|
+
result[key] = value;
|
|
1516
1109
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1110
|
+
}
|
|
1111
|
+
return result;
|
|
1112
|
+
}
|
|
1113
|
+
var FilesystemStateSchema = z3.object({
|
|
1114
|
+
files: withLangGraph(
|
|
1115
|
+
z3.record(z3.string(), FileDataSchema).default({}),
|
|
1116
|
+
{
|
|
1117
|
+
reducer: {
|
|
1118
|
+
fn: fileDataReducer,
|
|
1119
|
+
schema: z3.record(z3.string(), FileDataSchema.nullable())
|
|
1527
1120
|
}
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
file_path: z10.string().describe("Absolute path to the file to edit"),
|
|
1535
|
-
old_string: z10.string().describe("String to be replaced (must match exactly)"),
|
|
1536
|
-
new_string: z10.string().describe("String to replace with"),
|
|
1537
|
-
replace_all: z10.boolean().optional().default(false).describe("Whether to replace all occurrences")
|
|
1538
|
-
})
|
|
1121
|
+
}
|
|
1122
|
+
)
|
|
1123
|
+
});
|
|
1124
|
+
function getBackend(backend, stateAndStore) {
|
|
1125
|
+
if (typeof backend === "function") {
|
|
1126
|
+
return backend(stateAndStore);
|
|
1539
1127
|
}
|
|
1540
|
-
|
|
1128
|
+
return backend;
|
|
1129
|
+
}
|
|
1130
|
+
var FILESYSTEM_SYSTEM_PROMPT = `You have access to a virtual filesystem. All file paths must start with a /.
|
|
1131
|
+
|
|
1132
|
+
- ls: list files in a directory (requires absolute path)
|
|
1133
|
+
- read_file: read a file from the filesystem
|
|
1134
|
+
- write_file: write to a file in the filesystem
|
|
1135
|
+
- edit_file: edit a file in the filesystem
|
|
1136
|
+
- glob: find files matching a pattern (e.g., "**/*.py")
|
|
1137
|
+
- grep: search for text within files`;
|
|
1138
|
+
var LS_TOOL_DESCRIPTION = "List files and directories in a directory";
|
|
1139
|
+
var READ_FILE_TOOL_DESCRIPTION = "Read the contents of a file";
|
|
1140
|
+
var WRITE_FILE_TOOL_DESCRIPTION = "Write content to a new file. Returns an error if the file already exists";
|
|
1141
|
+
var EDIT_FILE_TOOL_DESCRIPTION = "Edit a file by replacing a specific string with a new string";
|
|
1142
|
+
var GLOB_TOOL_DESCRIPTION = "Find files matching a glob pattern (e.g., '**/*.py' for all Python files)";
|
|
1143
|
+
var GREP_TOOL_DESCRIPTION = "Search for a regex pattern in files. Returns matching files and line numbers";
|
|
1144
|
+
function createLsTool(backend, options) {
|
|
1145
|
+
const { customDescription } = options;
|
|
1146
|
+
return tool2(
|
|
1147
|
+
async (input, config) => {
|
|
1148
|
+
const stateAndStore = {
|
|
1149
|
+
state: getCurrentTaskInput(config),
|
|
1150
|
+
store: config.store
|
|
1151
|
+
};
|
|
1152
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1153
|
+
const path = input.path || "/";
|
|
1154
|
+
const infos = await resolvedBackend.lsInfo(path);
|
|
1155
|
+
if (infos.length === 0) {
|
|
1156
|
+
return `No files found in ${path}`;
|
|
1157
|
+
}
|
|
1158
|
+
const lines = [];
|
|
1159
|
+
for (const info of infos) {
|
|
1160
|
+
if (info.is_dir) {
|
|
1161
|
+
lines.push(`${info.path} (directory)`);
|
|
1162
|
+
} else {
|
|
1163
|
+
const size = info.size ? ` (${info.size} bytes)` : "";
|
|
1164
|
+
lines.push(`${info.path}${size}`);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
return lines.join("\n");
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
name: "ls",
|
|
1171
|
+
description: customDescription || LS_TOOL_DESCRIPTION,
|
|
1172
|
+
schema: z3.object({
|
|
1173
|
+
path: z3.string().optional().default("/").describe("Directory path to list (default: /)")
|
|
1174
|
+
})
|
|
1175
|
+
}
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
function createReadFileTool(backend, options) {
|
|
1179
|
+
const { customDescription } = options;
|
|
1180
|
+
return tool2(
|
|
1181
|
+
async (input, config) => {
|
|
1182
|
+
const stateAndStore = {
|
|
1183
|
+
state: getCurrentTaskInput(config),
|
|
1184
|
+
store: config.store
|
|
1185
|
+
};
|
|
1186
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1187
|
+
const { file_path, offset = 0, limit = 2e3 } = input;
|
|
1188
|
+
return await resolvedBackend.read(file_path, offset, limit);
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
name: "read_file",
|
|
1192
|
+
description: customDescription || READ_FILE_TOOL_DESCRIPTION,
|
|
1193
|
+
schema: z3.object({
|
|
1194
|
+
file_path: z3.string().describe("Absolute path to the file to read"),
|
|
1195
|
+
offset: z3.number({ coerce: true }).optional().default(0).describe("Line offset to start reading from (0-indexed)"),
|
|
1196
|
+
limit: z3.number({ coerce: true }).optional().default(2e3).describe("Maximum number of lines to read")
|
|
1197
|
+
})
|
|
1198
|
+
}
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
function createWriteFileTool(backend, options) {
|
|
1202
|
+
const { customDescription } = options;
|
|
1203
|
+
return tool2(
|
|
1204
|
+
async (input, config) => {
|
|
1205
|
+
const stateAndStore = {
|
|
1206
|
+
state: getCurrentTaskInput(config),
|
|
1207
|
+
store: config.store
|
|
1208
|
+
};
|
|
1209
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1210
|
+
const { file_path, content } = input;
|
|
1211
|
+
const result = await resolvedBackend.write(file_path, content);
|
|
1212
|
+
if (result.error) {
|
|
1213
|
+
return result.error;
|
|
1214
|
+
}
|
|
1215
|
+
const message = new ToolMessage({
|
|
1216
|
+
content: `Successfully wrote to '${file_path}'`,
|
|
1217
|
+
tool_call_id: config.toolCall?.id,
|
|
1218
|
+
name: "write_file",
|
|
1219
|
+
metadata: result.metadata
|
|
1220
|
+
});
|
|
1221
|
+
if (result.filesUpdate) {
|
|
1222
|
+
return new Command({
|
|
1223
|
+
update: { files: result.filesUpdate, messages: [message] }
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
return message;
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
name: "write_file",
|
|
1230
|
+
description: customDescription || WRITE_FILE_TOOL_DESCRIPTION,
|
|
1231
|
+
schema: z3.object({
|
|
1232
|
+
file_path: z3.string().describe("Absolute path to the file to write"),
|
|
1233
|
+
content: z3.string().describe("Content to write to the file")
|
|
1234
|
+
})
|
|
1235
|
+
}
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
function createEditFileTool(backend, options) {
|
|
1239
|
+
const { customDescription } = options;
|
|
1240
|
+
return tool2(
|
|
1241
|
+
async (input, config) => {
|
|
1242
|
+
const stateAndStore = {
|
|
1243
|
+
state: getCurrentTaskInput(config),
|
|
1244
|
+
store: config.store
|
|
1245
|
+
};
|
|
1246
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1247
|
+
const { file_path, old_string, new_string, replace_all = false } = input;
|
|
1248
|
+
const result = await resolvedBackend.edit(
|
|
1249
|
+
file_path,
|
|
1250
|
+
old_string,
|
|
1251
|
+
new_string,
|
|
1252
|
+
replace_all
|
|
1253
|
+
);
|
|
1254
|
+
if (result.error) {
|
|
1255
|
+
return result.error;
|
|
1256
|
+
}
|
|
1257
|
+
const message = new ToolMessage({
|
|
1258
|
+
content: `Successfully replaced ${result.occurrences} occurrence(s) in '${file_path}'`,
|
|
1259
|
+
tool_call_id: config.toolCall?.id,
|
|
1260
|
+
name: "edit_file",
|
|
1261
|
+
metadata: result.metadata
|
|
1262
|
+
});
|
|
1263
|
+
if (result.filesUpdate) {
|
|
1264
|
+
return new Command({
|
|
1265
|
+
update: { files: result.filesUpdate, messages: [message] }
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
return message;
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
name: "edit_file",
|
|
1272
|
+
description: customDescription || EDIT_FILE_TOOL_DESCRIPTION,
|
|
1273
|
+
schema: z3.object({
|
|
1274
|
+
file_path: z3.string().describe("Absolute path to the file to edit"),
|
|
1275
|
+
old_string: z3.string().describe("String to be replaced (must match exactly)"),
|
|
1276
|
+
new_string: z3.string().describe("String to replace with"),
|
|
1277
|
+
replace_all: z3.boolean().optional().default(false).describe("Whether to replace all occurrences")
|
|
1278
|
+
})
|
|
1279
|
+
}
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1282
|
+
function createGlobTool(backend, options) {
|
|
1283
|
+
const { customDescription } = options;
|
|
1284
|
+
return tool2(
|
|
1285
|
+
async (input, config) => {
|
|
1286
|
+
const stateAndStore = {
|
|
1287
|
+
state: getCurrentTaskInput(config),
|
|
1288
|
+
store: config.store
|
|
1289
|
+
};
|
|
1290
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1291
|
+
const { pattern, path = "/" } = input;
|
|
1292
|
+
const infos = await resolvedBackend.globInfo(pattern, path);
|
|
1293
|
+
if (infos.length === 0) {
|
|
1294
|
+
return `No files found matching pattern '${pattern}'`;
|
|
1295
|
+
}
|
|
1296
|
+
return infos.map((info) => info.path).join("\n");
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
name: "glob",
|
|
1300
|
+
description: customDescription || GLOB_TOOL_DESCRIPTION,
|
|
1301
|
+
schema: z3.object({
|
|
1302
|
+
pattern: z3.string().describe("Glob pattern (e.g., '*.py', '**/*.ts')"),
|
|
1303
|
+
path: z3.string().optional().default("/").describe("Base path to search from (default: /)")
|
|
1304
|
+
})
|
|
1305
|
+
}
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
function createGrepTool(backend, options) {
|
|
1309
|
+
const { customDescription } = options;
|
|
1310
|
+
return tool2(
|
|
1311
|
+
async (input, config) => {
|
|
1312
|
+
const stateAndStore = {
|
|
1313
|
+
state: getCurrentTaskInput(config),
|
|
1314
|
+
store: config.store
|
|
1315
|
+
};
|
|
1316
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1317
|
+
const { pattern, path = "/", glob = null } = input;
|
|
1318
|
+
const result = await resolvedBackend.grepRaw(pattern, path, glob);
|
|
1319
|
+
if (typeof result === "string") {
|
|
1320
|
+
return result;
|
|
1321
|
+
}
|
|
1322
|
+
if (result.length === 0) {
|
|
1323
|
+
return `No matches found for pattern '${pattern}'`;
|
|
1324
|
+
}
|
|
1325
|
+
const lines = [];
|
|
1326
|
+
let currentFile = null;
|
|
1327
|
+
for (const match of result) {
|
|
1328
|
+
if (match.path !== currentFile) {
|
|
1329
|
+
currentFile = match.path;
|
|
1330
|
+
lines.push(`
|
|
1331
|
+
${currentFile}:`);
|
|
1332
|
+
}
|
|
1333
|
+
lines.push(` ${match.line}: ${match.text}`);
|
|
1334
|
+
}
|
|
1335
|
+
return lines.join("\n");
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
name: "grep",
|
|
1339
|
+
description: customDescription || GREP_TOOL_DESCRIPTION,
|
|
1340
|
+
schema: z3.object({
|
|
1341
|
+
pattern: z3.string().describe("Regex pattern to search for"),
|
|
1342
|
+
path: z3.string().optional().default("/").describe("Base path to search from (default: /)"),
|
|
1343
|
+
glob: z3.string().optional().nullable().describe("Optional glob pattern to filter files (e.g., '*.py')")
|
|
1344
|
+
})
|
|
1345
|
+
}
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
function createFilesystemMiddleware(options = {}) {
|
|
1349
|
+
const {
|
|
1350
|
+
backend = (stateAndStore) => new StateBackend(stateAndStore),
|
|
1351
|
+
systemPrompt: customSystemPrompt = null,
|
|
1352
|
+
customToolDescriptions = null,
|
|
1353
|
+
toolTokenLimitBeforeEvict = 2e4
|
|
1354
|
+
} = options;
|
|
1355
|
+
const systemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
|
|
1356
|
+
const tools = [
|
|
1357
|
+
createLsTool(backend, {
|
|
1358
|
+
customDescription: customToolDescriptions?.ls
|
|
1359
|
+
}),
|
|
1360
|
+
createReadFileTool(backend, {
|
|
1361
|
+
customDescription: customToolDescriptions?.read_file
|
|
1362
|
+
}),
|
|
1363
|
+
createWriteFileTool(backend, {
|
|
1364
|
+
customDescription: customToolDescriptions?.write_file
|
|
1365
|
+
}),
|
|
1366
|
+
createEditFileTool(backend, {
|
|
1367
|
+
customDescription: customToolDescriptions?.edit_file
|
|
1368
|
+
}),
|
|
1369
|
+
createGlobTool(backend, {
|
|
1370
|
+
customDescription: customToolDescriptions?.glob
|
|
1371
|
+
}),
|
|
1372
|
+
createGrepTool(backend, {
|
|
1373
|
+
customDescription: customToolDescriptions?.grep
|
|
1374
|
+
})
|
|
1375
|
+
];
|
|
1376
|
+
return createMiddleware({
|
|
1377
|
+
name: "FilesystemMiddleware",
|
|
1378
|
+
stateSchema: FilesystemStateSchema,
|
|
1379
|
+
tools,
|
|
1380
|
+
wrapModelCall: systemPrompt ? async (request, handler) => {
|
|
1381
|
+
const currentSystemPrompt = request.systemPrompt || "";
|
|
1382
|
+
const newSystemPrompt = currentSystemPrompt ? `${currentSystemPrompt}
|
|
1383
|
+
|
|
1384
|
+
${systemPrompt}` : systemPrompt;
|
|
1385
|
+
return handler({ ...request, systemPrompt: newSystemPrompt });
|
|
1386
|
+
} : void 0,
|
|
1387
|
+
wrapToolCall: toolTokenLimitBeforeEvict ? (async (request, handler) => {
|
|
1388
|
+
const result = await handler(request);
|
|
1389
|
+
async function processToolMessage(msg) {
|
|
1390
|
+
if (typeof msg.content === "string" && msg.content.length > toolTokenLimitBeforeEvict * 4) {
|
|
1391
|
+
const stateAndStore = {
|
|
1392
|
+
state: request.state || {},
|
|
1393
|
+
store: request.config?.store
|
|
1394
|
+
};
|
|
1395
|
+
const resolvedBackend = getBackend(backend, stateAndStore);
|
|
1396
|
+
const sanitizedId = sanitizeToolCallId(
|
|
1397
|
+
request.toolCall?.id || msg.tool_call_id
|
|
1398
|
+
);
|
|
1399
|
+
const evictPath = `/large_tool_results/${sanitizedId}`;
|
|
1400
|
+
const writeResult = await resolvedBackend.write(
|
|
1401
|
+
evictPath,
|
|
1402
|
+
msg.content
|
|
1403
|
+
);
|
|
1404
|
+
if (writeResult.error) {
|
|
1405
|
+
return { message: msg, filesUpdate: null };
|
|
1406
|
+
}
|
|
1407
|
+
const truncatedMessage = new ToolMessage({
|
|
1408
|
+
content: `Tool result too large (${Math.round(
|
|
1409
|
+
msg.content.length / 4
|
|
1410
|
+
)} tokens). Content saved to ${evictPath}`,
|
|
1411
|
+
tool_call_id: msg.tool_call_id,
|
|
1412
|
+
name: msg.name
|
|
1413
|
+
});
|
|
1414
|
+
return {
|
|
1415
|
+
message: truncatedMessage,
|
|
1416
|
+
filesUpdate: writeResult.filesUpdate
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
return { message: msg, filesUpdate: null };
|
|
1420
|
+
}
|
|
1421
|
+
if (result instanceof ToolMessage) {
|
|
1422
|
+
const processed = await processToolMessage(result);
|
|
1423
|
+
if (processed.filesUpdate) {
|
|
1424
|
+
return new Command({
|
|
1425
|
+
update: {
|
|
1426
|
+
files: processed.filesUpdate,
|
|
1427
|
+
messages: [processed.message]
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
return processed.message;
|
|
1432
|
+
}
|
|
1433
|
+
if (isCommand(result)) {
|
|
1434
|
+
const update = result.update;
|
|
1435
|
+
if (!update?.messages) {
|
|
1436
|
+
return result;
|
|
1437
|
+
}
|
|
1438
|
+
let hasLargeResults = false;
|
|
1439
|
+
const accumulatedFiles = {
|
|
1440
|
+
...update.files || {}
|
|
1441
|
+
};
|
|
1442
|
+
const processedMessages = [];
|
|
1443
|
+
for (const msg of update.messages) {
|
|
1444
|
+
if (msg instanceof ToolMessage) {
|
|
1445
|
+
const processed = await processToolMessage(msg);
|
|
1446
|
+
processedMessages.push(processed.message);
|
|
1447
|
+
if (processed.filesUpdate) {
|
|
1448
|
+
hasLargeResults = true;
|
|
1449
|
+
Object.assign(accumulatedFiles, processed.filesUpdate);
|
|
1450
|
+
}
|
|
1451
|
+
} else {
|
|
1452
|
+
processedMessages.push(msg);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
if (hasLargeResults) {
|
|
1456
|
+
return new Command({
|
|
1457
|
+
update: {
|
|
1458
|
+
...update,
|
|
1459
|
+
messages: processedMessages,
|
|
1460
|
+
files: accumulatedFiles
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return result;
|
|
1466
|
+
}) : void 0
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1541
1469
|
|
|
1542
|
-
// src/
|
|
1543
|
-
import "
|
|
1544
|
-
import {
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1470
|
+
// src/deep_agent_new/middleware/subagents.ts
|
|
1471
|
+
import { z as z4 } from "zod/v3";
|
|
1472
|
+
import {
|
|
1473
|
+
createMiddleware as createMiddleware2,
|
|
1474
|
+
createAgent as createAgent2,
|
|
1475
|
+
tool as tool3,
|
|
1476
|
+
ToolMessage as ToolMessage2,
|
|
1477
|
+
humanInTheLoopMiddleware
|
|
1478
|
+
} from "langchain";
|
|
1479
|
+
import { Command as Command2, getCurrentTaskInput as getCurrentTaskInput2 } from "@langchain/langgraph";
|
|
1480
|
+
import { HumanMessage } from "@langchain/core/messages";
|
|
1481
|
+
var DEFAULT_SUBAGENT_PROMPT = "In order to complete the objective that the user asks of you, you have access to a number of standard tools.";
|
|
1482
|
+
var EXCLUDED_STATE_KEYS = ["messages", "todos", "jumpTo"];
|
|
1483
|
+
var DEFAULT_GENERAL_PURPOSE_DESCRIPTION = "General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. This agent has access to all tools as the main agent.";
|
|
1484
|
+
function getTaskToolDescription(subagentDescriptions) {
|
|
1485
|
+
return `
|
|
1486
|
+
Launch an ephemeral subagent to handle complex, multi-step independent tasks with isolated context windows.
|
|
1487
|
+
|
|
1488
|
+
Available agent types and the tools they have access to:
|
|
1489
|
+
${subagentDescriptions.join("\n")}
|
|
1490
|
+
|
|
1491
|
+
When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
|
|
1492
|
+
|
|
1493
|
+
## Usage notes:
|
|
1494
|
+
1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
|
|
1495
|
+
2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
|
|
1496
|
+
3. Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
|
|
1497
|
+
4. The agent's outputs should generally be trusted
|
|
1498
|
+
5. Clearly tell the agent whether you expect it to create content, perform analysis, or just do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
|
|
1499
|
+
6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
|
|
1500
|
+
7. When only the general-purpose agent is provided, you should use it for all tasks. It is great for isolating context and token usage, and completing specific, complex tasks, as it has all the same capabilities as the main agent.
|
|
1501
|
+
|
|
1502
|
+
### Example usage of the general-purpose agent:
|
|
1503
|
+
|
|
1504
|
+
<example_agent_descriptions>
|
|
1505
|
+
"general-purpose": use this agent for general purpose tasks, it has access to all tools as the main agent.
|
|
1506
|
+
</example_agent_descriptions>
|
|
1507
|
+
|
|
1508
|
+
<example>
|
|
1509
|
+
User: "I want to conduct research on the accomplishments of Lebron James, Michael Jordan, and Kobe Bryant, and then compare them."
|
|
1510
|
+
Assistant: *Uses the task tool in parallel to conduct isolated research on each of the three players*
|
|
1511
|
+
Assistant: *Synthesizes the results of the three isolated research tasks and responds to the User*
|
|
1512
|
+
<commentary>
|
|
1513
|
+
Research is a complex, multi-step task in it of itself.
|
|
1514
|
+
The research of each individual player is not dependent on the research of the other players.
|
|
1515
|
+
The assistant uses the task tool to break down the complex objective into three isolated tasks.
|
|
1516
|
+
Each research task only needs to worry about context and tokens about one player, then returns synthesized information about each player as the Tool Result.
|
|
1517
|
+
This means each research task can dive deep and spend tokens and context deeply researching each player, but the final result is synthesized information, and saves us tokens in the long run when comparing the players to each other.
|
|
1518
|
+
</commentary>
|
|
1519
|
+
</example>
|
|
1520
|
+
|
|
1521
|
+
<example>
|
|
1522
|
+
User: "Analyze a single large code repository for security vulnerabilities and generate a report."
|
|
1523
|
+
Assistant: *Launches a single \`task\` subagent for the repository analysis*
|
|
1524
|
+
Assistant: *Receives report and integrates results into final summary*
|
|
1525
|
+
<commentary>
|
|
1526
|
+
Subagent is used to isolate a large, context-heavy task, even though there is only one. This prevents the main thread from being overloaded with details.
|
|
1527
|
+
If the user then asks followup questions, we have a concise report to reference instead of the entire history of analysis and tool calls, which is good and saves us time and money.
|
|
1528
|
+
</commentary>
|
|
1529
|
+
</example>
|
|
1530
|
+
|
|
1531
|
+
<example>
|
|
1532
|
+
User: "Schedule two meetings for me and prepare agendas for each."
|
|
1533
|
+
Assistant: *Calls the task tool in parallel to launch two \`task\` subagents (one per meeting) to prepare agendas*
|
|
1534
|
+
Assistant: *Returns final schedules and agendas*
|
|
1535
|
+
<commentary>
|
|
1536
|
+
Tasks are simple individually, but subagents help silo agenda preparation.
|
|
1537
|
+
Each subagent only needs to worry about the agenda for one meeting.
|
|
1538
|
+
</commentary>
|
|
1539
|
+
</example>
|
|
1540
|
+
|
|
1541
|
+
<example>
|
|
1542
|
+
User: "I want to order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway."
|
|
1543
|
+
Assistant: *Calls tools directly in parallel to order a pizza from Dominos, a burger from McDonald's, and a salad from Subway*
|
|
1544
|
+
<commentary>
|
|
1545
|
+
The assistant did not use the task tool because the objective is super simple and clear and only requires a few trivial tool calls.
|
|
1546
|
+
It is better to just complete the task directly and NOT use the \`task\`tool.
|
|
1547
|
+
</commentary>
|
|
1548
|
+
</example>
|
|
1549
|
+
|
|
1550
|
+
### Example usage with custom agents:
|
|
1551
|
+
|
|
1552
|
+
<example_agent_descriptions>
|
|
1553
|
+
"content-reviewer": use this agent after you are done creating significant content or documents
|
|
1554
|
+
"greeting-responder": use this agent when to respond to user greetings with a friendly joke
|
|
1555
|
+
"research-analyst": use this agent to conduct thorough research on complex topics
|
|
1556
|
+
</example_agent_description>
|
|
1557
|
+
|
|
1558
|
+
<example>
|
|
1559
|
+
user: "Please write a function that checks if a number is prime"
|
|
1560
|
+
assistant: Sure let me write a function that checks if a number is prime
|
|
1561
|
+
assistant: First let me use the Write tool to write a function that checks if a number is prime
|
|
1562
|
+
assistant: I'm going to use the Write tool to write the following code:
|
|
1563
|
+
<code>
|
|
1564
|
+
function isPrime(n) {
|
|
1565
|
+
if (n <= 1) return false
|
|
1566
|
+
for (let i = 2; i * i <= n; i++) {
|
|
1567
|
+
if (n % i === 0) return false
|
|
1554
1568
|
}
|
|
1569
|
+
return true
|
|
1555
1570
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1571
|
+
</code>
|
|
1572
|
+
<commentary>
|
|
1573
|
+
Since significant content was created and the task was completed, now use the content-reviewer agent to review the work
|
|
1574
|
+
</commentary>
|
|
1575
|
+
assistant: Now let me use the content-reviewer agent to review the code
|
|
1576
|
+
assistant: Uses the Task tool to launch with the content-reviewer agent
|
|
1577
|
+
</example>
|
|
1578
|
+
|
|
1579
|
+
<example>
|
|
1580
|
+
user: "Can you help me research the environmental impact of different renewable energy sources and create a comprehensive report?"
|
|
1581
|
+
<commentary>
|
|
1582
|
+
This is a complex research task that would benefit from using the research-analyst agent to conduct thorough analysis
|
|
1583
|
+
</commentary>
|
|
1584
|
+
assistant: I'll help you research the environmental impact of renewable energy sources. Let me use the research-analyst agent to conduct comprehensive research on this topic.
|
|
1585
|
+
assistant: Uses the Task tool to launch with the research-analyst agent, providing detailed instructions about what research to conduct and what format the report should take
|
|
1586
|
+
</example>
|
|
1587
|
+
|
|
1588
|
+
<example>
|
|
1589
|
+
user: "Hello"
|
|
1590
|
+
<commentary>
|
|
1591
|
+
Since the user is greeting, use the greeting-responder agent to respond with a friendly joke
|
|
1592
|
+
</commentary>
|
|
1593
|
+
assistant: "I'm going to use the Task tool to launch with the greeting-responder agent"
|
|
1594
|
+
</example>
|
|
1595
|
+
`.trim();
|
|
1596
|
+
}
|
|
1597
|
+
var TASK_SYSTEM_PROMPT = `## \`task\` (subagent spawner)
|
|
1598
|
+
|
|
1599
|
+
You have access to a \`task\` tool to launch short-lived subagents that handle isolated tasks. These agents are ephemeral \u2014 they live only for the duration of the task and return a single result.
|
|
1600
|
+
|
|
1601
|
+
When to use the task tool:
|
|
1602
|
+
- When a task is complex and multi-step, and can be fully delegated in isolation
|
|
1603
|
+
- When a task is independent of other tasks and can run in parallel
|
|
1604
|
+
- When a task requires focused reasoning or heavy token/context usage that would bloat the orchestrator thread
|
|
1605
|
+
- When sandboxing improves reliability (e.g. code execution, structured searches, data formatting)
|
|
1606
|
+
- When you only care about the output of the subagent, and not the intermediate steps (ex. performing a lot of research and then returned a synthesized report, performing a series of computations or lookups to achieve a concise, relevant answer.)
|
|
1607
|
+
|
|
1608
|
+
Subagent lifecycle:
|
|
1609
|
+
1. **Spawn** \u2192 Provide clear role, instructions, and expected output
|
|
1610
|
+
2. **Run** \u2192 The subagent completes the task autonomously
|
|
1611
|
+
3. **Return** \u2192 The subagent provides a single structured result
|
|
1612
|
+
4. **Reconcile** \u2192 Incorporate or synthesize the result into the main thread
|
|
1613
|
+
|
|
1614
|
+
When NOT to use the task tool:
|
|
1615
|
+
- If you need to see the intermediate reasoning or steps after the subagent has completed (the task tool hides them)
|
|
1616
|
+
- If the task is trivial (a few tool calls or simple lookup)
|
|
1617
|
+
- If delegating does not reduce token usage, complexity, or context switching
|
|
1618
|
+
- If splitting would add latency without benefit
|
|
1619
|
+
|
|
1620
|
+
## Important Task Tool Usage Notes to Remember
|
|
1621
|
+
- Whenever possible, parallelize the work that you do. This is true for both tool_calls, and for tasks. Whenever you have independent steps to complete - make tool_calls, or kick off tasks (subagents) in parallel to accomplish them faster. This saves time for the user, which is incredibly important.
|
|
1622
|
+
- Remember to use the \`task\` tool to silo independent tasks within a multi-part objective.
|
|
1623
|
+
- You should use the \`task\` tool whenever you have a complex task that will take multiple steps, and is independent from other tasks that the agent needs to complete. These agents are highly competent and efficient.`;
|
|
1624
|
+
function filterStateForSubagent(state) {
|
|
1625
|
+
const filtered = {};
|
|
1626
|
+
for (const [key, value] of Object.entries(state)) {
|
|
1627
|
+
if (!EXCLUDED_STATE_KEYS.includes(key)) {
|
|
1628
|
+
filtered[key] = value;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
return filtered;
|
|
1632
|
+
}
|
|
1633
|
+
function returnCommandWithStateUpdate(result, toolCallId) {
|
|
1634
|
+
const stateUpdate = filterStateForSubagent(result);
|
|
1635
|
+
const messages = result.messages;
|
|
1636
|
+
const lastMessage = messages?.[messages.length - 1];
|
|
1637
|
+
return new Command2({
|
|
1638
|
+
update: {
|
|
1639
|
+
...stateUpdate,
|
|
1640
|
+
messages: [
|
|
1641
|
+
new ToolMessage2({
|
|
1642
|
+
content: lastMessage?.content || "Task completed",
|
|
1643
|
+
tool_call_id: toolCallId,
|
|
1644
|
+
name: "task"
|
|
1645
|
+
})
|
|
1646
|
+
]
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
function getSubagents(options) {
|
|
1651
|
+
const {
|
|
1652
|
+
defaultModel,
|
|
1653
|
+
defaultTools,
|
|
1654
|
+
defaultMiddleware,
|
|
1655
|
+
defaultInterruptOn,
|
|
1656
|
+
subagents,
|
|
1657
|
+
generalPurposeAgent
|
|
1658
|
+
} = options;
|
|
1659
|
+
const defaultSubagentMiddleware = defaultMiddleware || [];
|
|
1660
|
+
const agents = {};
|
|
1661
|
+
const subagentDescriptions = [];
|
|
1662
|
+
if (generalPurposeAgent) {
|
|
1663
|
+
const generalPurposeMiddleware = [...defaultSubagentMiddleware];
|
|
1664
|
+
if (defaultInterruptOn) {
|
|
1665
|
+
generalPurposeMiddleware.push(
|
|
1666
|
+
humanInTheLoopMiddleware({ interruptOn: defaultInterruptOn })
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
const generalPurposeSubagent = createAgent2({
|
|
1670
|
+
model: defaultModel,
|
|
1671
|
+
systemPrompt: DEFAULT_SUBAGENT_PROMPT,
|
|
1672
|
+
tools: defaultTools,
|
|
1673
|
+
middleware: generalPurposeMiddleware
|
|
1674
|
+
});
|
|
1675
|
+
agents["general-purpose"] = generalPurposeSubagent;
|
|
1676
|
+
subagentDescriptions.push(
|
|
1677
|
+
`- general-purpose: ${DEFAULT_GENERAL_PURPOSE_DESCRIPTION}`
|
|
1678
|
+
);
|
|
1559
1679
|
}
|
|
1560
|
-
|
|
1680
|
+
for (const agentParams of subagents) {
|
|
1681
|
+
subagentDescriptions.push(
|
|
1682
|
+
`- ${agentParams.name}: ${agentParams.description}`
|
|
1683
|
+
);
|
|
1684
|
+
if ("runnable" in agentParams) {
|
|
1685
|
+
agents[agentParams.name] = agentParams.runnable;
|
|
1686
|
+
} else {
|
|
1687
|
+
const middleware = agentParams.middleware ? [...defaultSubagentMiddleware, ...agentParams.middleware] : [...defaultSubagentMiddleware];
|
|
1688
|
+
const interruptOn = agentParams.interruptOn || defaultInterruptOn;
|
|
1689
|
+
if (interruptOn)
|
|
1690
|
+
middleware.push(humanInTheLoopMiddleware({ interruptOn }));
|
|
1691
|
+
agents[agentParams.name] = createAgent2({
|
|
1692
|
+
model: agentParams.model ?? defaultModel,
|
|
1693
|
+
systemPrompt: agentParams.systemPrompt,
|
|
1694
|
+
tools: agentParams.tools ?? defaultTools,
|
|
1695
|
+
middleware
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
return { agents, descriptions: subagentDescriptions };
|
|
1561
1700
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1701
|
+
function createTaskTool(options) {
|
|
1702
|
+
const {
|
|
1703
|
+
defaultModel,
|
|
1704
|
+
defaultTools,
|
|
1705
|
+
defaultMiddleware,
|
|
1706
|
+
defaultInterruptOn,
|
|
1707
|
+
subagents,
|
|
1708
|
+
generalPurposeAgent,
|
|
1709
|
+
taskDescription
|
|
1710
|
+
} = options;
|
|
1711
|
+
const { agents: subagentGraphs, descriptions: subagentDescriptions } = getSubagents({
|
|
1712
|
+
defaultModel,
|
|
1713
|
+
defaultTools,
|
|
1714
|
+
defaultMiddleware,
|
|
1715
|
+
defaultInterruptOn,
|
|
1716
|
+
subagents,
|
|
1717
|
+
generalPurposeAgent
|
|
1718
|
+
});
|
|
1719
|
+
const finalTaskDescription = taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions);
|
|
1720
|
+
return tool3(
|
|
1721
|
+
async (input, config) => {
|
|
1722
|
+
const { description, subagent_type } = input;
|
|
1723
|
+
if (!(subagent_type in subagentGraphs)) {
|
|
1724
|
+
const allowedTypes = Object.keys(subagentGraphs).map((k) => `\`${k}\``).join(", ");
|
|
1725
|
+
throw new Error(
|
|
1726
|
+
`Error: invoked agent of type ${subagent_type}, the only allowed types are ${allowedTypes}`
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
const subagent = subagentGraphs[subagent_type];
|
|
1730
|
+
const currentState = getCurrentTaskInput2();
|
|
1731
|
+
const subagentState = filterStateForSubagent(currentState);
|
|
1732
|
+
subagentState.messages = [new HumanMessage({ content: description })];
|
|
1733
|
+
const result = await subagent.invoke(subagentState, config);
|
|
1734
|
+
if (!config.toolCall?.id) {
|
|
1735
|
+
throw new Error("Tool call ID is required for subagent invocation");
|
|
1736
|
+
}
|
|
1737
|
+
return returnCommandWithStateUpdate(result, config.toolCall.id);
|
|
1738
|
+
},
|
|
1739
|
+
{
|
|
1740
|
+
name: "task",
|
|
1741
|
+
description: finalTaskDescription,
|
|
1742
|
+
schema: z4.object({
|
|
1743
|
+
description: z4.string().describe("The task to execute with the selected agent"),
|
|
1744
|
+
subagent_type: z4.string().describe(
|
|
1745
|
+
`Name of the agent to use. Available: ${Object.keys(
|
|
1746
|
+
subagentGraphs
|
|
1747
|
+
).join(", ")}`
|
|
1748
|
+
)
|
|
1749
|
+
})
|
|
1750
|
+
}
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
function createSubAgentMiddleware(options) {
|
|
1754
|
+
const {
|
|
1755
|
+
defaultModel,
|
|
1756
|
+
defaultTools = [],
|
|
1757
|
+
defaultMiddleware = null,
|
|
1758
|
+
defaultInterruptOn = null,
|
|
1759
|
+
subagents = [],
|
|
1760
|
+
systemPrompt = TASK_SYSTEM_PROMPT,
|
|
1761
|
+
generalPurposeAgent = true,
|
|
1762
|
+
taskDescription = null
|
|
1763
|
+
} = options;
|
|
1764
|
+
const taskTool = createTaskTool({
|
|
1765
|
+
defaultModel,
|
|
1766
|
+
defaultTools,
|
|
1767
|
+
defaultMiddleware,
|
|
1768
|
+
defaultInterruptOn,
|
|
1769
|
+
subagents,
|
|
1770
|
+
generalPurposeAgent,
|
|
1771
|
+
taskDescription
|
|
1772
|
+
});
|
|
1773
|
+
return createMiddleware2({
|
|
1774
|
+
name: "subAgentMiddleware",
|
|
1775
|
+
tools: [taskTool],
|
|
1776
|
+
wrapModelCall: async (request, handler) => {
|
|
1777
|
+
if (systemPrompt !== null) {
|
|
1778
|
+
const currentPrompt = request.systemPrompt || "";
|
|
1779
|
+
const newPrompt = currentPrompt ? `${currentPrompt}
|
|
1780
|
+
|
|
1781
|
+
${systemPrompt}` : systemPrompt;
|
|
1782
|
+
return handler({
|
|
1783
|
+
...request,
|
|
1784
|
+
systemPrompt: newPrompt
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
return handler(request);
|
|
1788
|
+
}
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
// src/deep_agent_new/middleware/patch_tool_calls.ts
|
|
1793
|
+
import {
|
|
1794
|
+
createMiddleware as createMiddleware3,
|
|
1795
|
+
ToolMessage as ToolMessage3,
|
|
1796
|
+
AIMessage
|
|
1797
|
+
} from "langchain";
|
|
1798
|
+
import { RemoveMessage } from "@langchain/core/messages";
|
|
1799
|
+
import { REMOVE_ALL_MESSAGES } from "@langchain/langgraph";
|
|
1800
|
+
function createPatchToolCallsMiddleware() {
|
|
1801
|
+
return createMiddleware3({
|
|
1802
|
+
name: "patchToolCallsMiddleware",
|
|
1803
|
+
beforeAgent: async (state) => {
|
|
1804
|
+
const messages = state.messages;
|
|
1805
|
+
if (!messages || messages.length === 0) {
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
const patchedMessages = [];
|
|
1809
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1810
|
+
const msg = messages[i];
|
|
1811
|
+
patchedMessages.push(msg);
|
|
1812
|
+
if (AIMessage.isInstance(msg) && msg.tool_calls != null) {
|
|
1813
|
+
for (const toolCall of msg.tool_calls) {
|
|
1814
|
+
const correspondingToolMsg = messages.slice(i).find(
|
|
1815
|
+
(m) => ToolMessage3.isInstance(m) && m.tool_call_id === toolCall.id
|
|
1816
|
+
);
|
|
1817
|
+
if (!correspondingToolMsg) {
|
|
1818
|
+
const toolMsg = `Tool call ${toolCall.name} with id ${toolCall.id} was cancelled - another message came in before it could be completed.`;
|
|
1819
|
+
patchedMessages.push(
|
|
1820
|
+
new ToolMessage3({
|
|
1821
|
+
content: toolMsg,
|
|
1822
|
+
name: toolCall.name,
|
|
1823
|
+
tool_call_id: toolCall.id
|
|
1824
|
+
})
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return {
|
|
1831
|
+
messages: [
|
|
1832
|
+
new RemoveMessage({ id: REMOVE_ALL_MESSAGES }),
|
|
1833
|
+
...patchedMessages
|
|
1834
|
+
]
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// src/deep_agent_new/backends/filesystem.ts
|
|
1841
|
+
import * as fsSync from "fs";
|
|
1842
|
+
import fg from "fast-glob";
|
|
1843
|
+
import micromatch2 from "micromatch";
|
|
1844
|
+
var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
|
|
1845
|
+
|
|
1846
|
+
// src/deep_agent_new/middleware/todos.ts
|
|
1847
|
+
import { Command as Command3 } from "@langchain/langgraph";
|
|
1848
|
+
import { z as z5 } from "zod";
|
|
1849
|
+
import { createMiddleware as createMiddleware4, tool as tool4, ToolMessage as ToolMessage4 } from "langchain";
|
|
1850
|
+
var WRITE_TODOS_DESCRIPTION = `Use this tool to create and manage a structured task list for your current work session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
|
|
1851
|
+
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
1852
|
+
Only use this tool if you think it will be helpful in staying organized. If the user's request is trivial and takes less than 3 steps, it is better to NOT use this tool and just do the taks directly.
|
|
1853
|
+
|
|
1854
|
+
## When to Use This Tool
|
|
1855
|
+
Use this tool in these scenarios:
|
|
1856
|
+
|
|
1857
|
+
1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
1858
|
+
2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
1859
|
+
3. User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
1860
|
+
4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
1861
|
+
5. The plan may need future revisions or updates based on results from the first few steps. Keeping track of this in a list is helpful.
|
|
1862
|
+
|
|
1863
|
+
## How to Use This Tool
|
|
1864
|
+
1. When you start working on a task - Mark it as in_progress BEFORE beginning work.
|
|
1865
|
+
2. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation.
|
|
1866
|
+
3. You can also update future tasks, such as deleting them if they are no longer necessary, or adding new tasks that are necessary. Don't change previously completed tasks.
|
|
1867
|
+
4. You can make several updates to the todo list at once. For example, when you complete a task, you can mark the next task you need to start as in_progress.
|
|
1868
|
+
|
|
1869
|
+
## When NOT to Use This Tool
|
|
1870
|
+
It is important to skip using this tool when:
|
|
1871
|
+
1. There is only a single, straightforward task
|
|
1872
|
+
2. The task is trivial and tracking it provides no benefit
|
|
1873
|
+
3. The task can be completed in less than 3 trivial steps
|
|
1874
|
+
4. The task is purely conversational or informational
|
|
1875
|
+
|
|
1876
|
+
## Examples of When to Use the Todo List
|
|
1877
|
+
|
|
1878
|
+
<example>
|
|
1879
|
+
User: I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when you're done!
|
|
1880
|
+
Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
|
|
1881
|
+
*Creates todo list with the following items:*
|
|
1882
|
+
1. Create dark mode toggle component in Settings page
|
|
1883
|
+
2. Add dark mode state management (context/store)
|
|
1884
|
+
3. Implement CSS-in-JS styles for dark theme
|
|
1885
|
+
4. Update existing components to support theme switching
|
|
1886
|
+
5. Run tests and build process, addressing any failures or errors that occur
|
|
1887
|
+
*Begins working on the first task*
|
|
1888
|
+
|
|
1889
|
+
<reasoning>
|
|
1890
|
+
The assistant used the todo list because:
|
|
1891
|
+
1. Adding dark mode in it of itself is a multi-step feature requiring UI, state management, and styling changes
|
|
1892
|
+
2. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
|
|
1893
|
+
3. Both of the user's requests are complex and require multiple steps to complete.
|
|
1894
|
+
</reasoning>
|
|
1895
|
+
</example>
|
|
1896
|
+
|
|
1897
|
+
<example>
|
|
1898
|
+
User: Help me plan a comprehensive marketing campaign for our new product launch, including social media, email marketing, and press releases.
|
|
1899
|
+
Assistant: I'll help you plan a comprehensive marketing campaign for your product launch. Let me create a todo list to organize all the components.
|
|
1900
|
+
*Creates todo list with the following items:*
|
|
1901
|
+
1. Define target audience and key messaging
|
|
1902
|
+
2. Create social media content calendar and posting schedule
|
|
1903
|
+
3. Design email marketing sequence and automation flows
|
|
1904
|
+
4. Draft press release and identify media contacts
|
|
1905
|
+
5. Set up tracking and analytics for campaign performance
|
|
1906
|
+
6. Create visual assets and brand materials
|
|
1907
|
+
7. Coordinate launch timeline and deliverables
|
|
1908
|
+
*Begins working on the first task*
|
|
1909
|
+
|
|
1910
|
+
<reasoning>
|
|
1911
|
+
The assistant used the todo list because:
|
|
1912
|
+
1. Marketing campaign planning involves multiple distinct channels and activities
|
|
1913
|
+
2. Each component requires careful coordination and planning
|
|
1914
|
+
3. The systematic approach ensures all aspects of the launch are covered
|
|
1915
|
+
</reasoning>
|
|
1916
|
+
</example>
|
|
1917
|
+
|
|
1918
|
+
<example>
|
|
1919
|
+
User: Help me rename the function getCwd to getCurrentWorkingDirectory across my project
|
|
1920
|
+
Assistant: Let me first search through your codebase to find all occurrences of 'getCwd'.
|
|
1921
|
+
*Uses search tools to locate all instances of getCwd in the codebase*
|
|
1922
|
+
Assistant: I've found 15 instances of 'getCwd' across 8 different files. Let me create a todo list to track these changes.
|
|
1923
|
+
*Creates todo list with specific items for each file that needs updating*
|
|
1924
|
+
|
|
1925
|
+
<reasoning>
|
|
1926
|
+
The assistant used the todo list because:
|
|
1927
|
+
1. The assistant searched to understand the scope of the task
|
|
1928
|
+
2. Upon finding multiple occurrences across different files, it determined this was a complex task with multiple steps (>3)
|
|
1929
|
+
3. The todo list helps ensure every instance is tracked and updated systematically
|
|
1930
|
+
4. This approach prevents missing any occurrences and maintains consistency.
|
|
1931
|
+
</reasoning>
|
|
1932
|
+
</example>
|
|
1933
|
+
|
|
1934
|
+
<example>
|
|
1935
|
+
User: I need help organizing my home office: decluttering the desk, setting up a filing system, organizing digital files, and creating a daily workflow.
|
|
1936
|
+
Assistant: I'll help you organize your home office systematically. Let me create a todo list to track each area of organization.
|
|
1937
|
+
*Creates todo list with items like: 1) Sort and declutter desk items, 2) Set up physical filing system for documents, 3) Organize digital files and folders, 4) Create daily workflow and productivity systems, 5) Establish maintenance routines*
|
|
1938
|
+
Let's start with decluttering your desk space to create a clean foundation for the other organizational systems.
|
|
1576
1939
|
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1940
|
+
<reasoning>
|
|
1941
|
+
The assistant used the todo list because:
|
|
1942
|
+
1. The user provided multiple distinct organizational tasks
|
|
1943
|
+
2. Each area requires different approaches and careful planning
|
|
1944
|
+
3. Home organization benefits from systematic, step-by-step completion
|
|
1945
|
+
4. Progress tracking ensures all areas are addressed thoroughly
|
|
1946
|
+
</reasoning>
|
|
1947
|
+
</example>
|
|
1580
1948
|
|
|
1581
|
-
##
|
|
1949
|
+
## Examples of When NOT to Use the Todo List
|
|
1582
1950
|
|
|
1583
|
-
|
|
1584
|
-
|
|
1951
|
+
<example>
|
|
1952
|
+
User: How do I print 'Hello World' in Python?
|
|
1953
|
+
Assistant: In Python, you can print "Hello World" with this simple code:
|
|
1585
1954
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1955
|
+
\`\`\`python
|
|
1956
|
+
print("Hello World")
|
|
1957
|
+
\`\`\`
|
|
1588
1958
|
|
|
1589
|
-
|
|
1590
|
-
var BUILTIN_TOOLS = [
|
|
1591
|
-
writeTodos,
|
|
1592
|
-
readFile,
|
|
1593
|
-
writeFile,
|
|
1594
|
-
editFile,
|
|
1595
|
-
ls
|
|
1596
|
-
];
|
|
1597
|
-
function createDeepAgent(params = {}) {
|
|
1598
|
-
const {
|
|
1599
|
-
tools = [],
|
|
1600
|
-
instructions,
|
|
1601
|
-
model = getModelLattice("default")?.client,
|
|
1602
|
-
subagents = []
|
|
1603
|
-
} = params;
|
|
1604
|
-
if (!model) {
|
|
1605
|
-
throw new Error("Model not found");
|
|
1606
|
-
}
|
|
1607
|
-
const stateSchema = params.stateSchema ? DeepAgentState.extend(params.stateSchema.shape) : DeepAgentState;
|
|
1608
|
-
const allTools = [...BUILTIN_TOOLS, ...tools];
|
|
1609
|
-
if (subagents.length > 0) {
|
|
1610
|
-
const toolsMap = {};
|
|
1611
|
-
for (const tool5 of allTools) {
|
|
1612
|
-
if (tool5.name) {
|
|
1613
|
-
toolsMap[tool5.name] = tool5;
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
const taskTool = createTaskTool({
|
|
1617
|
-
subagents,
|
|
1618
|
-
tools: toolsMap,
|
|
1619
|
-
model,
|
|
1620
|
-
stateSchema
|
|
1621
|
-
});
|
|
1622
|
-
allTools.push(taskTool);
|
|
1623
|
-
}
|
|
1624
|
-
const finalInstructions = instructions ? instructions + BASE_PROMPT : BASE_PROMPT;
|
|
1625
|
-
return createReactAgent2({
|
|
1626
|
-
llm: model,
|
|
1627
|
-
tools: allTools,
|
|
1628
|
-
stateSchema,
|
|
1629
|
-
prompt: finalInstructions,
|
|
1630
|
-
checkpointer: getCheckpointSaver("default")
|
|
1631
|
-
});
|
|
1632
|
-
}
|
|
1959
|
+
This will output the text "Hello World" to the console when executed.</assistant>
|
|
1633
1960
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
*
|
|
1639
|
-
* @param agentLattice Agent Lattice对象
|
|
1640
|
-
* @param params Agent构建参数
|
|
1641
|
-
* @returns 返回CompiledGraph对象
|
|
1642
|
-
*/
|
|
1643
|
-
build(agentLattice, params) {
|
|
1644
|
-
const tools = params.tools.map((t) => {
|
|
1645
|
-
const toolClient = getToolClient(t.key);
|
|
1646
|
-
return toolClient;
|
|
1647
|
-
}).filter((tool5) => tool5 !== void 0);
|
|
1648
|
-
return createDeepAgent({
|
|
1649
|
-
tools,
|
|
1650
|
-
model: params.model,
|
|
1651
|
-
stateSchema: params.stateSchema,
|
|
1652
|
-
instructions: params.prompt,
|
|
1653
|
-
subagents: params.subAgents.map((sa) => sa.config)
|
|
1654
|
-
});
|
|
1655
|
-
}
|
|
1656
|
-
};
|
|
1961
|
+
<reasoning>
|
|
1962
|
+
The assistant did not use the todo list because this is a single, trivial task that can be completed in one step. There's no need to track multiple tasks or steps for such a straightforward request.
|
|
1963
|
+
</reasoning>
|
|
1964
|
+
</example>
|
|
1657
1965
|
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
END as END2,
|
|
1662
|
-
GraphInterrupt as GraphInterrupt2,
|
|
1663
|
-
START,
|
|
1664
|
-
StateGraph,
|
|
1665
|
-
messagesStateReducer
|
|
1666
|
-
} from "@langchain/langgraph";
|
|
1667
|
-
import {
|
|
1668
|
-
HumanMessage as HumanMessage2
|
|
1669
|
-
} from "@langchain/core/messages";
|
|
1670
|
-
import { z as z12 } from "zod";
|
|
1671
|
-
|
|
1672
|
-
// src/logger/Logger.ts
|
|
1673
|
-
import pino from "pino";
|
|
1674
|
-
import "pino-pretty";
|
|
1675
|
-
import "pino-roll";
|
|
1676
|
-
var PinoLoggerFactory = class _PinoLoggerFactory {
|
|
1677
|
-
constructor() {
|
|
1678
|
-
const isProd = process.env.NODE_ENV === "production";
|
|
1679
|
-
const loggerConfig = {
|
|
1680
|
-
// 自定义时间戳格式
|
|
1681
|
-
timestamp: () => `,"@timestamp":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
|
|
1682
|
-
// 关闭默认的时间戳键
|
|
1683
|
-
base: {
|
|
1684
|
-
"@version": "1",
|
|
1685
|
-
app_name: "lattice",
|
|
1686
|
-
service_name: "lattice/graph-server",
|
|
1687
|
-
thread_name: "main",
|
|
1688
|
-
logger_name: "lattice-graph-logger"
|
|
1689
|
-
},
|
|
1690
|
-
formatters: {
|
|
1691
|
-
level: (label, number) => {
|
|
1692
|
-
return {
|
|
1693
|
-
level: label.toUpperCase(),
|
|
1694
|
-
level_value: number * 1e3
|
|
1695
|
-
};
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
};
|
|
1699
|
-
if (isProd) {
|
|
1700
|
-
try {
|
|
1701
|
-
this.pinoLogger = pino(
|
|
1702
|
-
loggerConfig,
|
|
1703
|
-
pino.transport({
|
|
1704
|
-
target: "pino-roll",
|
|
1705
|
-
options: {
|
|
1706
|
-
file: "./logs/fin_ai_graph_server",
|
|
1707
|
-
frequency: "daily",
|
|
1708
|
-
mkdir: true
|
|
1709
|
-
}
|
|
1710
|
-
})
|
|
1711
|
-
);
|
|
1712
|
-
} catch (error) {
|
|
1713
|
-
console.error(
|
|
1714
|
-
"\u65E0\u6CD5\u521D\u59CB\u5316 pino-roll \u65E5\u5FD7\u8BB0\u5F55\u5668\uFF0C\u56DE\u9000\u5230\u63A7\u5236\u53F0\u65E5\u5FD7",
|
|
1715
|
-
error
|
|
1716
|
-
);
|
|
1717
|
-
this.pinoLogger = pino({
|
|
1718
|
-
...loggerConfig,
|
|
1719
|
-
transport: {
|
|
1720
|
-
target: "pino-pretty",
|
|
1721
|
-
options: {
|
|
1722
|
-
colorize: true
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
});
|
|
1726
|
-
}
|
|
1727
|
-
} else {
|
|
1728
|
-
this.pinoLogger = pino({
|
|
1729
|
-
...loggerConfig,
|
|
1730
|
-
transport: {
|
|
1731
|
-
target: "pino-pretty",
|
|
1732
|
-
options: {
|
|
1733
|
-
colorize: true
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
static getInstance() {
|
|
1740
|
-
if (!_PinoLoggerFactory.instance) {
|
|
1741
|
-
_PinoLoggerFactory.instance = new _PinoLoggerFactory();
|
|
1742
|
-
}
|
|
1743
|
-
return _PinoLoggerFactory.instance;
|
|
1744
|
-
}
|
|
1745
|
-
getPinoLogger() {
|
|
1746
|
-
return this.pinoLogger;
|
|
1747
|
-
}
|
|
1748
|
-
};
|
|
1749
|
-
var Logger = class _Logger {
|
|
1750
|
-
constructor(options) {
|
|
1751
|
-
this.context = options?.context || {};
|
|
1752
|
-
this.name = options?.name || "lattice-graph-logger";
|
|
1753
|
-
this.serviceName = options?.serviceName || "lattice/graph-server";
|
|
1754
|
-
}
|
|
1755
|
-
/**
|
|
1756
|
-
* 获取合并了上下文的日志对象
|
|
1757
|
-
* @param additionalContext 额外的上下文数据
|
|
1758
|
-
* @returns 带有上下文的pino日志对象
|
|
1759
|
-
*/
|
|
1760
|
-
getContextualLogger(additionalContext) {
|
|
1761
|
-
const pinoLogger = PinoLoggerFactory.getInstance().getPinoLogger();
|
|
1762
|
-
const contextObj = {
|
|
1763
|
-
"x-user-id": this.context["x-user-id"] || "",
|
|
1764
|
-
"x-tenant-id": this.context["x-tenant-id"] || "",
|
|
1765
|
-
"x-request-id": this.context["x-request-id"] || "",
|
|
1766
|
-
"x-task-id": this.context["x-task-id"] || "",
|
|
1767
|
-
"x-thread-id": this.context["x-thread-id"] || "",
|
|
1768
|
-
service_name: this.serviceName,
|
|
1769
|
-
logger_name: this.name,
|
|
1770
|
-
...additionalContext
|
|
1771
|
-
};
|
|
1772
|
-
return pinoLogger.child(contextObj);
|
|
1773
|
-
}
|
|
1774
|
-
info(msg, obj) {
|
|
1775
|
-
this.getContextualLogger(obj).info(msg);
|
|
1776
|
-
}
|
|
1777
|
-
error(msg, obj) {
|
|
1778
|
-
this.getContextualLogger(obj).error(msg);
|
|
1779
|
-
}
|
|
1780
|
-
warn(msg, obj) {
|
|
1781
|
-
this.getContextualLogger(obj).warn(msg);
|
|
1782
|
-
}
|
|
1783
|
-
debug(msg, obj) {
|
|
1784
|
-
this.getContextualLogger(obj).debug(msg);
|
|
1785
|
-
}
|
|
1786
|
-
/**
|
|
1787
|
-
* 更新Logger实例的上下文
|
|
1788
|
-
*/
|
|
1789
|
-
updateContext(context) {
|
|
1790
|
-
this.context = {
|
|
1791
|
-
...this.context,
|
|
1792
|
-
...context
|
|
1793
|
-
};
|
|
1794
|
-
}
|
|
1795
|
-
/**
|
|
1796
|
-
* 创建一个新的Logger实例,继承当前Logger的上下文
|
|
1797
|
-
*/
|
|
1798
|
-
child(options) {
|
|
1799
|
-
return new _Logger({
|
|
1800
|
-
name: options.name || this.name,
|
|
1801
|
-
serviceName: options.serviceName || this.serviceName,
|
|
1802
|
-
context: {
|
|
1803
|
-
...this.context,
|
|
1804
|
-
...options.context
|
|
1805
|
-
}
|
|
1806
|
-
});
|
|
1807
|
-
}
|
|
1808
|
-
};
|
|
1966
|
+
<example>
|
|
1967
|
+
User: What's the best way to water houseplants?
|
|
1968
|
+
Assistant: The best way to water houseplants depends on the specific plant type, but here are some general guidelines:
|
|
1809
1969
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
const { think_id = v4(), content, title, status, type, data } = think;
|
|
1815
|
-
const contents = type ? [title, content, genUIMarkdown(type, data)] : [title, content];
|
|
1816
|
-
const message = {
|
|
1817
|
-
messages: [
|
|
1818
|
-
new ToolMessage6({
|
|
1819
|
-
content: contents.filter((item) => item).join("\n\n"),
|
|
1820
|
-
tool_call_id: config.configurable?.run_id + "_tool_" + think_id,
|
|
1821
|
-
status: status || "success"
|
|
1822
|
-
})
|
|
1823
|
-
]
|
|
1824
|
-
};
|
|
1825
|
-
return message;
|
|
1826
|
-
};
|
|
1970
|
+
- Check soil moisture by inserting your finger about 1-2 inches into the soil
|
|
1971
|
+
- Water thoroughly when the top inch feels dry, allowing water to drain from the bottom
|
|
1972
|
+
- Use room temperature water and water in the morning when possible
|
|
1973
|
+
- Avoid overwatering, which is more harmful than underwatering for most plants
|
|
1827
1974
|
|
|
1828
|
-
|
|
1829
|
-
import { AIMessage } from "@langchain/core/messages";
|
|
1830
|
-
var returnAIResponse = (config, content, type, data) => {
|
|
1831
|
-
const contents = type ? [content, genUIMarkdown(type, data)].join("\n") : content;
|
|
1832
|
-
const message = {
|
|
1833
|
-
messages: [
|
|
1834
|
-
new AIMessage({
|
|
1835
|
-
content: contents
|
|
1836
|
-
})
|
|
1837
|
-
// {
|
|
1838
|
-
// id: v4(),
|
|
1839
|
-
// role: "ai",
|
|
1840
|
-
// content: contents,
|
|
1841
|
-
// type: "message",
|
|
1842
|
-
// }, // 旧的message
|
|
1843
|
-
]
|
|
1844
|
-
};
|
|
1845
|
-
return message;
|
|
1846
|
-
};
|
|
1975
|
+
Different plants have different needs, so it's important to research your specific varieties for optimal care.
|
|
1847
1976
|
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
var getLastHumanMessageData = (messages) => {
|
|
1853
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1854
|
-
const message = messages[i];
|
|
1855
|
-
if (isHumanMessage(message)) {
|
|
1856
|
-
const files = message?.additional_kwargs?.files;
|
|
1857
|
-
return {
|
|
1858
|
-
files,
|
|
1859
|
-
content: message?.content
|
|
1860
|
-
};
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
return null;
|
|
1864
|
-
};
|
|
1977
|
+
<reasoning>
|
|
1978
|
+
The assistant did not use the todo list because this is an informational request with no actual tasks to complete. The user is simply asking for advice and explanation, not for the assistant to perform multiple steps or activities.
|
|
1979
|
+
</reasoning>
|
|
1980
|
+
</example>
|
|
1865
1981
|
|
|
1866
|
-
|
|
1867
|
-
|
|
1982
|
+
<example>
|
|
1983
|
+
User: Can you add a comment to the calculateTotal function to explain what it does?
|
|
1984
|
+
Assistant: Sure, let me add a comment to the calculateTotal function to explain what it does.
|
|
1985
|
+
* Uses the Edit tool to add a comment to the calculateTotal function *
|
|
1868
1986
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
var memory2 = new MemorySaver2();
|
|
1874
|
-
var _MemoryManager = class _MemoryManager {
|
|
1875
|
-
static getInstance() {
|
|
1876
|
-
return _MemoryManager.instance;
|
|
1877
|
-
}
|
|
1878
|
-
};
|
|
1879
|
-
_MemoryManager.instance = memory2;
|
|
1880
|
-
var MemoryManager = _MemoryManager;
|
|
1881
|
-
|
|
1882
|
-
// src/createPlanExecuteAgent.ts
|
|
1883
|
-
import { createReactAgent as createReactAgent3 } from "@langchain/langgraph/prebuilt";
|
|
1884
|
-
var PlanExecuteState = Annotation.Root({
|
|
1885
|
-
// 输入
|
|
1886
|
-
input: Annotation({
|
|
1887
|
-
reducer: (x, y) => y ?? x ?? ""
|
|
1888
|
-
}),
|
|
1889
|
-
// 计划步骤列表
|
|
1890
|
-
plan: Annotation({
|
|
1891
|
-
reducer: (x, y) => y ?? x ?? []
|
|
1892
|
-
}),
|
|
1893
|
-
// 已执行的步骤 [步骤名称, 执行结果]
|
|
1894
|
-
pastSteps: Annotation({
|
|
1895
|
-
reducer: (x, y) => x.concat(y),
|
|
1896
|
-
default: () => []
|
|
1897
|
-
}),
|
|
1898
|
-
// 最终响应
|
|
1899
|
-
response: Annotation({
|
|
1900
|
-
reducer: (x, y) => y ?? x
|
|
1901
|
-
}),
|
|
1902
|
-
// 错误信息
|
|
1903
|
-
error: Annotation({
|
|
1904
|
-
reducer: (x, y) => y ?? x
|
|
1905
|
-
}),
|
|
1906
|
-
// 继承基础状态
|
|
1907
|
-
"x-tenant-id": Annotation(),
|
|
1908
|
-
messages: Annotation({
|
|
1909
|
-
reducer: messagesStateReducer,
|
|
1910
|
-
default: () => []
|
|
1911
|
-
})
|
|
1912
|
-
});
|
|
1913
|
-
var planSchema = z12.object({
|
|
1914
|
-
steps: z12.array(z12.string()).describe("\u9700\u8981\u6267\u884C\u7684\u6B65\u9AA4\u5217\u8868\uFF0C\u5E94\u6309\u7167\u6267\u884C\u987A\u5E8F\u6392\u5E8F")
|
|
1915
|
-
});
|
|
1916
|
-
var responseSchema = z12.object({
|
|
1917
|
-
response: z12.string().describe("\u7ED9\u7528\u6237\u7684\u6700\u7EC8\u54CD\u5E94\uFF0C\u5982\u679C\u4E0D\u9700\u8981\u6267\u884C\u6B65\u9AA4\uFF0C\u5219\u8FD4\u56DE\u8FD9\u4E2A\u5B57\u6BB5")
|
|
1918
|
-
});
|
|
1919
|
-
function createPlanExecuteAgent(config) {
|
|
1920
|
-
const {
|
|
1921
|
-
name,
|
|
1922
|
-
description,
|
|
1923
|
-
tools: executorTools = [],
|
|
1924
|
-
checkpointer,
|
|
1925
|
-
llmManager,
|
|
1926
|
-
logger,
|
|
1927
|
-
maxSteps = 10,
|
|
1928
|
-
prompt
|
|
1929
|
-
} = config;
|
|
1930
|
-
const agentLogger = logger || new Logger({ name: `PlanExecuteAgent:${name}` });
|
|
1931
|
-
const llm = llmManager || getModelLattice("default")?.client;
|
|
1932
|
-
if (!llm) {
|
|
1933
|
-
throw new Error("LLM not found");
|
|
1934
|
-
}
|
|
1935
|
-
agentLogger.info(`\u521D\u59CB\u5316Plan-Execute\u4EE3\u7406: ${name}`, {
|
|
1936
|
-
description,
|
|
1937
|
-
maxSteps,
|
|
1938
|
-
toolsCount: executorTools.length
|
|
1939
|
-
});
|
|
1940
|
-
const agentExecutor = createReactAgent3({
|
|
1941
|
-
tools: executorTools,
|
|
1942
|
-
llm
|
|
1943
|
-
});
|
|
1944
|
-
async function planStep(state, config2) {
|
|
1945
|
-
const input = state.input || getLastHumanMessageData(state.messages)?.content;
|
|
1946
|
-
const plannerPrompt = `${prompt || ""}
|
|
1947
|
-
\u9488\u5BF9\u7ED9\u5B9A\u7684\u76EE\u6807\uFF0C\u5236\u5B9A\u4E00\u4E2A\u7B80\u5355\u7684\u5206\u6B65\u8BA1\u5212\u3002
|
|
1948
|
-
\u8FD9\u4E2A\u8BA1\u5212\u5E94\u8BE5\u5305\u542B\u72EC\u7ACB\u7684\u4EFB\u52A1\uFF0C\u5982\u679C\u6B63\u786E\u6267\u884C\u5C06\u4F1A\u5F97\u5230\u6B63\u786E\u7684\u7B54\u6848\u3002\u4E0D\u8981\u6DFB\u52A0\u591A\u4F59\u7684\u6B65\u9AA4\u3002
|
|
1949
|
-
\u6700\u540E\u4E00\u6B65\u7684\u7ED3\u679C\u5E94\u8BE5\u662F\u6700\u7EC8\u7B54\u6848\u3002\u786E\u4FDD\u6BCF\u4E00\u6B65\u90FD\u6709\u6240\u9700\u7684\u6240\u6709\u4FE1\u606F - \u4E0D\u8981\u8DF3\u8FC7\u6B65\u9AA4\u3002
|
|
1987
|
+
<reasoning>
|
|
1988
|
+
The assistant did not use the todo list because this is a single, straightforward task confined to one location. Adding a comment doesn't require tracking multiple steps or systematic organization.
|
|
1989
|
+
</reasoning>
|
|
1990
|
+
</example>
|
|
1950
1991
|
|
|
1951
|
-
|
|
1952
|
-
|
|
1992
|
+
<example>
|
|
1993
|
+
User: What time is it in Tokyo right now?
|
|
1994
|
+
Assistant: I'll check the current time in Tokyo for you.
|
|
1953
1995
|
|
|
1954
|
-
|
|
1955
|
-
try {
|
|
1956
|
-
const planResult = await llm.invokeWithStructuredOutput(
|
|
1957
|
-
[new HumanMessage2(plannerPrompt)],
|
|
1958
|
-
planSchema
|
|
1959
|
-
);
|
|
1960
|
-
const { messages } = await returnToolResponse(config2, {
|
|
1961
|
-
title: "\u6211\u7684\u8BA1\u5212",
|
|
1962
|
-
content: planResult.steps.map((step) => step).join("\n\n"),
|
|
1963
|
-
status: "success"
|
|
1964
|
-
});
|
|
1965
|
-
agentLogger.info("\u8BA1\u5212\u5236\u5B9A\u5B8C\u6210", { plan: planResult.steps });
|
|
1966
|
-
return {
|
|
1967
|
-
input,
|
|
1968
|
-
plan: planResult.steps,
|
|
1969
|
-
error: void 0,
|
|
1970
|
-
messages
|
|
1971
|
-
};
|
|
1972
|
-
} catch (error) {
|
|
1973
|
-
const errorMsg = `\u8BA1\u5212\u5236\u5B9A\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`;
|
|
1974
|
-
agentLogger.error(errorMsg, { error });
|
|
1975
|
-
return {
|
|
1976
|
-
error: errorMsg
|
|
1977
|
-
};
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
async function executeStep(state, config2) {
|
|
1981
|
-
if (!state.plan || state.plan.length === 0) {
|
|
1982
|
-
agentLogger.warn("\u6CA1\u6709\u53EF\u6267\u884C\u7684\u4EFB\u52A1");
|
|
1983
|
-
return { error: "\u6CA1\u6709\u53EF\u6267\u884C\u7684\u4EFB\u52A1" };
|
|
1984
|
-
}
|
|
1985
|
-
const currentTask = state.plan[0];
|
|
1986
|
-
agentLogger.info("\u5F00\u59CB\u6267\u884C\u4EFB\u52A1", { task: currentTask });
|
|
1987
|
-
try {
|
|
1988
|
-
const executorPrompt = `\u6267\u884C\u4EE5\u4E0B\u4EFB\u52A1\u5E76\u63D0\u4F9B\u7ED3\u679C\uFF1A
|
|
1996
|
+
*Searches for current time in Tokyo*
|
|
1989
1997
|
|
|
1990
|
-
|
|
1998
|
+
The current time in Tokyo, Japan is [current time]. Tokyo is in the Japan Standard Time (JST) zone, which is UTC+9.
|
|
1991
1999
|
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
2000
|
+
<reasoning>
|
|
2001
|
+
The assistant did not use the todo list because this is a single information lookup with immediate results. There are no multiple steps to track or organize, making the todo list unnecessary for this straightforward request.
|
|
2002
|
+
</reasoning>
|
|
2003
|
+
</example>
|
|
1995
2004
|
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
error: void 0
|
|
2007
|
-
};
|
|
2008
|
-
} catch (error) {
|
|
2009
|
-
if (error instanceof GraphInterrupt2) {
|
|
2010
|
-
throw error;
|
|
2011
|
-
}
|
|
2012
|
-
const errorMsg = `\u4EFB\u52A1\u6267\u884C\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`;
|
|
2013
|
-
agentLogger.error(errorMsg, { task: currentTask, error });
|
|
2014
|
-
return {
|
|
2015
|
-
error: errorMsg
|
|
2016
|
-
//messages,
|
|
2017
|
-
};
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
const responseTool = tool4(
|
|
2021
|
-
({ response }) => {
|
|
2022
|
-
return response;
|
|
2023
|
-
},
|
|
2024
|
-
{
|
|
2025
|
-
name: "response",
|
|
2026
|
-
description: "Respond to the user.",
|
|
2027
|
-
schema: responseSchema,
|
|
2028
|
-
returnDirect: true
|
|
2029
|
-
}
|
|
2030
|
-
);
|
|
2031
|
-
const planTool = tool4(
|
|
2032
|
-
({ steps }) => {
|
|
2033
|
-
return steps.join("\n\n");
|
|
2034
|
-
},
|
|
2035
|
-
{
|
|
2036
|
-
name: "plan",
|
|
2037
|
-
description: "This tool is used to plan the steps to follow.",
|
|
2038
|
-
schema: planSchema,
|
|
2039
|
-
returnDirect: true
|
|
2040
|
-
}
|
|
2041
|
-
);
|
|
2042
|
-
const replanAgent = createReactAgent3({
|
|
2043
|
-
tools: [responseTool, planTool],
|
|
2044
|
-
llm
|
|
2045
|
-
});
|
|
2046
|
-
async function replanStep(state, config2) {
|
|
2047
|
-
agentLogger.info("\u5F00\u59CB\u91CD\u65B0\u89C4\u5212");
|
|
2048
|
-
const replannerPrompt = `${prompt || ""}
|
|
2049
|
-
\u9488\u5BF9\u7ED9\u5B9A\u7684\u76EE\u6807\uFF0C\u6839\u636E\u5DF2\u6267\u884C\u7684\u6B65\u9AA4\u6765\u66F4\u65B0\u8BA1\u5212\u3002
|
|
2050
|
-
\u8FD9\u4E2A\u8BA1\u5212\u5E94\u8BE5\u5305\u542B\u72EC\u7ACB\u7684\u4EFB\u52A1\uFF0C\u5982\u679C\u6B63\u786E\u6267\u884C\u5C06\u4F1A\u5F97\u5230\u6B63\u786E\u7684\u7B54\u6848\u3002\u4E0D\u8981\u6DFB\u52A0\u591A\u4F59\u7684\u6B65\u9AA4\u3002
|
|
2051
|
-
\u6700\u540E\u4E00\u6B65\u7684\u7ED3\u679C\u5E94\u8BE5\u662F\u6700\u7EC8\u7B54\u6848\u3002\u786E\u4FDD\u6BCF\u4E00\u6B65\u90FD\u6709\u6240\u9700\u7684\u6240\u6709\u4FE1\u606F - \u4E0D\u8981\u8DF3\u8FC7\u6B65\u9AA4\u3002
|
|
2005
|
+
<example>
|
|
2006
|
+
User: I need to write a function that checks if a number is prime and then test it out.
|
|
2007
|
+
Assistant: I'll help you write a function that checks if a number is prime and then test it out.
|
|
2008
|
+
*Writes function that checks if a number is prime*
|
|
2009
|
+
*Tests the function*
|
|
2010
|
+
|
|
2011
|
+
<reasoning>
|
|
2012
|
+
Even though this is a multi-step task, it is very straightforward and can be completed in two trivial steps (which is less than 3 steps!). Using the todo list here is overkill and wastes time and tokens.
|
|
2013
|
+
</reasoning>
|
|
2014
|
+
</example>
|
|
2052
2015
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2016
|
+
<example>
|
|
2017
|
+
User: I want you to order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
|
|
2018
|
+
Assistant: I'll help you order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
|
|
2019
|
+
*Orders a pizza from Dominos*
|
|
2020
|
+
*Orders a burger from McDonald's*
|
|
2021
|
+
*Orders a salad from Subway*
|
|
2055
2022
|
|
|
2056
|
-
|
|
2057
|
-
|
|
2023
|
+
<reasoning>
|
|
2024
|
+
Even though this is a multi-step task, assuming the assistant has the ability to order from these restaurants, it is very straightforward and can be completed in three trivial tool calls.
|
|
2025
|
+
Using the todo list here is overkill and wastes time and tokens. These three tool calls should be made in parallel, in fact.
|
|
2026
|
+
</reasoning>
|
|
2027
|
+
</example>
|
|
2058
2028
|
|
|
2059
|
-
\u4F60\u76EE\u524D\u5DF2\u7ECF\u5B8C\u6210\u4E86\u4EE5\u4E0B\u6B65\u9AA4\uFF1A
|
|
2060
|
-
${state.pastSteps.map(([step, result]) => `${step}: ${result}`).join("\n")}
|
|
2061
2029
|
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2030
|
+
## Task States and Management
|
|
2031
|
+
|
|
2032
|
+
1. **Task States**: Use these states to track progress:
|
|
2033
|
+
- pending: Task not yet started
|
|
2034
|
+
- in_progress: Currently working on (you can have multiple tasks in_progress at a time if they are not related to each other and can be run in parallel)
|
|
2035
|
+
- completed: Task finished successfully
|
|
2036
|
+
|
|
2037
|
+
2. **Task Management**:
|
|
2038
|
+
- Update task status in real-time as you work
|
|
2039
|
+
- Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
|
|
2040
|
+
- Complete current tasks before starting new ones
|
|
2041
|
+
- Remove tasks that are no longer relevant from the list entirely
|
|
2042
|
+
- IMPORTANT: When you write this todo list, you should mark your first task (or tasks) as in_progress immediately!.
|
|
2043
|
+
- IMPORTANT: Unless all tasks are completed, you should always have at least one task in_progress to show the user that you are working on something.
|
|
2044
|
+
|
|
2045
|
+
3. **Task Completion Requirements**:
|
|
2046
|
+
- ONLY mark a task as completed when you have FULLY accomplished it
|
|
2047
|
+
- If you encounter errors, blockers, or cannot finish, keep the task as in_progress
|
|
2048
|
+
- When blocked, create a new task describing what needs to be resolved
|
|
2049
|
+
- Never mark a task as completed if:
|
|
2050
|
+
- There are unresolved issues or errors
|
|
2051
|
+
- Work is partial or incomplete
|
|
2052
|
+
- You encountered blockers that prevent completion
|
|
2053
|
+
- You couldn't find necessary resources or dependencies
|
|
2054
|
+
- Quality standards haven't been met
|
|
2055
|
+
|
|
2056
|
+
4. **Task Breakdown**:
|
|
2057
|
+
- Create specific, actionable items
|
|
2058
|
+
- Break complex tasks into smaller, manageable steps
|
|
2059
|
+
- Use clear, descriptive task names
|
|
2060
|
+
|
|
2061
|
+
Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully
|
|
2062
|
+
Remember: If you only need to make a few tool calls to complete a task, and it is clear what you need to do, it is better to just do the task directly and NOT call this tool at all.`;
|
|
2063
|
+
var TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT = `## \`write_todos\`
|
|
2064
|
+
|
|
2065
|
+
You have access to the \`write_todos\` tool to help you manage and plan complex objectives.
|
|
2066
|
+
Use this tool for complex objectives to ensure that you are tracking each necessary step and giving the user visibility into your progress.
|
|
2067
|
+
This tool is very helpful for planning complex objectives, and for breaking down these larger complex objectives into smaller steps.
|
|
2068
|
+
|
|
2069
|
+
It is critical that you mark todos as completed as soon as you are done with a step. Do not batch up multiple steps before marking them as completed.
|
|
2070
|
+
For simple objectives that only require a few steps, it is better to just complete the objective directly and NOT use this tool.
|
|
2071
|
+
Writing todos takes time and tokens, use it when it is helpful for managing complex many-step problems! But not for simple few-step requests.
|
|
2072
|
+
|
|
2073
|
+
## Important To-Do List Usage Notes to Remember
|
|
2074
|
+
- The \`write_todos\` tool should never be called multiple times in parallel.
|
|
2075
|
+
- Don't be afraid to revise the To-Do list as you go. New information may reveal new tasks that need to be done, or old tasks that are irrelevant.`;
|
|
2076
|
+
var TodoStatus = z5.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
|
|
2077
|
+
var TodoSchema = z5.object({
|
|
2078
|
+
content: z5.string().describe("Content of the todo item"),
|
|
2079
|
+
status: TodoStatus
|
|
2080
|
+
});
|
|
2081
|
+
var stateSchema = z5.object({ todos: z5.array(TodoSchema).default([]) });
|
|
2082
|
+
function todoListMiddleware(options) {
|
|
2083
|
+
const writeTodos = tool4(
|
|
2084
|
+
({ todos }, config) => {
|
|
2085
|
+
return new Command3({
|
|
2086
|
+
update: {
|
|
2087
|
+
todos,
|
|
2088
|
+
messages: [
|
|
2089
|
+
new ToolMessage4({
|
|
2090
|
+
content: genUIMarkdown("todo_list", todos),
|
|
2091
|
+
tool_call_id: config.toolCall?.id
|
|
2092
|
+
})
|
|
2093
|
+
]
|
|
2089
2094
|
}
|
|
2090
|
-
agentLogger.info("\u8BA1\u5212\u66F4\u65B0\u5B8C\u6210", { newPlan: toolCall.content });
|
|
2091
|
-
return {
|
|
2092
|
-
messages,
|
|
2093
|
-
plan: toolCall.content.split("\n\n"),
|
|
2094
|
-
error: void 0
|
|
2095
|
-
};
|
|
2096
|
-
} else {
|
|
2097
|
-
return {
|
|
2098
|
-
error: "\u91CD\u65B0\u89C4\u5212\u5931\u8D25"
|
|
2099
|
-
};
|
|
2100
|
-
}
|
|
2101
|
-
} catch (error) {
|
|
2102
|
-
if (error instanceof GraphInterrupt2) {
|
|
2103
|
-
throw error;
|
|
2104
|
-
}
|
|
2105
|
-
const errorMsg = `\u91CD\u65B0\u89C4\u5212\u5931\u8D25: ${error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"}`;
|
|
2106
|
-
agentLogger.error(errorMsg, { error });
|
|
2107
|
-
return {
|
|
2108
|
-
error: errorMsg
|
|
2109
|
-
};
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
function shouldEnd(state) {
|
|
2113
|
-
if (state.error) {
|
|
2114
|
-
agentLogger.warn("\u56E0\u9519\u8BEF\u7ED3\u675F\u6267\u884C", { error: state.error });
|
|
2115
|
-
return "end";
|
|
2116
|
-
}
|
|
2117
|
-
if (state.response) {
|
|
2118
|
-
agentLogger.info("\u4EFB\u52A1\u5B8C\u6210\uFF0C\u7ED3\u675F\u6267\u884C");
|
|
2119
|
-
return "end";
|
|
2120
|
-
}
|
|
2121
|
-
if (state.pastSteps.length >= maxSteps) {
|
|
2122
|
-
agentLogger.warn("\u8FBE\u5230\u6700\u5927\u6B65\u9AA4\u6570\u9650\u5236\uFF0C\u7ED3\u675F\u6267\u884C", {
|
|
2123
|
-
maxSteps,
|
|
2124
|
-
currentSteps: state.pastSteps.length
|
|
2125
2095
|
});
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2096
|
+
},
|
|
2097
|
+
{
|
|
2098
|
+
name: "write_todos",
|
|
2099
|
+
description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
|
|
2100
|
+
schema: z5.object({
|
|
2101
|
+
todos: z5.array(TodoSchema).describe("List of todo items to update")
|
|
2102
|
+
})
|
|
2131
2103
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2104
|
+
);
|
|
2105
|
+
return createMiddleware4({
|
|
2106
|
+
name: "todoListMiddleware",
|
|
2107
|
+
stateSchema,
|
|
2108
|
+
tools: [writeTodos],
|
|
2109
|
+
wrapModelCall: (request, handler) => handler({
|
|
2110
|
+
...request,
|
|
2111
|
+
systemPrompt: (request.systemPrompt ? `${request.systemPrompt}
|
|
2112
|
+
|
|
2113
|
+
` : "") + (options?.systemPrompt ?? TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT)
|
|
2114
|
+
})
|
|
2141
2115
|
});
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
// src/deep_agent_new/agent.ts
|
|
2119
|
+
var BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
|
|
2120
|
+
function createDeepAgent(params = {}) {
|
|
2121
|
+
const {
|
|
2122
|
+
model = "claude-sonnet-4-5-20250929",
|
|
2123
|
+
tools = [],
|
|
2124
|
+
systemPrompt,
|
|
2125
|
+
middleware: customMiddleware = [],
|
|
2126
|
+
subagents = [],
|
|
2127
|
+
responseFormat,
|
|
2128
|
+
contextSchema,
|
|
2129
|
+
checkpointer,
|
|
2130
|
+
store,
|
|
2131
|
+
backend,
|
|
2132
|
+
interruptOn,
|
|
2133
|
+
name
|
|
2134
|
+
} = params;
|
|
2135
|
+
const finalSystemPrompt = systemPrompt ? `${systemPrompt}
|
|
2136
|
+
|
|
2137
|
+
${BASE_PROMPT}` : BASE_PROMPT;
|
|
2138
|
+
const filesystemBackend = backend ? backend : (config) => new StateBackend(config);
|
|
2139
|
+
const middleware = [
|
|
2140
|
+
// Provides todo list management capabilities for tracking tasks
|
|
2141
|
+
todoListMiddleware(),
|
|
2142
|
+
// Enables filesystem operations and optional long-term memory storage
|
|
2143
|
+
createFilesystemMiddleware({ backend: filesystemBackend }),
|
|
2144
|
+
// Enables delegation to specialized subagents for complex tasks
|
|
2145
|
+
createSubAgentMiddleware({
|
|
2146
|
+
defaultModel: model,
|
|
2147
|
+
defaultTools: tools,
|
|
2148
|
+
defaultMiddleware: [
|
|
2149
|
+
// Subagent middleware: Todo list management
|
|
2150
|
+
todoListMiddleware(),
|
|
2151
|
+
// Subagent middleware: Filesystem operations
|
|
2152
|
+
createFilesystemMiddleware({
|
|
2153
|
+
backend: filesystemBackend
|
|
2154
|
+
}),
|
|
2155
|
+
// Subagent middleware: Automatic conversation summarization when token limits are approached
|
|
2156
|
+
summarizationMiddleware({
|
|
2157
|
+
model,
|
|
2158
|
+
trigger: { tokens: 17e4 },
|
|
2159
|
+
keep: { messages: 6 }
|
|
2160
|
+
}),
|
|
2161
|
+
// Subagent middleware: Anthropic prompt caching for improved performance
|
|
2162
|
+
anthropicPromptCachingMiddleware({
|
|
2163
|
+
unsupportedModelBehavior: "ignore"
|
|
2164
|
+
}),
|
|
2165
|
+
// Subagent middleware: Patches tool calls for compatibility
|
|
2166
|
+
createPatchToolCallsMiddleware()
|
|
2167
|
+
],
|
|
2168
|
+
defaultInterruptOn: interruptOn,
|
|
2169
|
+
subagents,
|
|
2170
|
+
generalPurposeAgent: true
|
|
2181
2171
|
}),
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2172
|
+
// Automatically summarizes conversation history when token limits are approached
|
|
2173
|
+
summarizationMiddleware({
|
|
2174
|
+
model,
|
|
2175
|
+
trigger: { tokens: 17e4 },
|
|
2176
|
+
keep: { messages: 6 }
|
|
2177
|
+
}),
|
|
2178
|
+
// Enables Anthropic prompt caching for improved performance and reduced costs
|
|
2179
|
+
anthropicPromptCachingMiddleware({
|
|
2180
|
+
unsupportedModelBehavior: "ignore"
|
|
2181
|
+
}),
|
|
2182
|
+
// Patches tool calls to ensure compatibility across different model providers
|
|
2183
|
+
createPatchToolCallsMiddleware()
|
|
2184
|
+
];
|
|
2185
|
+
if (interruptOn) {
|
|
2186
|
+
middleware.push(humanInTheLoopMiddleware2({ interruptOn }));
|
|
2187
|
+
}
|
|
2188
|
+
middleware.push(...customMiddleware);
|
|
2189
|
+
return createAgent3({
|
|
2190
|
+
model,
|
|
2191
|
+
systemPrompt: finalSystemPrompt,
|
|
2192
|
+
tools,
|
|
2193
|
+
middleware,
|
|
2194
|
+
responseFormat,
|
|
2195
|
+
contextSchema,
|
|
2196
|
+
checkpointer,
|
|
2197
|
+
store,
|
|
2198
|
+
name
|
|
2199
|
+
});
|
|
2187
2200
|
}
|
|
2188
2201
|
|
|
2189
|
-
// src/agent_lattice/builders/
|
|
2190
|
-
var
|
|
2202
|
+
// src/agent_lattice/builders/DeepAgentGraphBuilder.ts
|
|
2203
|
+
var DeepAgentGraphBuilder = class {
|
|
2191
2204
|
/**
|
|
2192
|
-
* 构建
|
|
2205
|
+
* 构建Deep Agent Graph
|
|
2193
2206
|
*
|
|
2194
2207
|
* @param agentLattice Agent Lattice对象
|
|
2195
2208
|
* @param params Agent构建参数
|
|
@@ -2197,17 +2210,36 @@ var PlanExecuteAgentGraphBuilder = class {
|
|
|
2197
2210
|
*/
|
|
2198
2211
|
build(agentLattice, params) {
|
|
2199
2212
|
const tools = params.tools.map((t) => {
|
|
2200
|
-
const
|
|
2201
|
-
return
|
|
2213
|
+
const toolClient = getToolClient(t.key);
|
|
2214
|
+
return toolClient;
|
|
2202
2215
|
}).filter((tool5) => tool5 !== void 0);
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2216
|
+
const subagents = params.subAgents.map((sa) => {
|
|
2217
|
+
if (sa.client) {
|
|
2218
|
+
return {
|
|
2219
|
+
name: sa.config.name,
|
|
2220
|
+
description: sa.config.description,
|
|
2221
|
+
runnable: sa.client
|
|
2222
|
+
};
|
|
2223
|
+
} else {
|
|
2224
|
+
const subagentClient = createAgentClientFromAgentLattice({
|
|
2225
|
+
config: sa.config
|
|
2226
|
+
});
|
|
2227
|
+
return {
|
|
2228
|
+
name: sa.config.name,
|
|
2229
|
+
description: sa.config.description,
|
|
2230
|
+
runnable: subagentClient
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
});
|
|
2234
|
+
const deepAgent = createDeepAgent({
|
|
2205
2235
|
tools,
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2236
|
+
model: params.model,
|
|
2237
|
+
contextSchema: params.stateSchema,
|
|
2238
|
+
systemPrompt: params.prompt,
|
|
2239
|
+
subagents,
|
|
2240
|
+
checkpointer: getCheckpointSaver("default")
|
|
2209
2241
|
});
|
|
2210
|
-
return
|
|
2242
|
+
return deepAgent;
|
|
2211
2243
|
}
|
|
2212
2244
|
};
|
|
2213
2245
|
|
|
@@ -2232,10 +2264,6 @@ var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
|
|
|
2232
2264
|
registerDefaultBuilders() {
|
|
2233
2265
|
this.builders.set(AgentType.REACT, new ReActAgentGraphBuilder());
|
|
2234
2266
|
this.builders.set(AgentType.DEEP_AGENT, new DeepAgentGraphBuilder());
|
|
2235
|
-
this.builders.set(
|
|
2236
|
-
AgentType.PLAN_EXECUTE,
|
|
2237
|
-
new PlanExecuteAgentGraphBuilder()
|
|
2238
|
-
);
|
|
2239
2267
|
}
|
|
2240
2268
|
/**
|
|
2241
2269
|
* 注册自定义Builder
|
|
@@ -2263,7 +2291,7 @@ var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
|
|
|
2263
2291
|
|
|
2264
2292
|
// src/agent_lattice/builders/AgentParamsBuilder.ts
|
|
2265
2293
|
import {
|
|
2266
|
-
getToolsFromConfig as
|
|
2294
|
+
getToolsFromConfig as getToolsFromConfig2,
|
|
2267
2295
|
getSubAgentsFromConfig as getSubAgentsFromConfig2,
|
|
2268
2296
|
isDeepAgentConfig as isDeepAgentConfig2
|
|
2269
2297
|
} from "@axiom-lattice/protocols";
|
|
@@ -2284,7 +2312,7 @@ var AgentParamsBuilder = class {
|
|
|
2284
2312
|
* @returns Agent build parameters
|
|
2285
2313
|
*/
|
|
2286
2314
|
buildParams(agentLattice, options) {
|
|
2287
|
-
const toolKeys = options?.overrideTools ||
|
|
2315
|
+
const toolKeys = options?.overrideTools || getToolsFromConfig2(agentLattice.config);
|
|
2288
2316
|
const tools = toolKeys.map((toolKey) => {
|
|
2289
2317
|
const toolLattice = toolLatticeManager.getToolLattice(toolKey);
|
|
2290
2318
|
if (!toolLattice) {
|
|
@@ -2480,7 +2508,7 @@ var getAgentLattice = (key) => agentLatticeManager.getAgentLattice(key);
|
|
|
2480
2508
|
var getAgentConfig = (key) => agentLatticeManager.getAgentConfig(key);
|
|
2481
2509
|
var getAllAgentConfigs = () => agentLatticeManager.getAllAgentConfigs();
|
|
2482
2510
|
var validateAgentInput = (key, input) => agentLatticeManager.validateAgentInput(key, input);
|
|
2483
|
-
var
|
|
2511
|
+
var getAgentClient = (key, options) => agentLatticeManager.initializeClient(key, options);
|
|
2484
2512
|
var createAgentClientFromAgentLattice = (agentLattice, options) => agentLatticeManager.createAgentClientFromConfig(agentLattice, options);
|
|
2485
2513
|
|
|
2486
2514
|
// src/chunk_buffer_lattice/ChunkBuffer.ts
|
|
@@ -2811,7 +2839,7 @@ export {
|
|
|
2811
2839
|
ThreadStatus,
|
|
2812
2840
|
ToolLatticeManager,
|
|
2813
2841
|
agentLatticeManager,
|
|
2814
|
-
|
|
2842
|
+
getAgentClient,
|
|
2815
2843
|
getAgentConfig,
|
|
2816
2844
|
getAgentLattice,
|
|
2817
2845
|
getAllAgentConfigs,
|