@librechat/agents 3.3.3 → 3.3.5
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/cjs/agents/AgentContext.cjs +2 -1
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/constants.cjs +21 -0
- package/dist/cjs/common/constants.cjs.map +1 -1
- package/dist/cjs/events.cjs +10 -1
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +793 -111
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/hooks/HookRegistry.cjs +25 -0
- package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
- package/dist/cjs/hooks/index.cjs +12 -0
- package/dist/cjs/hooks/index.cjs.map +1 -1
- package/dist/cjs/hooks/types.cjs +1 -0
- package/dist/cjs/hooks/types.cjs.map +1 -1
- package/dist/cjs/langfuse.cjs +8 -0
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +38 -13
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/google/utils/common.cjs +19 -7
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +331 -5
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +41 -5
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +25 -4
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +4 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/llm/preempt.cjs +132 -0
- package/dist/cjs/llm/preempt.cjs.map +1 -0
- package/dist/cjs/main.cjs +36 -5
- package/dist/cjs/messages/alternation.cjs +92 -0
- package/dist/cjs/messages/alternation.cjs.map +1 -0
- package/dist/cjs/messages/cache.cjs +54 -33
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/contextPruning.cjs +17 -43
- package/dist/cjs/messages/contextPruning.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +315 -23
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +290 -60
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/handoffCue.cjs +63 -0
- package/dist/cjs/messages/handoffCue.cjs.map +1 -0
- package/dist/cjs/messages/index.cjs +4 -1
- package/dist/cjs/messages/injected.cjs +60 -0
- package/dist/cjs/messages/injected.cjs.map +1 -0
- package/dist/cjs/messages/prune.cjs +503 -127
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/run.cjs +80 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/handlers.cjs +18 -9
- package/dist/cjs/session/handlers.cjs.map +1 -1
- package/dist/cjs/stream.cjs +35 -10
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +28 -14
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +56 -63
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs +90 -0
- package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +9 -1
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +6 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/utils/llm.cjs +1 -1
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +307 -20
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/cjs/utils/toolContent.cjs +1514 -0
- package/dist/cjs/utils/toolContent.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +2 -1
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/constants.mjs +19 -1
- package/dist/esm/common/constants.mjs.map +1 -1
- package/dist/esm/events.mjs +10 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +798 -116
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +3 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/hooks/HookRegistry.mjs +25 -0
- package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
- package/dist/esm/hooks/index.mjs +12 -1
- package/dist/esm/hooks/index.mjs.map +1 -1
- package/dist/esm/hooks/types.mjs +1 -0
- package/dist/esm/hooks/types.mjs.map +1 -1
- package/dist/esm/langfuse.mjs +9 -1
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +38 -13
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/google/utils/common.mjs +19 -7
- package/dist/esm/llm/google/utils/common.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +332 -8
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +41 -5
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +25 -4
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -1
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/llm/preempt.mjs +131 -0
- package/dist/esm/llm/preempt.mjs.map +1 -0
- package/dist/esm/main.mjs +14 -11
- package/dist/esm/messages/alternation.mjs +91 -0
- package/dist/esm/messages/alternation.mjs.map +1 -0
- package/dist/esm/messages/cache.mjs +55 -34
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/contextPruning.mjs +17 -43
- package/dist/esm/messages/contextPruning.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +305 -24
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +290 -61
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/handoffCue.mjs +61 -0
- package/dist/esm/messages/handoffCue.mjs.map +1 -0
- package/dist/esm/messages/index.mjs +4 -1
- package/dist/esm/messages/injected.mjs +60 -0
- package/dist/esm/messages/injected.mjs.map +1 -0
- package/dist/esm/messages/prune.mjs +500 -128
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/run.mjs +80 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/handlers.mjs +19 -10
- package/dist/esm/session/handlers.mjs.map +1 -1
- package/dist/esm/stream.mjs +34 -11
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +28 -14
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +56 -63
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-scraper.mjs +88 -0
- package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +9 -1
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +6 -0
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/esm/utils/llm.mjs +1 -1
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +307 -21
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/esm/utils/toolContent.mjs +1503 -0
- package/dist/esm/utils/toolContent.mjs.map +1 -0
- package/dist/types/common/constants.d.ts +18 -0
- package/dist/types/graphs/Graph.d.ts +139 -2
- package/dist/types/hooks/HookRegistry.d.ts +15 -0
- package/dist/types/hooks/index.d.ts +12 -1
- package/dist/types/hooks/types.d.ts +45 -6
- package/dist/types/index.d.ts +1 -1
- package/dist/types/llm/invoke.d.ts +34 -1
- package/dist/types/llm/openai/index.d.ts +4 -0
- package/dist/types/llm/openai/utils/index.d.ts +2 -0
- package/dist/types/llm/openrouter/index.d.ts +5 -0
- package/dist/types/llm/preempt.d.ts +40 -0
- package/dist/types/messages/alternation.d.ts +23 -0
- package/dist/types/messages/contextPruning.d.ts +2 -1
- package/dist/types/messages/core.d.ts +36 -0
- package/dist/types/messages/format.d.ts +6 -0
- package/dist/types/messages/handoffCue.d.ts +40 -0
- package/dist/types/messages/index.d.ts +3 -0
- package/dist/types/messages/injected.d.ts +3 -0
- package/dist/types/messages/prune.d.ts +16 -12
- package/dist/types/run.d.ts +7 -0
- package/dist/types/stream.d.ts +14 -0
- package/dist/types/tools/ToolNode.d.ts +0 -7
- package/dist/types/tools/search/keenable-scraper.d.ts +15 -0
- package/dist/types/tools/search/types.d.ts +31 -2
- package/dist/types/types/graph.d.ts +8 -1
- package/dist/types/types/run.d.ts +65 -0
- package/dist/types/types/stream.d.ts +1 -26
- package/dist/types/utils/tokens.d.ts +7 -0
- package/dist/types/utils/toolContent.d.ts +107 -0
- package/package.json +1 -1
- package/src/__tests__/stream.eagerEventExecution.test.ts +74 -0
- package/src/agents/AgentContext.ts +1 -0
- package/src/{splitStream.test.ts → aggregator.test.ts} +59 -666
- package/src/common/constants.ts +21 -0
- package/src/events.ts +15 -1
- package/src/graphs/Graph.ts +1261 -198
- package/src/graphs/MultiAgentGraph.ts +6 -1
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1139 -4
- package/src/graphs/__tests__/Graph.preemptSignal.test.ts +126 -0
- package/src/hooks/HookRegistry.ts +40 -0
- package/src/hooks/__tests__/preemptBoundary.test.ts +152 -0
- package/src/hooks/index.ts +16 -2
- package/src/hooks/types.ts +47 -3
- package/src/index.ts +1 -1
- package/src/langfuse.ts +26 -1
- package/src/llm/anthropic/utils/message_inputs.ts +158 -12
- package/src/llm/anthropic/utils/tool-id-normalization.test.ts +109 -0
- package/src/llm/bedrock/utils/message_inputs.test.ts +131 -1
- package/src/llm/bedrock/utils/message_inputs.ts +100 -24
- package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +15 -0
- package/src/llm/google/utils/common.test.ts +75 -9
- package/src/llm/google/utils/common.ts +34 -3
- package/src/llm/invoke.alternation.test.ts +87 -0
- package/src/llm/invoke.handoffCue.test.ts +112 -0
- package/src/llm/invoke.test.ts +488 -0
- package/src/llm/invoke.ts +611 -8
- package/src/llm/openai/index.ts +106 -6
- package/src/llm/openai/utils/index.ts +109 -53
- package/src/llm/openai/utils/messages.test.ts +330 -1
- package/src/llm/openrouter/index.ts +12 -2
- package/src/llm/openrouter/reasoning.test.ts +314 -0
- package/src/llm/preempt.test.ts +323 -0
- package/src/llm/preempt.ts +178 -0
- package/src/messages/__tests__/observationMasking.test.ts +93 -2
- package/src/messages/alternation.test.ts +212 -0
- package/src/messages/alternation.ts +112 -0
- package/src/messages/cache.tail.test.ts +193 -0
- package/src/messages/cache.test.ts +113 -0
- package/src/messages/cache.ts +92 -60
- package/src/messages/contextPruning.test.ts +184 -0
- package/src/messages/contextPruning.ts +49 -42
- package/src/messages/core.ts +653 -40
- package/src/messages/ensureThinkingBlock.test.ts +49 -1
- package/src/messages/foldToollessToolBlocks.test.ts +163 -5
- package/src/messages/format.ts +454 -91
- package/src/messages/formatAgentMessages.steer.test.ts +267 -0
- package/src/messages/formatAgentMessages.test.ts +604 -14
- package/src/messages/formatAgentMessages.tools.test.ts +103 -0
- package/src/messages/handoffCue.test.ts +96 -0
- package/src/messages/handoffCue.ts +78 -0
- package/src/messages/index.ts +3 -0
- package/src/messages/injected.test.ts +90 -0
- package/src/messages/injected.ts +74 -0
- package/src/messages/prune.ts +996 -183
- package/src/run.ts +91 -6
- package/src/scripts/preempt-probe.ts +330 -0
- package/src/scripts/preempt-scenarios.ts +388 -0
- package/src/session/handlers.ts +32 -12
- package/src/specs/handoffCue.test.ts +165 -0
- package/src/specs/langfuse-callbacks.test.ts +352 -2
- package/src/specs/preemptSeal.test.ts +309 -0
- package/src/specs/prune.test.ts +1083 -6
- package/src/specs/summarization-unit.test.ts +105 -0
- package/src/specs/tokens.test.ts +609 -32
- package/src/stream.dispatch.test.ts +63 -0
- package/src/stream.ts +59 -23
- package/src/summarization/__tests__/node.test.ts +77 -0
- package/src/summarization/node.ts +46 -18
- package/src/tools/ToolNode.ts +150 -99
- package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +148 -1
- package/src/tools/__tests__/ToolNode.outputReferences.test.ts +112 -3
- package/src/tools/__tests__/annotateMessagesForLLM.test.ts +32 -0
- package/src/tools/__tests__/directToolHooks.test.ts +255 -7
- package/src/tools/search/keenable-scraper.test.ts +153 -0
- package/src/tools/search/keenable-scraper.ts +137 -0
- package/src/tools/search/tool.ts +13 -2
- package/src/tools/search/types.ts +50 -3
- package/src/tools/toolOutputReferences.ts +6 -0
- package/src/types/graph.ts +12 -1
- package/src/types/run.ts +67 -0
- package/src/types/stream.ts +1 -42
- package/src/utils/llm.test.ts +18 -0
- package/src/utils/llm.ts +4 -1
- package/src/utils/tokens.ts +586 -31
- package/src/utils/toolContent.test.ts +1432 -0
- package/src/utils/toolContent.ts +2707 -0
- package/dist/cjs/splitStream.cjs +0 -151
- package/dist/cjs/splitStream.cjs.map +0 -1
- package/dist/esm/splitStream.mjs +0 -150
- package/dist/esm/splitStream.mjs.map +0 -1
- package/dist/types/mockStream.d.ts +0 -32
- package/dist/types/splitStream.d.ts +0 -37
- package/src/mockStream.ts +0 -99
- package/src/splitStream.ts +0 -234
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
require("../common/enum.cjs");
|
|
2
2
|
require("../common/index.cjs");
|
|
3
|
+
const require_truncation = require("../utils/truncation.cjs");
|
|
4
|
+
const require_toolContent = require("../utils/toolContent.cjs");
|
|
3
5
|
const require_langchain = require("./langchain.cjs");
|
|
4
6
|
const require_message_inputs = require("../llm/anthropic/utils/message_inputs.cjs");
|
|
5
7
|
const require_events = require("../utils/events.cjs");
|
|
@@ -175,6 +177,23 @@ function hasToolCallOutput(part) {
|
|
|
175
177
|
const output = part.tool_call?.output;
|
|
176
178
|
return output != null && output !== "";
|
|
177
179
|
}
|
|
180
|
+
function formatToolCallOutput(output) {
|
|
181
|
+
if (output == null) return "";
|
|
182
|
+
return require_toolContent.compactToolContent(output, require_truncation.HARD_MAX_TOOL_RESULT_CHARS).content;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Content for the synthetic assistant turn that separates a trailing steer
|
|
186
|
+
* from the next user turn. Non-empty by necessity — see the push site.
|
|
187
|
+
*/
|
|
188
|
+
const STEER_ANCHOR_PLACEHOLDER = "_";
|
|
189
|
+
/**
|
|
190
|
+
* True when an assistant message replayed as a steer and nothing followed it,
|
|
191
|
+
* so the emitted run ends on the steer's `HumanMessage`.
|
|
192
|
+
*/
|
|
193
|
+
function endsWithSteerMessage(formatted) {
|
|
194
|
+
if (formatted.length === 0) return false;
|
|
195
|
+
return formatted[formatted.length - 1].additional_kwargs.source === "steer";
|
|
196
|
+
}
|
|
178
197
|
/**
|
|
179
198
|
* Helper function to format an assistant message
|
|
180
199
|
* @param message The message to format
|
|
@@ -312,7 +331,7 @@ function formatAssistantMessage(message, options) {
|
|
|
312
331
|
formattedMessages.push(withMessageRole(new _langchain_core_messages.ToolMessage({
|
|
313
332
|
tool_call_id: tool_call.id ?? "",
|
|
314
333
|
name: tool_call.name,
|
|
315
|
-
content: output
|
|
334
|
+
content: formatToolCallOutput(output)
|
|
316
335
|
}), "tool"));
|
|
317
336
|
} else if (part.type === "think" || part.type === "thinking" || part.type === "reasoning" || part.type === "reasoning_content" || part.type === "redacted_thinking") {
|
|
318
337
|
hasReasoning = true;
|
|
@@ -583,7 +602,10 @@ function contentPartCharLength(part) {
|
|
|
583
602
|
if (typeof record.thinking === "string") len += record.thinking.length;
|
|
584
603
|
const { input } = record;
|
|
585
604
|
if (typeof input === "string") len += input.length;
|
|
586
|
-
else if (input != null && typeof input === "object")
|
|
605
|
+
else if (input != null && typeof input === "object") {
|
|
606
|
+
const measured = require_toolContent.serializeStructuredValueBounded(input, 0).originalChars;
|
|
607
|
+
len += measured === Number.MAX_SAFE_INTEGER ? require_truncation.HARD_MAX_TOOL_RESULT_CHARS : Math.min(measured, require_truncation.HARD_MAX_TOOL_RESULT_CHARS);
|
|
608
|
+
}
|
|
587
609
|
return len;
|
|
588
610
|
}
|
|
589
611
|
/** Extracts the skillName from a skill tool_call's args (string or object). */
|
|
@@ -608,6 +630,27 @@ function extractSkillName(args) {
|
|
|
608
630
|
*/
|
|
609
631
|
const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options) => {
|
|
610
632
|
const messages = [];
|
|
633
|
+
/**
|
|
634
|
+
* A steer ended the previous payload entry, so the next message emitted —
|
|
635
|
+
* whichever entry finally produces one — must be separated from it by an
|
|
636
|
+
* assistant turn. Held rather than emitted so an entry that produces
|
|
637
|
+
* nothing cannot leave the anchor stranded as the final turn.
|
|
638
|
+
*/
|
|
639
|
+
let pendingSteerAnchor = false;
|
|
640
|
+
/**
|
|
641
|
+
* Emits the deferred anchor ahead of `next` — the message about to be
|
|
642
|
+
* pushed. When that message is itself an assistant turn, it already IS the
|
|
643
|
+
* separation the anchor exists to synthesize, so the intent is simply
|
|
644
|
+
* discharged: emitting the placeholder anyway would put two assistant turns
|
|
645
|
+
* back to back, which strict-alternation providers can reject and nothing downstream
|
|
646
|
+
* repairs (`coalesceAdjacentUserTurns` merges user turns only).
|
|
647
|
+
*/
|
|
648
|
+
const flushSteerAnchor = (next) => {
|
|
649
|
+
if (!pendingSteerAnchor) return;
|
|
650
|
+
pendingSteerAnchor = false;
|
|
651
|
+
if (next.role === "assistant") return;
|
|
652
|
+
messages.push(withMessageRole(new _langchain_core_messages.AIMessage({ content: STEER_ANCHOR_PLACEHOLDER }), "assistant"));
|
|
653
|
+
};
|
|
611
654
|
const updatedIndexTokenCountMap = {};
|
|
612
655
|
let boundaryTokenAdjustment;
|
|
613
656
|
const indexMapping = {};
|
|
@@ -643,6 +686,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
|
|
|
643
686
|
langChain: true
|
|
644
687
|
});
|
|
645
688
|
if (sourceMessageId != null && sourceMessageId !== "") formattedMessage.id = sourceMessageId;
|
|
689
|
+
flushSteerAnchor(formattedMessage);
|
|
646
690
|
messages.push(formattedMessage);
|
|
647
691
|
indexMapping[i] = [messages.length - 1];
|
|
648
692
|
continue;
|
|
@@ -746,7 +790,44 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
|
|
|
746
790
|
provider: options?.provider
|
|
747
791
|
});
|
|
748
792
|
if (sourceMessageId != null && sourceMessageId !== "") for (const formattedMessage of formattedMessages) formattedMessage.id = sourceMessageId;
|
|
793
|
+
/**
|
|
794
|
+
* A steer that ends an assistant message leaves the replay on a
|
|
795
|
+
* `HumanMessage`. The next payload message is itself a user turn, so the
|
|
796
|
+
* sequence would reach the provider as two adjacent user turns — rejected
|
|
797
|
+
* by strict-alternation providers. Anchor it with a placeholder assistant
|
|
798
|
+
* turn.
|
|
799
|
+
*
|
|
800
|
+
* The placeholder must be NON-EMPTY. A string-content assistant message
|
|
801
|
+
* with no tool calls passes through `_convertMessagesToAnthropicPayload`
|
|
802
|
+
* verbatim — the empty-text repair there only covers array content and
|
|
803
|
+
* tool-call turns — so an empty anchor would reach Anthropic as
|
|
804
|
+
* `{role: 'assistant', content: ''}` and trade one invalid sequence for
|
|
805
|
+
* another. Same single-underscore convention the Anthropic converter
|
|
806
|
+
* already uses when it has to synthesize a non-empty block.
|
|
807
|
+
*
|
|
808
|
+
* Deferred rather than decided by lookahead. `i < payload.length - 1` only
|
|
809
|
+
* proves a later ENTRY exists, not that it EMITS: entries with empty
|
|
810
|
+
* content, and entries dropped by `applySummaryBoundary`, are skipped
|
|
811
|
+
* silently. A trailing steer followed only by those would get the anchor
|
|
812
|
+
* as the FINAL turn — an assistant prefill with no request after it, which
|
|
813
|
+
* the model may simply never answer. So the intent is recorded and flushed
|
|
814
|
+
* only when a message actually follows.
|
|
815
|
+
*
|
|
816
|
+
* Pushed AFTER the id stamping above, deliberately. `messagesStateReducer`
|
|
817
|
+
* treats a repeated id as replace-in-place, so an anchor carrying the
|
|
818
|
+
* shared `sourceMessageId` would overwrite the steer it exists to protect.
|
|
819
|
+
* Left unstamped, it reaches the reducer with a null id and is assigned a
|
|
820
|
+
* fresh one. `endsWithSteerMessage` reads only `additional_kwargs.source`,
|
|
821
|
+
* so the deferral cannot change which messages get anchored.
|
|
822
|
+
*/
|
|
823
|
+
/**
|
|
824
|
+
* Guarded on emission: an assistant entry whose blocks all filtered away
|
|
825
|
+
* emits nothing, and flushing for it would strand the anchor as the final
|
|
826
|
+
* turn — the pending flag stays set for whichever entry emits next.
|
|
827
|
+
*/
|
|
828
|
+
if (formattedMessages.length > 0) flushSteerAnchor(formattedMessages[0]);
|
|
749
829
|
messages.push(...formattedMessages);
|
|
830
|
+
if (endsWithSteerMessage(formattedMessages)) pendingSteerAnchor = true;
|
|
750
831
|
const endMessageIndex = messages.length;
|
|
751
832
|
if (pendingSkillNames?.size != null && pendingSkillNames.size > 0) {
|
|
752
833
|
const skipSkillBodyNames = options?.skipSkillBodyNames;
|
|
@@ -821,7 +902,10 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
|
|
|
821
902
|
if (typeof tc.name === "string") len += tc.name.length;
|
|
822
903
|
const { args } = tc;
|
|
823
904
|
if (typeof args === "string") len += args.length;
|
|
824
|
-
else if (args != null)
|
|
905
|
+
else if (args != null) {
|
|
906
|
+
const measured = require_toolContent.serializeStructuredValueBounded(args, 0).originalChars;
|
|
907
|
+
len += measured === Number.MAX_SAFE_INTEGER ? require_truncation.HARD_MAX_TOOL_RESULT_CHARS : Math.min(measured, require_truncation.HARD_MAX_TOOL_RESULT_CHARS);
|
|
908
|
+
}
|
|
825
909
|
}
|
|
826
910
|
lengths[k] = len;
|
|
827
911
|
totalLength += len;
|
|
@@ -867,10 +951,99 @@ function shiftIndexTokenCountMap(indexTokenCountMap, instructionsTokenCount) {
|
|
|
867
951
|
}
|
|
868
952
|
return shiftedMap;
|
|
869
953
|
}
|
|
870
|
-
/** Block types that contain binary image data and must be preserved structurally. */
|
|
871
|
-
const IMAGE_BLOCK_TYPES = new Set(["image_url", "image"]);
|
|
872
954
|
/** Checks whether a BaseMessage is a tool-role message. */
|
|
873
955
|
const isToolMessage = (m) => m instanceof _langchain_core_messages.ToolMessage || "role" in m && m.role === "tool";
|
|
956
|
+
const PORTABLE_FOLDED_MEDIA_TYPES = new Set(["image", "image_url"]);
|
|
957
|
+
const MAX_FOLDED_BLOCK_CHARS = 8e3;
|
|
958
|
+
const MAX_FOLDED_CONTEXT_CHARS = require_truncation.HARD_MAX_TOOL_RESULT_CHARS;
|
|
959
|
+
const MAX_FOLDED_CONTEXT_WORK = 1e5;
|
|
960
|
+
const FOLDED_CONTEXT_TRUNCATION_NOTICE = "… [additional folded context omitted]";
|
|
961
|
+
const syntheticProviderContextMessages = /* @__PURE__ */ new WeakSet();
|
|
962
|
+
function createFoldContextBudget() {
|
|
963
|
+
return {
|
|
964
|
+
remainingChars: MAX_FOLDED_CONTEXT_CHARS,
|
|
965
|
+
remainingWork: MAX_FOLDED_CONTEXT_WORK,
|
|
966
|
+
truncated: false
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function readFoldedDataProperty(value, key) {
|
|
970
|
+
if (value == null || typeof value !== "object") return;
|
|
971
|
+
try {
|
|
972
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
973
|
+
return descriptor != null && "value" in descriptor ? descriptor.value : void 0;
|
|
974
|
+
} catch {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Adds one logical line without first concatenating caller-controlled strings.
|
|
980
|
+
* The shared budget covers every synthetic message emitted by one transform,
|
|
981
|
+
* so many individually bounded blocks cannot build an unbounded aggregate.
|
|
982
|
+
*/
|
|
983
|
+
function appendFoldedLine(textChunks, budget, pieces) {
|
|
984
|
+
if (budget.remainingChars <= 0) {
|
|
985
|
+
budget.truncated = true;
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
const separatorChars = textChunks.length > 0 ? 1 : 0;
|
|
989
|
+
const available = budget.remainingChars - separatorChars;
|
|
990
|
+
if (available <= 0) {
|
|
991
|
+
budget.remainingChars = 0;
|
|
992
|
+
budget.truncated = true;
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
let totalChars = 0;
|
|
996
|
+
for (const piece of pieces) totalChars = Math.min(Number.MAX_SAFE_INTEGER, totalChars + piece.length);
|
|
997
|
+
if (totalChars <= available) {
|
|
998
|
+
const line = pieces.join("");
|
|
999
|
+
textChunks.push(line);
|
|
1000
|
+
budget.remainingChars -= separatorChars + line.length;
|
|
1001
|
+
return true;
|
|
1002
|
+
}
|
|
1003
|
+
const notice = FOLDED_CONTEXT_TRUNCATION_NOTICE.slice(0, available);
|
|
1004
|
+
let remainingHeadChars = Math.max(0, available - notice.length);
|
|
1005
|
+
const boundedPieces = [];
|
|
1006
|
+
for (const piece of pieces) {
|
|
1007
|
+
if (remainingHeadChars <= 0) break;
|
|
1008
|
+
const retained = piece.slice(0, remainingHeadChars);
|
|
1009
|
+
boundedPieces.push(retained);
|
|
1010
|
+
remainingHeadChars -= retained.length;
|
|
1011
|
+
}
|
|
1012
|
+
boundedPieces.push(notice);
|
|
1013
|
+
textChunks.push(boundedPieces.join(""));
|
|
1014
|
+
budget.remainingChars = 0;
|
|
1015
|
+
budget.truncated = true;
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
function consumeFoldedWork(textChunks, budget) {
|
|
1019
|
+
if (budget.remainingChars <= 0) return false;
|
|
1020
|
+
if (budget.remainingWork-- > 0) return true;
|
|
1021
|
+
appendFoldedLine(textChunks, budget, [FOLDED_CONTEXT_TRUNCATION_NOTICE]);
|
|
1022
|
+
budget.remainingChars = 0;
|
|
1023
|
+
budget.truncated = true;
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
function serializeFoldedValue(value) {
|
|
1027
|
+
const compacted = require_toolContent.compactToolContent(value, MAX_FOLDED_BLOCK_CHARS).content;
|
|
1028
|
+
return typeof compacted === "string" ? compacted : require_toolContent.serializeStructuredValueBounded(compacted, MAX_FOLDED_BLOCK_CHARS).content;
|
|
1029
|
+
}
|
|
1030
|
+
function markSyntheticProviderContext(message) {
|
|
1031
|
+
syntheticProviderContextMessages.add(message);
|
|
1032
|
+
return message;
|
|
1033
|
+
}
|
|
1034
|
+
function appendSyntheticProviderContextMessage(result, parts) {
|
|
1035
|
+
if (parts.length === 0) return false;
|
|
1036
|
+
result.push(markSyntheticProviderContext(withMessageRole(new _langchain_core_messages.HumanMessage({ content: require_langchain.toLangChainContent(parts) }), "user")));
|
|
1037
|
+
return true;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Identifies provider-context placeholders created by this module without
|
|
1041
|
+
* trusting user-controlled content prefixes or leaking marker metadata onto
|
|
1042
|
+
* the provider wire.
|
|
1043
|
+
*/
|
|
1044
|
+
function isSyntheticProviderContextMessage(message) {
|
|
1045
|
+
return syntheticProviderContextMessages.has(message);
|
|
1046
|
+
}
|
|
874
1047
|
/** Flushes accumulated text chunks into `parts` as a single text block. */
|
|
875
1048
|
function flushTextChunks(textChunks, parts) {
|
|
876
1049
|
if (textChunks.length === 0) return;
|
|
@@ -882,88 +1055,139 @@ function flushTextChunks(textChunks, parts) {
|
|
|
882
1055
|
}
|
|
883
1056
|
/**
|
|
884
1057
|
* Appends a single message's content to the running `textChunks` / `parts`
|
|
885
|
-
* accumulators.
|
|
886
|
-
* binary data
|
|
887
|
-
*
|
|
888
|
-
*
|
|
1058
|
+
* accumulators. Portable image blocks are shallow-copied into `parts` so
|
|
1059
|
+
* binary data never becomes text tokens. Provider-specific media/resource
|
|
1060
|
+
* blocks are retained as bounded text so a folded cross-provider history
|
|
1061
|
+
* cannot contain an unsupported native block or JSON-expand without limit.
|
|
889
1062
|
*
|
|
890
1063
|
* When `content` is an array containing tool_use blocks, `tool_calls` is NOT
|
|
891
1064
|
* additionally serialized (avoiding double output). `tool_calls` is used as
|
|
892
1065
|
* a fallback when `content` is a plain string or an array with no tool_use.
|
|
893
1066
|
*/
|
|
894
|
-
function appendMessageContent(msg, role, textChunks, parts) {
|
|
1067
|
+
function appendMessageContent(msg, role, textChunks, parts, budget) {
|
|
895
1068
|
const { content } = msg;
|
|
896
1069
|
if (typeof content === "string") {
|
|
897
|
-
if (content) textChunks
|
|
898
|
-
appendToolCalls(msg, role, textChunks);
|
|
1070
|
+
if (content && consumeFoldedWork(textChunks, budget)) appendFoldedLine(textChunks, budget, [`${role}: `, content]);
|
|
1071
|
+
appendToolCalls(msg, role, textChunks, budget);
|
|
899
1072
|
return;
|
|
900
1073
|
}
|
|
901
1074
|
if (!Array.isArray(content)) {
|
|
902
|
-
appendToolCalls(msg, role, textChunks);
|
|
1075
|
+
appendToolCalls(msg, role, textChunks, budget);
|
|
903
1076
|
return;
|
|
904
1077
|
}
|
|
905
1078
|
let hasToolUseBlock = false;
|
|
906
1079
|
for (const block of content) {
|
|
907
|
-
if (
|
|
908
|
-
|
|
909
|
-
|
|
1080
|
+
if (!consumeFoldedWork(textChunks, budget)) {
|
|
1081
|
+
hasToolUseBlock = true;
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
const blockTypeValue = readFoldedDataProperty(block, "type");
|
|
1085
|
+
const blockType = typeof blockTypeValue === "string" ? blockTypeValue : void 0;
|
|
1086
|
+
if (blockType !== "tool_use" && blockType !== "tool_call" && blockType !== "tool_result" && require_toolContent.isAtomicToolContentBlock(block)) {
|
|
1087
|
+
if (PORTABLE_FOLDED_MEDIA_TYPES.has(blockType ?? "")) {
|
|
1088
|
+
const blockChars = require_toolContent.getToolContentCharLength([block]);
|
|
1089
|
+
if (blockChars > budget.remainingChars) {
|
|
1090
|
+
appendFoldedLine(textChunks, budget, [`${role}: [${blockType ?? "media"} omitted: folded context limit]`]);
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
flushTextChunks(textChunks, parts);
|
|
1094
|
+
parts.push({ ...block });
|
|
1095
|
+
budget.remainingChars -= blockChars;
|
|
1096
|
+
} else appendFoldedLine(textChunks, budget, [`${role}: [${blockType ?? "media"}] `, serializeFoldedValue(block)]);
|
|
910
1097
|
continue;
|
|
911
1098
|
}
|
|
912
|
-
if (
|
|
1099
|
+
if (blockType === "tool_use") {
|
|
913
1100
|
hasToolUseBlock = true;
|
|
914
|
-
|
|
1101
|
+
const name = readFoldedDataProperty(block, "name");
|
|
1102
|
+
const input = readFoldedDataProperty(block, "input");
|
|
1103
|
+
appendFoldedLine(textChunks, budget, [`${role}: [tool_use] ${typeof name === "string" ? name : ""} `, serializeFoldedValue(input ?? {})]);
|
|
915
1104
|
continue;
|
|
916
1105
|
}
|
|
917
|
-
if (
|
|
1106
|
+
if (blockType === "tool_call") {
|
|
918
1107
|
hasToolUseBlock = true;
|
|
919
|
-
const nested = block
|
|
920
|
-
const
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1108
|
+
const nested = readFoldedDataProperty(block, "tool_call");
|
|
1109
|
+
const nestedName = readFoldedDataProperty(nested, "name");
|
|
1110
|
+
const topLevelName = readFoldedDataProperty(block, "name");
|
|
1111
|
+
let name = "";
|
|
1112
|
+
if (typeof nestedName === "string") name = nestedName;
|
|
1113
|
+
else if (typeof topLevelName === "string") name = topLevelName;
|
|
1114
|
+
const nestedArgs = readFoldedDataProperty(nested, "args");
|
|
1115
|
+
const topLevelArgs = readFoldedDataProperty(block, "args");
|
|
1116
|
+
const rawArgs = nestedArgs ?? topLevelArgs ?? {};
|
|
1117
|
+
const argsText = typeof rawArgs === "string" ? rawArgs : serializeFoldedValue(rawArgs);
|
|
1118
|
+
appendFoldedLine(textChunks, budget, [`${role}: [tool_use] ${name}${argsText ? " " : ""}`, argsText]);
|
|
1119
|
+
const output = readFoldedDataProperty(nested, "output");
|
|
1120
|
+
if (output != null && output !== "") appendFoldedLine(textChunks, budget, ["Tool: ", typeof output === "string" ? output : serializeFoldedValue(output)]);
|
|
926
1121
|
continue;
|
|
927
1122
|
}
|
|
928
|
-
if (
|
|
1123
|
+
if (blockType === "tool_result") {
|
|
929
1124
|
hasToolUseBlock = true;
|
|
930
|
-
const inner = block
|
|
1125
|
+
const inner = readFoldedDataProperty(block, "content");
|
|
931
1126
|
if (typeof inner === "string") {
|
|
932
|
-
if (inner) textChunks
|
|
933
|
-
} else if (Array.isArray(inner)) for (const innerBlock of inner)
|
|
934
|
-
if (
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1127
|
+
if (inner) appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, inner]);
|
|
1128
|
+
} else if (Array.isArray(inner)) for (const innerBlock of inner) {
|
|
1129
|
+
if (!consumeFoldedWork(textChunks, budget)) break;
|
|
1130
|
+
if (typeof innerBlock === "string") {
|
|
1131
|
+
if (innerBlock) appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, innerBlock]);
|
|
1132
|
+
} else {
|
|
1133
|
+
const innerTypeValue = readFoldedDataProperty(innerBlock, "type");
|
|
1134
|
+
const innerType = typeof innerTypeValue === "string" ? innerTypeValue : void 0;
|
|
1135
|
+
if (require_toolContent.isAtomicToolContentBlock(innerBlock) && PORTABLE_FOLDED_MEDIA_TYPES.has(innerType ?? "")) {
|
|
1136
|
+
const blockChars = require_toolContent.getToolContentCharLength([innerBlock]);
|
|
1137
|
+
if (blockChars <= budget.remainingChars) {
|
|
1138
|
+
flushTextChunks(textChunks, parts);
|
|
1139
|
+
parts.push({ ...innerBlock });
|
|
1140
|
+
budget.remainingChars -= blockChars;
|
|
1141
|
+
} else appendFoldedLine(textChunks, budget, [`${role}: [${innerType ?? "media"} omitted: folded context limit]`]);
|
|
1142
|
+
} else {
|
|
1143
|
+
const textValue = readFoldedDataProperty(innerBlock, "text");
|
|
1144
|
+
const inputValue = readFoldedDataProperty(innerBlock, "input");
|
|
1145
|
+
const innerText = textValue ?? inputValue;
|
|
1146
|
+
appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, typeof innerText === "string" && innerText ? innerText : serializeFoldedValue(innerBlock)]);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
941
1149
|
}
|
|
942
|
-
else if (inner != null) textChunks
|
|
1150
|
+
else if (inner != null) appendFoldedLine(textChunks, budget, [`${role}: [tool_result] `, serializeFoldedValue(inner)]);
|
|
943
1151
|
continue;
|
|
944
1152
|
}
|
|
945
|
-
const text = block
|
|
1153
|
+
const text = readFoldedDataProperty(block, "text") ?? readFoldedDataProperty(block, "input");
|
|
946
1154
|
if (typeof text === "string" && text) {
|
|
947
|
-
textChunks
|
|
1155
|
+
appendFoldedLine(textChunks, budget, [`${role}: `, text]);
|
|
948
1156
|
continue;
|
|
949
1157
|
}
|
|
950
|
-
if (
|
|
1158
|
+
if (blockType != null && blockType !== "") appendFoldedLine(textChunks, budget, [`${role}: [${blockType}] `, serializeFoldedValue(block)]);
|
|
951
1159
|
}
|
|
952
|
-
if (!hasToolUseBlock) appendToolCalls(msg, role, textChunks);
|
|
1160
|
+
if (!hasToolUseBlock) appendToolCalls(msg, role, textChunks, budget);
|
|
953
1161
|
}
|
|
954
|
-
function appendToolCalls(msg, role, textChunks) {
|
|
1162
|
+
function appendToolCalls(msg, role, textChunks, budget) {
|
|
955
1163
|
if (role !== "AI") return;
|
|
956
1164
|
const aiMsg = msg;
|
|
957
1165
|
if (aiMsg.tool_calls && aiMsg.tool_calls.length > 0) {
|
|
958
|
-
for (const tc of aiMsg.tool_calls)
|
|
1166
|
+
for (const tc of aiMsg.tool_calls) {
|
|
1167
|
+
if (!consumeFoldedWork(textChunks, budget)) break;
|
|
1168
|
+
const name = readFoldedDataProperty(tc, "name");
|
|
1169
|
+
const args = readFoldedDataProperty(tc, "args");
|
|
1170
|
+
appendFoldedLine(textChunks, budget, [
|
|
1171
|
+
`AI: [tool_call] ${typeof name === "string" ? name : ""}(`,
|
|
1172
|
+
serializeFoldedValue(args),
|
|
1173
|
+
")"
|
|
1174
|
+
]);
|
|
1175
|
+
}
|
|
959
1176
|
return;
|
|
960
1177
|
}
|
|
961
1178
|
const rawToolCalls = aiMsg.additional_kwargs.tool_calls;
|
|
962
1179
|
if (!Array.isArray(rawToolCalls)) return;
|
|
963
1180
|
for (const tc of rawToolCalls) {
|
|
964
|
-
|
|
1181
|
+
if (!consumeFoldedWork(textChunks, budget)) break;
|
|
1182
|
+
const fn = readFoldedDataProperty(tc, "function");
|
|
965
1183
|
if (fn == null) continue;
|
|
966
|
-
|
|
1184
|
+
const name = readFoldedDataProperty(fn, "name");
|
|
1185
|
+
const args = readFoldedDataProperty(fn, "arguments");
|
|
1186
|
+
appendFoldedLine(textChunks, budget, [
|
|
1187
|
+
`AI: [tool_call] ${typeof name === "string" ? name : ""}(`,
|
|
1188
|
+
typeof args === "string" ? args : "",
|
|
1189
|
+
")"
|
|
1190
|
+
]);
|
|
967
1191
|
}
|
|
968
1192
|
}
|
|
969
1193
|
/**
|
|
@@ -1004,6 +1228,7 @@ function ensureThinkingBlockInMessages(messages, _provider, config, runStartInde
|
|
|
1004
1228
|
}
|
|
1005
1229
|
if (lastHumanIndex === messages.length - 1) return messages;
|
|
1006
1230
|
const result = lastHumanIndex >= 0 ? messages.slice(0, lastHumanIndex + 1) : [];
|
|
1231
|
+
const foldBudget = createFoldContextBudget();
|
|
1007
1232
|
let i = lastHumanIndex + 1;
|
|
1008
1233
|
while (i < messages.length) {
|
|
1009
1234
|
const msg = messages[i];
|
|
@@ -1019,8 +1244,9 @@ function ensureThinkingBlockInMessages(messages, _provider, config, runStartInde
|
|
|
1019
1244
|
let hasThinkingBlock = false;
|
|
1020
1245
|
if (contentIsArray && aiMsg.content.length > 0) for (const c of aiMsg.content) {
|
|
1021
1246
|
if (typeof c !== "object") continue;
|
|
1022
|
-
|
|
1023
|
-
|
|
1247
|
+
const type = readFoldedDataProperty(c, "type");
|
|
1248
|
+
if (type === "tool_use") hasToolUse = true;
|
|
1249
|
+
else if (type === "thinking" || type === "reasoning_content" || type === "reasoning" || type === "redacted_thinking") hasThinkingBlock = true;
|
|
1024
1250
|
if (hasToolUse && hasThinkingBlock) break;
|
|
1025
1251
|
}
|
|
1026
1252
|
if (!hasThinkingBlock && aiMsg.additional_kwargs.reasoning_content != null) hasThinkingBlock = true;
|
|
@@ -1036,16 +1262,16 @@ function ensureThinkingBlockInMessages(messages, _provider, config, runStartInde
|
|
|
1036
1262
|
continue;
|
|
1037
1263
|
}
|
|
1038
1264
|
const parts = [];
|
|
1039
|
-
const textChunks = [
|
|
1040
|
-
|
|
1265
|
+
const textChunks = [];
|
|
1266
|
+
appendFoldedLine(textChunks, foldBudget, ["[Previous agent context]"]);
|
|
1267
|
+
appendMessageContent(msg, "AI", textChunks, parts, foldBudget);
|
|
1041
1268
|
let j = i + 1;
|
|
1042
1269
|
while (j < messages.length && isToolMessage(messages[j])) {
|
|
1043
|
-
appendMessageContent(messages[j], "Tool", textChunks, parts);
|
|
1270
|
+
appendMessageContent(messages[j], "Tool", textChunks, parts, foldBudget);
|
|
1044
1271
|
j++;
|
|
1045
1272
|
}
|
|
1046
1273
|
flushTextChunks(textChunks, parts);
|
|
1047
|
-
require_events.emitAgentLog(config, "warn", "format", `ensureThinkingBlockInMessages: injecting [Previous agent context] HumanMessage (${parts.length} msgs at index ${i}, no thinking block in chain)`);
|
|
1048
|
-
result.push(withMessageRole(new _langchain_core_messages.HumanMessage({ content: require_langchain.toLangChainContent(parts) }), "user"));
|
|
1274
|
+
if (appendSyntheticProviderContextMessage(result, parts)) require_events.emitAgentLog(config, "warn", "format", `ensureThinkingBlockInMessages: injecting [Previous agent context] HumanMessage (${parts.length} msgs at index ${i}, no thinking block in chain)`);
|
|
1049
1275
|
i = j;
|
|
1050
1276
|
} else {
|
|
1051
1277
|
result.push(msg);
|
|
@@ -1069,8 +1295,9 @@ function messageHasToolContent(msg) {
|
|
|
1069
1295
|
if (aiMsg.tool_calls != null && aiMsg.tool_calls.length > 0) return true;
|
|
1070
1296
|
const rawToolCalls = aiMsg.additional_kwargs.tool_calls;
|
|
1071
1297
|
if (Array.isArray(rawToolCalls) && rawToolCalls.length > 0) return true;
|
|
1072
|
-
if (Array.isArray(msg.content)) {
|
|
1073
|
-
|
|
1298
|
+
if (Array.isArray(msg.content)) for (const block of msg.content) {
|
|
1299
|
+
const type = readFoldedDataProperty(block, "type");
|
|
1300
|
+
if (typeof block === "object" && (type === "tool_use" || type === "tool_call" || type === "tool_result")) return true;
|
|
1074
1301
|
}
|
|
1075
1302
|
return false;
|
|
1076
1303
|
}
|
|
@@ -1081,7 +1308,7 @@ function messageHasToolContent(msg) {
|
|
|
1081
1308
|
* and labelled as tool output. */
|
|
1082
1309
|
function isToolResultMessage(msg) {
|
|
1083
1310
|
if (isToolMessage(msg)) return true;
|
|
1084
|
-
if (Array.isArray(msg.content)) return msg.content.some((block) => typeof block === "object" && block
|
|
1311
|
+
if (Array.isArray(msg.content)) return msg.content.some((block) => typeof block === "object" && readFoldedDataProperty(block, "type") === "tool_result");
|
|
1085
1312
|
return false;
|
|
1086
1313
|
}
|
|
1087
1314
|
/**
|
|
@@ -1108,6 +1335,7 @@ function isToolResultMessage(msg) {
|
|
|
1108
1335
|
function foldToolBlocksForToollessAgent(messages, config) {
|
|
1109
1336
|
let result = null;
|
|
1110
1337
|
let foldedCount = 0;
|
|
1338
|
+
const foldBudget = createFoldContextBudget();
|
|
1111
1339
|
let i = 0;
|
|
1112
1340
|
while (i < messages.length) {
|
|
1113
1341
|
const msg = messages[i];
|
|
@@ -1119,17 +1347,18 @@ function foldToolBlocksForToollessAgent(messages, config) {
|
|
|
1119
1347
|
/** First fold — copy the untouched prefix once, then append from here. */
|
|
1120
1348
|
if (result === null) result = messages.slice(0, i);
|
|
1121
1349
|
const parts = [];
|
|
1122
|
-
const textChunks = [
|
|
1123
|
-
|
|
1350
|
+
const textChunks = [];
|
|
1351
|
+
appendFoldedLine(textChunks, foldBudget, ["[Previous tool interaction]"]);
|
|
1352
|
+
appendMessageContent(msg, isToolResultMessage(msg) ? "Tool" : "AI", textChunks, parts, foldBudget);
|
|
1124
1353
|
foldedCount++;
|
|
1125
1354
|
let j = i + 1;
|
|
1126
1355
|
while (j < messages.length && isToolResultMessage(messages[j])) {
|
|
1127
|
-
appendMessageContent(messages[j], "Tool", textChunks, parts);
|
|
1356
|
+
appendMessageContent(messages[j], "Tool", textChunks, parts, foldBudget);
|
|
1128
1357
|
foldedCount++;
|
|
1129
1358
|
j++;
|
|
1130
1359
|
}
|
|
1131
1360
|
flushTextChunks(textChunks, parts);
|
|
1132
|
-
result
|
|
1361
|
+
appendSyntheticProviderContextMessage(result, parts);
|
|
1133
1362
|
i = j;
|
|
1134
1363
|
}
|
|
1135
1364
|
if (result === null) return messages;
|
|
@@ -1169,6 +1398,7 @@ exports.formatFromLangChain = formatFromLangChain;
|
|
|
1169
1398
|
exports.formatLangChainMessages = formatLangChainMessages;
|
|
1170
1399
|
exports.formatMediaMessage = formatMediaMessage;
|
|
1171
1400
|
exports.formatMessage = formatMessage;
|
|
1401
|
+
exports.isSyntheticProviderContextMessage = isSyntheticProviderContextMessage;
|
|
1172
1402
|
exports.labelContentByAgent = labelContentByAgent;
|
|
1173
1403
|
exports.shiftIndexTokenCountMap = shiftIndexTokenCountMap;
|
|
1174
1404
|
exports.withMessageRole = withMessageRole;
|