illuma-agents 1.0.37 → 1.0.39

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 (139) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +112 -14
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +5 -1
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +148 -8
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +277 -11
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +128 -61
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/main.cjs +22 -7
  12. package/dist/cjs/main.cjs.map +1 -1
  13. package/dist/cjs/messages/cache.cjs +140 -46
  14. package/dist/cjs/messages/cache.cjs.map +1 -1
  15. package/dist/cjs/messages/core.cjs +1 -1
  16. package/dist/cjs/messages/core.cjs.map +1 -1
  17. package/dist/cjs/messages/tools.cjs +2 -2
  18. package/dist/cjs/messages/tools.cjs.map +1 -1
  19. package/dist/cjs/schemas/validate.cjs +173 -0
  20. package/dist/cjs/schemas/validate.cjs.map +1 -0
  21. package/dist/cjs/stream.cjs +4 -2
  22. package/dist/cjs/stream.cjs.map +1 -1
  23. package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
  24. package/dist/cjs/tools/CodeExecutor.cjs +22 -21
  25. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  26. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +14 -11
  27. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  28. package/dist/cjs/tools/ToolNode.cjs +101 -2
  29. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  30. package/dist/cjs/tools/ToolSearch.cjs +862 -0
  31. package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
  32. package/dist/esm/agents/AgentContext.mjs +112 -14
  33. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  34. package/dist/esm/common/enum.mjs +5 -1
  35. package/dist/esm/common/enum.mjs.map +1 -1
  36. package/dist/esm/graphs/Graph.mjs +149 -9
  37. package/dist/esm/graphs/Graph.mjs.map +1 -1
  38. package/dist/esm/graphs/MultiAgentGraph.mjs +278 -12
  39. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  40. package/dist/esm/llm/bedrock/index.mjs +127 -60
  41. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  42. package/dist/esm/main.mjs +2 -1
  43. package/dist/esm/main.mjs.map +1 -1
  44. package/dist/esm/messages/cache.mjs +140 -46
  45. package/dist/esm/messages/cache.mjs.map +1 -1
  46. package/dist/esm/messages/core.mjs +1 -1
  47. package/dist/esm/messages/core.mjs.map +1 -1
  48. package/dist/esm/messages/tools.mjs +2 -2
  49. package/dist/esm/messages/tools.mjs.map +1 -1
  50. package/dist/esm/schemas/validate.mjs +167 -0
  51. package/dist/esm/schemas/validate.mjs.map +1 -0
  52. package/dist/esm/stream.mjs +4 -2
  53. package/dist/esm/stream.mjs.map +1 -1
  54. package/dist/esm/tools/BrowserTools.mjs.map +1 -1
  55. package/dist/esm/tools/CodeExecutor.mjs +22 -21
  56. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  57. package/dist/esm/tools/ProgrammaticToolCalling.mjs +14 -11
  58. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  59. package/dist/esm/tools/ToolNode.mjs +102 -3
  60. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  61. package/dist/esm/tools/ToolSearch.mjs +827 -0
  62. package/dist/esm/tools/ToolSearch.mjs.map +1 -0
  63. package/dist/types/agents/AgentContext.d.ts +51 -1
  64. package/dist/types/common/enum.d.ts +6 -2
  65. package/dist/types/graphs/Graph.d.ts +12 -0
  66. package/dist/types/graphs/MultiAgentGraph.d.ts +16 -0
  67. package/dist/types/index.d.ts +2 -1
  68. package/dist/types/llm/bedrock/index.d.ts +89 -11
  69. package/dist/types/llm/bedrock/types.d.ts +27 -0
  70. package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
  71. package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
  72. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
  73. package/dist/types/messages/cache.d.ts +4 -1
  74. package/dist/types/schemas/index.d.ts +1 -0
  75. package/dist/types/schemas/validate.d.ts +36 -0
  76. package/dist/types/tools/CodeExecutor.d.ts +0 -3
  77. package/dist/types/tools/ProgrammaticToolCalling.d.ts +0 -3
  78. package/dist/types/tools/ToolNode.d.ts +3 -1
  79. package/dist/types/tools/ToolSearch.d.ts +148 -0
  80. package/dist/types/types/graph.d.ts +71 -0
  81. package/dist/types/types/llm.d.ts +3 -1
  82. package/dist/types/types/tools.d.ts +42 -2
  83. package/package.json +13 -6
  84. package/src/agents/AgentContext.test.ts +312 -0
  85. package/src/agents/AgentContext.ts +144 -16
  86. package/src/common/enum.ts +5 -1
  87. package/src/graphs/Graph.ts +214 -13
  88. package/src/graphs/MultiAgentGraph.ts +350 -13
  89. package/src/index.ts +4 -1
  90. package/src/llm/bedrock/index.ts +221 -99
  91. package/src/llm/bedrock/llm.spec.ts +616 -0
  92. package/src/llm/bedrock/types.ts +51 -0
  93. package/src/llm/bedrock/utils/index.ts +18 -0
  94. package/src/llm/bedrock/utils/message_inputs.ts +563 -0
  95. package/src/llm/bedrock/utils/message_outputs.ts +310 -0
  96. package/src/messages/__tests__/tools.test.ts +21 -21
  97. package/src/messages/cache.test.ts +304 -0
  98. package/src/messages/cache.ts +183 -53
  99. package/src/messages/core.ts +1 -1
  100. package/src/messages/tools.ts +2 -2
  101. package/src/schemas/index.ts +2 -0
  102. package/src/schemas/validate.test.ts +358 -0
  103. package/src/schemas/validate.ts +238 -0
  104. package/src/scripts/caching.ts +27 -19
  105. package/src/scripts/code_exec_files.ts +58 -15
  106. package/src/scripts/code_exec_multi_session.ts +241 -0
  107. package/src/scripts/code_exec_session.ts +282 -0
  108. package/src/scripts/multi-agent-conditional.ts +1 -0
  109. package/src/scripts/multi-agent-supervisor.ts +1 -0
  110. package/src/scripts/programmatic_exec_agent.ts +4 -4
  111. package/src/scripts/test-handoff-preamble.ts +277 -0
  112. package/src/scripts/test-parallel-handoffs.ts +291 -0
  113. package/src/scripts/test-tools-before-handoff.ts +8 -4
  114. package/src/scripts/test_code_api.ts +361 -0
  115. package/src/scripts/thinking-bedrock.ts +159 -0
  116. package/src/scripts/thinking.ts +39 -18
  117. package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
  118. package/src/scripts/tools.ts +7 -3
  119. package/src/specs/cache.simple.test.ts +396 -0
  120. package/src/stream.ts +4 -2
  121. package/src/tools/BrowserTools.ts +39 -17
  122. package/src/tools/CodeExecutor.ts +26 -23
  123. package/src/tools/ProgrammaticToolCalling.ts +18 -14
  124. package/src/tools/ToolNode.ts +114 -1
  125. package/src/tools/ToolSearch.ts +1041 -0
  126. package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -2
  127. package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
  128. package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
  129. package/src/types/graph.test.ts +183 -0
  130. package/src/types/graph.ts +73 -0
  131. package/src/types/llm.ts +3 -1
  132. package/src/types/tools.ts +51 -2
  133. package/dist/cjs/tools/ToolSearchRegex.cjs +0 -455
  134. package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
  135. package/dist/esm/tools/ToolSearchRegex.mjs +0 -448
  136. package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
  137. package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
  138. package/src/tools/ToolSearchRegex.ts +0 -535
  139. package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
