illuma-agents 1.0.4 → 1.0.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.
Files changed (46) hide show
  1. package/dist/cjs/graphs/Graph.cjs +80 -1
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  4. package/dist/cjs/main.cjs +4 -0
  5. package/dist/cjs/main.cjs.map +1 -1
  6. package/dist/cjs/messages/cache.cjs +87 -14
  7. package/dist/cjs/messages/cache.cjs.map +1 -1
  8. package/dist/cjs/messages/format.cjs +242 -6
  9. package/dist/cjs/messages/format.cjs.map +1 -1
  10. package/dist/cjs/stream.cjs +3 -2
  11. package/dist/cjs/stream.cjs.map +1 -1
  12. package/dist/cjs/tools/handlers.cjs +5 -5
  13. package/dist/cjs/tools/handlers.cjs.map +1 -1
  14. package/dist/esm/graphs/Graph.mjs +80 -1
  15. package/dist/esm/graphs/Graph.mjs.map +1 -1
  16. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  17. package/dist/esm/main.mjs +2 -2
  18. package/dist/esm/messages/cache.mjs +86 -15
  19. package/dist/esm/messages/cache.mjs.map +1 -1
  20. package/dist/esm/messages/format.mjs +242 -8
  21. package/dist/esm/messages/format.mjs.map +1 -1
  22. package/dist/esm/stream.mjs +3 -2
  23. package/dist/esm/stream.mjs.map +1 -1
  24. package/dist/esm/tools/handlers.mjs +5 -5
  25. package/dist/esm/tools/handlers.mjs.map +1 -1
  26. package/dist/types/graphs/Graph.d.ts +19 -2
  27. package/dist/types/messages/cache.d.ts +16 -0
  28. package/dist/types/messages/format.d.ts +25 -1
  29. package/dist/types/tools/handlers.d.ts +2 -1
  30. package/dist/types/types/stream.d.ts +1 -0
  31. package/package.json +4 -1
  32. package/src/graphs/Graph.ts +99 -2
  33. package/src/llm/anthropic/utils/message_outputs.ts +289 -289
  34. package/src/messages/cache.test.ts +499 -3
  35. package/src/messages/cache.ts +115 -25
  36. package/src/messages/ensureThinkingBlock.test.ts +393 -0
  37. package/src/messages/format.ts +312 -6
  38. package/src/messages/labelContentByAgent.test.ts +887 -0
  39. package/src/scripts/test-multi-agent-list-handoff.ts +169 -13
  40. package/src/scripts/test-parallel-agent-labeling.ts +325 -0
  41. package/src/scripts/test-thinking-handoff-bedrock.ts +153 -0
  42. package/src/scripts/test-thinking-handoff.ts +147 -0
  43. package/src/specs/thinking-handoff.test.ts +620 -0
  44. package/src/stream.ts +19 -10
  45. package/src/tools/handlers.ts +36 -18
  46. package/src/types/stream.ts +1 -0
@@ -2,6 +2,9 @@ import { ContentTypes } from '../common/enum.mjs';
2
2
 
3
3
  /**
4
4
  * Anthropic API: Adds cache control to the appropriate user messages in the payload.
5
+ * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,
6
+ * then adds fresh cache control to the last 2 user messages in a single backward pass.
7
+ * This ensures we don't accumulate stale cache points across multiple turns.
5
8
  * @param messages - The array of message objects.
6
9
  * @returns - The updated array of message objects with cache control added.
7
10
  */
@@ -11,12 +14,20 @@ function addCacheControl(messages) {
11
14
  }
12
15
  const updatedMessages = [...messages];
13
16
  let userMessagesModified = 0;
