@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.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 stateSchema = createReactAgentSchema(params.stateSchema);
970
- return createReactAgent({
971
- llm: params.model,
722
+ const stateSchema2 = createReactAgentSchema(params.stateSchema);
723
+ return createAgent({
724
+ model: params.model,
972
725
  tools,
973
- prompt: params.prompt,
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/deep_agent/subAgent.ts
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
- Command as Command4,
986
- getCurrentTaskInput as getCurrentTaskInput5,
987
- GraphInterrupt
988
- } from "@langchain/langgraph";
989
- import { z as z9 } from "zod";
990
-
991
- // src/deep_agent/prompts.ts
992
- var WRITE_TODOS_DESCRIPTION2 = `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.
993
-
994
- When to Use This Tool
995
- Use this tool proactively in these scenarios:
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
- <example>
1192
- user: "Please write a function that checks if a number is prime"
1193
- assistant: Sure let me write a function that checks if a number is prime
1194
- assistant: First let me use the Write tool to write a function that checks if a number is prime
1195
- assistant: I'm going to use the Write tool to write the following code:
1196
- <code>
1197
- function isPrime(n) {
1198
- if (n <= 1) return false
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
- </code>
1205
- <commentary>
1206
- Since significant content was created and the task was completed, now use the content-reviewer agent to review the work
1207
- </commentary>
1208
- assistant: Now let me use the content-reviewer agent to review the code
1209
- assistant: Uses the Task tool to launch with the content-reviewer agent
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
- return tools;
1267
- }
1268
- function createTaskTool(inputs) {
1269
- const {
1270
- subagents,
1271
- tools = {},
1272
- model = getModelLattice("default")?.client,
1273
- stateSchema,
1274
- postModelHook
1275
- } = inputs;
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 subagentTools = [];
1288
- if (hasTools2(subagent)) {
1289
- for (const toolName of getToolsFromConfig2(subagent)) {
1290
- const resolvedTool = allTools[toolName];
1291
- if (resolvedTool) {
1292
- subagentTools.push(resolvedTool);
1293
- } else {
1294
- console.warn(
1295
- `Warning: Tool '${toolName}' not found for agent '${subagent.name}'`
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 tool2(
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
- // src/deep_agent/tools.ts
1372
- import { tool as tool3 } from "@langchain/core/tools";
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
- var ls = tool3(
1404
- () => {
1405
- const state = getCurrentTaskInput6();
1406
- const files = state.files || {};
1407
- return Object.keys(files);
1408
- },
1409
- {
1410
- name: "ls",
1411
- description: "List all files in the mock filesystem",
1412
- schema: z10.object({})
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
- var readFile = tool3(
1416
- (input) => {
1417
- const state = getCurrentTaskInput6();
1418
- const mockFilesystem = state.files || {};
1419
- const { file_path, offset = 0, limit = 2e3 } = input;
1420
- if (!(file_path in mockFilesystem)) {
1421
- return `Error: File '${file_path}' not found`;
1422
- }
1423
- const content = mockFilesystem[file_path];
1424
- if (!content || content.trim() === "") {
1425
- return "System reminder: File exists but has empty contents";
1426
- }
1427
- const lines = content.split("\n");
1428
- const startIdx = offset;
1429
- const endIdx = Math.min(startIdx + limit, lines.length);
1430
- if (startIdx >= lines.length) {
1431
- return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
1432
- }
1433
- const resultLines = [];
1434
- for (let i = startIdx; i < endIdx; i++) {
1435
- let lineContent = lines[i];
1436
- if (lineContent.length > 2e3) {
1437
- lineContent = lineContent.substring(0, 2e3);
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
- var writeFile = tool3(
1455
- (input, config) => {
1456
- const state = getCurrentTaskInput6();
1457
- const files = { ...state.files || {} };
1458
- files[input.file_path] = input.content;
1459
- return new Command5({
1460
- update: {
1461
- files,
1462
- messages: [
1463
- new ToolMessage5({
1464
- content: `Updated file ${input.file_path}`,
1465
- tool_call_id: config.toolCall?.id
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
- name: "write_file",
1473
- description: "Write content to a file in the mock filesystem",
1474
- schema: z10.object({
1475
- file_path: z10.string().describe("Absolute path to the file to write"),
1476
- content: z10.string().describe("Content to write to the file")
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
- var editFile = tool3(
1481
- (input, config) => {
1482
- const state = getCurrentTaskInput6();
1483
- const mockFilesystem = { ...state.files || {} };
1484
- const { file_path, old_string, new_string, replace_all = false } = input;
1485
- if (!(file_path in mockFilesystem)) {
1486
- return `Error: File '${file_path}' not found`;
1487
- }
1488
- const content = mockFilesystem[file_path];
1489
- if (!content.includes(old_string)) {
1490
- return `Error: String not found in file: '${old_string}'`;
1491
- }
1492
- if (!replace_all) {
1493
- const escapedOldString = old_string.replace(
1494
- /[.*+?^${}()|[\]\\]/g,
1495
- "\\$&"
1496
- );
1497
- const occurrences = (content.match(new RegExp(escapedOldString, "g")) || []).length;
1498
- if (occurrences > 1) {
1499
- 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.`;
1500
- } else if (occurrences === 0) {
1501
- return `Error: String not found in file: '${old_string}'`;
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
- let newContent;
1505
- if (replace_all) {
1506
- const escapedOldString = old_string.replace(
1507
- /[.*+?^${}()|[\]\\]/g,
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
- newContent = content.replace(old_string, new_string);
1108
+ result[key] = value;
1516
1109
  }
1517
- mockFilesystem[file_path] = newContent;
1518
- return new Command5({
1519
- update: {
1520
- files: mockFilesystem,
1521
- messages: [
1522
- new ToolMessage5({
1523
- content: `Updated file ${file_path}`,
1524
- tool_call_id: config.toolCall?.id
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
- name: "edit_file",
1532
- description: EDIT_DESCRIPTION,
1533
- schema: z10.object({
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/deep_agent/state.ts
1543
- import "@langchain/langgraph/zod";
1544
- import { MessagesZodState as MessagesZodState2 } from "@langchain/langgraph";
1545
- import { withLangGraph } from "@langchain/langgraph/zod";
1546
- import { z as z11 } from "zod";
1547
- function fileReducer(left, right) {
1548
- if (left == null) {
1549
- return right || {};
1550
- } else if (right == null) {
1551
- return left;
1552
- } else {
1553
- return { ...left, ...right };
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
- function todoReducer(left, right) {
1557
- if (right != null) {
1558
- return right;
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
- return left || [];
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
- var DeepAgentState = MessagesZodState2.extend({
1563
- todos: withLangGraph(z11.custom(), {
1564
- reducer: {
1565
- schema: z11.custom(),
1566
- fn: todoReducer
1567
- }
1568
- }),
1569
- files: withLangGraph(z11.custom(), {
1570
- reducer: {
1571
- schema: z11.custom(),
1572
- fn: fileReducer
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
- // src/deep_agent/graph.ts
1578
- import { createReactAgent as createReactAgent2 } from "@langchain/langgraph/prebuilt";
1579
- var BASE_PROMPT = `You have access to a number of standard tools
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
- ## \`write_todos\`
1949
+ ## Examples of When NOT to Use the Todo List
1582
1950
 
1583
- You have access to the \`write_todos\` tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.
1584
- These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
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
- It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.
1587
- ## \`task\`
1955
+ \`\`\`python
1956
+ print("Hello World")
1957
+ \`\`\`
1588
1958
 
1589
- - When doing web search, prefer to use the \`task\` tool in order to reduce context usage.`;
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
- // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
1635
- var DeepAgentGraphBuilder = class {
1636
- /**
1637
- * 构建Deep Agent Graph
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
- // src/createPlanExecuteAgent.ts
1659
- import {
1660
- Annotation,
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
- // src/util/returnToolResponse.ts
1811
- import { ToolMessage as ToolMessage6 } from "@langchain/core/messages";
1812
- import { v4 } from "uuid";
1813
- var returnToolResponse = (config, think) => {
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
- // src/util/returnAIResponse.ts
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
- // src/util/getLastHumanMessageData.ts
1849
- import {
1850
- isHumanMessage
1851
- } from "@langchain/core/messages";
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
- // src/createPlanExecuteAgent.ts
1867
- import { tool as tool4 } from "@langchain/core/tools";
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
- // src/util/PGMemory.ts
1870
- import { MemorySaver as MemorySaver2 } from "@langchain/langgraph";
1871
- import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
1872
- var globalMemory = PostgresSaver.fromConnString(process.env.DATABASE_URL);
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
- \u53EF\u4EE5\u4F7F\u7528\u7684\u5DE5\u5177\u5982\u4E0B\uFF1A
1952
- ${executorTools.map((tool5) => tool5.name).join("\n")}
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
- \u76EE\u6807: ${input}`;
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
- \u4EFB\u52A1: ${currentTask}
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
- \u4E0A\u4E0B\u6587\u4FE1\u606F:
1993
- - \u539F\u59CB\u76EE\u6807: ${state.input}
1994
- - \u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4: ${state.pastSteps.map(([step, result]) => `${step}: ${result}`).join("\n")}
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
- \u8BF7\u63D0\u4F9B\u6267\u884C\u6B64\u4EFB\u52A1\u7684\u8BE6\u7EC6\u7ED3\u679C\u3002`;
1997
- const executionResult = await agentExecutor.invoke({
1998
- messages: [new HumanMessage2(executorPrompt)]
1999
- });
2000
- const resultContent = executionResult.messages[executionResult.messages.length - 1].content;
2001
- return {
2002
- pastSteps: [[currentTask, resultContent]],
2003
- plan: state.plan.slice(1),
2004
- // 移除已执行的任务
2005
- //messages,
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
- \u4F60\u7684\u76EE\u6807\u662F\uFF1A
2054
- ${state.input}
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
- \u4F60\u7684\u539F\u59CB\u8BA1\u5212\u662F\uFF1A
2057
- ${state.plan.join("\n")}
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
- Update your plan accordingly. If no more steps are needed and you can return to the user, then respond with that and use the 'response' function.
2063
- Otherwise, fill out the plan.
2064
- Only add steps to the plan that still NEED to be done. Do not return previously done steps as part of the plan.`;
2065
- try {
2066
- const responseResult = await replanAgent.invoke({
2067
- messages: [new HumanMessage2(replannerPrompt)]
2068
- });
2069
- const toolCall = responseResult.messages[responseResult.messages.length - 1];
2070
- if (toolCall?.name == "response") {
2071
- agentLogger.info("\u4EFB\u52A1\u5B8C\u6210\uFF0C\u63D0\u4F9B\u6700\u7EC8\u54CD\u5E94");
2072
- const { messages } = await returnAIResponse(
2073
- config2,
2074
- toolCall.content
2075
- );
2076
- return {
2077
- response: toolCall.content,
2078
- messages,
2079
- error: void 0
2080
- };
2081
- } else if (toolCall?.name == "plan") {
2082
- let messages = [];
2083
- if (toolCall.content?.length > 0) {
2084
- messages = await returnToolResponse(config2, {
2085
- title: "\u65B0\u7684\u8BA1\u5212",
2086
- content: toolCall.content,
2087
- status: "success"
2088
- }).messages;
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
- return "end";
2127
- }
2128
- if (!state.plan || state.plan.length === 0) {
2129
- agentLogger.info("\u8BA1\u5212\u4E3A\u7A7A\uFF0C\u7ED3\u675F\u6267\u884C");
2130
- return "end";
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
- return "continue";
2133
- }
2134
- const workflow = new StateGraph(PlanExecuteState).addNode("planner", planStep).addNode("executor", executeStep).addNode("replanner", replanStep).addEdge(START, "planner").addEdge("planner", "executor").addEdge("executor", "replanner").addConditionalEdges("replanner", shouldEnd, {
2135
- end: END2,
2136
- continue: "executor"
2137
- });
2138
- const compiledGraph = workflow.compile({
2139
- checkpointer: checkpointer || MemoryManager.getInstance(),
2140
- name: `PlanExecuteAgent:${name}`
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
- agentLogger.info(`Plan-Execute\u4EE3\u7406\u7F16\u8BD1\u5B8C\u6210: ${name}`);
2143
- return {
2144
- /**
2145
- * 执行代理
2146
- */
2147
- invoke: async (input, config2) => {
2148
- agentLogger.info(`\u5F00\u59CB\u6267\u884CPlan-Execute\u4EE3\u7406: ${name}`, {
2149
- input: input.input
2150
- });
2151
- try {
2152
- const result = await compiledGraph.invoke(input, config2);
2153
- agentLogger.info(`\u4EE3\u7406\u6267\u884C\u5B8C\u6210: ${name}`);
2154
- return result;
2155
- } catch (error) {
2156
- agentLogger.error(
2157
- `\u4EE3\u7406\u6267\u884C\u5931\u8D25: ${name}`,
2158
- error instanceof Error ? error : { error }
2159
- );
2160
- throw error;
2161
- }
2162
- },
2163
- /**
2164
- * 流式执行代理
2165
- */
2166
- stream: async (input, config2) => {
2167
- agentLogger.info(`\u5F00\u59CB\u6D41\u5F0F\u6267\u884CPlan-Execute\u4EE3\u7406: ${name}`, {
2168
- input: input.input
2169
- });
2170
- return compiledGraph.stream(input, config2);
2171
- },
2172
- /**
2173
- * 获取代理信息
2174
- */
2175
- getInfo: () => ({
2176
- name,
2177
- description,
2178
- type: "PlanExecuteAgent",
2179
- toolsCount: executorTools.length,
2180
- maxSteps
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
- getCompiledGraph: () => compiledGraph
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/PlanExecuteAgentGraphBuilder.ts
2190
- var PlanExecuteAgentGraphBuilder = class {
2202
+ // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
2203
+ var DeepAgentGraphBuilder = class {
2191
2204
  /**
2192
- * 构建Plan Execute Agent Graph
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 tool5 = getToolClient(t.key);
2201
- return tool5;
2213
+ const toolClient = getToolClient(t.key);
2214
+ return toolClient;
2202
2215
  }).filter((tool5) => tool5 !== void 0);
2203
- const agent = createPlanExecuteAgent({
2204
- llmManager: params.model,
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
- prompt: params.prompt,
2207
- name: agentLattice.config.name,
2208
- description: agentLattice.config.description
2236
+ model: params.model,
2237
+ contextSchema: params.stateSchema,
2238
+ systemPrompt: params.prompt,
2239
+ subagents,
2240
+ checkpointer: getCheckpointSaver("default")
2209
2241
  });
2210
- return agent.getCompiledGraph();
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 getToolsFromConfig3,
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 || getToolsFromConfig3(agentLattice.config);
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 getAgentClient2 = (key, options) => agentLatticeManager.initializeClient(key, options);
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
- getAgentClient2 as getAgentClient,
2842
+ getAgentClient,
2815
2843
  getAgentConfig,
2816
2844
  getAgentLattice,
2817
2845
  getAllAgentConfigs,