deepagents 1.8.6 → 1.8.7
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.cjs +91 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +91 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -658,6 +658,62 @@ indicate omitted lines in the middle of the content):
|
|
|
658
658
|
|
|
659
659
|
{content_sample}`;
|
|
660
660
|
/**
|
|
661
|
+
* Message template for evicted HumanMessages.
|
|
662
|
+
*/
|
|
663
|
+
const TOO_LARGE_HUMAN_MSG = `Message content too large and was saved to the filesystem at: {file_path}
|
|
664
|
+
|
|
665
|
+
You can read the full content using the read_file tool with pagination (offset and limit parameters).
|
|
666
|
+
|
|
667
|
+
Here is a preview showing the head and tail of the content:
|
|
668
|
+
|
|
669
|
+
{content_sample}`;
|
|
670
|
+
/**
|
|
671
|
+
* Extract text content from a message.
|
|
672
|
+
*
|
|
673
|
+
* For string content, returns it directly. For array content (mixed block types
|
|
674
|
+
* like text + image), joins all text blocks. Returns empty string if no text found.
|
|
675
|
+
*/
|
|
676
|
+
function extractTextFromMessage(message) {
|
|
677
|
+
if (typeof message.content === "string") return message.content;
|
|
678
|
+
if (Array.isArray(message.content)) return message.content.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("\n");
|
|
679
|
+
return String(message.content);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Build replacement content for an evicted HumanMessage, preserving non-text blocks.
|
|
683
|
+
*
|
|
684
|
+
* For plain string content, returns the replacement text directly. For list content
|
|
685
|
+
* with mixed block types (e.g., text + image), replaces all text blocks with a single
|
|
686
|
+
* text block containing the replacement text while keeping non-text blocks intact.
|
|
687
|
+
*/
|
|
688
|
+
function buildEvictedHumanContent(message, replacementText) {
|
|
689
|
+
if (typeof message.content === "string") return replacementText;
|
|
690
|
+
if (Array.isArray(message.content)) {
|
|
691
|
+
const mediaBlocks = message.content.filter((block) => typeof block === "object" && block !== null && block.type !== "text");
|
|
692
|
+
if (mediaBlocks.length === 0) return replacementText;
|
|
693
|
+
return [{
|
|
694
|
+
type: "text",
|
|
695
|
+
text: replacementText
|
|
696
|
+
}, ...mediaBlocks];
|
|
697
|
+
}
|
|
698
|
+
return replacementText;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Build a truncated HumanMessage for the model request.
|
|
702
|
+
*
|
|
703
|
+
* Computes a preview from the full content still in state and returns a
|
|
704
|
+
* lightweight replacement the model will see. Pure string computation — no
|
|
705
|
+
* backend I/O.
|
|
706
|
+
*/
|
|
707
|
+
function buildTruncatedHumanMessage(message, filePath) {
|
|
708
|
+
const contentSample = createContentPreview(extractTextFromMessage(message));
|
|
709
|
+
return new langchain.HumanMessage({
|
|
710
|
+
content: buildEvictedHumanContent(message, TOO_LARGE_HUMAN_MSG.replace("{file_path}", filePath).replace("{content_sample}", contentSample)),
|
|
711
|
+
id: message.id,
|
|
712
|
+
additional_kwargs: { ...message.additional_kwargs },
|
|
713
|
+
response_metadata: { ...message.response_metadata }
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
661
717
|
* Create a preview of content showing head and tail with truncation marker.
|
|
662
718
|
*
|
|
663
719
|
* @param contentStr - The full content string to preview.
|
|
@@ -1026,7 +1082,7 @@ function createExecuteTool(backend, options) {
|
|
|
1026
1082
|
* Create filesystem middleware with all tools and features.
|
|
1027
1083
|
*/
|
|
1028
1084
|
function createFilesystemMiddleware(options = {}) {
|
|
1029
|
-
const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4 } = options;
|
|
1085
|
+
const { backend = (runtime) => new StateBackend(runtime), systemPrompt: customSystemPrompt = null, customToolDescriptions = null, toolTokenLimitBeforeEvict = 2e4, humanMessageTokenLimitBeforeEvict = 5e4 } = options;
|
|
1030
1086
|
const baseSystemPrompt = customSystemPrompt || FILESYSTEM_SYSTEM_PROMPT;
|
|
1031
1087
|
const allToolsByName = {
|
|
1032
1088
|
ls: createLsTool(backend, { customDescription: customToolDescriptions?.ls }),
|
|
@@ -1044,6 +1100,32 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
1044
1100
|
name: "FilesystemMiddleware",
|
|
1045
1101
|
stateSchema: FilesystemStateSchema,
|
|
1046
1102
|
tools: Object.values(allToolsByName),
|
|
1103
|
+
async beforeAgent(state) {
|
|
1104
|
+
if (!humanMessageTokenLimitBeforeEvict) return;
|
|
1105
|
+
const messages = state.messages;
|
|
1106
|
+
if (!messages || messages.length === 0) return;
|
|
1107
|
+
const last = messages[messages.length - 1];
|
|
1108
|
+
if (!langchain.HumanMessage.isInstance(last)) return;
|
|
1109
|
+
if (last.additional_kwargs?.lc_evicted_to) return;
|
|
1110
|
+
const contentStr = extractTextFromMessage(last);
|
|
1111
|
+
const threshold = 4 * humanMessageTokenLimitBeforeEvict;
|
|
1112
|
+
if (contentStr.length <= threshold) return;
|
|
1113
|
+
const resolvedBackend = await resolveBackend(backend, { state: state || {} });
|
|
1114
|
+
const filePath = `/conversation_history/${crypto.randomUUID().replace(/-/g, "").slice(0, 12)}`;
|
|
1115
|
+
const writeResult = await resolvedBackend.write(filePath, contentStr);
|
|
1116
|
+
if (writeResult.error) return;
|
|
1117
|
+
const result = { messages: [new langchain.HumanMessage({
|
|
1118
|
+
content: last.content,
|
|
1119
|
+
id: last.id,
|
|
1120
|
+
additional_kwargs: {
|
|
1121
|
+
...last.additional_kwargs,
|
|
1122
|
+
lc_evicted_to: filePath
|
|
1123
|
+
},
|
|
1124
|
+
response_metadata: { ...last.response_metadata }
|
|
1125
|
+
})] };
|
|
1126
|
+
if (writeResult.filesUpdate) result.files = writeResult.filesUpdate;
|
|
1127
|
+
return result;
|
|
1128
|
+
},
|
|
1047
1129
|
wrapModelCall: async (request, handler) => {
|
|
1048
1130
|
const supportsExecution = isSandboxBackend(await resolveBackend(backend, {
|
|
1049
1131
|
...request.runtime,
|
|
@@ -1054,9 +1136,17 @@ function createFilesystemMiddleware(options = {}) {
|
|
|
1054
1136
|
let filesystemPrompt = baseSystemPrompt;
|
|
1055
1137
|
if (supportsExecution) filesystemPrompt = `${filesystemPrompt}\n\n${EXECUTION_SYSTEM_PROMPT}`;
|
|
1056
1138
|
const newSystemMessage = request.systemMessage.concat(filesystemPrompt);
|
|
1139
|
+
let messages = request.messages;
|
|
1140
|
+
if (humanMessageTokenLimitBeforeEvict && messages) {
|
|
1141
|
+
if (messages.some((msg) => langchain.HumanMessage.isInstance(msg) && msg.additional_kwargs?.lc_evicted_to)) messages = messages.map((msg) => {
|
|
1142
|
+
if (langchain.HumanMessage.isInstance(msg) && msg.additional_kwargs?.lc_evicted_to) return buildTruncatedHumanMessage(msg, msg.additional_kwargs.lc_evicted_to);
|
|
1143
|
+
return msg;
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1057
1146
|
return handler({
|
|
1058
1147
|
...request,
|
|
1059
1148
|
tools,
|
|
1149
|
+
messages,
|
|
1060
1150
|
systemMessage: newSystemMessage
|
|
1061
1151
|
});
|
|
1062
1152
|
},
|