14
- for (let i = updatedMessages.length - 1; i >= 0 && userMessagesModified < 2; i--) {
17
+ for (let i = updatedMessages.length - 1; i >= 0; i--) {
15
18
  const message = updatedMessages[i];
16
- if ('getType' in message && message.getType() !== 'human') {
17
- continue;
19
+ const isUserMessage = ('getType' in message && message.getType() === 'human') ||
20
+ ('role' in message && message.role === 'user');
21
+ if (Array.isArray(message.content)) {
22
+ message.content = message.content.filter((block) => !isCachePoint(block));
23
+ for (let j = 0; j < message.content.length; j++) {
24
+ const block = message.content[j];
25
+ if ('cache_control' in block) {
26
+ delete block.cache_control;
27
+ }
28
+ }
18
29
  }
19
- else if ('role' in message && message.role !== 'user') {
30
+ if (userMessagesModified >= 2 || !isUserMessage) {
20
31
  continue;
21
32
  }
22
33
  if (typeof message.content === 'string') {
@@ -44,10 +55,60 @@ function addCacheControl(messages) {
44
55
  }
45
56
  return updatedMessages;
46
57
  }
58
+ /**
59
+ * Checks if a content block is a cache point
60
+ */
61
+ function isCachePoint(block) {
62
+ return 'cachePoint' in block && !('type' in block);
63
+ }
64
+ /**
65
+ * Removes all Anthropic cache_control fields from messages
66
+ * Used when switching from Anthropic to Bedrock provider
67
+ */
68
+ function stripAnthropicCacheControl(messages) {
69
+ if (!Array.isArray(messages)) {
70
+ return messages;
71
+ }
72
+ const updatedMessages = [...messages];
73
+ for (let i = 0; i < updatedMessages.length; i++) {
74
+ const message = updatedMessages[i];
75
+ const content = message.content;
76
+ if (Array.isArray(content)) {
77
+ for (let j = 0; j < content.length; j++) {
78
+ const block = content[j];
79
+ if ('cache_control' in block) {
80
+ delete block.cache_control;
81
+ }
82
+ }
83
+ }
84
+ }
85
+ return updatedMessages;
86
+ }
87
+ /**
88
+ * Removes all Bedrock cachePoint blocks from messages
89
+ * Used when switching from Bedrock to Anthropic provider
90
+ */
91
+ function stripBedrockCacheControl(messages) {
92
+ if (!Array.isArray(messages)) {
93
+ return messages;
94
+ }
95
+ const updatedMessages = [...messages];
96
+ for (let i = 0; i < updatedMessages.length; i++) {
97
+ const message = updatedMessages[i];
98
+ const content = message.content;
99
+ if (Array.isArray(content)) {
100
+ message.content = content.filter((block) => !isCachePoint(block));
101
+ }
102
+ }
103
+ return updatedMessages;
104
+ }
47
105
  /**
48
106
  * Adds Bedrock Converse API cache points to the last two messages.
49
107
  * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block
50
108
  * immediately after the last text block in each targeted message.
109
+ * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,
110
+ * then adds fresh cache points to the last 2 messages in a single backward pass.
111
+ * This ensures we don't accumulate stale cache points across multiple turns.
51
112
  * @param messages - The array of message objects.
52
113
  * @returns - The updated array of message objects with cache points added.
53
114
  */
@@ -57,14 +118,24 @@ function addBedrockCacheControl(messages) {
57
118
  }
58
119
  const updatedMessages = messages.slice();
59
120
  let messagesModified = 0;
60
- for (let i = updatedMessages.length - 1; i >= 0 && messagesModified < 2; i--) {
121
+ for (let i = updatedMessages.length - 1; i >= 0; i--) {
61
122
  const message = updatedMessages[i];
62
- if ('getType' in message &&
123
+ const isToolMessage = 'getType' in message &&
63
124
  typeof message.getType === 'function' &&
64
- message.getType() === 'tool') {
125
+ message.getType() === 'tool';
126
+ const content = message.content;
127
+ if (Array.isArray(content)) {
128
+ message.content = content.filter((block) => !isCachePoint(block));
129
+ for (let j = 0; j < message.content.length; j++) {
130
+ const block = message.content[j];
131
+ if ('cache_control' in block) {
132
+ delete block.cache_control;
133
+ }
134
+ }
135
+ }
136
+ if (messagesModified >= 2 || isToolMessage) {
65
137
  continue;
66
138
  }
67
- const content = message.content;
68
139
  if (typeof content === 'string' && content === '') {
69
140
  continue;
70
141
  }
@@ -76,9 +147,9 @@ function addBedrockCacheControl(messages) {
76
147
  messagesModified++;
77
148
  continue;
78
149
  }
79
- if (Array.isArray(content)) {
150
+ if (Array.isArray(message.content)) {
80
151
  let hasCacheableContent = false;
81
- for (const block of content) {
152
+ for (const block of message.content) {
82
153
  if (block.type === ContentTypes.TEXT) {
83
154
  if (typeof block.text === 'string' && block.text !== '') {
84
155
  hasCacheableContent = true;
@@ -90,15 +161,15 @@ function addBedrockCacheControl(messages) {
90
161
  continue;
91
162
  }
92
163
  let inserted = false;
93
- for (let j = content.length - 1; j >= 0; j--) {
94
- const block = content[j];
164
+ for (let j = message.content.length - 1; j >= 0; j--) {
165
+ const block = message.content[j];
95
166
  const type = block.type;
96
167
  if (type === ContentTypes.TEXT || type === 'text') {
97
168
  const text = block.text;
98
169
  if (text === '' || text === undefined) {
99
170
  continue;
100
171
  }
101
- content.splice(j + 1, 0, {
172
+ message.content.splice(j + 1, 0, {
102
173
  cachePoint: { type: 'default' },
103
174
  });
104
175
  inserted = true;
@@ -106,7 +177,7 @@ function addBedrockCacheControl(messages) {
106
177
  }
107
178
  }
108
179
  if (!inserted) {
109
- content.push({
180
+ message.content.push({
110
181
  cachePoint: { type: 'default' },
111
182
  });
112
183
  }
@@ -116,5 +187,5 @@ function addBedrockCacheControl(messages) {
116
187
  return updatedMessages;
117
188
  }
118
189
 
119
- export { addBedrockCacheControl, addCacheControl };
190
+ export { addBedrockCacheControl, addCacheControl, stripAnthropicCacheControl, stripBedrockCacheControl };
120
191
  //# sourceMappingURL=cache.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import { BaseMessage, MessageContentComplex } from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n let userMessagesModified = 0;\n\n for (\n let i = updatedMessages.length - 1;\n i >= 0 && userMessagesModified < 2;\n i--\n ) {\n const message = updatedMessages[i];\n if ('getType' in message && message.getType() !== 'human') {\n continue;\n } else if ('role' in message && message.role !== 'user') {\n continue;\n }\n\n if (typeof message.content === 'string') {\n message.content = [\n {\n type: 'text',\n text: message.content,\n cache_control: { type: 'ephemeral' },\n },\n ];\n userMessagesModified++;\n } else if (Array.isArray(message.content)) {\n for (let j = message.content.length - 1; j >= 0; j--) {\n const contentPart = message.content[j];\n if ('type' in contentPart && contentPart.type === 'text') {\n (contentPart as Anthropic.TextBlockParam).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n break;\n }\n }\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the last two messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = messages.slice();\n let messagesModified = 0;\n\n for (\n let i = updatedMessages.length - 1;\n i >= 0 && messagesModified < 2;\n i--\n ) {\n const message = updatedMessages[i];\n\n if (\n 'getType' in message &&\n typeof message.getType === 'function' &&\n message.getType() === 'tool'\n ) {\n continue;\n }\n\n const content = message.content;\n\n if (typeof content === 'string' && content === '') {\n continue;\n }\n\n if (typeof content === 'string') {\n message.content = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } },\n ] as MessageContentComplex[];\n messagesModified++;\n continue;\n }\n\n if (Array.isArray(content)) {\n let hasCacheableContent = false;\n for (const block of content) {\n if (block.type === ContentTypes.TEXT) {\n if (typeof block.text === 'string' && block.text !== '') {\n hasCacheableContent = true;\n break;\n }\n }\n }\n\n if (!hasCacheableContent) {\n continue;\n }\n\n let inserted = false;\n for (let j = content.length - 1; j >= 0; j--) {\n const block = content[j] as MessageContentComplex;\n const type = (block as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (block as { text?: string }).text;\n if (text === '' || text === undefined) {\n continue;\n }\n content.splice(j + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n inserted = true;\n break;\n }\n }\n if (!inserted) {\n content.push({\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n }\n messagesModified++;\n }\n }\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.mjs","sources":["../../../src/messages/cache.ts"],"sourcesContent":["import { BaseMessage, MessageContentComplex } from '@langchain/core/messages';\nimport type { AnthropicMessage } from '@/types/messages';\nimport type Anthropic from '@anthropic-ai/sdk';\nimport { ContentTypes } from '@/common/enum';\n\ntype MessageWithContent = {\n content?: string | MessageContentComplex[];\n};\n\n/**\n * Anthropic API: Adds cache control to the appropriate user messages in the payload.\n * Strips ALL existing cache control (both Anthropic and Bedrock formats) from all messages,\n * then adds fresh cache control to the last 2 user messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache control added.\n */\nexport function addCacheControl<T extends AnthropicMessage | BaseMessage>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n let userMessagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const message = updatedMessages[i];\n const isUserMessage =\n ('getType' in message && message.getType() === 'human') ||\n ('role' in message && message.role === 'user');\n\n if (Array.isArray(message.content)) {\n message.content = message.content.filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n ) as typeof message.content;\n\n for (let j = 0; j < message.content.length; j++) {\n const block = message.content[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n }\n\n if (userMessagesModified >= 2 || !isUserMessage) {\n continue;\n }\n\n if (typeof message.content === 'string') {\n message.content = [\n {\n type: 'text',\n text: message.content,\n cache_control: { type: 'ephemeral' },\n },\n ];\n userMessagesModified++;\n } else if (Array.isArray(message.content)) {\n for (let j = message.content.length - 1; j >= 0; j--) {\n const contentPart = message.content[j];\n if ('type' in contentPart && contentPart.type === 'text') {\n (contentPart as Anthropic.TextBlockParam).cache_control = {\n type: 'ephemeral',\n };\n userMessagesModified++;\n break;\n }\n }\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Checks if a content block is a cache point\n */\nfunction isCachePoint(block: MessageContentComplex): boolean {\n return 'cachePoint' in block && !('type' in block);\n}\n\n/**\n * Removes all Anthropic cache_control fields from messages\n * Used when switching from Anthropic to Bedrock provider\n */\nexport function stripAnthropicCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const message = updatedMessages[i];\n const content = message.content;\n\n if (Array.isArray(content)) {\n for (let j = 0; j < content.length; j++) {\n const block = content[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Removes all Bedrock cachePoint blocks from messages\n * Used when switching from Bedrock to Anthropic provider\n */\nexport function stripBedrockCacheControl<T extends MessageWithContent>(\n messages: T[]\n): T[] {\n if (!Array.isArray(messages)) {\n return messages;\n }\n\n const updatedMessages = [...messages];\n\n for (let i = 0; i < updatedMessages.length; i++) {\n const message = updatedMessages[i];\n const content = message.content;\n\n if (Array.isArray(content)) {\n message.content = content.filter(\n (block) => !isCachePoint(block as MessageContentComplex)\n ) as typeof content;\n }\n }\n\n return updatedMessages;\n}\n\n/**\n * Adds Bedrock Converse API cache points to the last two messages.\n * Inserts `{ cachePoint: { type: 'default' } }` as a separate content block\n * immediately after the last text block in each targeted message.\n * Strips ALL existing cache control (both Bedrock and Anthropic formats) from all messages,\n * then adds fresh cache points to the last 2 messages in a single backward pass.\n * This ensures we don't accumulate stale cache points across multiple turns.\n * @param messages - The array of message objects.\n * @returns - The updated array of message objects with cache points added.\n */\nexport function addBedrockCacheControl<\n T extends Partial<BaseMessage> & MessageWithContent,\n>(messages: T[]): T[] {\n if (!Array.isArray(messages) || messages.length < 2) {\n return messages;\n }\n\n const updatedMessages: T[] = messages.slice();\n let messagesModified = 0;\n\n for (let i = updatedMessages.length - 1; i >= 0; i--) {\n const message = updatedMessages[i];\n const isToolMessage =\n 'getType' in message &&\n typeof message.getType === 'function' &&\n message.getType() === 'tool';\n\n const content = message.content;\n\n if (Array.isArray(content)) {\n message.content = content.filter(\n (block) => !isCachePoint(block)\n ) as typeof content;\n\n for (let j = 0; j < message.content.length; j++) {\n const block = message.content[j] as Record<string, unknown>;\n if ('cache_control' in block) {\n delete block.cache_control;\n }\n }\n }\n\n if (messagesModified >= 2 || isToolMessage) {\n continue;\n }\n\n if (typeof content === 'string' && content === '') {\n continue;\n }\n\n if (typeof content === 'string') {\n message.content = [\n { type: ContentTypes.TEXT, text: content },\n { cachePoint: { type: 'default' } },\n ] as MessageContentComplex[];\n messagesModified++;\n continue;\n }\n\n if (Array.isArray(message.content)) {\n let hasCacheableContent = false;\n for (const block of message.content) {\n if (block.type === ContentTypes.TEXT) {\n if (typeof block.text === 'string' && block.text !== '') {\n hasCacheableContent = true;\n break;\n }\n }\n }\n\n if (!hasCacheableContent) {\n continue;\n }\n\n let inserted = false;\n for (let j = message.content.length - 1; j >= 0; j--) {\n const block = message.content[j] as MessageContentComplex;\n const type = (block as { type?: string }).type;\n if (type === ContentTypes.TEXT || type === 'text') {\n const text = (block as { text?: string }).text;\n if (text === '' || text === undefined) {\n continue;\n }\n message.content.splice(j + 1, 0, {\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n inserted = true;\n break;\n }\n }\n if (!inserted) {\n message.content.push({\n cachePoint: { type: 'default' },\n } as MessageContentComplex);\n }\n messagesModified++;\n }\n }\n\n return updatedMessages;\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { HumanMessage, AIMessage, SystemMessage, getBufferString, ToolMessage } from '@langchain/core/messages';
1
+ import { HumanMessage, AIMessage, SystemMessage, getBufferString, ToolMessage, AIMessageChunk } from '@langchain/core/messages';
2
2
  import { Providers, ContentTypes } from '../common/enum.mjs';
3
3
 
4
4
  /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -180,19 +180,20 @@ function formatAssistantMessage(message) {
180
180
  }
181
181
  // Create a new AIMessage with this text and prepare for tool calls
182
182
  lastAIMessage = new AIMessage({
183
- content: part.text || '',
183
+ content: part.text != null ? part.text : '',
184
184
  });
185
185
  formattedMessages.push(lastAIMessage);
186
186
  }
187
187
  else if (part.type === ContentTypes.TOOL_CALL) {
188
188
  // Skip malformed tool call entries without tool_call property
189
- if (!part.tool_call) {
189
+ if (part.tool_call == null) {
190
190
  continue;
191
191
  }
192
192
  // Note: `tool_calls` list is defined when constructed by `AIMessage` class, and outputs should be excluded from it
193
193
  const { output, args: _args, ..._tool_call } = part.tool_call;
194
194
  // Skip invalid tool calls that have no name AND no output
195
- if (!_tool_call.name && (output == null || output === '')) {
195
+ if (_tool_call.name == null ||
196
+ (_tool_call.name === '' && (output == null || output === ''))) {
196
197
  continue;
197
198
  }
198
199
  if (!lastAIMessage) {
@@ -221,7 +222,7 @@ function formatAssistantMessage(message) {
221
222
  formattedMessages.push(new ToolMessage({
222
223
  tool_call_id: tool_call.id ?? '',
223
224
  name: tool_call.name,
224
- content: output || '',
225
+ content: output != null ? output : '',
225
226
  }));
226
227
  }
227
228
  else if (part.type === ContentTypes.THINK) {
@@ -255,6 +256,177 @@ function formatAssistantMessage(message) {
255
256
  }
256
257
  return formattedMessages;
257
258
  }
259
+ /**
260
+ * Labels all agent content for parallel patterns (fan-out/fan-in)
261
+ * Groups consecutive content by agent and wraps with clear labels
262
+ */
263
+ function labelAllAgentContent(contentParts, agentIdMap, agentNames) {
264
+ const result = [];
265
+ let currentAgentId;
266
+ let agentContentBuffer = [];
267
+ const flushAgentBuffer = () => {
268
+ if (agentContentBuffer.length === 0) {
269
+ return;
270
+ }
271
+ if (currentAgentId != null && currentAgentId !== '') {
272
+ const agentName = (agentNames?.[currentAgentId] ?? '') || currentAgentId;
273
+ const formattedParts = [];
274
+ formattedParts.push(`--- ${agentName} ---`);
275
+ for (const part of agentContentBuffer) {
276
+ if (part.type === ContentTypes.THINK) {
277
+ const thinkContent = part.think || '';
278
+ if (thinkContent) {
279
+ formattedParts.push(`${agentName}: ${JSON.stringify({
280
+ type: 'think',
281
+ think: thinkContent,
282
+ })}`);
283
+ }
284
+ }
285
+ else if (part.type === ContentTypes.TEXT) {
286
+ const textContent = part.text ?? '';
287
+ if (textContent) {
288
+ formattedParts.push(`${agentName}: ${textContent}`);
289
+ }
290
+ }
291
+ else if (part.type === ContentTypes.TOOL_CALL) {
292
+ formattedParts.push(`${agentName}: ${JSON.stringify({
293
+ type: 'tool_call',
294
+ tool_call: part.tool_call,
295
+ })}`);
296
+ }
297
+ }
298
+ formattedParts.push(`--- End of ${agentName} ---`);
299
+ // Create a single text content part with all agent content
300
+ result.push({
301
+ type: ContentTypes.TEXT,
302
+ text: formattedParts.join('\n\n'),
303
+ });
304
+ }
305
+ else {
306
+ // No agent ID, pass through as-is
307
+ result.push(...agentContentBuffer);
308
+ }
309
+ agentContentBuffer = [];
310
+ };
311
+ for (let i = 0; i < contentParts.length; i++) {
312
+ const part = contentParts[i];
313
+ const agentId = agentIdMap[i];
314
+ // If agent changed, flush previous buffer
315
+ if (agentId !== currentAgentId && currentAgentId !== undefined) {
316
+ flushAgentBuffer();
317
+ }
318
+ currentAgentId = agentId;
319
+ agentContentBuffer.push(part);
320
+ }
321
+ // Flush any remaining content
322
+ flushAgentBuffer();
323
+ return result;
324
+ }
325
+ /**
326
+ * Groups content parts by agent and formats them with agent labels
327
+ * This preprocesses multi-agent content to prevent identity confusion
328
+ *
329
+ * @param contentParts - The content parts from a run
330
+ * @param agentIdMap - Map of content part index to agent ID
331
+ * @param agentNames - Optional map of agent ID to display name
332
+ * @param options - Configuration options
333
+ * @param options.labelNonTransferContent - If true, labels all agent transitions (for parallel patterns)
334
+ * @returns Modified content parts with agent labels where appropriate
335
+ */
336
+ const labelContentByAgent = (contentParts, agentIdMap, agentNames, options) => {
337
+ if (!agentIdMap || Object.keys(agentIdMap).length === 0) {
338
+ return contentParts;
339
+ }
340
+ // If labelNonTransferContent is true, use a different strategy for parallel patterns
341
+ if (options?.labelNonTransferContent === true) {
342
+ return labelAllAgentContent(contentParts, agentIdMap, agentNames);
343
+ }
344
+ const result = [];
345
+ let currentAgentId;
346
+ let agentContentBuffer = [];
347
+ let transferToolCallIndex;
348
+ let transferToolCallId;
349
+ const flushAgentBuffer = () => {
350
+ if (agentContentBuffer.length === 0) {
351
+ return;
352
+ }
353
+ // If this is content from a transferred agent, format it specially
354
+ if (currentAgentId != null &&
355
+ currentAgentId !== '' &&
356
+ transferToolCallIndex !== undefined) {
357
+ const agentName = (agentNames?.[currentAgentId] ?? '') || currentAgentId;
358
+ const formattedParts = [];
359
+ formattedParts.push(`--- Transfer to ${agentName} ---`);
360
+ for (const part of agentContentBuffer) {
361
+ if (part.type === ContentTypes.THINK) {
362
+ formattedParts.push(`${agentName}: ${JSON.stringify({
363
+ type: 'think',
364
+ think: part.think,
365
+ })}`);
366
+ }
367
+ else if ('text' in part && part.type === ContentTypes.TEXT) {
368
+ const textContent = part.text ?? '';
369
+ if (textContent) {
370
+ formattedParts.push(`${agentName}: ${JSON.stringify({
371
+ type: 'text',
372
+ text: textContent,
373
+ })}`);
374
+ }
375
+ }
376
+ else if (part.type === ContentTypes.TOOL_CALL) {
377
+ formattedParts.push(`${agentName}: ${JSON.stringify({
378
+ type: 'tool_call',
379
+ tool_call: part.tool_call,
380
+ })}`);
381
+ }
382
+ }
383
+ formattedParts.push(`--- End of ${agentName} response ---`);
384
+ // Find the tool call that triggered this transfer and update its output
385
+ if (transferToolCallIndex < result.length) {
386
+ const transferToolCall = result[transferToolCallIndex];
387
+ if (transferToolCall.type === ContentTypes.TOOL_CALL &&
388
+ transferToolCall.tool_call?.id === transferToolCallId) {
389
+ transferToolCall.tool_call.output = formattedParts.join('\n\n');
390
+ }
391
+ }
392
+ }
393
+ else {
394
+ // Not from a transfer, add as-is
395
+ result.push(...agentContentBuffer);
396
+ }
397
+ agentContentBuffer = [];
398
+ transferToolCallIndex = undefined;
399
+ transferToolCallId = undefined;
400
+ };
401
+ for (let i = 0; i < contentParts.length; i++) {
402
+ const part = contentParts[i];
403
+ const agentId = agentIdMap[i];
404
+ // Check if this is a transfer tool call
405
+ const isTransferTool = (part.type === ContentTypes.TOOL_CALL &&
406
+ part.tool_call?.name?.startsWith('lc_transfer_to_')) ??
407
+ false;
408
+ // If agent changed, flush previous buffer
409
+ if (agentId !== currentAgentId && currentAgentId !== undefined) {
410
+ flushAgentBuffer();
411
+ }
412
+ currentAgentId = agentId;
413
+ if (isTransferTool) {
414
+ // Flush any existing buffer first
415
+ flushAgentBuffer();
416
+ // Add the transfer tool call to result
417
+ result.push(part);
418
+ // Mark that the next agent's content should be captured
419
+ transferToolCallIndex = result.length - 1;
420
+ transferToolCallId = part.tool_call?.id;
421
+ currentAgentId = undefined; // Reset to capture the next agent
422
+ }
423
+ else {
424
+ agentContentBuffer.push(part);
425
+ }
426
+ }
427
+ flushAgentBuffer();
428
+ return result;
429
+ };
258
430
  /**
259
431
  * Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
260
432
  *
@@ -305,7 +477,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools) => {
305
477
  break;
306
478
  }
307
479
  // Protect against malformed tool call entries
308
- if (!part.tool_call ||
480
+ if (part.tool_call == null ||
309
481
  part.tool_call.name == null ||
310
482
  part.tool_call.name === '') {
311
483
  hasInvalidTool = true;
@@ -332,7 +504,7 @@ const formatAgentMessages = (payload, indexTokenCountMap, tools) => {
332
504
  // Check if this is a continuation of the tool sequence
333
505
  let isToolResponse = false;
334
506
  const content = payload[j].content;
335
- if (content && Array.isArray(content)) {
507
+ if (content != null && Array.isArray(content)) {
336
508
  for (const part of content) {
337
509
  if (part.type === ContentTypes.TOOL_CALL) {
338
510
  isToolResponse = true;
@@ -431,6 +603,68 @@ function shiftIndexTokenCountMap(indexTokenCountMap, instructionsTokenCount) {
431
603
  }
432
604
  return shiftedMap;
433
605
  }
606
+ /**
607
+ * Ensures compatibility when switching from a non-thinking agent to a thinking-enabled agent.
608
+ * Converts AI messages with tool calls (that lack thinking blocks) into buffer strings,
609
+ * avoiding the thinking block signature requirement.
610
+ *
611
+ * @param messages - Array of messages to process
612
+ * @param provider - The provider being used (unused but kept for future compatibility)
613
+ * @returns The messages array with tool sequences converted to buffer strings if necessary
614
+ */
615
+ function ensureThinkingBlockInMessages(messages, _provider) {
616
+ const result = [];
617
+ let i = 0;
618
+ while (i < messages.length) {
619
+ const msg = messages[i];
620
+ const isAI = msg instanceof AIMessage || msg instanceof AIMessageChunk;
621
+ if (!isAI) {
622
+ result.push(msg);
623
+ i++;
624
+ continue;
625
+ }
626
+ const aiMsg = msg;
627
+ const hasToolCalls = aiMsg.tool_calls && aiMsg.tool_calls.length > 0;
628
+ const contentIsArray = Array.isArray(aiMsg.content);
629
+ // Check if the message has tool calls or tool_use content
630
+ let hasToolUse = hasToolCalls ?? false;
631
+ let firstContentType;
632
+ if (contentIsArray && aiMsg.content.length > 0) {
633
+ const content = aiMsg.content;
634
+ firstContentType = content[0]?.type;
635
+ hasToolUse =
636
+ hasToolUse ||
637
+ content.some((c) => typeof c === 'object' && c.type === 'tool_use');
638
+ }
639
+ // If message has tool use but no thinking block, convert to buffer string
640
+ if (hasToolUse &&
641
+ firstContentType !== ContentTypes.THINKING &&
642
+ firstContentType !== 'redacted_thinking') {
643
+ // Collect the AI message and any following tool messages
644
+ const toolSequence = [msg];
645
+ let j = i + 1;
646
+ // Look ahead for tool messages that belong to this AI message
647
+ while (j < messages.length && messages[j] instanceof ToolMessage) {
648
+ toolSequence.push(messages[j]);
649
+ j++;
650
+ }
651
+ // Convert the sequence to a buffer string and wrap in a HumanMessage
652
+ // This avoids the thinking block requirement which only applies to AI messages
653
+ const bufferString = getBufferString(toolSequence);
654
+ result.push(new HumanMessage({
655
+ content: `[Previous agent context]\n${bufferString}`,
656
+ }));
657
+ // Skip the messages we've processed
658
+ i = j;
659
+ }
660
+ else {
661
+ // Keep the message as is
662
+ result.push(msg);
663
+ i++;
664
+ }
665
+ }
666
+ return result;
667
+ }
434
668
 
435
- export { formatAgentMessages, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, shiftIndexTokenCountMap };
669
+ export { ensureThinkingBlockInMessages, formatAgentMessages, formatFromLangChain, formatLangChainMessages, formatMediaMessage, formatMessage, labelContentByAgent, shiftIndexTokenCountMap };
436
670
  //# sourceMappingURL=format.mjs.map