@nebulaos/core 0.1.1

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 (150) hide show
  1. package/README.md +206 -0
  2. package/dist/__tests__/mocks/mock-provider.d.ts +15 -0
  3. package/dist/__tests__/mocks/mock-provider.js +44 -0
  4. package/dist/agent/Agent.d.ts +96 -0
  5. package/dist/agent/Agent.js +861 -0
  6. package/dist/agent/BaseAgent.d.ts +53 -0
  7. package/dist/agent/BaseAgent.js +126 -0
  8. package/dist/agent/events/events.d.ts +14 -0
  9. package/dist/agent/events/events.js +2 -0
  10. package/dist/agent/events/events.spec.d.ts +1 -0
  11. package/dist/agent/events/events.spec.js +75 -0
  12. package/dist/agent/instruction/index.d.ts +23 -0
  13. package/dist/agent/instruction/index.js +76 -0
  14. package/dist/agent/memory/in-memory.d.ts +24 -0
  15. package/dist/agent/memory/in-memory.js +78 -0
  16. package/dist/agent/memory/index.d.ts +2 -0
  17. package/dist/agent/memory/index.js +18 -0
  18. package/dist/agent/memory/memory.d.ts +43 -0
  19. package/dist/agent/memory/memory.js +7 -0
  20. package/dist/agent/provider/file-parts.spec.d.ts +1 -0
  21. package/dist/agent/provider/file-parts.spec.js +83 -0
  22. package/dist/agent/provider/index.d.ts +130 -0
  23. package/dist/agent/provider/index.js +8 -0
  24. package/dist/agent/skills/index.d.ts +61 -0
  25. package/dist/agent/skills/index.js +9 -0
  26. package/dist/agent/tools/index.d.ts +35 -0
  27. package/dist/agent/tools/index.js +87 -0
  28. package/dist/cost/add-cost.d.ts +10 -0
  29. package/dist/cost/add-cost.js +80 -0
  30. package/dist/cost/add-cost.spec.d.ts +1 -0
  31. package/dist/cost/add-cost.spec.js +36 -0
  32. package/dist/cost/index.d.ts +1 -0
  33. package/dist/cost/index.js +17 -0
  34. package/dist/domain-events/index.d.ts +16 -0
  35. package/dist/domain-events/index.js +38 -0
  36. package/dist/eval/index.d.ts +19 -0
  37. package/dist/eval/index.js +24 -0
  38. package/dist/events/base.d.ts +5 -0
  39. package/dist/events/base.js +2 -0
  40. package/dist/events/schemas.d.ts +3463 -0
  41. package/dist/events/schemas.js +244 -0
  42. package/dist/execution-context/index.d.ts +21 -0
  43. package/dist/execution-context/index.js +17 -0
  44. package/dist/index.cjs +2958 -0
  45. package/dist/index.cjs.map +1 -0
  46. package/dist/index.d.cts +3425 -0
  47. package/dist/index.d.ts +22 -0
  48. package/dist/index.js +53 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/lgpd/index.d.ts +7 -0
  51. package/dist/lgpd/index.js +21 -0
  52. package/dist/logger/agent-logger.d.ts +16 -0
  53. package/dist/logger/agent-logger.js +110 -0
  54. package/dist/logger/formatters.d.ts +32 -0
  55. package/dist/logger/formatters.js +146 -0
  56. package/dist/logger/index.d.ts +30 -0
  57. package/dist/logger/index.js +88 -0
  58. package/dist/logger/styles.d.ts +46 -0
  59. package/dist/logger/styles.js +53 -0
  60. package/dist/logger/workflow-logger.d.ts +16 -0
  61. package/dist/logger/workflow-logger.js +79 -0
  62. package/dist/multi-agent/agent-as-tool/AgentAsTool.d.ts +16 -0
  63. package/dist/multi-agent/agent-as-tool/AgentAsTool.js +54 -0
  64. package/dist/multi-agent/agent-as-tool/AgentAsTool.spec.d.ts +1 -0
  65. package/dist/multi-agent/agent-as-tool/AgentAsTool.spec.js +76 -0
  66. package/dist/multi-agent/committee-team/CommitteeTeam.d.ts +16 -0
  67. package/dist/multi-agent/committee-team/CommitteeTeam.js +150 -0
  68. package/dist/multi-agent/committee-team/CommitteeTeam.spec.d.ts +1 -0
  69. package/dist/multi-agent/committee-team/CommitteeTeam.spec.js +43 -0
  70. package/dist/multi-agent/handoff-team/HandoffTeam.d.ts +16 -0
  71. package/dist/multi-agent/handoff-team/HandoffTeam.js +185 -0
  72. package/dist/multi-agent/handoff-team/HandoffTeam.spec.d.ts +1 -0
  73. package/dist/multi-agent/handoff-team/HandoffTeam.spec.js +105 -0
  74. package/dist/multi-agent/hierarchical-team/HierarchicalTeam.d.ts +18 -0
  75. package/dist/multi-agent/hierarchical-team/HierarchicalTeam.js +164 -0
  76. package/dist/multi-agent/hierarchical-team/HierarchicalTeam.spec.d.ts +1 -0
  77. package/dist/multi-agent/hierarchical-team/HierarchicalTeam.spec.js +53 -0
  78. package/dist/multi-agent/index.d.ts +10 -0
  79. package/dist/multi-agent/index.js +26 -0
  80. package/dist/multi-agent/pipeline-team/PipelineTeam.d.ts +13 -0
  81. package/dist/multi-agent/pipeline-team/PipelineTeam.js +104 -0
  82. package/dist/multi-agent/pipeline-team/PipelineTeam.spec.d.ts +1 -0
  83. package/dist/multi-agent/pipeline-team/PipelineTeam.spec.js +54 -0
  84. package/dist/multi-agent/router-team/RouterTeam.d.ts +15 -0
  85. package/dist/multi-agent/router-team/RouterTeam.js +153 -0
  86. package/dist/multi-agent/router-team/RouterTeam.spec.d.ts +1 -0
  87. package/dist/multi-agent/router-team/RouterTeam.spec.js +69 -0
  88. package/dist/multi-agent/types/index.d.ts +349 -0
  89. package/dist/multi-agent/types/index.js +79 -0
  90. package/dist/multi-agent/utils/guardrails.d.ts +6 -0
  91. package/dist/multi-agent/utils/guardrails.js +34 -0
  92. package/dist/multi-agent/utils/memory.d.ts +8 -0
  93. package/dist/multi-agent/utils/memory.js +40 -0
  94. package/dist/multi-agent/utils/prompts.d.ts +4 -0
  95. package/dist/multi-agent/utils/prompts.js +25 -0
  96. package/dist/tracing/index.d.ts +89 -0
  97. package/dist/tracing/index.js +188 -0
  98. package/dist/tsup.config.d.ts +2 -0
  99. package/dist/tsup.config.js +11 -0
  100. package/dist/utils/schema-to-zod.d.ts +7 -0
  101. package/dist/utils/schema-to-zod.js +36 -0
  102. package/dist/workflow/Workflow.d.ts +106 -0
  103. package/dist/workflow/Workflow.js +204 -0
  104. package/dist/workflow/adapters.d.ts +61 -0
  105. package/dist/workflow/adapters.js +29 -0
  106. package/dist/workflow/definition/DefinitionBuilder.d.ts +9 -0
  107. package/dist/workflow/definition/DefinitionBuilder.js +91 -0
  108. package/dist/workflow/definition/DefinitionBuilder.spec.d.ts +1 -0
  109. package/dist/workflow/definition/DefinitionBuilder.spec.js +66 -0
  110. package/dist/workflow/definition/DefinitionHasher.d.ts +8 -0
  111. package/dist/workflow/definition/DefinitionHasher.js +11 -0
  112. package/dist/workflow/definition/DefinitionHasher.spec.d.ts +1 -0
  113. package/dist/workflow/definition/DefinitionHasher.spec.js +28 -0
  114. package/dist/workflow/definition/types.d.ts +27 -0
  115. package/dist/workflow/definition/types.js +2 -0
  116. package/dist/workflow/events.d.ts +9 -0
  117. package/dist/workflow/events.js +2 -0
  118. package/dist/workflow/execution/AgentNodeIntegration.spec.d.ts +1 -0
  119. package/dist/workflow/execution/AgentNodeIntegration.spec.js +50 -0
  120. package/dist/workflow/execution/NodeExecutor.d.ts +9 -0
  121. package/dist/workflow/execution/NodeExecutor.js +43 -0
  122. package/dist/workflow/execution/NodeExecutor.spec.d.ts +1 -0
  123. package/dist/workflow/execution/NodeExecutor.spec.js +45 -0
  124. package/dist/workflow/execution/WorkflowEventBus.d.ts +14 -0
  125. package/dist/workflow/execution/WorkflowEventBus.js +42 -0
  126. package/dist/workflow/execution/WorkflowEventBus.spec.d.ts +1 -0
  127. package/dist/workflow/execution/WorkflowEventBus.spec.js +78 -0
  128. package/dist/workflow/execution/WorkflowRunner.d.ts +26 -0
  129. package/dist/workflow/execution/WorkflowRunner.js +212 -0
  130. package/dist/workflow/execution/WorkflowRunner.spec.d.ts +1 -0
  131. package/dist/workflow/execution/WorkflowRunner.spec.js +92 -0
  132. package/dist/workflow/execution/WorkflowTelemetry.d.ts +13 -0
  133. package/dist/workflow/execution/WorkflowTelemetry.js +43 -0
  134. package/dist/workflow/execution/WorkflowTelemetry.spec.d.ts +1 -0
  135. package/dist/workflow/execution/WorkflowTelemetry.spec.js +31 -0
  136. package/dist/workflow/graph/NodeNameRegistry.d.ts +20 -0
  137. package/dist/workflow/graph/NodeNameRegistry.js +21 -0
  138. package/dist/workflow/graph/NodeNameRegistry.spec.d.ts +1 -0
  139. package/dist/workflow/graph/NodeNameRegistry.spec.js +18 -0
  140. package/dist/workflow/graph/WorkflowGraph.d.ts +14 -0
  141. package/dist/workflow/graph/WorkflowGraph.js +23 -0
  142. package/dist/workflow/graph/nodes.d.ts +26 -0
  143. package/dist/workflow/graph/nodes.js +2 -0
  144. package/dist/workflow/queue/WorkflowQueueService.d.ts +22 -0
  145. package/dist/workflow/queue/WorkflowQueueService.js +47 -0
  146. package/dist/workflow/state/WorkflowStateService.d.ts +7 -0
  147. package/dist/workflow/state/WorkflowStateService.js +20 -0
  148. package/dist/workflow/types.d.ts +16 -0
  149. package/dist/workflow/types.js +2 -0
  150. package/package.json +56 -0