@@ -0,0 +1,312 @@
1
+ // src/agents/AgentContext.test.ts
2
+ import { AgentContext } from './AgentContext';
3
+ import { Providers } from '@/common';
4
+ import type * as t from '@/types';
5
+
6
+ describe('AgentContext', () => {
7
+ describe('structured output', () => {
8
+ const baseConfig: t.AgentInputs = {
9
+ agentId: 'test-agent',
10
+ provider: Providers.OPENAI,
11
+ clientOptions: {
12
+ model: 'gpt-4',
13
+ streaming: true,
14
+ },
15
+ };
16
+
17
+ describe('isStructuredOutputMode', () => {
18
+ it('should return false when structuredOutput is not configured', () => {
19
+ const context = AgentContext.fromConfig(baseConfig);
20
+ expect(context.isStructuredOutputMode).toBe(false);
21
+ });
22
+
23
+ it('should return false when structuredOutput has no schema', () => {
24
+ const config: t.AgentInputs = {
25
+ ...baseConfig,
26
+ structuredOutput: {
27
+ schema: undefined as unknown as Record<string, unknown>,
28
+ },
29
+ };
30
+ const context = AgentContext.fromConfig(config);
31
+ expect(context.isStructuredOutputMode).toBe(false);
32
+ });
33
+
34
+ it('should return false when structuredOutput schema is null', () => {
35
+ const config: t.AgentInputs = {
36
+ ...baseConfig,
37
+ structuredOutput: {
38
+ schema: null as unknown as Record<string, unknown>,
39
+ },
40
+ };
41
+ const context = AgentContext.fromConfig(config);
42
+ expect(context.isStructuredOutputMode).toBe(false);
43
+ });
44
+
45
+ it('should return true when structuredOutput has a valid schema', () => {
46
+ const config: t.AgentInputs = {
47
+ ...baseConfig,
48
+ structuredOutput: {
49
+ schema: {
50
+ type: 'object',
51
+ properties: {
52
+ answer: { type: 'string' },
53
+ },
54
+ },
55
+ },
56
+ };
57
+ const context = AgentContext.fromConfig(config);
58
+ expect(context.isStructuredOutputMode).toBe(true);
59
+ });
60
+
61
+ it('should return true with minimal schema', () => {
62
+ const config: t.AgentInputs = {
63
+ ...baseConfig,
64
+ structuredOutput: {
65
+ schema: { type: 'string' },
66
+ },
67
+ };
68
+ const context = AgentContext.fromConfig(config);
69
+ expect(context.isStructuredOutputMode).toBe(true);
70
+ });
71
+ });
72
+
73
+ describe('getStructuredOutputSchema', () => {
74
+ it('should return undefined when structuredOutput is not configured', () => {
75
+ const context = AgentContext.fromConfig(baseConfig);
76
+ expect(context.getStructuredOutputSchema()).toBeUndefined();
77
+ });
78
+
79
+ it('should return undefined when schema is not set', () => {
80
+ const config: t.AgentInputs = {
81
+ ...baseConfig,
82
+ structuredOutput: {} as t.StructuredOutputConfig,
83
+ };
84
+ const context = AgentContext.fromConfig(config);
85
+ expect(context.getStructuredOutputSchema()).toBeUndefined();
86
+ });
87
+
88
+ it('should return the schema as-is when properly configured', () => {
89
+ const schema = {
90
+ type: 'object',
91
+ properties: {
92
+ name: { type: 'string' },
93
+ age: { type: 'number' },
94
+ },
95
+ };
96
+ const config: t.AgentInputs = {
97
+ ...baseConfig,
98
+ structuredOutput: { schema },
99
+ };
100
+ const context = AgentContext.fromConfig(config);
101
+ const result = context.getStructuredOutputSchema();
102
+ expect(result).toMatchObject(schema);
103
+ });
104
+
105
+ it('should add type: object when properties exist but type is missing', () => {
106
+ const schema = {
107
+ properties: {
108
+ name: { type: 'string' },
109
+ },
110
+ };
111
+ const config: t.AgentInputs = {
112
+ ...baseConfig,
113
+ structuredOutput: { schema },
114
+ };
115
+ const context = AgentContext.fromConfig(config);
116
+ const result = context.getStructuredOutputSchema();
117
+ expect(result?.type).toBe('object');
118
+ });
119
+
120
+ it('should add title from config name', () => {
121
+ const config: t.AgentInputs = {
122
+ ...baseConfig,
123
+ structuredOutput: {
124
+ schema: { type: 'object' },
125
+ name: 'ResponseSchema',
126
+ },
127
+ };
128
+ const context = AgentContext.fromConfig(config);
129
+ const result = context.getStructuredOutputSchema();
130
+ expect(result?.title).toBe('ResponseSchema');
131
+ });
132
+
133
+ it('should not override existing title', () => {
134
+ const config: t.AgentInputs = {
135
+ ...baseConfig,
136
+ structuredOutput: {
137
+ schema: { type: 'object', title: 'ExistingTitle' },
138
+ name: 'NewName',
139
+ },
140
+ };
141
+ const context = AgentContext.fromConfig(config);
142
+ const result = context.getStructuredOutputSchema();
143
+ expect(result?.title).toBe('ExistingTitle');
144
+ });
145
+
146
+ it('should add description from config', () => {
147
+ const config: t.AgentInputs = {
148
+ ...baseConfig,
149
+ structuredOutput: {
150
+ schema: { type: 'object' },
151
+ description: 'A structured response',
152
+ },
153
+ };
154
+ const context = AgentContext.fromConfig(config);
155
+ const result = context.getStructuredOutputSchema();
156
+ expect(result?.description).toBe('A structured response');
157
+ });
158
+
159
+ it('should not override existing description', () => {
160
+ const config: t.AgentInputs = {
161
+ ...baseConfig,
162
+ structuredOutput: {
163
+ schema: { type: 'object', description: 'Original description' },
164
+ description: 'New description',
165
+ },
166
+ };
167
+ const context = AgentContext.fromConfig(config);
168
+ const result = context.getStructuredOutputSchema();
169
+ expect(result?.description).toBe('Original description');
170
+ });
171
+
172
+ it('should set additionalProperties: false in strict mode by default', () => {
173
+ const config: t.AgentInputs = {
174
+ ...baseConfig,
175
+ structuredOutput: {
176
+ schema: { type: 'object', properties: {} },
177
+ },
178
+ };
179
+ const context = AgentContext.fromConfig(config);
180
+ const result = context.getStructuredOutputSchema();
181
+ expect(result?.additionalProperties).toBe(false);
182
+ });
183
+
184
+ it('should set additionalProperties: false when strict is explicitly true', () => {
185
+ const config: t.AgentInputs = {
186
+ ...baseConfig,
187
+ structuredOutput: {
188
+ schema: { type: 'object', properties: {} },
189
+ strict: true,
190
+ },
191
+ };
192
+ const context = AgentContext.fromConfig(config);
193
+ const result = context.getStructuredOutputSchema();
194
+ expect(result?.additionalProperties).toBe(false);
195
+ });
196
+
197
+ it('should not set additionalProperties when strict is false', () => {
198
+ const config: t.AgentInputs = {
199
+ ...baseConfig,
200
+ structuredOutput: {
201
+ schema: { type: 'object', properties: {} },
202
+ strict: false,
203
+ },
204
+ };
205
+ const context = AgentContext.fromConfig(config);
206
+ const result = context.getStructuredOutputSchema();
207
+ expect(result?.additionalProperties).toBeUndefined();
208
+ });
209
+
210
+ it('should preserve existing additionalProperties value', () => {
211
+ const config: t.AgentInputs = {
212
+ ...baseConfig,
213
+ structuredOutput: {
214
+ schema: {
215
+ type: 'object',
216
+ properties: {},
217
+ additionalProperties: true,
218
+ },
219
+ strict: true,
220
+ },
221
+ };
222
+ const context = AgentContext.fromConfig(config);
223
+ const result = context.getStructuredOutputSchema();
224
+ expect(result?.additionalProperties).toBe(true);
225
+ });
226
+
227
+ it('should not affect non-object types', () => {
228
+ const config: t.AgentInputs = {
229
+ ...baseConfig,
230
+ structuredOutput: {
231
+ schema: { type: 'array', items: { type: 'string' } },
232
+ },
233
+ };
234
+ const context = AgentContext.fromConfig(config);
235
+ const result = context.getStructuredOutputSchema();
236
+ expect(result?.additionalProperties).toBeUndefined();
237
+ expect(result?.type).toBe('array');
238
+ });
239
+
240
+ it('should not mutate the original schema', () => {
241
+ const originalSchema = {
242
+ type: 'object',
243
+ properties: { name: { type: 'string' } },
244
+ };
245
+ const config: t.AgentInputs = {
246
+ ...baseConfig,
247
+ structuredOutput: {
248
+ schema: originalSchema,
249
+ name: 'TestSchema',
250
+ description: 'Test description',
251
+ },
252
+ };
253
+ const context = AgentContext.fromConfig(config);
254
+ context.getStructuredOutputSchema();
255
+
256
+ // Original should not have been modified
257
+ expect(originalSchema).not.toHaveProperty('title');
258
+ expect(originalSchema).not.toHaveProperty('description');
259
+ expect(originalSchema).not.toHaveProperty('additionalProperties');
260
+ });
261
+ });
262
+
263
+ describe('fromConfig with structuredOutput', () => {
264
+ it('should properly initialize structuredOutput from config', () => {
265
+ const structuredOutput: t.StructuredOutputConfig = {
266
+ schema: {
267
+ type: 'object',
268
+ properties: {
269
+ response: { type: 'string' },
270
+ confidence: { type: 'number' },
271
+ },
272
+ required: ['response'],
273
+ },
274
+ name: 'AnalysisResult',
275
+ description: 'The analysis result',
276
+ mode: 'auto',
277
+ strict: true,
278
+ };
279
+
280
+ const config: t.AgentInputs = {
281
+ ...baseConfig,
282
+ structuredOutput,
283
+ };
284
+
285
+ const context = AgentContext.fromConfig(config);
286
+
287
+ expect(context.isStructuredOutputMode).toBe(true);
288
+ expect(context.structuredOutput).toEqual(structuredOutput);
289
+ });
290
+
291
+ it('should handle all mode options', () => {
292
+ const modes: Array<t.StructuredOutputMode> = [
293
+ 'auto',
294
+ 'tool',
295
+ 'provider',
296
+ ];
297
+
298
+ for (const mode of modes) {
299
+ const config: t.AgentInputs = {
300
+ ...baseConfig,
301
+ structuredOutput: {
302
+ schema: { type: 'object' },
303
+ mode,
304
+ },
305
+ };
306
+ const context = AgentContext.fromConfig(config);
307
+ expect(context.structuredOutput?.mode).toBe(mode);
308
+ }
309
+ });
310
+ });
311
+ });
312
+ });
@@ -27,6 +27,7 @@ export class AgentContext {
27
27
  ): AgentContext {
28
28
  const {
29
29
  agentId,
30
+ name,
30
31
  provider,
31
32
  clientOptions,
32
33
  tools,
@@ -40,10 +41,12 @@ export class AgentContext {
40
41
  reasoningKey,
41
42
  useLegacyContent,
42
43
  dynamicContext,
44
+ structuredOutput,
43
45
  } = agentConfig;
44
46
 
45
47
  const agentContext = new AgentContext({
46
48
  agentId,
49
+ name: name ?? agentId,
47
50
  provider,
48
51
  clientOptions,
49
52
  maxContextTokens,
@@ -59,6 +62,7 @@ export class AgentContext {
59
62
  tokenCounter,
60
63
  useLegacyContent,
61
64
  dynamicContext,
65
+ structuredOutput,
62
66
  });
63
67
 
64
68
  if (tokenCounter) {
@@ -87,6 +91,8 @@ export class AgentContext {
87
91
 
88
92
  /** Agent identifier */
89
93
  agentId: string;
94
+ /** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
95
+ name?: string;
90
96
  /** Provider for this specific agent */
91
97
  provider: Providers;
92
98
  /** Client options for this agent */
@@ -173,9 +179,26 @@ export class AgentContext {
173
179
  artifacts: 0,
174
180
  memory: 0,
175
181
  };
182
+ /**
183
+ * Handoff context when this agent receives control via handoff.
184
+ * Contains source and parallel execution info for system message context.
185
+ */
186
+ handoffContext?: {
187
+ /** Source agent that transferred control */
188
+ sourceAgentName: string;
189
+ /** Names of sibling agents executing in parallel (empty if sequential) */
190
+ parallelSiblings: string[];
191
+ };
192
+ /**
193
+ * Structured output configuration.
194
+ * When set, the agent will return a validated JSON response
195
+ * instead of streaming text.
196
+ */
197
+ structuredOutput?: t.StructuredOutputConfig;
176
198
 
177
199
  constructor({
178
200
  agentId,
201
+ name,
179
202
  provider,
180
203
  clientOptions,
181
204
  maxContextTokens,
@@ -191,8 +214,10 @@ export class AgentContext {
191
214
  toolEnd,
192
215
  instructionTokens,
193
216
  useLegacyContent,
217
+ structuredOutput,
194
218
  }: {
195
219
  agentId: string;
220
+ name?: string;
196
221
  provider: Providers;
197
222
  clientOptions?: t.ClientOptions;
198
223
  maxContextTokens?: number;
@@ -208,8 +233,10 @@ export class AgentContext {
208
233
  toolEnd?: boolean;
209
234
  instructionTokens?: number;
210
235
  useLegacyContent?: boolean;
236
+ structuredOutput?: t.StructuredOutputConfig;
211
237
  }) {
212
238
  this.agentId = agentId;
239
+ this.name = name;
213
240
  this.provider = provider;
214
241
  this.clientOptions = clientOptions;
215
242
  this.maxContextTokens = maxContextTokens;
@@ -221,6 +248,7 @@ export class AgentContext {
221
248
  this.instructions = instructions;
222
249
  this.additionalInstructions = additionalInstructions;
223
250
  this.dynamicContext = dynamicContext;
251
+ this.structuredOutput = structuredOutput;
224
252
  if (reasoningKey) {
225
253
  this.reasoningKey = reasoningKey;
226
254
  }
@@ -234,6 +262,51 @@ export class AgentContext {
234
262
  this.useLegacyContent = useLegacyContent ?? false;
235
263
  }
236
264
 
265
+ /**
266
+ * Checks if structured output mode is enabled for this agent.
267
+ * When enabled, the agent will use model.invoke() instead of streaming
268
+ * and return a validated JSON response.
269
+ */
270
+ get isStructuredOutputMode(): boolean {
271
+ return (
272
+ this.structuredOutput != null && this.structuredOutput.schema != null
273
+ );
274
+ }
275
+
276
+ /**
277
+ * Gets the structured output schema with normalized defaults.
278
+ * Returns undefined if structured output is not configured.
279
+ */
280
+ getStructuredOutputSchema(): Record<string, unknown> | undefined {
281
+ if (!this.structuredOutput?.schema) {
282
+ return undefined;
283
+ }
284
+
285
+ const schema = { ...this.structuredOutput.schema };
286
+
287
+ // Ensure type is set
288
+ if (!schema.type && schema.properties) {
289
+ schema.type = 'object';
290
+ }
291
+
292
+ // Add title from config name
293
+ if (this.structuredOutput.name && !schema.title) {
294
+ schema.title = this.structuredOutput.name;
295
+ }
296
+
297
+ // Add description from config
298
+ if (this.structuredOutput.description && !schema.description) {
299
+ schema.description = this.structuredOutput.description;
300
+ }
301
+
302
+ // Enable strict mode by default
303
+ if (this.structuredOutput.strict !== false && schema.type === 'object') {
304
+ schema.additionalProperties = schema.additionalProperties ?? false;
305
+ }
306
+
307
+ return schema;
308
+ }
309
+
237
310
  /**
238
311
  * Builds instructions text for tools that are ONLY callable via programmatic code execution.
239
312
  * These tools cannot be called directly by the LLM but are available through the
@@ -324,27 +397,65 @@ export class AgentContext {
324
397
 
325
398
  /**
326
399
  * Builds the raw instructions string (without creating SystemMessage).
400
+ * Includes agent identity preamble and handoff context when available.
327
401
  */
328
402
  private buildInstructionsString(): string {
329
- let result = this.instructions ?? '';
403
+ const parts: string[] = [];
404
+
405
+ /** Build agent identity and handoff context preamble */
406
+ const identityPreamble = this.buildIdentityPreamble();
407
+ if (identityPreamble) {
408
+ parts.push(identityPreamble);
409
+ }
410
+
411
+ /** Add main instructions */
412
+ if (this.instructions != null && this.instructions !== '') {
413
+ parts.push(this.instructions);
414
+ }
330
415
 
416
+ /** Add additional instructions */
331
417
  if (
332
418
  this.additionalInstructions != null &&
333
419
  this.additionalInstructions !== ''
334
420
  ) {
335
- result = result
336
- ? `${result}\n\n${this.additionalInstructions}`
337
- : this.additionalInstructions;
421
+ parts.push(this.additionalInstructions);
338
422
  }
339
423
 
424
+ /** Add programmatic tools documentation */
340
425
  const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
341
426
  if (programmaticToolsDoc) {
342
- result = result
343
- ? `${result}${programmaticToolsDoc}`
344
- : programmaticToolsDoc;
427
+ parts.push(programmaticToolsDoc);
345
428
  }
346
429
 
347
- return result;
430
+ return parts.join('\n\n');
431
+ }
432
+
433
+ /**
434
+ * Builds the agent identity preamble including handoff context if present.
435
+ * This helps the agent understand its role in the multi-agent workflow.
436
+ */
437
+ private buildIdentityPreamble(): string {
438
+ if (!this.handoffContext) return '';
439
+
440
+ const displayName = this.name ?? this.agentId;
441
+ const { sourceAgentName, parallelSiblings } = this.handoffContext;
442
+ const isParallel = parallelSiblings.length > 0;
443
+
444
+ const lines: string[] = [];
445
+ lines.push('## Multi-Agent Workflow');
446
+ lines.push(
447
+ `You are "${displayName}", transferred from "${sourceAgentName}".`
448
+ );
449
+
450
+ if (isParallel) {
451
+ lines.push(`Running in parallel with: ${parallelSiblings.join(', ')}.`);
452
+ }
453
+
454
+ lines.push(
455
+ 'Execute only tasks relevant to your role. Routing is already handled if requested, unless you can route further.'
456
+ );
457
+
458
+ return lines.join('\n');
348
459
  }
349
460
 
350
461
  /**
@@ -374,14 +485,7 @@ export class AgentContext {
374
485
  const anthropicOptions = this.clientOptions as
375
486
  | t.AnthropicClientOptions
376
487
  | undefined;
377
- const defaultHeaders = anthropicOptions?.clientOptions?.defaultHeaders as
378
- | Record<string, string>
379
- | undefined;
380
- const anthropicBeta = defaultHeaders?.['anthropic-beta'];
381
- if (
382
- typeof anthropicBeta === 'string' &&
383
- anthropicBeta.includes('prompt-caching')
384
- ) {
488
+ if (anthropicOptions?.promptCache === true) {
385
489
  finalInstructions = {
386
490
  content: [
387
491
  {
@@ -455,6 +559,7 @@ export class AgentContext {
455
559
  this.tokenTypeSwitch = undefined;
456
560
  this.currentTokenType = ContentTypes.TEXT;
457
561
  this.discoveredToolNames.clear();
562
+ this.handoffContext = undefined;
458
563
  }
459
564
 
460
565
  /**
@@ -626,6 +731,29 @@ export class AgentContext {
626
731
  return registry;
627
732
  }
628
733
 
734
+ /**
735
+ * Sets the handoff context for this agent.
736
+ * Call this when the agent receives control via handoff from another agent.
737
+ * Marks system runnable as stale to include handoff context in system message.
738
+ * @param sourceAgentName - Name of the agent that transferred control
739
+ * @param parallelSiblings - Names of other agents executing in parallel with this one
740
+ */
741
+ setHandoffContext(sourceAgentName: string, parallelSiblings: string[]): void {
742
+ this.handoffContext = { sourceAgentName, parallelSiblings };
743
+ this.systemRunnableStale = true;
744
+ }
745
+
746
+ /**
747
+ * Clears any handoff context.
748
+ * Call this when resetting the agent or when handoff context is no longer relevant.
749
+ */
750
+ clearHandoffContext(): void {
751
+ if (this.handoffContext) {
752
+ this.handoffContext = undefined;
753
+ this.systemRunnableStale = true;
754
+ }
755
+ }
756
+
629
757
  /**
630
758
  * Marks tools as discovered via tool search.
631
759
  * Discovered tools will be included in the next model binding.
@@ -21,6 +21,8 @@ export enum GraphEvents {
21
21
  ON_REASONING_DELTA = 'on_reasoning_delta',
22
22
  /** [Custom] Context analytics event for traces */
23
23
  ON_CONTEXT_ANALYTICS = 'on_context_analytics',
24
+ /** [Custom] Structured output event - emitted when agent returns structured JSON */
25
+ ON_STRUCTURED_OUTPUT = 'on_structured_output',
24
26
 
25
27
  /* Official Events */
26
28
 
@@ -161,11 +163,13 @@ export enum Callback {
161
163
  export enum Constants {
162
164
  OFFICIAL_CODE_BASEURL = 'https://api.illuma.ai/v1',
163
165
  EXECUTE_CODE = 'execute_code',
164
- TOOL_SEARCH_REGEX = 'tool_search_regex',
166
+ TOOL_SEARCH = 'tool_search',
165
167
  PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_code',
166
168
  WEB_SEARCH = 'web_search',
167
169
  CONTENT_AND_ARTIFACT = 'content_and_artifact',
168
170
  LC_TRANSFER_TO_ = 'lc_transfer_to_',
171
+ /** Delimiter for MCP tools: toolName_mcp_serverName */
172
+ MCP_DELIMITER = '_mcp_',
169
173
  }
170
174
 
171
175
  export enum TitleMethod {