@librechat/agents 3.3.4 → 3.3.6
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/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 +456 -7
- package/dist/cjs/graphs/Graph.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/createWorkspacePolicyHook.cjs +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/bedrock/utils/message_inputs.cjs +1 -3
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +268 -3
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +51 -6
- package/dist/cjs/llm/openai/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 +31 -3
- package/dist/cjs/messages/alternation.cjs +92 -0
- package/dist/cjs/messages/alternation.cjs.map +1 -0
- package/dist/cjs/messages/format.cjs +72 -0
- 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 +3 -0
- package/dist/cjs/messages/injected.cjs +60 -0
- package/dist/cjs/messages/injected.cjs.map +1 -0
- package/dist/cjs/run.cjs +80 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +1 -1
- package/dist/cjs/session/handlers.cjs +18 -9
- package/dist/cjs/session/handlers.cjs.map +1 -1
- package/dist/cjs/stream.cjs +34 -8
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -1
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -0
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +19 -16
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +2 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ReadFile.cjs +8 -4
- package/dist/cjs/tools/ReadFile.cjs.map +1 -1
- package/dist/cjs/tools/SkillTool.cjs +2 -0
- package/dist/cjs/tools/SkillTool.cjs.map +1 -1
- package/dist/cjs/tools/SubagentTool.cjs +3 -0
- package/dist/cjs/tools/SubagentTool.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +91 -50
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +5 -1
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/intentArg.cjs +230 -0
- package/dist/cjs/tools/intentArg.cjs.map +1 -0
- package/dist/cjs/tools/local/CompileCheckTool.cjs +3 -2
- package/dist/cjs/tools/local/CompileCheckTool.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +13 -12
- package/dist/cjs/tools/local/LocalCodingTools.cjs.map +1 -1
- package/dist/cjs/tools/local/LocalExecutionEngine.cjs +2 -2
- package/dist/cjs/tools/ptcTimeout.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/schema.cjs +2 -0
- package/dist/cjs/tools/search/schema.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +40 -2
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -0
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/toolOutputReferences.cjs +41 -7
- package/dist/cjs/tools/toolOutputReferences.cjs.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 +455 -6
- package/dist/esm/graphs/Graph.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/createWorkspacePolicyHook.mjs +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/bedrock/utils/message_inputs.mjs +1 -3
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +270 -6
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +51 -6
- package/dist/esm/llm/openai/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 +11 -7
- package/dist/esm/messages/alternation.mjs +91 -0
- package/dist/esm/messages/alternation.mjs.map +1 -0
- package/dist/esm/messages/format.mjs +72 -0
- 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 +3 -0
- package/dist/esm/messages/injected.mjs +60 -0
- package/dist/esm/messages/injected.mjs.map +1 -0
- package/dist/esm/run.mjs +80 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +1 -1
- package/dist/esm/session/handlers.mjs +19 -10
- package/dist/esm/session/handlers.mjs.map +1 -1
- package/dist/esm/stream.mjs +33 -9
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -1
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +2 -0
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +19 -16
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +2 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ReadFile.mjs +8 -4
- package/dist/esm/tools/ReadFile.mjs.map +1 -1
- package/dist/esm/tools/SkillTool.mjs +2 -0
- package/dist/esm/tools/SkillTool.mjs.map +1 -1
- package/dist/esm/tools/SubagentTool.mjs +3 -0
- package/dist/esm/tools/SubagentTool.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +91 -51
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +5 -1
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/intentArg.mjs +220 -0
- package/dist/esm/tools/intentArg.mjs.map +1 -0
- package/dist/esm/tools/local/CompileCheckTool.mjs +3 -2
- package/dist/esm/tools/local/CompileCheckTool.mjs.map +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +13 -12
- package/dist/esm/tools/local/LocalCodingTools.mjs.map +1 -1
- package/dist/esm/tools/local/LocalExecutionEngine.mjs +2 -2
- package/dist/esm/tools/ptcTimeout.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/schema.mjs +2 -0
- package/dist/esm/tools/search/schema.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +40 -3
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -0
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/toolOutputReferences.mjs +41 -7
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/types/common/constants.d.ts +18 -0
- package/dist/types/graphs/Graph.d.ts +138 -1
- 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 +2 -1
- package/dist/types/llm/invoke.d.ts +7 -0
- package/dist/types/llm/openai/index.d.ts +0 -11
- package/dist/types/llm/preempt.d.ts +40 -0
- package/dist/types/messages/alternation.d.ts +23 -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/run.d.ts +7 -0
- package/dist/types/stream.d.ts +14 -0
- package/dist/types/tools/BashExecutor.d.ts +18 -0
- package/dist/types/tools/CodeExecutor.d.ts +18 -0
- package/dist/types/tools/ReadFile.d.ts +18 -0
- package/dist/types/tools/SkillTool.d.ts +18 -0
- package/dist/types/tools/SubagentTool.d.ts +9 -0
- package/dist/types/tools/ToolNode.d.ts +37 -12
- package/dist/types/tools/ToolSearch.d.ts +18 -0
- package/dist/types/tools/intentArg.d.ts +119 -0
- package/dist/types/tools/ptcTimeout.d.ts +2 -0
- package/dist/types/tools/search/keenable-scraper.d.ts +15 -0
- package/dist/types/tools/search/schema.d.ts +18 -0
- package/dist/types/tools/search/tool.d.ts +18 -0
- package/dist/types/tools/search/types.d.ts +31 -2
- package/dist/types/tools/toolOutputReferences.d.ts +12 -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 +12 -25
- package/dist/types/types/tools.d.ts +27 -2
- package/package.json +1 -1
- 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 +568 -3
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +18 -7
- 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 +2 -1
- package/src/langfuse.ts +26 -1
- package/src/llm/bedrock/utils/message_inputs.test.ts +82 -0
- package/src/llm/bedrock/utils/message_inputs.ts +14 -11
- package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +15 -0
- package/src/llm/invoke.alternation.test.ts +87 -0
- package/src/llm/invoke.handoffCue.test.ts +112 -0
- package/src/llm/invoke.ts +417 -8
- package/src/llm/openai/index.ts +74 -10
- package/src/llm/openai/llm.spec.ts +105 -0
- package/src/llm/preempt.test.ts +323 -0
- package/src/llm/preempt.ts +178 -0
- package/src/messages/alternation.test.ts +212 -0
- package/src/messages/alternation.ts +112 -0
- package/src/messages/format.ts +91 -0
- package/src/messages/formatAgentMessages.steer.test.ts +267 -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/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/stream.dispatch.test.ts +63 -0
- package/src/stream.ts +48 -9
- package/src/tools/BashExecutor.ts +6 -0
- package/src/tools/BashProgrammaticToolCalling.ts +2 -0
- package/src/tools/CodeExecutor.ts +6 -0
- package/src/tools/ProgrammaticToolCalling.ts +2 -0
- package/src/tools/ReadFile.ts +2 -0
- package/src/tools/SkillTool.ts +2 -0
- package/src/tools/SubagentTool.ts +3 -0
- package/src/tools/ToolNode.ts +193 -74
- package/src/tools/ToolSearch.ts +3 -0
- package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +174 -0
- package/src/tools/__tests__/intentArg.test.ts +281 -0
- package/src/tools/__tests__/intentCoverage.test.ts +139 -0
- package/src/tools/__tests__/toolOutputReferences.test.ts +83 -0
- package/src/tools/intentArg.ts +323 -0
- package/src/tools/local/CompileCheckTool.ts +4 -3
- package/src/tools/local/LocalCodingTools.ts +14 -13
- package/src/tools/ptcTimeout.ts +2 -0
- package/src/tools/search/keenable-scraper.test.ts +153 -0
- package/src/tools/search/keenable-scraper.ts +137 -0
- package/src/tools/search/outcome.test.ts +90 -0
- package/src/tools/search/schema.ts +3 -0
- package/src/tools/search/tool.ts +60 -3
- package/src/tools/search/types.ts +50 -3
- package/src/tools/subagent/SubagentExecutor.ts +2 -1
- package/src/tools/toolOutputReferences.ts +77 -9
- package/src/types/graph.ts +12 -1
- package/src/types/run.ts +67 -0
- package/src/types/stream.ts +12 -41
- package/src/types/tools.ts +24 -2
- 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
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require("../common/constants.cjs");
|
|
2
|
+
require("../common/enum.cjs");
|
|
3
|
+
require("../common/index.cjs");
|
|
4
|
+
//#region src/llm/preempt.ts
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes a host-supplied seal budget.
|
|
7
|
+
*
|
|
8
|
+
* Read in two places that interpret it differently — a numeric comparison in
|
|
9
|
+
* the seal gate and an addition into the recursion limit — so a value like
|
|
10
|
+
* `1.5` would permit two seals while reserving fractional headroom, `NaN`
|
|
11
|
+
* would poison the recursion limit outright, and `Infinity` would remove both
|
|
12
|
+
* bounds at once. Normalizing once keeps the two readings in agreement.
|
|
13
|
+
*
|
|
14
|
+
* `0` is honored as a deliberate "never seal"; anything not finite falls back
|
|
15
|
+
* to the default rather than silently disabling the feature.
|
|
16
|
+
*/
|
|
17
|
+
function resolveMaxSeals(maxSeals) {
|
|
18
|
+
if (maxSeals == null || !Number.isFinite(maxSeals)) return 8;
|
|
19
|
+
const whole = Math.floor(maxSeals);
|
|
20
|
+
return whole > 0 ? whole : 0;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* True when the accumulated content carries at least one non-whitespace text
|
|
24
|
+
* block. `thinking` / `reasoning` blocks deliberately do not count: they are
|
|
25
|
+
* stripped or signed on several providers and cannot stand in for the visible
|
|
26
|
+
* assistant turn a sealed sequence needs.
|
|
27
|
+
*/
|
|
28
|
+
function hasNonEmptyTextContent(content) {
|
|
29
|
+
if (typeof content === "string") return content.trim() !== "";
|
|
30
|
+
for (const block of content) {
|
|
31
|
+
if (block.type !== "text") continue;
|
|
32
|
+
const text = block["text"];
|
|
33
|
+
if (typeof text === "string" && text.trim() !== "") return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* True while a Gemini server-side tool call is still awaiting its response.
|
|
39
|
+
*
|
|
40
|
+
* Google's server-side tools (Search, URL context) never populate
|
|
41
|
+
* `tool_calls` or `tool_call_chunks` — those are derived from `functionCall`
|
|
42
|
+
* parts only, while a server-side invocation arrives as a `toolCall` CONTENT
|
|
43
|
+
* block and its result as a later `toolResponse` block. The tool-call gates
|
|
44
|
+
* cannot see it, so sealing between the two would replay a model turn holding
|
|
45
|
+
* an unanswered server-side call.
|
|
46
|
+
*
|
|
47
|
+
* Counted rather than id-matched: Google answers calls in order, and counting
|
|
48
|
+
* stays correct when a part omits its id.
|
|
49
|
+
*/
|
|
50
|
+
function hasOpenGoogleServerToolCall(content) {
|
|
51
|
+
if (typeof content === "string") return false;
|
|
52
|
+
let calls = 0;
|
|
53
|
+
let responses = 0;
|
|
54
|
+
for (const block of content) if (block.type === "toolCall") calls += 1;
|
|
55
|
+
else if (block.type === "toolResponse") responses += 1;
|
|
56
|
+
return calls > responses;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Ids of Anthropic server-side tool calls whose paired result block is already
|
|
60
|
+
* on the accumulated content. Those calls are answered and cannot be orphaned
|
|
61
|
+
* by a seal.
|
|
62
|
+
*/
|
|
63
|
+
function settledServerToolCallIds(content) {
|
|
64
|
+
const settled = /* @__PURE__ */ new Set();
|
|
65
|
+
if (typeof content === "string") return settled;
|
|
66
|
+
for (const block of content) {
|
|
67
|
+
if (block.type !== "web_search_tool_result") continue;
|
|
68
|
+
const id = block.tool_use_id;
|
|
69
|
+
if (typeof id === "string") settled.add(id);
|
|
70
|
+
}
|
|
71
|
+
return settled;
|
|
72
|
+
}
|
|
73
|
+
/** Tool calls still awaiting an answer, ignoring ones already settled. */
|
|
74
|
+
function countOpenToolCalls(calls, settled) {
|
|
75
|
+
if (calls == null || calls.length === 0) return 0;
|
|
76
|
+
if (settled.size === 0) return calls.length;
|
|
77
|
+
let open = 0;
|
|
78
|
+
for (const call of calls) if (call.id == null || !settled.has(call.id)) open += 1;
|
|
79
|
+
return open;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Cooperative mid-generation seal gate. Returns true ONLY when sealing here
|
|
83
|
+
* yields a message sequence valid on EVERY supported provider:
|
|
84
|
+
* - non-whitespace TEXT content, so the FIRST injected user turn is preceded
|
|
85
|
+
* by a non-empty assistant turn — no empty-content 400s. Note this says
|
|
86
|
+
* nothing about adjacency AMONG several injected turns: a boundary that
|
|
87
|
+
* drains two steers emits two consecutive user messages, which strict
|
|
88
|
+
* providers reject. That is normalized at the provider-facing hop by
|
|
89
|
+
* `coalesceAdjacentUserTurns`, not here;
|
|
90
|
+
* - no tool call in flight, so no `tool_use` can be orphaned AND no eagerly
|
|
91
|
+
* prestarted execution can be stripped out from under the model.
|
|
92
|
+
*
|
|
93
|
+
* Anthropic's server-side tools need no check of their own. Every
|
|
94
|
+
* `server_tool_use` content block also emits a `tool_call_chunk`
|
|
95
|
+
* (`_makeMessageChunkFromAnthropicEvent`), and `concat` keeps that chunk on
|
|
96
|
+
* the accumulated message for the remainder of the turn, so the tool-call
|
|
97
|
+
* gates below already cover it. The practical consequence is worth stating
|
|
98
|
+
* plainly: once a turn starts a web search it is no longer preemptible, and
|
|
99
|
+
* a queued message waits for the ordinary tool boundary instead.
|
|
100
|
+
*
|
|
101
|
+
* Nothing is stripped and nothing is repaired: when the accumulated shape is
|
|
102
|
+
* not already safe the stream simply runs on, and whatever the host queued
|
|
103
|
+
* lands at the next tool boundary instead. Chunks accumulate monotonically
|
|
104
|
+
* through `concat`, so an unsafe shape can never be observed at a seal point.
|
|
105
|
+
*/
|
|
106
|
+
function canSealPreempt(chunk) {
|
|
107
|
+
if (chunk == null) return false;
|
|
108
|
+
/**
|
|
109
|
+
* Anthropic's server-side tools run inside the provider, so unlike a client
|
|
110
|
+
* tool they never reach `ToolNode` and never produce a `PostToolBatch`
|
|
111
|
+
* boundary. `concat` also keeps their `server_tool_use` chunk on the
|
|
112
|
+
* accumulated message for the rest of the turn. Together that means a naive
|
|
113
|
+
* tool-call gate makes a turn permanently unsealable the moment a web
|
|
114
|
+
* search starts, with no later boundary to drain into — the queued message
|
|
115
|
+
* waits for the whole turn to finish rather than for the next tool step.
|
|
116
|
+
*
|
|
117
|
+
* So calls whose paired result block has already landed are treated as
|
|
118
|
+
* settled: they cannot be orphaned, because the provider has already
|
|
119
|
+
* answered them.
|
|
120
|
+
*/
|
|
121
|
+
const settled = settledServerToolCallIds(chunk.content);
|
|
122
|
+
if (countOpenToolCalls(chunk.tool_calls, settled) > 0) return false;
|
|
123
|
+
if (countOpenToolCalls(chunk.tool_call_chunks, settled) > 0) return false;
|
|
124
|
+
if ((chunk.invalid_tool_calls?.length ?? 0) > 0) return false;
|
|
125
|
+
if (hasOpenGoogleServerToolCall(chunk.content)) return false;
|
|
126
|
+
return hasNonEmptyTextContent(chunk.content);
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
exports.canSealPreempt = canSealPreempt;
|
|
130
|
+
exports.resolveMaxSeals = resolveMaxSeals;
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=preempt.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preempt.cjs","names":[],"sources":["../../../src/llm/preempt.ts"],"sourcesContent":["// src/llm/preempt.ts\nimport type { AIMessageChunk } from '@langchain/core/messages';\nimport { ContentTypes, DEFAULT_MAX_SEALS } from '@/common';\n\n/**\n * Normalizes a host-supplied seal budget.\n *\n * Read in two places that interpret it differently — a numeric comparison in\n * the seal gate and an addition into the recursion limit — so a value like\n * `1.5` would permit two seals while reserving fractional headroom, `NaN`\n * would poison the recursion limit outright, and `Infinity` would remove both\n * bounds at once. Normalizing once keeps the two readings in agreement.\n *\n * `0` is honored as a deliberate \"never seal\"; anything not finite falls back\n * to the default rather than silently disabling the feature.\n */\nexport function resolveMaxSeals(maxSeals: number | undefined): number {\n if (maxSeals == null || !Number.isFinite(maxSeals)) {\n return DEFAULT_MAX_SEALS;\n }\n const whole = Math.floor(maxSeals);\n return whole > 0 ? whole : 0;\n}\n\n/**\n * True when the accumulated content carries at least one non-whitespace text\n * block. `thinking` / `reasoning` blocks deliberately do not count: they are\n * stripped or signed on several providers and cannot stand in for the visible\n * assistant turn a sealed sequence needs.\n */\nfunction hasNonEmptyTextContent(content: AIMessageChunk['content']): boolean {\n if (typeof content === 'string') {\n return content.trim() !== '';\n }\n for (const block of content) {\n if (block.type !== ContentTypes.TEXT) {\n continue;\n }\n const text = block[ContentTypes.TEXT];\n if (typeof text === 'string' && text.trim() !== '') {\n return true;\n }\n }\n return false;\n}\n\n/**\n * True while a Gemini server-side tool call is still awaiting its response.\n *\n * Google's server-side tools (Search, URL context) never populate\n * `tool_calls` or `tool_call_chunks` — those are derived from `functionCall`\n * parts only, while a server-side invocation arrives as a `toolCall` CONTENT\n * block and its result as a later `toolResponse` block. The tool-call gates\n * cannot see it, so sealing between the two would replay a model turn holding\n * an unanswered server-side call.\n *\n * Counted rather than id-matched: Google answers calls in order, and counting\n * stays correct when a part omits its id.\n */\nfunction hasOpenGoogleServerToolCall(\n content: AIMessageChunk['content']\n): boolean {\n if (typeof content === 'string') {\n return false;\n }\n let calls = 0;\n let responses = 0;\n for (const block of content) {\n if (block.type === 'toolCall') {\n calls += 1;\n } else if (block.type === 'toolResponse') {\n responses += 1;\n }\n }\n return calls > responses;\n}\n\n/**\n * Ids of Anthropic server-side tool calls whose paired result block is already\n * on the accumulated content. Those calls are answered and cannot be orphaned\n * by a seal.\n */\nfunction settledServerToolCallIds(\n content: AIMessageChunk['content']\n): Set<string> {\n const settled = new Set<string>();\n if (typeof content === 'string') {\n return settled;\n }\n for (const block of content) {\n if (block.type !== 'web_search_tool_result') {\n continue;\n }\n const id = block.tool_use_id;\n if (typeof id === 'string') {\n settled.add(id);\n }\n }\n return settled;\n}\n\n/** Tool calls still awaiting an answer, ignoring ones already settled. */\nfunction countOpenToolCalls(\n calls: ReadonlyArray<{ id?: string }> | undefined,\n settled: Set<string>\n): number {\n if (calls == null || calls.length === 0) {\n return 0;\n }\n if (settled.size === 0) {\n return calls.length;\n }\n let open = 0;\n for (const call of calls) {\n if (call.id == null || !settled.has(call.id)) {\n open += 1;\n }\n }\n return open;\n}\n\n/**\n * Cooperative mid-generation seal gate. Returns true ONLY when sealing here\n * yields a message sequence valid on EVERY supported provider:\n * - non-whitespace TEXT content, so the FIRST injected user turn is preceded\n * by a non-empty assistant turn — no empty-content 400s. Note this says\n * nothing about adjacency AMONG several injected turns: a boundary that\n * drains two steers emits two consecutive user messages, which strict\n * providers reject. That is normalized at the provider-facing hop by\n * `coalesceAdjacentUserTurns`, not here;\n * - no tool call in flight, so no `tool_use` can be orphaned AND no eagerly\n * prestarted execution can be stripped out from under the model.\n *\n * Anthropic's server-side tools need no check of their own. Every\n * `server_tool_use` content block also emits a `tool_call_chunk`\n * (`_makeMessageChunkFromAnthropicEvent`), and `concat` keeps that chunk on\n * the accumulated message for the remainder of the turn, so the tool-call\n * gates below already cover it. The practical consequence is worth stating\n * plainly: once a turn starts a web search it is no longer preemptible, and\n * a queued message waits for the ordinary tool boundary instead.\n *\n * Nothing is stripped and nothing is repaired: when the accumulated shape is\n * not already safe the stream simply runs on, and whatever the host queued\n * lands at the next tool boundary instead. Chunks accumulate monotonically\n * through `concat`, so an unsafe shape can never be observed at a seal point.\n */\nexport function canSealPreempt(chunk: AIMessageChunk | undefined): boolean {\n if (chunk == null) {\n return false;\n }\n /**\n * Anthropic's server-side tools run inside the provider, so unlike a client\n * tool they never reach `ToolNode` and never produce a `PostToolBatch`\n * boundary. `concat` also keeps their `server_tool_use` chunk on the\n * accumulated message for the rest of the turn. Together that means a naive\n * tool-call gate makes a turn permanently unsealable the moment a web\n * search starts, with no later boundary to drain into — the queued message\n * waits for the whole turn to finish rather than for the next tool step.\n *\n * So calls whose paired result block has already landed are treated as\n * settled: they cannot be orphaned, because the provider has already\n * answered them.\n */\n const settled = settledServerToolCallIds(chunk.content);\n if (countOpenToolCalls(chunk.tool_calls, settled) > 0) {\n return false;\n }\n if (countOpenToolCalls(chunk.tool_call_chunks, settled) > 0) {\n return false;\n }\n if ((chunk.invalid_tool_calls?.length ?? 0) > 0) {\n return false;\n }\n if (hasOpenGoogleServerToolCall(chunk.content)) {\n return false;\n }\n return hasNonEmptyTextContent(chunk.content);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,UAAsC;CACpE,IAAI,YAAY,QAAQ,CAAC,OAAO,SAAS,QAAQ,GAC/C,OAAA;CAEF,MAAM,QAAQ,KAAK,MAAM,QAAQ;CACjC,OAAO,QAAQ,IAAI,QAAQ;AAC7B;;;;;;;AAQA,SAAS,uBAAuB,SAA6C;CAC3E,IAAI,OAAO,YAAY,UACrB,OAAO,QAAQ,KAAK,MAAM;CAE5B,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,MAAM,SAAA,QACR;EAEF,MAAM,OAAO,MAAA;EACb,IAAI,OAAO,SAAS,YAAY,KAAK,KAAK,MAAM,IAC9C,OAAO;CAEX;CACA,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAS,4BACP,SACS;CACT,IAAI,OAAO,YAAY,UACrB,OAAO;CAET,IAAI,QAAQ;CACZ,IAAI,YAAY;CAChB,KAAK,MAAM,SAAS,SAClB,IAAI,MAAM,SAAS,YACjB,SAAS;MACJ,IAAI,MAAM,SAAS,gBACxB,aAAa;CAGjB,OAAO,QAAQ;AACjB;;;;;;AAOA,SAAS,yBACP,SACa;CACb,MAAM,0BAAU,IAAI,IAAY;CAChC,IAAI,OAAO,YAAY,UACrB,OAAO;CAET,KAAK,MAAM,SAAS,SAAS;EAC3B,IAAI,MAAM,SAAS,0BACjB;EAEF,MAAM,KAAK,MAAM;EACjB,IAAI,OAAO,OAAO,UAChB,QAAQ,IAAI,EAAE;CAElB;CACA,OAAO;AACT;;AAGA,SAAS,mBACP,OACA,SACQ;CACR,IAAI,SAAS,QAAQ,MAAM,WAAW,GACpC,OAAO;CAET,IAAI,QAAQ,SAAS,GACnB,OAAO,MAAM;CAEf,IAAI,OAAO;CACX,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,MAAM,QAAQ,CAAC,QAAQ,IAAI,KAAK,EAAE,GACzC,QAAQ;CAGZ,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,eAAe,OAA4C;CACzE,IAAI,SAAS,MACX,OAAO;;;;;;;;;;;;;;CAeT,MAAM,UAAU,yBAAyB,MAAM,OAAO;CACtD,IAAI,mBAAmB,MAAM,YAAY,OAAO,IAAI,GAClD,OAAO;CAET,IAAI,mBAAmB,MAAM,kBAAkB,OAAO,IAAI,GACxD,OAAO;CAET,KAAK,MAAM,oBAAoB,UAAU,KAAK,GAC5C,OAAO;CAET,IAAI,4BAA4B,MAAM,OAAO,GAC3C,OAAO;CAET,OAAO,uBAAuB,MAAM,OAAO;AAC7C"}
|
package/dist/cjs/main.cjs
CHANGED
|
@@ -5,6 +5,7 @@ const require_enum = require("./common/enum.cjs");
|
|
|
5
5
|
require("./common/index.cjs");
|
|
6
6
|
const require_truncation = require("./utils/truncation.cjs");
|
|
7
7
|
const require_tokens = require("./utils/tokens.cjs");
|
|
8
|
+
const require_preempt = require("./llm/preempt.cjs");
|
|
8
9
|
const require_cache = require("./messages/cache.cjs");
|
|
9
10
|
const require_core = require("./messages/core.cjs");
|
|
10
11
|
const require_ids = require("./messages/ids.cjs");
|
|
@@ -16,12 +17,16 @@ const require_format = require("./messages/format.cjs");
|
|
|
16
17
|
const require_anthropicToolCache = require("./messages/anthropicToolCache.cjs");
|
|
17
18
|
const require_content = require("./messages/content.cjs");
|
|
18
19
|
const require_tools = require("./messages/tools.cjs");
|
|
20
|
+
const require_injected = require("./messages/injected.cjs");
|
|
21
|
+
const require_alternation = require("./messages/alternation.cjs");
|
|
22
|
+
const require_handoffCue = require("./messages/handoffCue.cjs");
|
|
19
23
|
const require_reducer = require("./messages/reducer.cjs");
|
|
20
24
|
const require_recency = require("./messages/recency.cjs");
|
|
21
25
|
require("./messages/index.cjs");
|
|
22
26
|
const require_graph = require("./utils/graph.cjs");
|
|
23
27
|
const require_llm = require("./utils/llm.cjs");
|
|
24
28
|
const require_handlers = require("./tools/handlers.cjs");
|
|
29
|
+
const require_intentArg = require("./tools/intentArg.cjs");
|
|
25
30
|
const require_stream = require("./stream.cjs");
|
|
26
31
|
const require_events = require("./events.cjs");
|
|
27
32
|
const require_handlers$1 = require("./utils/handlers.cjs");
|
|
@@ -75,7 +80,6 @@ const require_fake = require("./llm/fake.cjs");
|
|
|
75
80
|
const require_Graph = require("./graphs/Graph.cjs");
|
|
76
81
|
const require_MultiAgentGraph = require("./graphs/MultiAgentGraph.cjs");
|
|
77
82
|
const require_run$1 = require("./run.cjs");
|
|
78
|
-
const require_splitStream = require("./splitStream.cjs");
|
|
79
83
|
require("./graphs/index.cjs");
|
|
80
84
|
const require_projection = require("./agents/projection.cjs");
|
|
81
85
|
const require_Calculator = require("./tools/Calculator.cjs");
|
|
@@ -182,8 +186,10 @@ exports.DATE_RANGE = require_schema$2.DATE_RANGE;
|
|
|
182
186
|
exports.DEFAULT_CONTEXT_PRUNING_SETTINGS = require_contextPruningSettings.DEFAULT_CONTEXT_PRUNING_SETTINGS;
|
|
183
187
|
exports.DEFAULT_COUNTRY_DESCRIPTION = require_schema$2.DEFAULT_COUNTRY_DESCRIPTION;
|
|
184
188
|
exports.DEFAULT_HOOK_TIMEOUT_MS = require_executeHooks.DEFAULT_HOOK_TIMEOUT_MS;
|
|
189
|
+
exports.DEFAULT_MAX_SEALS = require_constants.DEFAULT_MAX_SEALS;
|
|
185
190
|
exports.DEFAULT_PROMPT_CACHE_TTL = require_cache.DEFAULT_PROMPT_CACHE_TTL;
|
|
186
191
|
exports.DEFAULT_QUERY_DESCRIPTION = require_schema$2.DEFAULT_QUERY_DESCRIPTION;
|
|
192
|
+
exports.DEFAULT_RECURSION_LIMIT = require_constants.DEFAULT_RECURSION_LIMIT;
|
|
187
193
|
exports.DEFAULT_RESERVE_RATIO = require_prune.DEFAULT_RESERVE_RATIO;
|
|
188
194
|
exports.DEFAULT_RETAIN_RECENT_TURNS = require_recency.DEFAULT_RETAIN_RECENT_TURNS;
|
|
189
195
|
exports.DEFAULT_TOOL_TOKEN_MULTIPLIER = require_constants.DEFAULT_TOOL_TOKEN_MULTIPLIER;
|
|
@@ -204,6 +210,7 @@ exports.HARD_MAX_TOOL_RESULT_CHARS = require_truncation.HARD_MAX_TOOL_RESULT_CHA
|
|
|
204
210
|
exports.HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE = require_truncation.HARD_MAX_TOTAL_TOOL_OUTPUT_SIZE;
|
|
205
211
|
exports.HOOK_EVENTS = require_types.HOOK_EVENTS;
|
|
206
212
|
exports.HOOK_INJECTED_MESSAGES_CAPABLE = require_index$5.HOOK_INJECTED_MESSAGES_CAPABLE;
|
|
213
|
+
exports.HOOK_PREEMPT_BOUNDARY_CAPABLE = require_index$5.HOOK_PREEMPT_BOUNDARY_CAPABLE;
|
|
207
214
|
exports.HandlerRegistry = require_events.HandlerRegistry;
|
|
208
215
|
exports.HookRegistry = require_HookRegistry.HookRegistry;
|
|
209
216
|
Object.defineProperty(exports, "HumanMessage", {
|
|
@@ -213,6 +220,9 @@ Object.defineProperty(exports, "HumanMessage", {
|
|
|
213
220
|
}
|
|
214
221
|
});
|
|
215
222
|
exports.IMAGE_TOKEN_SAFETY_MARGIN = require_tokens.IMAGE_TOKEN_SAFETY_MARGIN;
|
|
223
|
+
exports.INTENT_ARG = require_intentArg.INTENT_ARG;
|
|
224
|
+
exports.INTENT_DESCRIPTION = require_intentArg.INTENT_DESCRIPTION;
|
|
225
|
+
exports.INTENT_PROPERTY = require_intentArg.INTENT_PROPERTY;
|
|
216
226
|
Object.defineProperty(exports, "INTERRUPT", {
|
|
217
227
|
enumerable: true,
|
|
218
228
|
get: function() {
|
|
@@ -249,6 +259,8 @@ Object.defineProperty(exports, "MemorySaver", {
|
|
|
249
259
|
exports.ModelEndHandler = require_events.ModelEndHandler;
|
|
250
260
|
exports.MultiAgentGraph = require_MultiAgentGraph.MultiAgentGraph;
|
|
251
261
|
exports.ORIGINAL_CONTENT_MAX_CHARS = require_prune.ORIGINAL_CONTENT_MAX_CHARS;
|
|
262
|
+
exports.PREDECESSOR_HANDOFF_CUE = require_handoffCue.PREDECESSOR_HANDOFF_CUE;
|
|
263
|
+
exports.PREEMPT_BOUNDARY_HOOK_TIMEOUT_MS = require_constants.PREEMPT_BOUNDARY_HOOK_TIMEOUT_MS;
|
|
252
264
|
exports.ProgrammaticToolCallingDefinition = require_ProgrammaticToolCalling.ProgrammaticToolCallingDefinition;
|
|
253
265
|
exports.ProgrammaticToolCallingDescription = require_ProgrammaticToolCalling.ProgrammaticToolCallingDescription;
|
|
254
266
|
exports.ProgrammaticToolCallingName = require_ProgrammaticToolCalling.ProgrammaticToolCallingName;
|
|
@@ -286,7 +298,7 @@ Object.defineProperty(exports, "RunnableSequence", {
|
|
|
286
298
|
return _langchain_core_runnables.RunnableSequence;
|
|
287
299
|
}
|
|
288
300
|
});
|
|
289
|
-
exports.
|
|
301
|
+
exports.SDK_STREAM_DISPATCH = require_stream.SDK_STREAM_DISPATCH;
|
|
290
302
|
exports.STATEFUL_BASH_NOTE = require_BashExecutor.STATEFUL_BASH_NOTE;
|
|
291
303
|
exports.STATEFUL_ENV_NOTE = require_CodeExecutor.STATEFUL_ENV_NOTE;
|
|
292
304
|
exports.SessionManager = require_JsonlSessionStore.SessionManager;
|
|
@@ -294,7 +306,6 @@ exports.SkillToolDefinition = require_SkillTool.SkillToolDefinition;
|
|
|
294
306
|
exports.SkillToolDescription = require_SkillTool.SkillToolDescription;
|
|
295
307
|
exports.SkillToolName = require_SkillTool.SkillToolName;
|
|
296
308
|
exports.SkillToolSchema = require_SkillTool.SkillToolSchema;
|
|
297
|
-
exports.SplitStreamHandler = require_splitStream.SplitStreamHandler;
|
|
298
309
|
exports.StandardGraph = require_Graph.StandardGraph;
|
|
299
310
|
exports.StatefulBashExecutionToolDescription = require_BashExecutor.StatefulBashExecutionToolDescription;
|
|
300
311
|
exports.StatefulCodeExecutionToolDescription = require_CodeExecutor.StatefulCodeExecutionToolDescription;
|
|
@@ -356,9 +367,11 @@ exports.addCacheControlToStablePrefixMessages = require_cache.addCacheControlToS
|
|
|
356
367
|
exports.addTailCacheControl = require_cache.addTailCacheControl;
|
|
357
368
|
exports.appendCodeSessionFileSummary = require_CodeSessionFileSummary.appendCodeSessionFileSummary;
|
|
358
369
|
exports.appendFailedExecutionFileReminder = require_CodeExecutor.appendFailedExecutionFileReminder;
|
|
370
|
+
exports.appendPredecessorHandoffCue = require_handoffCue.appendPredecessorHandoffCue;
|
|
359
371
|
exports.appendTmpScratchReminder = require_CodeExecutor.appendTmpScratchReminder;
|
|
360
372
|
exports.applyContextPruning = require_contextPruning.applyContextPruning;
|
|
361
373
|
exports.applyEdit = require_editStrategies.applyEdit;
|
|
374
|
+
exports.applyOutcome = require_intentArg.applyOutcome;
|
|
362
375
|
exports.applyPreToolUseHooksForBridge = require_LocalProgrammaticToolCalling.applyPreToolUseHooksForBridge;
|
|
363
376
|
exports.apportionTokenCounts = require_tokens.apportionTokenCounts;
|
|
364
377
|
exports.askUserQuestion = require_askUserQuestion.askUserQuestion;
|
|
@@ -379,13 +392,16 @@ exports.calculateMaxToolCallInputChars = require_prune.calculateMaxToolCallInput
|
|
|
379
392
|
exports.calculateMaxToolResultChars = require_truncation.calculateMaxToolResultChars;
|
|
380
393
|
exports.calculateMaxTotalToolOutputSize = require_truncation.calculateMaxTotalToolOutputSize;
|
|
381
394
|
exports.calculateTotalTokens = require_prune.calculateTotalTokens;
|
|
395
|
+
exports.canSealPreempt = require_preempt.canSealPreempt;
|
|
382
396
|
exports.checkValidNumber = require_prune.checkValidNumber;
|
|
383
397
|
exports.clampCalibrationRatio = require_prune.clampCalibrationRatio;
|
|
384
398
|
exports.classifyAttachment = require_attachments.classifyAttachment;
|
|
385
399
|
exports.clientExecTimeoutMs = require_CloudflareSandboxExecutionEngine.clientExecTimeoutMs;
|
|
386
400
|
exports.clientFsTimeoutMs = require_CloudflareSandboxExecutionEngine.clientFsTimeoutMs;
|
|
387
401
|
exports.cloneMessage = require_cache.cloneMessage;
|
|
402
|
+
exports.coalesceAdjacentUserTurns = require_alternation.coalesceAdjacentUserTurns;
|
|
388
403
|
exports.composeEventHandlers = require_events.composeEventHandlers;
|
|
404
|
+
exports.convertInjectedMessages = require_injected.convertInjectedMessages;
|
|
389
405
|
exports.convertMessagesToContent = require_core.convertMessagesToContent;
|
|
390
406
|
exports.countNestedGroups = require_ToolSearch.countNestedGroups;
|
|
391
407
|
exports.countrySchema = require_schema$2.countrySchema;
|
|
@@ -435,6 +451,7 @@ exports.createSchemaOnlyTools = require_schema$1.createSchemaOnlyTools;
|
|
|
435
451
|
exports.createSearchTool = require_tool.createSearchTool;
|
|
436
452
|
exports.createSubagentToolDefinition = require_SubagentTool.createSubagentToolDefinition;
|
|
437
453
|
exports.createTokenCounter = require_tokens.createTokenCounter;
|
|
454
|
+
exports.createToolErrorOwnership = require_ToolNode.createToolErrorOwnership;
|
|
438
455
|
exports.createToolPolicyHook = require_createToolPolicyHook.createToolPolicyHook;
|
|
439
456
|
exports.createToolSearch = require_ToolSearch.createToolSearch;
|
|
440
457
|
exports.createWorkspacePolicyHook = require_createWorkspacePolicyHook.createWorkspacePolicyHook;
|
|
@@ -442,6 +459,7 @@ exports.dateSchema = require_schema$2.dateSchema;
|
|
|
442
459
|
exports.decodeFile = require_textEncoding.decodeFile;
|
|
443
460
|
exports.defaultOmitOptions = require_run$1.defaultOmitOptions;
|
|
444
461
|
exports.deserializeMessage = require_messageSerialization.deserializeMessage;
|
|
462
|
+
exports.dispatchesChatModelStream = require_stream.dispatchesChatModelStream;
|
|
445
463
|
exports.emptyOutputMessage = require_CodeExecutor.emptyOutputMessage;
|
|
446
464
|
exports.encodeFile = require_textEncoding.encodeFile;
|
|
447
465
|
exports.encodingForModel = require_tokens.encodingForModel;
|
|
@@ -548,6 +566,7 @@ exports.isDangerousPattern = require_ToolSearch.isDangerousPattern;
|
|
|
548
566
|
exports.isFromAnyMcpServer = require_ToolSearch.isFromAnyMcpServer;
|
|
549
567
|
exports.isFromMcpServer = require_ToolSearch.isFromMcpServer;
|
|
550
568
|
exports.isGoogleLike = require_llm.isGoogleLike;
|
|
569
|
+
exports.isIntentLabelProperty = require_intentArg.isIntentLabelProperty;
|
|
551
570
|
Object.defineProperty(exports, "isInterrupted", {
|
|
552
571
|
enumerable: true,
|
|
553
572
|
get: function() {
|
|
@@ -582,6 +601,7 @@ exports.normalizeCodeApiRequestError = require_CodeExecutor.normalizeCodeApiRequ
|
|
|
582
601
|
exports.normalizeServerFilter = require_ToolSearch.normalizeServerFilter;
|
|
583
602
|
exports.normalizeToBashIdentifier = require_BashProgrammaticToolCalling.normalizeToBashIdentifier;
|
|
584
603
|
exports.normalizeToPythonIdentifier = require_ProgrammaticToolCalling.normalizeToPythonIdentifier;
|
|
604
|
+
exports.outcomeFieldsFromResult = require_intentArg.outcomeFieldsFromResult;
|
|
585
605
|
exports.parseBooleanEnv = require_misc.parseBooleanEnv;
|
|
586
606
|
exports.partitionAndMarkAnthropicToolCache = require_anthropicToolCache.partitionAndMarkAnthropicToolCache;
|
|
587
607
|
exports.performLocalSearch = require_ToolSearch.performLocalSearch;
|
|
@@ -601,6 +621,9 @@ exports.projectStructuredToolOutputsToText = require_core.projectStructuredToolO
|
|
|
601
621
|
exports.projectToolCallInputs = require_prune.projectToolCallInputs;
|
|
602
622
|
exports.projectToolStreamContentForProvider = require_core.projectToolStreamContentForProvider;
|
|
603
623
|
exports.querySchema = require_schema$2.querySchema;
|
|
624
|
+
exports.readIntent = require_intentArg.readIntent;
|
|
625
|
+
exports.readOutcomeFields = require_intentArg.readOutcomeFields;
|
|
626
|
+
exports.removePredecessorHandoffCue = require_handoffCue.removePredecessorHandoffCue;
|
|
604
627
|
exports.repairOrphanedToolMessages = require_prune.repairOrphanedToolMessages;
|
|
605
628
|
exports.resetIfNotEmpty = require_graph.resetIfNotEmpty;
|
|
606
629
|
exports.resolveBedrockPromptCacheTtl = require_cache.resolveBedrockPromptCacheTtl;
|
|
@@ -612,7 +635,9 @@ exports.resolveLocalExecutionTools = require_resolveLocalExecutionTools.resolveL
|
|
|
612
635
|
exports.resolveLocalToolRegistry = require_resolveLocalExecutionTools.resolveLocalToolRegistry;
|
|
613
636
|
exports.resolveLocalToolsForBinding = require_resolveLocalExecutionTools.resolveLocalToolsForBinding;
|
|
614
637
|
exports.resolvePromptCacheTtl = require_cache.resolvePromptCacheTtl;
|
|
638
|
+
exports.resolveSearchOutcome = require_tool.resolveSearchOutcome;
|
|
615
639
|
exports.resolveSubagentConfigs = require_SubagentExecutor.resolveSubagentConfigs;
|
|
640
|
+
exports.resolveToolOutcome = require_intentArg.resolveToolOutcome;
|
|
616
641
|
exports.resolveWorkspacePath = require_LocalExecutionEngine.resolveWorkspacePath;
|
|
617
642
|
exports.resolveWorkspacePathSafe = require_LocalExecutionEngine.resolveWorkspacePathSafe;
|
|
618
643
|
exports.runBashAstChecks = require_bashAst.runBashAstChecks;
|
|
@@ -627,9 +652,11 @@ exports.shouldTriggerSummarization = require_index$9.shouldTriggerSummarization;
|
|
|
627
652
|
exports.sleep = require_run.sleep;
|
|
628
653
|
exports.spawnLocalProcess = require_LocalExecutionEngine.spawnLocalProcess;
|
|
629
654
|
exports.splitAtRecencyBoundary = require_recency.splitAtRecencyBoundary;
|
|
655
|
+
exports.strictAlternationProviders = require_alternation.strictAlternationProviders;
|
|
630
656
|
exports.stripAnthropicCacheControl = require_cache.stripAnthropicCacheControl;
|
|
631
657
|
exports.stripBedrockCacheControl = require_cache.stripBedrockCacheControl;
|
|
632
658
|
exports.stripCodeSessionFileSummary = require_CodeSessionFileSummary.stripCodeSessionFileSummary;
|
|
659
|
+
exports.stripIntent = require_intentArg.stripIntent;
|
|
633
660
|
exports.summarizeEvent = require_SubagentExecutor.summarizeEvent;
|
|
634
661
|
exports.supportsBedrockToolCache = require_cache.supportsBedrockToolCache;
|
|
635
662
|
exports.syncBudgetDerivedFields = require_budget.syncBudgetDerivedFields;
|
|
@@ -652,4 +679,5 @@ exports.validateBashCommand = require_LocalExecutionEngine.validateBashCommand;
|
|
|
652
679
|
exports.validateCloudflareBashCommand = require_CloudflareSandboxExecutionEngine.validateCloudflareBashCommand;
|
|
653
680
|
exports.videosSchema = require_schema$2.videosSchema;
|
|
654
681
|
exports.withClientTimeout = require_CloudflareSandboxExecutionEngine.withClientTimeout;
|
|
682
|
+
exports.withIntent = require_intentArg.withIntent;
|
|
655
683
|
exports.withMessageRole = require_format.withMessageRole;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require("../common/enum.cjs");
|
|
2
|
+
require("../common/index.cjs");
|
|
3
|
+
let _langchain_core_messages = require("@langchain/core/messages");
|
|
4
|
+
//#region src/messages/alternation.ts
|
|
5
|
+
/**
|
|
6
|
+
* Providers whose APIs specify strict user/assistant alternation. Mistral
|
|
7
|
+
* rejects consecutive user turns outright. Bedrock's Converse API documents
|
|
8
|
+
* the alternation requirement across many model families; enforcement varies
|
|
9
|
+
* by family — Claude on Converse currently tolerates adjacent user turns
|
|
10
|
+
* (verified live, 2026-07-28) — so the payload is normalized for all of them
|
|
11
|
+
* rather than betting on per-family leniency. Anthropic's own Messages API,
|
|
12
|
+
* OpenAI and Gemini all accept consecutive user turns, so they are
|
|
13
|
+
* deliberately absent.
|
|
14
|
+
*/
|
|
15
|
+
const strictAlternationProviders = new Set([
|
|
16
|
+
"bedrock",
|
|
17
|
+
"mistral",
|
|
18
|
+
"mistralai"
|
|
19
|
+
]);
|
|
20
|
+
const TOOL_RESULT_TYPES = new Set(["tool_result", "toolResult"]);
|
|
21
|
+
/**
|
|
22
|
+
* True when every block is a tool result. Both vendored converters already
|
|
23
|
+
* merge adjacent runs of these, and folding one into a text turn would break
|
|
24
|
+
* the tool pairing they depend on — so they are left alone here.
|
|
25
|
+
*/
|
|
26
|
+
function isToolResultMessage(message) {
|
|
27
|
+
const { content } = message;
|
|
28
|
+
if (typeof content === "string" || content.length === 0) return false;
|
|
29
|
+
return content.every((block) => typeof block.type === "string" && TOOL_RESULT_TYPES.has(block.type));
|
|
30
|
+
}
|
|
31
|
+
function toBlocks(content) {
|
|
32
|
+
if (typeof content === "string") return content === "" ? [] : [{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: content
|
|
35
|
+
}];
|
|
36
|
+
return content;
|
|
37
|
+
}
|
|
38
|
+
function joinContent(left, right) {
|
|
39
|
+
if (typeof left === "string" && typeof right === "string") return left === "" ? right : `${left}\n\n${right}`;
|
|
40
|
+
return [...toBlocks(left), ...toBlocks(right)];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Merges runs of consecutive human turns into one, for providers that reject
|
|
44
|
+
* them. Purely a wire-shaping pass: it returns a new array of new messages,
|
|
45
|
+
* so graph state and the host's persisted messages keep the per-message
|
|
46
|
+
* identity that steer rendering and the trailing-steer anchor rely on.
|
|
47
|
+
*
|
|
48
|
+
* Tool-result turns are excluded — the converters merge those themselves, and
|
|
49
|
+
* combining one with a text turn would orphan the pairing.
|
|
50
|
+
*/
|
|
51
|
+
function coalesceAdjacentUserTurns(messages) {
|
|
52
|
+
const result = [];
|
|
53
|
+
let mergedAny = false;
|
|
54
|
+
for (const message of messages) {
|
|
55
|
+
const previous = result[result.length - 1];
|
|
56
|
+
if (!(result.length > 0 && previous.getType() === "human" && message.getType() === "human" && !isToolResultMessage(previous) && !isToolResultMessage(message))) {
|
|
57
|
+
result.push(message);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
mergedAny = true;
|
|
61
|
+
result[result.length - 1] = new _langchain_core_messages.HumanMessage({
|
|
62
|
+
content: joinContent(previous.content, message.content),
|
|
63
|
+
/**
|
|
64
|
+
* The LATER turn's kwargs, deliberately. The one provider-path consumer
|
|
65
|
+
* of these flags is the prompt-cache tail anchor, and it reasons
|
|
66
|
+
* positionally: `isSyntheticMetaMessage` decides whether a breakpoint
|
|
67
|
+
* may be inserted after the message's LAST text block. Merging keeps
|
|
68
|
+
* the last block's provenance only if the last part's kwargs survive —
|
|
69
|
+
* a skill body absorbed into a real user turn must stay anchorable
|
|
70
|
+
* (the real turn ends it), while a real steer absorbed into a trailing
|
|
71
|
+
* skill body must not pin the cache to the volatile body. The first
|
|
72
|
+
* turn's id is kept so origin tracking can re-attach by key.
|
|
73
|
+
*/
|
|
74
|
+
additional_kwargs: message.additional_kwargs,
|
|
75
|
+
...previous.id != null && { id: previous.id }
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Identity on the no-merge path. The pass runs twice for a primary
|
|
80
|
+
* Bedrock/Mistral call — once in `createCallModel` (must precede the cache
|
|
81
|
+
* breakpoint) and once in the `attemptInvoke` funnel (must cover fallback
|
|
82
|
+
* and summarization sends) — so the already-normalized second pass returns
|
|
83
|
+
* the SAME array rather than reallocating a context-sized copy, and
|
|
84
|
+
* callers can cheaply detect "nothing changed" by identity.
|
|
85
|
+
*/
|
|
86
|
+
return mergedAny ? result : messages;
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
exports.coalesceAdjacentUserTurns = coalesceAdjacentUserTurns;
|
|
90
|
+
exports.strictAlternationProviders = strictAlternationProviders;
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=alternation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alternation.cjs","names":["HumanMessage"],"sources":["../../../src/messages/alternation.ts"],"sourcesContent":["// src/messages/alternation.ts\nimport { HumanMessage } from '@langchain/core/messages';\nimport type { BaseMessage, MessageContent } from '@langchain/core/messages';\nimport { Providers } from '@/common';\n\n/**\n * Providers whose APIs specify strict user/assistant alternation. Mistral\n * rejects consecutive user turns outright. Bedrock's Converse API documents\n * the alternation requirement across many model families; enforcement varies\n * by family — Claude on Converse currently tolerates adjacent user turns\n * (verified live, 2026-07-28) — so the payload is normalized for all of them\n * rather than betting on per-family leniency. Anthropic's own Messages API,\n * OpenAI and Gemini all accept consecutive user turns, so they are\n * deliberately absent.\n */\nexport const strictAlternationProviders: ReadonlySet<Providers> = new Set([\n Providers.BEDROCK,\n Providers.MISTRAL,\n Providers.MISTRALAI,\n]);\n\nconst TOOL_RESULT_TYPES = new Set(['tool_result', 'toolResult']);\n\n/**\n * True when every block is a tool result. Both vendored converters already\n * merge adjacent runs of these, and folding one into a text turn would break\n * the tool pairing they depend on — so they are left alone here.\n */\nfunction isToolResultMessage(message: BaseMessage): boolean {\n const { content } = message;\n if (typeof content === 'string' || content.length === 0) {\n return false;\n }\n return content.every(\n (block) =>\n typeof block.type === 'string' && TOOL_RESULT_TYPES.has(block.type)\n );\n}\n\nfunction toBlocks(content: MessageContent): Exclude<MessageContent, string> {\n if (typeof content === 'string') {\n return content === '' ? [] : [{ type: 'text', text: content }];\n }\n return content;\n}\n\nfunction joinContent(\n left: MessageContent,\n right: MessageContent\n): MessageContent {\n if (typeof left === 'string' && typeof right === 'string') {\n return left === '' ? right : `${left}\\n\\n${right}`;\n }\n return [...toBlocks(left), ...toBlocks(right)];\n}\n\n/**\n * Merges runs of consecutive human turns into one, for providers that reject\n * them. Purely a wire-shaping pass: it returns a new array of new messages,\n * so graph state and the host's persisted messages keep the per-message\n * identity that steer rendering and the trailing-steer anchor rely on.\n *\n * Tool-result turns are excluded — the converters merge those themselves, and\n * combining one with a text turn would orphan the pairing.\n */\nexport function coalesceAdjacentUserTurns(\n messages: BaseMessage[]\n): BaseMessage[] {\n const result: BaseMessage[] = [];\n let mergedAny = false;\n for (const message of messages) {\n const previous = result[result.length - 1];\n const mergeable =\n result.length > 0 &&\n previous.getType() === 'human' &&\n message.getType() === 'human' &&\n !isToolResultMessage(previous) &&\n !isToolResultMessage(message);\n\n if (!mergeable) {\n result.push(message);\n continue;\n }\n mergedAny = true;\n\n result[result.length - 1] = new HumanMessage({\n content: joinContent(previous.content, message.content),\n /**\n * The LATER turn's kwargs, deliberately. The one provider-path consumer\n * of these flags is the prompt-cache tail anchor, and it reasons\n * positionally: `isSyntheticMetaMessage` decides whether a breakpoint\n * may be inserted after the message's LAST text block. Merging keeps\n * the last block's provenance only if the last part's kwargs survive —\n * a skill body absorbed into a real user turn must stay anchorable\n * (the real turn ends it), while a real steer absorbed into a trailing\n * skill body must not pin the cache to the volatile body. The first\n * turn's id is kept so origin tracking can re-attach by key.\n */\n additional_kwargs: message.additional_kwargs,\n ...(previous.id != null && { id: previous.id }),\n });\n }\n /**\n * Identity on the no-merge path. The pass runs twice for a primary\n * Bedrock/Mistral call — once in `createCallModel` (must precede the cache\n * breakpoint) and once in the `attemptInvoke` funnel (must cover fallback\n * and summarization sends) — so the already-normalized second pass returns\n * the SAME array rather than reallocating a context-sized copy, and\n * callers can cheaply detect \"nothing changed\" by identity.\n */\n return mergedAny ? result : messages;\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,MAAa,6BAAqD,IAAI,IAAI;;;;AAI1E,CAAC;AAED,MAAM,oBAAoB,IAAI,IAAI,CAAC,eAAe,YAAY,CAAC;;;;;;AAO/D,SAAS,oBAAoB,SAA+B;CAC1D,MAAM,EAAE,YAAY;CACpB,IAAI,OAAO,YAAY,YAAY,QAAQ,WAAW,GACpD,OAAO;CAET,OAAO,QAAQ,OACZ,UACC,OAAO,MAAM,SAAS,YAAY,kBAAkB,IAAI,MAAM,IAAI,CACtE;AACF;AAEA,SAAS,SAAS,SAA0D;CAC1E,IAAI,OAAO,YAAY,UACrB,OAAO,YAAY,KAAK,CAAC,IAAI,CAAC;EAAE,MAAM;EAAQ,MAAM;CAAQ,CAAC;CAE/D,OAAO;AACT;AAEA,SAAS,YACP,MACA,OACgB;CAChB,IAAI,OAAO,SAAS,YAAY,OAAO,UAAU,UAC/C,OAAO,SAAS,KAAK,QAAQ,GAAG,KAAK,MAAM;CAE7C,OAAO,CAAC,GAAG,SAAS,IAAI,GAAG,GAAG,SAAS,KAAK,CAAC;AAC/C;;;;;;;;;;AAWA,SAAgB,0BACd,UACe;CACf,MAAM,SAAwB,CAAC;CAC/B,IAAI,YAAY;CAChB,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,WAAW,OAAO,OAAO,SAAS;EAQxC,IAAI,EANF,OAAO,SAAS,KAChB,SAAS,QAAQ,MAAM,WACvB,QAAQ,QAAQ,MAAM,WACtB,CAAC,oBAAoB,QAAQ,KAC7B,CAAC,oBAAoB,OAAO,IAEd;GACd,OAAO,KAAK,OAAO;GACnB;EACF;EACA,YAAY;EAEZ,OAAO,OAAO,SAAS,KAAK,IAAIA,yBAAAA,aAAa;GAC3C,SAAS,YAAY,SAAS,SAAS,QAAQ,OAAO;;;;;;;;;;;;GAYtD,mBAAmB,QAAQ;GAC3B,GAAI,SAAS,MAAM,QAAQ,EAAE,IAAI,SAAS,GAAG;EAC/C,CAAC;CACH;;;;;;;;;CASA,OAAO,YAAY,SAAS;AAC9B"}
|
|
@@ -182,6 +182,19 @@ function formatToolCallOutput(output) {
|
|
|
182
182
|
return require_toolContent.compactToolContent(output, require_truncation.HARD_MAX_TOOL_RESULT_CHARS).content;
|
|
183
183
|
}
|
|
184
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
|
+
}
|
|
197
|
+
/**
|
|
185
198
|
* Helper function to format an assistant message
|
|
186
199
|
* @param message The message to format
|
|
187
200
|
* @param options Optional formatting options
|
|
@@ -617,6 +630,27 @@ function extractSkillName(args) {
|
|
|
617
630
|
*/
|
|
618
631
|
const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options) => {
|
|
619
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
|
+
};
|
|
620
654
|
const updatedIndexTokenCountMap = {};
|
|
621
655
|
let boundaryTokenAdjustment;
|
|
622
656
|
const indexMapping = {};
|
|
@@ -652,6 +686,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
|
|
|
652
686
|
langChain: true
|
|
653
687
|
});
|
|
654
688
|
if (sourceMessageId != null && sourceMessageId !== "") formattedMessage.id = sourceMessageId;
|
|
689
|
+
flushSteerAnchor(formattedMessage);
|
|
655
690
|
messages.push(formattedMessage);
|
|
656
691
|
indexMapping[i] = [messages.length - 1];
|
|
657
692
|
continue;
|
|
@@ -755,7 +790,44 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools, skills, options
|
|
|
755
790
|
provider: options?.provider
|
|
756
791
|
});
|
|
757
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]);
|
|
758
829
|
messages.push(...formattedMessages);
|
|
830
|
+
if (endsWithSteerMessage(formattedMessages)) pendingSteerAnchor = true;
|
|
759
831
|
const endMessageIndex = messages.length;
|
|
760
832
|
if (pendingSkillNames?.size != null && pendingSkillNames.size > 0) {
|
|
761
833
|
const skipSkillBodyNames = options?.skipSkillBodyNames;
|