@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.
- package/README.md +206 -0
- package/dist/__tests__/mocks/mock-provider.d.ts +15 -0
- package/dist/__tests__/mocks/mock-provider.js +44 -0
- package/dist/agent/Agent.d.ts +96 -0
- package/dist/agent/Agent.js +861 -0
- package/dist/agent/BaseAgent.d.ts +53 -0
- package/dist/agent/BaseAgent.js +126 -0
- package/dist/agent/events/events.d.ts +14 -0
- package/dist/agent/events/events.js +2 -0
- package/dist/agent/events/events.spec.d.ts +1 -0
- package/dist/agent/events/events.spec.js +75 -0
- package/dist/agent/instruction/index.d.ts +23 -0
- package/dist/agent/instruction/index.js +76 -0
- package/dist/agent/memory/in-memory.d.ts +24 -0
- package/dist/agent/memory/in-memory.js +78 -0
- package/dist/agent/memory/index.d.ts +2 -0
- package/dist/agent/memory/index.js +18 -0
- package/dist/agent/memory/memory.d.ts +43 -0
- package/dist/agent/memory/memory.js +7 -0
- package/dist/agent/provider/file-parts.spec.d.ts +1 -0
- package/dist/agent/provider/file-parts.spec.js +83 -0
- package/dist/agent/provider/index.d.ts +130 -0
- package/dist/agent/provider/index.js +8 -0
- package/dist/agent/skills/index.d.ts +61 -0
- package/dist/agent/skills/index.js +9 -0
- package/dist/agent/tools/index.d.ts +35 -0
- package/dist/agent/tools/index.js +87 -0
- package/dist/cost/add-cost.d.ts +10 -0
- package/dist/cost/add-cost.js +80 -0
- package/dist/cost/add-cost.spec.d.ts +1 -0
- package/dist/cost/add-cost.spec.js +36 -0
- package/dist/cost/index.d.ts +1 -0
- package/dist/cost/index.js +17 -0
- package/dist/domain-events/index.d.ts +16 -0
- package/dist/domain-events/index.js +38 -0
- package/dist/eval/index.d.ts +19 -0
- package/dist/eval/index.js +24 -0
- package/dist/events/base.d.ts +5 -0
- package/dist/events/base.js +2 -0
- package/dist/events/schemas.d.ts +3463 -0
- package/dist/events/schemas.js +244 -0
- package/dist/execution-context/index.d.ts +21 -0
- package/dist/execution-context/index.js +17 -0
- package/dist/index.cjs +2958 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3425 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/lgpd/index.d.ts +7 -0
- package/dist/lgpd/index.js +21 -0
- package/dist/logger/agent-logger.d.ts +16 -0
- package/dist/logger/agent-logger.js +110 -0
- package/dist/logger/formatters.d.ts +32 -0
- package/dist/logger/formatters.js +146 -0
- package/dist/logger/index.d.ts +30 -0
- package/dist/logger/index.js +88 -0
- package/dist/logger/styles.d.ts +46 -0
- package/dist/logger/styles.js +53 -0
- package/dist/logger/workflow-logger.d.ts +16 -0
- package/dist/logger/workflow-logger.js +79 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.d.ts +16 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.js +54 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.spec.d.ts +1 -0
- package/dist/multi-agent/agent-as-tool/AgentAsTool.spec.js +76 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.d.ts +16 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.js +150 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.spec.d.ts +1 -0
- package/dist/multi-agent/committee-team/CommitteeTeam.spec.js +43 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.d.ts +16 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.js +185 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.spec.d.ts +1 -0
- package/dist/multi-agent/handoff-team/HandoffTeam.spec.js +105 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.d.ts +18 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.js +164 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.spec.d.ts +1 -0
- package/dist/multi-agent/hierarchical-team/HierarchicalTeam.spec.js +53 -0
- package/dist/multi-agent/index.d.ts +10 -0
- package/dist/multi-agent/index.js +26 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.d.ts +13 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.js +104 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.spec.d.ts +1 -0
- package/dist/multi-agent/pipeline-team/PipelineTeam.spec.js +54 -0
- package/dist/multi-agent/router-team/RouterTeam.d.ts +15 -0
- package/dist/multi-agent/router-team/RouterTeam.js +153 -0
- package/dist/multi-agent/router-team/RouterTeam.spec.d.ts +1 -0
- package/dist/multi-agent/router-team/RouterTeam.spec.js +69 -0
- package/dist/multi-agent/types/index.d.ts +349 -0
- package/dist/multi-agent/types/index.js +79 -0
- package/dist/multi-agent/utils/guardrails.d.ts +6 -0
- package/dist/multi-agent/utils/guardrails.js +34 -0
- package/dist/multi-agent/utils/memory.d.ts +8 -0
- package/dist/multi-agent/utils/memory.js +40 -0
- package/dist/multi-agent/utils/prompts.d.ts +4 -0
- package/dist/multi-agent/utils/prompts.js +25 -0
- package/dist/tracing/index.d.ts +89 -0
- package/dist/tracing/index.js +188 -0
- package/dist/tsup.config.d.ts +2 -0
- package/dist/tsup.config.js +11 -0
- package/dist/utils/schema-to-zod.d.ts +7 -0
- package/dist/utils/schema-to-zod.js +36 -0
- package/dist/workflow/Workflow.d.ts +106 -0
- package/dist/workflow/Workflow.js +204 -0
- package/dist/workflow/adapters.d.ts +61 -0
- package/dist/workflow/adapters.js +29 -0
- package/dist/workflow/definition/DefinitionBuilder.d.ts +9 -0
- package/dist/workflow/definition/DefinitionBuilder.js +91 -0
- package/dist/workflow/definition/DefinitionBuilder.spec.d.ts +1 -0
- package/dist/workflow/definition/DefinitionBuilder.spec.js +66 -0
- package/dist/workflow/definition/DefinitionHasher.d.ts +8 -0
- package/dist/workflow/definition/DefinitionHasher.js +11 -0
- package/dist/workflow/definition/DefinitionHasher.spec.d.ts +1 -0
- package/dist/workflow/definition/DefinitionHasher.spec.js +28 -0
- package/dist/workflow/definition/types.d.ts +27 -0
- package/dist/workflow/definition/types.js +2 -0
- package/dist/workflow/events.d.ts +9 -0
- package/dist/workflow/events.js +2 -0
- package/dist/workflow/execution/AgentNodeIntegration.spec.d.ts +1 -0
- package/dist/workflow/execution/AgentNodeIntegration.spec.js +50 -0
- package/dist/workflow/execution/NodeExecutor.d.ts +9 -0
- package/dist/workflow/execution/NodeExecutor.js +43 -0
- package/dist/workflow/execution/NodeExecutor.spec.d.ts +1 -0
- package/dist/workflow/execution/NodeExecutor.spec.js +45 -0
- package/dist/workflow/execution/WorkflowEventBus.d.ts +14 -0
- package/dist/workflow/execution/WorkflowEventBus.js +42 -0
- package/dist/workflow/execution/WorkflowEventBus.spec.d.ts +1 -0
- package/dist/workflow/execution/WorkflowEventBus.spec.js +78 -0
- package/dist/workflow/execution/WorkflowRunner.d.ts +26 -0
- package/dist/workflow/execution/WorkflowRunner.js +212 -0
- package/dist/workflow/execution/WorkflowRunner.spec.d.ts +1 -0
- package/dist/workflow/execution/WorkflowRunner.spec.js +92 -0
- package/dist/workflow/execution/WorkflowTelemetry.d.ts +13 -0
- package/dist/workflow/execution/WorkflowTelemetry.js +43 -0
- package/dist/workflow/execution/WorkflowTelemetry.spec.d.ts +1 -0
- package/dist/workflow/execution/WorkflowTelemetry.spec.js +31 -0
- package/dist/workflow/graph/NodeNameRegistry.d.ts +20 -0
- package/dist/workflow/graph/NodeNameRegistry.js +21 -0
- package/dist/workflow/graph/NodeNameRegistry.spec.d.ts +1 -0
- package/dist/workflow/graph/NodeNameRegistry.spec.js +18 -0
- package/dist/workflow/graph/WorkflowGraph.d.ts +14 -0
- package/dist/workflow/graph/WorkflowGraph.js +23 -0
- package/dist/workflow/graph/nodes.d.ts +26 -0
- package/dist/workflow/graph/nodes.js +2 -0
- package/dist/workflow/queue/WorkflowQueueService.d.ts +22 -0
- package/dist/workflow/queue/WorkflowQueueService.js +47 -0
- package/dist/workflow/state/WorkflowStateService.d.ts +7 -0
- package/dist/workflow/state/WorkflowStateService.js +20 -0
- package/dist/workflow/types.d.ts +16 -0
- package/dist/workflow/types.js +2 -0
- 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;
|