@@ -0,0 +1,861 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Agent = void 0;
4
+ const index_js_1 = require("./skills/index.js");
5
+ const node_crypto_1 = require("node:crypto");
6
+ const index_js_2 = require("../logger/index.js");
7
+ const agent_logger_js_1 = require("../logger/agent-logger.js");
8
+ const index_js_3 = require("../tracing/index.js");
9
+ const BaseAgent_js_1 = require("./BaseAgent.js");
10
+ const index_js_4 = require("../execution-context/index.js");
11
+ function generateCorrelationId() {
12
+ return `exec_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
13
+ }
14
+ const MAX_MESSAGE_FILE_BYTES_DEFAULT = 10 * 1024 * 1024; // 10MB
15
+ // =============================================================================
16
+ // Agent Class
17
+ // =============================================================================
18
+ class Agent extends BaseAgent_js_1.BaseAgent {
19
+ config;
20
+ resolvedInstructions;
21
+ requestInterceptors = [];
22
+ responseInterceptors = [];
23
+ // @ts-ignore: used for side-effects (binding events)
24
+ loggerAdapter;
25
+ skillsInitialized = false;
26
+ httpClient;
27
+ constructor(config) {
28
+ super(config.id, config.name, config.piiMasker, config.memory);
29
+ this.config = config;
30
+ this.requestInterceptors = config.interceptors?.request || [];
31
+ this.responseInterceptors = config.interceptors?.response || [];
32
+ // Setup Logger
33
+ const logger = config.logger || new index_js_2.ConsoleLogger(config.logLevel || "error");
34
+ this.loggerAdapter = new agent_logger_js_1.AgentLogger(this, logger);
35
+ }
36
+ /**
37
+ * Sets the HTTP client for tool execution context.
38
+ * This is typically called by the client package with InstrumentedHttpClient.
39
+ */
40
+ setHttpClient(client) {
41
+ this.httpClient = client;
42
+ }
43
+ // ==========================================================================
44
+ // Skills Initialization
45
+ // ==========================================================================
46
+ async initializeSkills() {
47
+ if (this.skillsInitialized)
48
+ return;
49
+ const skills = this.config.skills || [];
50
+ if (skills.length === 0) {
51
+ this.skillsInitialized = true;
52
+ return;
53
+ }
54
+ // Initialize each skill
55
+ for (const skill of skills) {
56
+ if (!(0, index_js_1.isSkill)(skill)) {
57
+ throw new Error(`Invalid skill: object does not implement ISkill interface`);
58
+ }
59
+ // Call optional initialize method
60
+ if (skill.initialize) {
61
+ await skill.initialize();
62
+ }
63
+ // Get tools from skill
64
+ const skillTools = await Promise.resolve(skill.getTools());
65
+ // Merge skill tools with existing tools
66
+ this.config.tools = [...(this.config.tools || []), ...skillTools];
67
+ }
68
+ this.skillsInitialized = true;
69
+ }
70
+ // ==========================================================================
71
+ // Instructions
72
+ // ==========================================================================
73
+ async resolveInstructions() {
74
+ if (this.resolvedInstructions !== undefined)
75
+ return this.resolvedInstructions;
76
+ let baseInstructions = "";
77
+ if (typeof this.config.instructions === "string") {
78
+ baseInstructions = this.config.instructions;
79
+ }
80
+ else if (this.isInstruction(this.config.instructions)) {
81
+ baseInstructions = await this.config.instructions.resolve();
82
+ }
83
+ // Append skill instructions if any
84
+ const skills = this.config.skills || [];
85
+ const skillInstructions = [];
86
+ for (const skill of skills) {
87
+ if (skill.getInstructions) {
88
+ const instructions = await Promise.resolve(skill.getInstructions());
89
+ if (instructions) {
90
+ skillInstructions.push(instructions);
91
+ }
92
+ }
93
+ }
94
+ if (skillInstructions.length > 0) {
95
+ this.resolvedInstructions = [baseInstructions, ...skillInstructions].filter(Boolean).join("\n\n");
96
+ }
97
+ else {
98
+ this.resolvedInstructions = baseInstructions;
99
+ }
100
+ return this.resolvedInstructions;
101
+ }
102
+ isInstruction(obj) {
103
+ return obj && typeof obj.resolve === "function";
104
+ }
105
+ clearInstructionsCache() {
106
+ this.resolvedInstructions = undefined;
107
+ }
108
+ // ==========================================================================
109
+ // Memory
110
+ // ==========================================================================
111
+ async addMessage(message, ctx) {
112
+ this.memory = this.config.memory;
113
+ await this.config.memory.addMessage(message, ctx);
114
+ }
115
+ async getMessagesWithContext(ctx) {
116
+ const instructions = await this.resolveInstructions();
117
+ const msgs = await this.config.memory.getMessages(ctx);
118
+ // Ensure system prompt is present if instructions exist
119
+ if (instructions && !msgs.some((m) => m.role === "system")) {
120
+ msgs.unshift({ role: "system", content: instructions });
121
+ }
122
+ return msgs;
123
+ }
124
+ async execute(arg1 = {}, arg2 = {}) {
125
+ // Parse input: can be string, ContentPart[], or undefined
126
+ const input = typeof arg1 === "string" || Array.isArray(arg1) ? arg1 : undefined;
127
+ const options = typeof arg1 === "string" || Array.isArray(arg1) ? (arg2 ?? {}) : (arg1 ?? {});
128
+ const startTime = Date.now();
129
+ const correlationId = generateCorrelationId();
130
+ const maxSteps = options.maxSteps ?? this.config.maxSteps ?? 10;
131
+ // Create memory context for partitioning
132
+ const memoryCtx = options.memoryKey
133
+ ? { memoryKey: options.memoryKey }
134
+ : undefined;
135
+ const toolExecutions = [];
136
+ const collectedToolCalls = [];
137
+ let llmCalls = 0;
138
+ let truncated = false;
139
+ let totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
140
+ const runWithExecutionContext = async () => index_js_3.Tracing.withSpan({
141
+ kind: "agent",
142
+ name: `agent:${this.name}`,
143
+ correlationId,
144
+ executionId: options.executionId,
145
+ data: { agentName: this.name, input },
146
+ }, async (agentSpan) => {
147
+ this.emit("agent:execution:start", {
148
+ correlationId,
149
+ executionId: options.executionId,
150
+ data: {
151
+ agentId: this.id,
152
+ agentName: this.name,
153
+ input,
154
+ },
155
+ });
156
+ try {
157
+ // Initialize skills if not already initialized
158
+ await this.initializeSkills();
159
+ if (input) {
160
+ await this.addMessage({ role: "user", content: input }, memoryCtx);
161
+ }
162
+ let step = 0;
163
+ while (step < maxSteps) {
164
+ step++;
165
+ llmCalls++;
166
+ let messages = await this.getMessagesWithContext(memoryCtx);
167
+ let tools = !options.disableTools && this.config.tools ? [...this.config.tools] : [];
168
+ // Apply Request Interceptors
169
+ let context = { messages, tools };
170
+ for (const interceptor of this.requestInterceptors) {
171
+ context = await interceptor(context);
172
+ }
173
+ messages = context.messages;
174
+ tools = context.tools;
175
+ this.validateMessagesForModel(messages, {
176
+ maxFileBytes: MAX_MESSAGE_FILE_BYTES_DEFAULT,
177
+ providerName: this.config.model.providerName,
178
+ modelName: this.config.model.modelName,
179
+ capabilities: this.config.model.capabilities,
180
+ });
181
+ const llmTools = tools.length > 0
182
+ ? tools.map(t => t.toLLMDefinition())
183
+ : undefined;
184
+ this.emit("agent:llm:call", {
185
+ correlationId,
186
+ executionId: options.executionId,
187
+ data: {
188
+ agentId: this.id,
189
+ agentName: this.name,
190
+ step,
191
+ messages,
192
+ tools: llmTools,
193
+ responseFormat: options.responseFormat,
194
+ model: {
195
+ provider: this.config.model.providerName,
196
+ model: this.config.model.modelName,
197
+ },
198
+ },
199
+ });
200
+ // No SDK-side LLM span: the LLM Gateway creates its own span
201
+ // with richer data. We just call generate under the agent span so
202
+ // the provider can read the current trace context (traceparent).
203
+ const rawResponse = await this.config.model.generate(messages, llmTools, {
204
+ responseFormat: options.responseFormat,
205
+ });
206
+ let response = rawResponse;
207
+ this.emit("agent:llm:response", {
208
+ correlationId,
209
+ executionId: options.executionId,
210
+ data: {
211
+ agentId: this.id,
212
+ agentName: this.name,
213
+ step,
214
+ content: response.content,
215
+ toolCalls: response.toolCalls,
216
+ usage: response.usage,
217
+ model: {
218
+ provider: this.config.model.providerName,
219
+ model: this.config.model.modelName,
220
+ },
221
+ },
222
+ });
223
+ // Accumulate token usage
224
+ if (response.usage) {
225
+ totalUsage.promptTokens += response.usage.promptTokens;
226
+ totalUsage.completionTokens += response.usage.completionTokens;
227
+ totalUsage.totalTokens += response.usage.totalTokens;
228
+ if (response.usage.reasoningTokens) {
229
+ totalUsage.reasoningTokens = (totalUsage.reasoningTokens || 0) + response.usage.reasoningTokens;
230
+ }
231
+ }
232
+ if (!response.toolCalls || response.toolCalls.length === 0 || options.disableTools) {
233
+ // Apply Response Interceptors (Final Answer)
234
+ for (const interceptor of this.responseInterceptors) {
235
+ response = await interceptor(response);
236
+ }
237
+ await this.addMessage({ role: "assistant", content: response.content }, memoryCtx);
238
+ await agentSpan.end({
239
+ status: "success",
240
+ data: {
241
+ output: options.responseFormat?.type === "json"
242
+ ? this.safeParseJson(response.content)
243
+ : response.content,
244
+ usage: totalUsage,
245
+ },
246
+ });
247
+ return this.finishResult({
248
+ correlationId,
249
+ executionId: options.executionId,
250
+ content: options.responseFormat?.type === "json"
251
+ ? this.safeParseJson(response.content)
252
+ : response.content,
253
+ toolExecutions,
254
+ toolCalls: options.disableTools ? response.toolCalls : undefined,
255
+ llmCalls,
256
+ totalDurationMs: Date.now() - startTime,
257
+ truncated: false,
258
+ totalUsage,
259
+ });
260
+ }
261
+ // Add assistant message with tool calls before executing tools
262
+ await this.addMessage({
263
+ role: "assistant",
264
+ content: response.content,
265
+ tool_calls: response.toolCalls
266
+ }, memoryCtx);
267
+ for (const toolCall of response.toolCalls) {
268
+ const parentExec = index_js_4.ExecutionContext.getOrUndefined();
269
+ const toolExecutionId = parentExec?.executionId || options.executionId ? (0, node_crypto_1.randomUUID)() : undefined;
270
+ await index_js_3.Tracing.withSpan({
271
+ kind: "tool",
272
+ name: `tool:${toolCall.function.name}`,
273
+ correlationId,
274
+ executionId: toolExecutionId ?? options.executionId,
275
+ data: {
276
+ toolName: toolCall.function.name,
277
+ toolCallId: toolCall.id,
278
+ args: toolCall.function.arguments,
279
+ },
280
+ }, async (toolSpan) => {
281
+ this.emit("agent:tool:call", {
282
+ correlationId,
283
+ executionId: options.executionId,
284
+ data: {
285
+ agentId: this.id,
286
+ agentName: this.name,
287
+ toolName: toolCall.function.name,
288
+ toolCallId: toolCall.id,
289
+ args: toolCall.function.arguments,
290
+ },
291
+ });
292
+ const execution = await this.executeToolCall(toolCall, toolExecutionId);
293
+ toolExecutions.push(execution);
294
+ collectedToolCalls.push(toolCall);
295
+ this.emit("agent:tool:result", {
296
+ correlationId,
297
+ executionId: options.executionId,
298
+ data: {
299
+ agentId: this.id,
300
+ agentName: this.name,
301
+ toolName: toolCall.function.name,
302
+ toolCallId: toolCall.id,
303
+ output: execution.output,
304
+ error: execution.error,
305
+ durationMs: execution.durationMs,
306
+ },
307
+ });
308
+ await toolSpan.end({
309
+ status: execution.error ? "error" : "success",
310
+ data: {
311
+ output: execution.output,
312
+ error: execution.error,
313
+ },
314
+ });
315
+ await this.addMessage({
316
+ role: "tool",
317
+ tool_call_id: toolCall.id,
318
+ content: execution.error ? `Error: ${execution.error}` : JSON.stringify(execution.output),
319
+ }, memoryCtx);
320
+ });
321
+ }
322
+ }
323
+ truncated = true;
324
+ llmCalls++;
325
+ // Final run after max steps (force answer)
326
+ let finalMessages = await this.getMessagesWithContext(memoryCtx);
327
+ let finalTools = !options.disableTools && this.config.tools ? [...this.config.tools] : [];
328
+ // Interceptors for final run
329
+ let context = { messages: finalMessages, tools: finalTools };
330
+ for (const interceptor of this.requestInterceptors) {
331
+ context = await interceptor(context);
332
+ }
333
+ finalMessages = context.messages;
334
+ // We ignore tools for final run usually, but keep consistency
335
+ this.validateMessagesForModel(finalMessages, {
336
+ maxFileBytes: MAX_MESSAGE_FILE_BYTES_DEFAULT,
337
+ providerName: this.config.model.providerName,
338
+ modelName: this.config.model.modelName,
339
+ capabilities: this.config.model.capabilities,
340
+ });
341
+ let finalResponse = await this.config.model.generate(finalMessages);
342
+ // Response interceptors for final run
343
+ for (const interceptor of this.responseInterceptors) {
344
+ finalResponse = await interceptor(finalResponse);
345
+ }
346
+ await this.addMessage({ role: "assistant", content: finalResponse.content }, memoryCtx);
347
+ await agentSpan.end({
348
+ status: truncated ? "cancelled" : "success",
349
+ data: {
350
+ output: finalResponse.content,
351
+ usage: totalUsage,
352
+ },
353
+ });
354
+ return this.finishResult({
355
+ correlationId,
356
+ executionId: options.executionId,
357
+ content: finalResponse.content,
358
+ toolExecutions,
359
+ toolCalls: collectedToolCalls,
360
+ llmCalls,
361
+ totalDurationMs: Date.now() - startTime,
362
+ truncated,
363
+ totalUsage,
364
+ });
365
+ }
366
+ catch (err) {
367
+ const error = err instanceof Error ? err : new Error(String(err));
368
+ this.emit("agent:execution:error", {
369
+ correlationId,
370
+ executionId: options.executionId,
371
+ data: {
372
+ agentId: this.id,
373
+ agentName: this.name,
374
+ error: {
375
+ message: error.message,
376
+ stack: error.stack,
377
+ name: error.name,
378
+ },
379
+ durationMs: Date.now() - startTime,
380
+ },
381
+ });
382
+ await agentSpan.end({ status: "error" });
383
+ throw err;
384
+ }
385
+ });
386
+ // Ensure execution hierarchy is available for tool sub-executions.
387
+ // IMPORTANT: Do NOT override an existing ExecutionContext (e.g. when this agent is a sub-execution inside a workflow).
388
+ const existingExecContext = index_js_4.ExecutionContext.getOrUndefined();
389
+ if (!existingExecContext && options.executionId) {
390
+ return index_js_4.ExecutionContext.run({ executionId: options.executionId, rootExecutionId: options.executionId, parentExecutionId: undefined }, runWithExecutionContext);
391
+ }
392
+ return runWithExecutionContext();
393
+ }
394
+ async *executeStream(options = {}) {
395
+ const startTime = Date.now();
396
+ const correlationId = generateCorrelationId();
397
+ const maxSteps = options.maxSteps ?? this.config.maxSteps ?? 10;
398
+ // Create memory context for partitioning
399
+ const memoryCtx = options.memoryKey
400
+ ? { memoryKey: options.memoryKey }
401
+ : undefined;
402
+ const toolExecutions = [];
403
+ const collectedToolCalls = [];
404
+ let llmCalls = 0;
405
+ let truncated = false;
406
+ let totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
407
+ this.emit("agent:execution:start", {
408
+ correlationId,
409
+ executionId: options.executionId,
410
+ data: {
411
+ agentId: this.id,
412
+ agentName: this.name,
413
+ input: undefined,
414
+ },
415
+ });
416
+ try {
417
+ // Initialize skills if not already initialized
418
+ await this.initializeSkills();
419
+ let step = 0;
420
+ while (step < maxSteps) {
421
+ step++;
422
+ llmCalls++;
423
+ let messages = await this.getMessagesWithContext(memoryCtx);
424
+ let tools = !options.disableTools && this.config.tools ? [...this.config.tools] : [];
425
+ // Apply Request Interceptors
426
+ let context = { messages, tools };
427
+ for (const interceptor of this.requestInterceptors) {
428
+ context = await interceptor(context);
429
+ }
430
+ messages = context.messages;
431
+ tools = context.tools;
432
+ this.validateMessagesForModel(messages, {
433
+ maxFileBytes: MAX_MESSAGE_FILE_BYTES_DEFAULT,
434
+ providerName: this.config.model.providerName,
435
+ modelName: this.config.model.modelName,
436
+ capabilities: this.config.model.capabilities,
437
+ });
438
+ const llmTools = tools.length > 0
439
+ ? tools.map(t => t.toLLMDefinition())
440
+ : undefined;
441
+ this.emit("agent:llm:call", {
442
+ correlationId,
443
+ executionId: options.executionId,
444
+ data: {
445
+ agentId: this.id,
446
+ agentName: this.name,
447
+ step,
448
+ messages,
449
+ tools: llmTools,
450
+ responseFormat: options.responseFormat,
451
+ model: {
452
+ provider: this.config.model.providerName,
453
+ model: this.config.model.modelName,
454
+ },
455
+ },
456
+ });
457
+ const stream = this.config.model.generateStream(messages, llmTools, { responseFormat: options.responseFormat });
458
+ let fullContent = "";
459
+ let toolCallsBuffer = {};
460
+ let chunkUsage;
461
+ for await (const chunk of stream) {
462
+ yield chunk;
463
+ if (chunk.type === "content_delta") {
464
+ fullContent += chunk.delta;
465
+ }
466
+ else if (chunk.type === "tool_call_start") {
467
+ toolCallsBuffer[chunk.index] = {
468
+ id: chunk.id,
469
+ name: chunk.name,
470
+ args: "",
471
+ thought_signature: chunk.thought_signature
472
+ };
473
+ }
474
+ else if (chunk.type === "tool_call_delta") {
475
+ if (toolCallsBuffer[chunk.index]) {
476
+ toolCallsBuffer[chunk.index].args += chunk.args;
477
+ }
478
+ }
479
+ else if (chunk.type === "finish") {
480
+ chunkUsage = chunk.usage;
481
+ }
482
+ }
483
+ // Accumulate token usage
484
+ if (chunkUsage) {
485
+ totalUsage.promptTokens += chunkUsage.promptTokens;
486
+ totalUsage.completionTokens += chunkUsage.completionTokens;
487
+ totalUsage.totalTokens += chunkUsage.totalTokens;
488
+ if (chunkUsage.reasoningTokens) {
489
+ totalUsage.reasoningTokens = (totalUsage.reasoningTokens || 0) + chunkUsage.reasoningTokens;
490
+ }
491
+ }
492
+ const toolCalls = Object.values(toolCallsBuffer).map(tc => ({
493
+ id: tc.id,
494
+ type: "function",
495
+ function: { name: tc.name, arguments: tc.args },
496
+ ...(tc.thought_signature ? { thought_signature: tc.thought_signature } : {})
497
+ }));
498
+ this.emit("agent:llm:response", {
499
+ correlationId,
500
+ executionId: options.executionId,
501
+ data: {
502
+ agentId: this.id,
503
+ agentName: this.name,
504
+ step,
505
+ content: fullContent,
506
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
507
+ usage: chunkUsage,
508
+ model: {
509
+ provider: this.config.model.providerName,
510
+ model: this.config.model.modelName,
511
+ },
512
+ },
513
+ });
514
+ if (toolCalls.length === 0 || options.disableTools) {
515
+ await this.addMessage({ role: "assistant", content: fullContent }, memoryCtx);
516
+ // Emit execution:end event
517
+ this.emit("agent:execution:end", {
518
+ correlationId,
519
+ executionId: options.executionId,
520
+ data: {
521
+ agentId: this.id,
522
+ agentName: this.name,
523
+ status: "success",
524
+ output: options.responseFormat?.type === "json"
525
+ ? this.safeParseJson(fullContent)
526
+ : fullContent,
527
+ durationMs: Date.now() - startTime,
528
+ usage: totalUsage,
529
+ },
530
+ });
531
+ return;
532
+ }
533
+ await this.addMessage({ role: "assistant", content: fullContent || null, tool_calls: toolCalls }, memoryCtx);
534
+ for (const toolCall of toolCalls) {
535
+ const execution = await index_js_3.Tracing.withSpan({
536
+ kind: "tool",
537
+ name: `tool:${toolCall.function.name}`,
538
+ correlationId,
539
+ executionId: options.executionId,
540
+ data: {
541
+ toolName: toolCall.function.name,
542
+ toolCallId: toolCall.id,
543
+ args: toolCall.function.arguments,
544
+ },
545
+ }, async (toolSpan) => {
546
+ this.emit("agent:tool:call", {
547
+ correlationId,
548
+ executionId: options.executionId,
549
+ data: {
550
+ agentId: this.id,
551
+ agentName: this.name,
552
+ toolName: toolCall.function.name,
553
+ toolCallId: toolCall.id,
554
+ args: toolCall.function.arguments,
555
+ },
556
+ });
557
+ const exec = await this.executeToolCall(toolCall);
558
+ toolExecutions.push(exec);
559
+ collectedToolCalls.push(toolCall);
560
+ this.emit("agent:tool:result", {
561
+ correlationId,
562
+ executionId: options.executionId,
563
+ data: {
564
+ agentId: this.id,
565
+ agentName: this.name,
566
+ toolName: toolCall.function.name,
567
+ toolCallId: toolCall.id,
568
+ output: exec.output,
569
+ error: exec.error,
570
+ durationMs: exec.durationMs,
571
+ },
572
+ });
573
+ await toolSpan.end({
574
+ status: exec.error ? "error" : "success",
575
+ data: { output: exec.output, error: exec.error },
576
+ });
577
+ return exec;
578
+ });
579
+ yield {
580
+ type: "tool_result",
581
+ toolCallId: toolCall.id,
582
+ result: execution.output
583
+ };
584
+ await this.addMessage({
585
+ role: "tool",
586
+ tool_call_id: toolCall.id,
587
+ content: execution.error ? `Error: ${execution.error}` : JSON.stringify(execution.output),
588
+ }, memoryCtx);
589
+ }
590
+ }
591
+ // Max steps reached - do final non-stream call
592
+ truncated = true;
593
+ llmCalls++;
594
+ let finalMessages = await this.getMessagesWithContext(memoryCtx);
595
+ let finalTools = !options.disableTools && this.config.tools ? [...this.config.tools] : [];
596
+ // Apply Request Interceptors for final call
597
+ let finalContext = { messages: finalMessages, tools: finalTools };
598
+ for (const interceptor of this.requestInterceptors) {
599
+ finalContext = await interceptor(finalContext);
600
+ }
601
+ finalMessages = finalContext.messages;
602
+ this.validateMessagesForModel(finalMessages, {
603
+ maxFileBytes: MAX_MESSAGE_FILE_BYTES_DEFAULT,
604
+ providerName: this.config.model.providerName,
605
+ modelName: this.config.model.modelName,
606
+ capabilities: this.config.model.capabilities,
607
+ });
608
+ const finalResponse = await this.config.model.generate(finalMessages);
609
+ // Accumulate final usage
610
+ if (finalResponse.usage) {
611
+ totalUsage.promptTokens += finalResponse.usage.promptTokens;
612
+ totalUsage.completionTokens += finalResponse.usage.completionTokens;
613
+ totalUsage.totalTokens += finalResponse.usage.totalTokens;
614
+ if (finalResponse.usage.reasoningTokens) {
615
+ totalUsage.reasoningTokens = (totalUsage.reasoningTokens || 0) + finalResponse.usage.reasoningTokens;
616
+ }
617
+ }
618
+ await this.addMessage({ role: "assistant", content: finalResponse.content }, memoryCtx);
619
+ // Emit final result
620
+ this.emit("agent:execution:end", {
621
+ correlationId,
622
+ executionId: options.executionId,
623
+ data: {
624
+ agentId: this.id,
625
+ agentName: this.name,
626
+ status: truncated ? "truncated" : "success",
627
+ output: finalResponse.content,
628
+ durationMs: Date.now() - startTime,
629
+ usage: totalUsage,
630
+ },
631
+ });
632
+ }
633
+ catch (err) {
634
+ const error = err instanceof Error ? err : new Error(String(err));
635
+ this.emit("agent:execution:error", {
636
+ correlationId,
637
+ executionId: options.executionId,
638
+ data: {
639
+ agentId: this.id,
640
+ agentName: this.name,
641
+ error: {
642
+ message: error.message,
643
+ stack: error.stack,
644
+ name: error.name,
645
+ },
646
+ durationMs: Date.now() - startTime,
647
+ },
648
+ });
649
+ yield { type: "error", error };
650
+ }
651
+ }
652
+ // ==========================================================================
653
+ // Tools Execution
654
+ // ==========================================================================
655
+ async executeToolCall(toolCall, toolExecutionId) {
656
+ const startTime = Date.now();
657
+ const toolName = toolCall.function.name;
658
+ const tool = this.config.tools?.find((t) => t.id === toolName);
659
+ if (!tool) {
660
+ return {
661
+ id: toolCall.id,
662
+ name: toolName,
663
+ input: toolCall.function.arguments,
664
+ output: null,
665
+ durationMs: Date.now() - startTime,
666
+ error: `Tool '${toolName}' not found`,
667
+ };
668
+ }
669
+ try {
670
+ const parentExec = index_js_4.ExecutionContext.getOrUndefined();
671
+ if (toolExecutionId && parentExec) {
672
+ return index_js_4.ExecutionContext.run({
673
+ executionId: toolExecutionId,
674
+ rootExecutionId: parentExec.rootExecutionId ?? parentExec.executionId,
675
+ parentExecutionId: parentExec.executionId,
676
+ }, async () => {
677
+ this.emit("tool:execution:start", {
678
+ data: {
679
+ toolId: tool.id,
680
+ toolName,
681
+ toolCallId: toolCall.id,
682
+ input: toolCall.function.arguments,
683
+ },
684
+ });
685
+ try {
686
+ const toolCtx = this.httpClient ? { http: this.httpClient } : {};
687
+ const output = await tool.execute(toolCtx, toolCall.function.arguments);
688
+ const durationMs = Date.now() - startTime;
689
+ this.emit("tool:execution:end", {
690
+ data: {
691
+ toolId: tool.id,
692
+ toolName,
693
+ toolCallId: toolCall.id,
694
+ status: "success",
695
+ // IMPORTANT:
696
+ // Tool outputs are not guaranteed to be JSON and can be large.
697
+ // For domain events (materialization stream), we intentionally do NOT send the full output.
698
+ // The execution detail screen should use tracing/parent execution output for deep debugging.
699
+ durationMs,
700
+ },
701
+ });
702
+ return {
703
+ id: toolCall.id,
704
+ name: toolName,
705
+ input: toolCall.function.arguments,
706
+ output,
707
+ durationMs,
708
+ };
709
+ }
710
+ catch (err) {
711
+ const durationMs = Date.now() - startTime;
712
+ const msg = err instanceof Error ? err.message : String(err);
713
+ this.emit("tool:execution:error", {
714
+ data: {
715
+ toolId: tool.id,
716
+ toolName,
717
+ toolCallId: toolCall.id,
718
+ error: { message: msg },
719
+ durationMs,
720
+ },
721
+ });
722
+ return {
723
+ id: toolCall.id,
724
+ name: toolName,
725
+ input: toolCall.function.arguments,
726
+ output: null,
727
+ durationMs,
728
+ error: msg,
729
+ };
730
+ }
731
+ });
732
+ }
733
+ const toolCtx = this.httpClient ? { http: this.httpClient } : {};
734
+ const output = await tool.execute(toolCtx, toolCall.function.arguments);
735
+ return {
736
+ id: toolCall.id,
737
+ name: toolName,
738
+ input: toolCall.function.arguments,
739
+ output,
740
+ durationMs: Date.now() - startTime,
741
+ };
742
+ }
743
+ catch (error) {
744
+ return {
745
+ id: toolCall.id,
746
+ name: toolName,
747
+ input: toolCall.function.arguments,
748
+ output: null,
749
+ durationMs: Date.now() - startTime,
750
+ error: error instanceof Error ? error.message : String(error),
751
+ };
752
+ }
753
+ }
754
+ // ==========================================================================
755
+ // Helpers
756
+ // ==========================================================================
757
+ safeParseJson(content) {
758
+ if (!content)
759
+ return null;
760
+ try {
761
+ return JSON.parse(content);
762
+ }
763
+ catch {
764
+ return content;
765
+ }
766
+ }
767
+ validateMessagesForModel(messages, params) {
768
+ const fileParts = [];
769
+ for (const msg of messages) {
770
+ if (!Array.isArray(msg.content))
771
+ continue;
772
+ for (const part of msg.content) {
773
+ if (part.type === "file")
774
+ fileParts.push(part);
775
+ }
776
+ }
777
+ if (fileParts.length === 0)
778
+ return;
779
+ // Enforce max size for base64 (URL is best-effort unless sizeBytes is provided)
780
+ for (const part of fileParts) {
781
+ const source = part.file.source;
782
+ if (source.type === "base64") {
783
+ const bytes = this.estimateBase64Bytes(source.base64);
784
+ if (bytes > params.maxFileBytes) {
785
+ const name = part.file.name ?? "unknown";
786
+ throw new Error(`Message file '${name}' exceeds max size (${bytes} bytes > ${params.maxFileBytes} bytes)`);
787
+ }
788
+ }
789
+ else {
790
+ const size = source.sizeBytes;
791
+ if (typeof size === "number" && size > params.maxFileBytes) {
792
+ const name = part.file.name ?? "unknown";
793
+ throw new Error(`Message file '${name}' exceeds max size (${size} bytes > ${params.maxFileBytes} bytes)`);
794
+ }
795
+ }
796
+ }
797
+ const caps = params.capabilities?.inputFiles;
798
+ if (!caps) {
799
+ throw new Error(`Model does not support file inputs (provider=${params.providerName}, model=${params.modelName})`);
800
+ }
801
+ for (const part of fileParts) {
802
+ const mimeType = part.file.mimeType;
803
+ const sourceType = part.file.source.type;
804
+ if (!caps.sources.includes(sourceType)) {
805
+ throw new Error(`Model does not support file source '${sourceType}' (provider=${params.providerName}, model=${params.modelName})`);
806
+ }
807
+ if (caps.mimeTypes === "any")
808
+ continue;
809
+ const ok = caps.mimeTypes.some((allowed) => this.mimeTypeMatches(allowed, mimeType));
810
+ if (!ok) {
811
+ throw new Error(`Model does not support file mimeType '${mimeType}' (provider=${params.providerName}, model=${params.modelName})`);
812
+ }
813
+ }
814
+ }
815
+ mimeTypeMatches(allowed, actual) {
816
+ if (allowed === actual)
817
+ return true;
818
+ if (allowed.endsWith("/*")) {
819
+ const prefix = allowed.slice(0, allowed.length - 1); // keep trailing '/'
820
+ return actual.startsWith(prefix);
821
+ }
822
+ return false;
823
+ }
824
+ /**
825
+ * Estimates the decoded byte length of a base64 string without decoding it.
826
+ */
827
+ estimateBase64Bytes(base64) {
828
+ const trimmed = base64.trim();
829
+ if (!trimmed)
830
+ return 0;
831
+ // Remove whitespace/newlines if present
832
+ const normalized = trimmed.replace(/\s+/g, "");
833
+ const len = normalized.length;
834
+ const padding = normalized.endsWith("==") ? 2 : normalized.endsWith("=") ? 1 : 0;
835
+ return Math.floor((len * 3) / 4) - padding;
836
+ }
837
+ finishResult(params) {
838
+ const result = {
839
+ content: params.content,
840
+ toolExecutions: params.toolExecutions,
841
+ toolCalls: params.toolCalls,
842
+ llmCalls: params.llmCalls,
843
+ totalDurationMs: params.totalDurationMs,
844
+ truncated: params.truncated,
845
+ };
846
+ this.emit("agent:execution:end", {
847
+ correlationId: params.correlationId,
848
+ executionId: params.executionId,
849
+ data: {
850
+ agentId: this.id,
851
+ agentName: this.name,
852
+ status: params.truncated ? "truncated" : "success",
853
+ output: result.content,
854
+ usage: params.totalUsage,
855
+ durationMs: params.totalDurationMs,
856
+ },
857
+ });
858
+ return result;
859
+ }
860
+ }
861
+ exports.Agent = Agent;