agent-worker 0.13.0 → 0.15.0
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 +6 -3
- package/dist/{backends-BWzhErjT.mjs → backends-BYWmuyF9.mjs} +1 -1
- package/dist/{backends-CziIqKRg.mjs → backends-C7pQwuAx.mjs} +310 -222
- package/dist/cli/index.mjs +2044 -478
- package/dist/context-CdcZpO-0.mjs +4 -0
- package/dist/create-tool-gcUuI1FD.mjs +32 -0
- package/dist/index.d.mts +65 -87
- package/dist/index.mjs +465 -21
- package/dist/{memory-provider-BtLYtdQH.mjs → memory-provider-ZLOKyCxA.mjs} +8 -3
- package/dist/runner-DB-b57iZ.mjs +670 -0
- package/dist/workflow-DQ6Eju4n.mjs +664 -0
- package/package.json +4 -3
- package/dist/context-BqEyt2SF.mjs +0 -4
- package/dist/logger-Bfdo83xL.mjs +0 -63
- package/dist/runner-CnxROIev.mjs +0 -1496
- package/dist/worker-DBJ8136Q.mjs +0 -448
- package/dist/workflow-CIE3WPNx.mjs +0 -272
- /package/dist/{display-pretty-BCJq5v9d.mjs → display-pretty-Kyd40DEF.mjs} +0 -0
package/dist/worker-DBJ8136Q.mjs
DELETED
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
import { F as createModelWithProvider, P as createModelAsync } from "./backends-CziIqKRg.mjs";
|
|
2
|
-
import { ToolLoopAgent, stepCountIs } from "ai";
|
|
3
|
-
|
|
4
|
-
//#region src/agent/worker.ts
|
|
5
|
-
/**
|
|
6
|
-
* AgentWorker - Stateful worker for controlled agent execution
|
|
7
|
-
*
|
|
8
|
-
* Uses ToolLoopAgent internally for multi-step reasoning loops.
|
|
9
|
-
* Maintains conversation state across multiple send() calls,
|
|
10
|
-
* enabling improvisational testing where you observe responses
|
|
11
|
-
* and decide next actions.
|
|
12
|
-
*
|
|
13
|
-
* Tools are AI SDK tool() objects passed as Record<name, tool()>.
|
|
14
|
-
* Approval is configured separately via Record<name, check>.
|
|
15
|
-
*/
|
|
16
|
-
var AgentWorker = class {
|
|
17
|
-
id;
|
|
18
|
-
model;
|
|
19
|
-
system;
|
|
20
|
-
createdAt;
|
|
21
|
-
tools;
|
|
22
|
-
approval;
|
|
23
|
-
maxTokens;
|
|
24
|
-
maxSteps;
|
|
25
|
-
messages = [];
|
|
26
|
-
totalUsage = {
|
|
27
|
-
input: 0,
|
|
28
|
-
output: 0,
|
|
29
|
-
total: 0
|
|
30
|
-
};
|
|
31
|
-
pendingApprovals = [];
|
|
32
|
-
backend;
|
|
33
|
-
provider;
|
|
34
|
-
cachedAgent = null;
|
|
35
|
-
toolsChanged = false;
|
|
36
|
-
/**
|
|
37
|
-
* Whether this session supports tool management (SDK backend only)
|
|
38
|
-
*/
|
|
39
|
-
get supportsTools() {
|
|
40
|
-
return this.backend === null;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Convert AgentMessage[] to ModelMessage[] for AI SDK
|
|
44
|
-
*/
|
|
45
|
-
toModelMessages() {
|
|
46
|
-
return this.messages.filter((m) => m.status !== "responding").map((m) => ({
|
|
47
|
-
role: m.role,
|
|
48
|
-
content: m.content
|
|
49
|
-
}));
|
|
50
|
-
}
|
|
51
|
-
constructor(config, restore) {
|
|
52
|
-
if (restore) {
|
|
53
|
-
this.id = restore.id;
|
|
54
|
-
this.createdAt = restore.createdAt;
|
|
55
|
-
this.messages = [...restore.messages];
|
|
56
|
-
this.totalUsage = { ...restore.totalUsage };
|
|
57
|
-
this.pendingApprovals = [...restore.pendingApprovals ?? []];
|
|
58
|
-
} else {
|
|
59
|
-
this.id = crypto.randomUUID();
|
|
60
|
-
this.createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
61
|
-
}
|
|
62
|
-
this.model = config.model;
|
|
63
|
-
this.system = config.system;
|
|
64
|
-
this.tools = config.tools ? { ...config.tools } : {};
|
|
65
|
-
this.approval = config.approval ? { ...config.approval } : {};
|
|
66
|
-
this.maxTokens = config.maxTokens ?? 4096;
|
|
67
|
-
this.maxSteps = config.maxSteps ?? 200;
|
|
68
|
-
this.backend = config.backend ?? null;
|
|
69
|
-
this.provider = config.provider;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Check if a tool needs approval for given arguments
|
|
73
|
-
*/
|
|
74
|
-
checkApproval(name, args) {
|
|
75
|
-
const check = this.approval[name];
|
|
76
|
-
if (!check) return false;
|
|
77
|
-
if (typeof check === "function") return check(args);
|
|
78
|
-
return check;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Build tools with approval wrapping for ToolLoopAgent
|
|
82
|
-
*/
|
|
83
|
-
buildTools(autoApprove) {
|
|
84
|
-
if (Object.keys(this.tools).length === 0) return void 0;
|
|
85
|
-
if (autoApprove || Object.keys(this.approval).length === 0) return this.tools;
|
|
86
|
-
const wrapped = {};
|
|
87
|
-
for (const [name, t] of Object.entries(this.tools)) {
|
|
88
|
-
if (!this.approval[name]) {
|
|
89
|
-
wrapped[name] = t;
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
wrapped[name] = {
|
|
93
|
-
...t,
|
|
94
|
-
execute: async (args, options) => {
|
|
95
|
-
if (this.checkApproval(name, args)) {
|
|
96
|
-
const approval = {
|
|
97
|
-
id: crypto.randomUUID(),
|
|
98
|
-
toolName: name,
|
|
99
|
-
toolCallId: crypto.randomUUID(),
|
|
100
|
-
arguments: args,
|
|
101
|
-
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
102
|
-
status: "pending"
|
|
103
|
-
};
|
|
104
|
-
this.pendingApprovals.push(approval);
|
|
105
|
-
return {
|
|
106
|
-
__approvalRequired: true,
|
|
107
|
-
approvalId: approval.id
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
return t.execute?.(args, options);
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
return wrapped;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Get or create cached agent, rebuild if tools changed
|
|
118
|
-
*/
|
|
119
|
-
async getAgent(autoApprove) {
|
|
120
|
-
if (!this.cachedAgent || this.toolsChanged || !autoApprove) {
|
|
121
|
-
this.cachedAgent = new ToolLoopAgent({
|
|
122
|
-
model: this.provider ? await createModelWithProvider(this.model, this.provider) : await createModelAsync(this.model),
|
|
123
|
-
instructions: this.system,
|
|
124
|
-
tools: this.buildTools(autoApprove),
|
|
125
|
-
maxOutputTokens: this.maxTokens,
|
|
126
|
-
stopWhen: stepCountIs(this.maxSteps)
|
|
127
|
-
});
|
|
128
|
-
if (autoApprove) this.toolsChanged = false;
|
|
129
|
-
}
|
|
130
|
-
return this.cachedAgent;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Send a message via CLI backend (non-SDK path)
|
|
134
|
-
*/
|
|
135
|
-
async sendViaBackend(content) {
|
|
136
|
-
const startTime = performance.now();
|
|
137
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
138
|
-
this.messages.push({
|
|
139
|
-
role: "user",
|
|
140
|
-
content,
|
|
141
|
-
status: "complete",
|
|
142
|
-
timestamp
|
|
143
|
-
});
|
|
144
|
-
const result = await this.backend.send(content, { system: this.system });
|
|
145
|
-
const latency = Math.round(performance.now() - startTime);
|
|
146
|
-
this.messages.push({
|
|
147
|
-
role: "assistant",
|
|
148
|
-
content: result.content,
|
|
149
|
-
status: "complete",
|
|
150
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
151
|
-
});
|
|
152
|
-
const usage = {
|
|
153
|
-
input: result.usage?.input ?? 0,
|
|
154
|
-
output: result.usage?.output ?? 0,
|
|
155
|
-
total: result.usage?.total ?? 0
|
|
156
|
-
};
|
|
157
|
-
this.totalUsage.input += usage.input;
|
|
158
|
-
this.totalUsage.output += usage.output;
|
|
159
|
-
this.totalUsage.total += usage.total;
|
|
160
|
-
const toolCalls = (result.toolCalls ?? []).map((tc) => ({
|
|
161
|
-
name: tc.name,
|
|
162
|
-
arguments: tc.arguments,
|
|
163
|
-
result: tc.result,
|
|
164
|
-
timing: 0
|
|
165
|
-
}));
|
|
166
|
-
return {
|
|
167
|
-
content: result.content,
|
|
168
|
-
toolCalls,
|
|
169
|
-
pendingApprovals: [],
|
|
170
|
-
usage,
|
|
171
|
-
latency
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Send a message and get the agent's response
|
|
176
|
-
*/
|
|
177
|
-
async send(content, options = {}) {
|
|
178
|
-
if (this.backend) return this.sendViaBackend(content);
|
|
179
|
-
const { autoApprove = true, onStepFinish } = options;
|
|
180
|
-
const startTime = performance.now();
|
|
181
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
182
|
-
this.messages.push({
|
|
183
|
-
role: "user",
|
|
184
|
-
content,
|
|
185
|
-
status: "complete",
|
|
186
|
-
timestamp
|
|
187
|
-
});
|
|
188
|
-
const agent = await this.getAgent(autoApprove);
|
|
189
|
-
const allToolCalls = [];
|
|
190
|
-
let stepNumber = 0;
|
|
191
|
-
const result = await agent.generate({
|
|
192
|
-
messages: this.toModelMessages(),
|
|
193
|
-
onStepFinish: async ({ usage, toolCalls, toolResults }) => {
|
|
194
|
-
stepNumber++;
|
|
195
|
-
const stepToolCalls = [];
|
|
196
|
-
if (toolCalls) for (const tc of toolCalls) {
|
|
197
|
-
const toolResult = toolResults?.find((tr) => tr.toolCallId === tc.toolCallId);
|
|
198
|
-
const toolCall = {
|
|
199
|
-
name: tc.toolName,
|
|
200
|
-
arguments: tc.input,
|
|
201
|
-
result: toolResult?.output ?? null,
|
|
202
|
-
timing: 0
|
|
203
|
-
};
|
|
204
|
-
stepToolCalls.push(toolCall);
|
|
205
|
-
allToolCalls.push(toolCall);
|
|
206
|
-
}
|
|
207
|
-
if (onStepFinish) {
|
|
208
|
-
const stepUsage = {
|
|
209
|
-
input: usage?.inputTokens ?? 0,
|
|
210
|
-
output: usage?.outputTokens ?? 0,
|
|
211
|
-
total: (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)
|
|
212
|
-
};
|
|
213
|
-
await onStepFinish({
|
|
214
|
-
stepNumber,
|
|
215
|
-
toolCalls: stepToolCalls,
|
|
216
|
-
usage: stepUsage
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
const latency = Math.round(performance.now() - startTime);
|
|
222
|
-
this.messages.push({
|
|
223
|
-
role: "assistant",
|
|
224
|
-
content: result.text,
|
|
225
|
-
status: "complete",
|
|
226
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
227
|
-
});
|
|
228
|
-
const usage = {
|
|
229
|
-
input: result.usage?.inputTokens ?? 0,
|
|
230
|
-
output: result.usage?.outputTokens ?? 0,
|
|
231
|
-
total: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0)
|
|
232
|
-
};
|
|
233
|
-
this.totalUsage.input += usage.input;
|
|
234
|
-
this.totalUsage.output += usage.output;
|
|
235
|
-
this.totalUsage.total += usage.total;
|
|
236
|
-
if (this.maxSteps > 0 && stepNumber >= this.maxSteps && allToolCalls.length > 0) console.warn(`⚠️ Agent reached maxSteps limit (${this.maxSteps}) but wanted to continue. Consider increasing maxSteps or removing the limit.`);
|
|
237
|
-
const currentPending = this.pendingApprovals.filter((p) => p.status === "pending");
|
|
238
|
-
return {
|
|
239
|
-
content: result.text,
|
|
240
|
-
toolCalls: allToolCalls,
|
|
241
|
-
pendingApprovals: currentPending,
|
|
242
|
-
usage,
|
|
243
|
-
latency
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Send a message and stream the response
|
|
248
|
-
*/
|
|
249
|
-
async *sendStream(content, options = {}) {
|
|
250
|
-
if (this.backend) {
|
|
251
|
-
const response = await this.sendViaBackend(content);
|
|
252
|
-
yield response.content;
|
|
253
|
-
return response;
|
|
254
|
-
}
|
|
255
|
-
const { autoApprove = true, onStepFinish } = options;
|
|
256
|
-
const startTime = performance.now();
|
|
257
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
258
|
-
this.messages.push({
|
|
259
|
-
role: "user",
|
|
260
|
-
content,
|
|
261
|
-
status: "complete",
|
|
262
|
-
timestamp
|
|
263
|
-
});
|
|
264
|
-
const assistantMsg = {
|
|
265
|
-
role: "assistant",
|
|
266
|
-
content: "",
|
|
267
|
-
status: "responding",
|
|
268
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
269
|
-
};
|
|
270
|
-
this.messages.push(assistantMsg);
|
|
271
|
-
const agent = await this.getAgent(autoApprove);
|
|
272
|
-
const allToolCalls = [];
|
|
273
|
-
let stepNumber = 0;
|
|
274
|
-
const result = await agent.stream({
|
|
275
|
-
messages: this.toModelMessages(),
|
|
276
|
-
onStepFinish: async ({ usage, toolCalls, toolResults }) => {
|
|
277
|
-
stepNumber++;
|
|
278
|
-
const stepToolCalls = [];
|
|
279
|
-
if (toolCalls) for (const tc of toolCalls) {
|
|
280
|
-
const toolResult = toolResults?.find((tr) => tr.toolCallId === tc.toolCallId);
|
|
281
|
-
const toolCall = {
|
|
282
|
-
name: tc.toolName,
|
|
283
|
-
arguments: tc.input,
|
|
284
|
-
result: toolResult?.output ?? null,
|
|
285
|
-
timing: 0
|
|
286
|
-
};
|
|
287
|
-
stepToolCalls.push(toolCall);
|
|
288
|
-
allToolCalls.push(toolCall);
|
|
289
|
-
}
|
|
290
|
-
if (onStepFinish) {
|
|
291
|
-
const stepUsage = {
|
|
292
|
-
input: usage?.inputTokens ?? 0,
|
|
293
|
-
output: usage?.outputTokens ?? 0,
|
|
294
|
-
total: (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)
|
|
295
|
-
};
|
|
296
|
-
await onStepFinish({
|
|
297
|
-
stepNumber,
|
|
298
|
-
toolCalls: stepToolCalls,
|
|
299
|
-
usage: stepUsage
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
for await (const chunk of result.textStream) {
|
|
305
|
-
assistantMsg.content += chunk;
|
|
306
|
-
yield chunk;
|
|
307
|
-
}
|
|
308
|
-
const latency = Math.round(performance.now() - startTime);
|
|
309
|
-
const text = await result.text;
|
|
310
|
-
assistantMsg.content = text;
|
|
311
|
-
assistantMsg.status = "complete";
|
|
312
|
-
const finalUsage = await result.usage;
|
|
313
|
-
const usage = {
|
|
314
|
-
input: finalUsage?.inputTokens ?? 0,
|
|
315
|
-
output: finalUsage?.outputTokens ?? 0,
|
|
316
|
-
total: (finalUsage?.inputTokens ?? 0) + (finalUsage?.outputTokens ?? 0)
|
|
317
|
-
};
|
|
318
|
-
this.totalUsage.input += usage.input;
|
|
319
|
-
this.totalUsage.output += usage.output;
|
|
320
|
-
this.totalUsage.total += usage.total;
|
|
321
|
-
return {
|
|
322
|
-
content: text,
|
|
323
|
-
toolCalls: allToolCalls,
|
|
324
|
-
pendingApprovals: this.pendingApprovals.filter((p) => p.status === "pending"),
|
|
325
|
-
usage,
|
|
326
|
-
latency
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Add an AI SDK tool
|
|
331
|
-
* Only supported for SDK backends (ToolLoopAgent)
|
|
332
|
-
*/
|
|
333
|
-
addTool(name, t) {
|
|
334
|
-
if (this.backend) throw new Error("Tool management not supported for CLI backends");
|
|
335
|
-
this.tools[name] = t;
|
|
336
|
-
this.toolsChanged = true;
|
|
337
|
-
this.cachedAgent = null;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* Set approval requirement for a tool
|
|
341
|
-
*/
|
|
342
|
-
setApproval(name, check) {
|
|
343
|
-
this.approval[name] = check;
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Replace a tool's execute function (for testing)
|
|
347
|
-
*/
|
|
348
|
-
mockTool(name, mockFn) {
|
|
349
|
-
if (this.backend) throw new Error("Tool management not supported for CLI backends");
|
|
350
|
-
const t = this.tools[name];
|
|
351
|
-
if (!t) throw new Error(`Tool not found: ${name}`);
|
|
352
|
-
this.tools[name] = {
|
|
353
|
-
...t,
|
|
354
|
-
execute: mockFn
|
|
355
|
-
};
|
|
356
|
-
this.toolsChanged = true;
|
|
357
|
-
this.cachedAgent = null;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Set a static mock response for an existing tool
|
|
361
|
-
*/
|
|
362
|
-
setMockResponse(name, response) {
|
|
363
|
-
if (this.backend) throw new Error("Tool management not supported for CLI backends");
|
|
364
|
-
const t = this.tools[name];
|
|
365
|
-
if (!t) throw new Error(`Tool not found: ${name}`);
|
|
366
|
-
this.tools[name] = {
|
|
367
|
-
...t,
|
|
368
|
-
execute: () => response
|
|
369
|
-
};
|
|
370
|
-
this.toolsChanged = true;
|
|
371
|
-
this.cachedAgent = null;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Get tool info (names, descriptions, approval status)
|
|
375
|
-
*/
|
|
376
|
-
getTools() {
|
|
377
|
-
return Object.entries(this.tools).map(([name, t]) => {
|
|
378
|
-
return {
|
|
379
|
-
name,
|
|
380
|
-
description: t?.description,
|
|
381
|
-
needsApproval: !!this.approval[name]
|
|
382
|
-
};
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
history() {
|
|
386
|
-
return [...this.messages];
|
|
387
|
-
}
|
|
388
|
-
stats() {
|
|
389
|
-
return {
|
|
390
|
-
messageCount: this.messages.length,
|
|
391
|
-
usage: { ...this.totalUsage }
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
export() {
|
|
395
|
-
return {
|
|
396
|
-
sessionId: this.id,
|
|
397
|
-
model: this.model,
|
|
398
|
-
system: this.system,
|
|
399
|
-
messages: [...this.messages],
|
|
400
|
-
totalUsage: { ...this.totalUsage },
|
|
401
|
-
createdAt: this.createdAt
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
getState() {
|
|
405
|
-
return {
|
|
406
|
-
id: this.id,
|
|
407
|
-
createdAt: this.createdAt,
|
|
408
|
-
messages: [...this.messages],
|
|
409
|
-
totalUsage: { ...this.totalUsage },
|
|
410
|
-
pendingApprovals: [...this.pendingApprovals]
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
getPendingApprovals() {
|
|
414
|
-
return this.pendingApprovals.filter((p) => p.status === "pending");
|
|
415
|
-
}
|
|
416
|
-
async approve(approvalId) {
|
|
417
|
-
const approval = this.pendingApprovals.find((p) => p.id === approvalId);
|
|
418
|
-
if (!approval) throw new Error(`Approval not found: ${approvalId}`);
|
|
419
|
-
if (approval.status !== "pending") throw new Error(`Approval already ${approval.status}: ${approvalId}`);
|
|
420
|
-
const t = this.tools[approval.toolName];
|
|
421
|
-
if (!t) throw new Error(`Tool not found: ${approval.toolName}`);
|
|
422
|
-
let result;
|
|
423
|
-
const tool = t;
|
|
424
|
-
if (typeof tool.execute === "function") result = await tool.execute(approval.arguments);
|
|
425
|
-
else result = { error: "No implementation provided" };
|
|
426
|
-
approval.status = "approved";
|
|
427
|
-
return result;
|
|
428
|
-
}
|
|
429
|
-
deny(approvalId, reason) {
|
|
430
|
-
const approval = this.pendingApprovals.find((p) => p.id === approvalId);
|
|
431
|
-
if (!approval) throw new Error(`Approval not found: ${approvalId}`);
|
|
432
|
-
if (approval.status !== "pending") throw new Error(`Approval already ${approval.status}: ${approvalId}`);
|
|
433
|
-
approval.status = "denied";
|
|
434
|
-
approval.denyReason = reason;
|
|
435
|
-
}
|
|
436
|
-
clear() {
|
|
437
|
-
this.messages = [];
|
|
438
|
-
this.totalUsage = {
|
|
439
|
-
input: 0,
|
|
440
|
-
output: 0,
|
|
441
|
-
total: 0
|
|
442
|
-
};
|
|
443
|
-
this.pendingApprovals = [];
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
//#endregion
|
|
448
|
-
export { AgentWorker as t };
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import "./backends-CziIqKRg.mjs";
|
|
2
|
-
import { c as CONTEXT_DEFAULTS, i as resolveContextDir } from "./cli/index.mjs";
|
|
3
|
-
import "./memory-provider-BtLYtdQH.mjs";
|
|
4
|
-
import { createChannelLogger, createSilentLogger } from "./logger-Bfdo83xL.mjs";
|
|
5
|
-
import { a as runSdkAgent, c as buildAgentPrompt, createWorkflowProvider, d as createContext, f as interpolate, i as createAgentController, initWorkflow, l as formatInbox, n as getBackendForModel, o as runMockAgent, r as checkWorkflowIdle, runWorkflowWithControllers, s as generateWorkflowMCPConfig, shutdownControllers, t as getBackendByType, u as CONTROLLER_DEFAULTS } from "./runner-CnxROIev.mjs";
|
|
6
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
8
|
-
import { parse } from "yaml";
|
|
9
|
-
|
|
10
|
-
//#region src/workflow/parser.ts
|
|
11
|
-
/**
|
|
12
|
-
* Workflow file parser
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Parse a workflow file
|
|
16
|
-
*/
|
|
17
|
-
async function parseWorkflowFile(filePath, options) {
|
|
18
|
-
const absolutePath = resolve(filePath);
|
|
19
|
-
const workflow = options?.workflow ?? options?.instance ?? "global";
|
|
20
|
-
const tag = options?.tag ?? "main";
|
|
21
|
-
if (!existsSync(absolutePath)) throw new Error(`Workflow file not found: ${absolutePath}`);
|
|
22
|
-
const content = readFileSync(absolutePath, "utf-8");
|
|
23
|
-
const workflowDir = dirname(absolutePath);
|
|
24
|
-
let raw;
|
|
25
|
-
try {
|
|
26
|
-
raw = parse(content);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
throw new Error(`Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`);
|
|
29
|
-
}
|
|
30
|
-
const validation = validateWorkflow(raw);
|
|
31
|
-
if (!validation.valid) {
|
|
32
|
-
const messages = validation.errors.map((e) => ` - ${e.path}: ${e.message}`).join("\n");
|
|
33
|
-
throw new Error(`Invalid workflow file:\n${messages}`);
|
|
34
|
-
}
|
|
35
|
-
const name = raw.name || basename(absolutePath, ".yml").replace(".yaml", "");
|
|
36
|
-
const agents = {};
|
|
37
|
-
for (const [agentName, agentDef] of Object.entries(raw.agents)) agents[agentName] = await resolveAgent(agentDef, workflowDir);
|
|
38
|
-
return {
|
|
39
|
-
name,
|
|
40
|
-
filePath: absolutePath,
|
|
41
|
-
agents,
|
|
42
|
-
context: resolveContext(raw.context, workflowDir, name, workflow, tag),
|
|
43
|
-
setup: raw.setup || [],
|
|
44
|
-
kickoff: raw.kickoff
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Resolve context configuration
|
|
49
|
-
*
|
|
50
|
-
* - undefined (not set): default file provider enabled
|
|
51
|
-
* - null: default file provider enabled (YAML `context:` syntax)
|
|
52
|
-
* - false: explicitly disabled
|
|
53
|
-
* - { provider: 'file', config?: { dir | bind } }: file provider (ephemeral or persistent)
|
|
54
|
-
* - { provider: 'memory' }: memory provider (for testing)
|
|
55
|
-
*/
|
|
56
|
-
function resolveContext(config, workflowDir, workflowName, workflow, tag) {
|
|
57
|
-
const resolve = (template) => resolveContextDir(template, {
|
|
58
|
-
workflowName,
|
|
59
|
-
workflow,
|
|
60
|
-
tag,
|
|
61
|
-
instance: workflow,
|
|
62
|
-
baseDir: workflowDir
|
|
63
|
-
});
|
|
64
|
-
if (config === false) return;
|
|
65
|
-
if (config === void 0 || config === null) return {
|
|
66
|
-
provider: "file",
|
|
67
|
-
dir: resolve(CONTEXT_DEFAULTS.dir)
|
|
68
|
-
};
|
|
69
|
-
if (config.provider === "memory") return {
|
|
70
|
-
provider: "memory",
|
|
71
|
-
documentOwner: config.documentOwner
|
|
72
|
-
};
|
|
73
|
-
const bindPath = config.config?.bind;
|
|
74
|
-
if (bindPath) return {
|
|
75
|
-
provider: "file",
|
|
76
|
-
dir: resolve(bindPath),
|
|
77
|
-
persistent: true,
|
|
78
|
-
documentOwner: config.documentOwner
|
|
79
|
-
};
|
|
80
|
-
return {
|
|
81
|
-
provider: "file",
|
|
82
|
-
dir: resolve(config.config?.dir || CONTEXT_DEFAULTS.dir),
|
|
83
|
-
documentOwner: config.documentOwner
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Resolve agent definition (load system prompt from file if needed)
|
|
88
|
-
*/
|
|
89
|
-
async function resolveAgent(agent, workflowDir) {
|
|
90
|
-
let resolvedSystemPrompt = agent.system_prompt;
|
|
91
|
-
if (resolvedSystemPrompt?.endsWith(".txt") || resolvedSystemPrompt?.endsWith(".md")) {
|
|
92
|
-
const promptPath = resolvedSystemPrompt.startsWith("/") ? resolvedSystemPrompt : join(workflowDir, resolvedSystemPrompt);
|
|
93
|
-
if (existsSync(promptPath)) resolvedSystemPrompt = readFileSync(promptPath, "utf-8");
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
...agent,
|
|
97
|
-
resolvedSystemPrompt
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Validate workflow structure
|
|
102
|
-
*/
|
|
103
|
-
function validateWorkflow(workflow) {
|
|
104
|
-
const errors = [];
|
|
105
|
-
if (!workflow || typeof workflow !== "object") {
|
|
106
|
-
errors.push({
|
|
107
|
-
path: "",
|
|
108
|
-
message: "Workflow must be an object"
|
|
109
|
-
});
|
|
110
|
-
return {
|
|
111
|
-
valid: false,
|
|
112
|
-
errors
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
const w = workflow;
|
|
116
|
-
if (!w.agents || typeof w.agents !== "object") errors.push({
|
|
117
|
-
path: "agents",
|
|
118
|
-
message: "Required field \"agents\" must be an object"
|
|
119
|
-
});
|
|
120
|
-
else {
|
|
121
|
-
const agents = w.agents;
|
|
122
|
-
for (const [name, agent] of Object.entries(agents)) validateAgent(name, agent, errors);
|
|
123
|
-
}
|
|
124
|
-
if (w.context !== void 0 && w.context !== null && w.context !== false) validateContext(w.context, errors);
|
|
125
|
-
if (w.setup !== void 0) if (!Array.isArray(w.setup)) errors.push({
|
|
126
|
-
path: "setup",
|
|
127
|
-
message: "Setup must be an array"
|
|
128
|
-
});
|
|
129
|
-
else for (let i = 0; i < w.setup.length; i++) validateSetupTask(`setup[${i}]`, w.setup[i], errors);
|
|
130
|
-
if (w.kickoff !== void 0 && typeof w.kickoff !== "string") errors.push({
|
|
131
|
-
path: "kickoff",
|
|
132
|
-
message: "Kickoff must be a string"
|
|
133
|
-
});
|
|
134
|
-
return {
|
|
135
|
-
valid: errors.length === 0,
|
|
136
|
-
errors
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function validateContext(context, errors) {
|
|
140
|
-
if (typeof context !== "object" || context === null) {
|
|
141
|
-
errors.push({
|
|
142
|
-
path: "context",
|
|
143
|
-
message: "Context must be an object or false"
|
|
144
|
-
});
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const c = context;
|
|
148
|
-
if (!c.provider || typeof c.provider !== "string") {
|
|
149
|
-
errors.push({
|
|
150
|
-
path: "context.provider",
|
|
151
|
-
message: "Context requires \"provider\" field (file or memory)"
|
|
152
|
-
});
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
if (c.provider !== "file" && c.provider !== "memory") {
|
|
156
|
-
errors.push({
|
|
157
|
-
path: "context.provider",
|
|
158
|
-
message: "Context provider must be \"file\" or \"memory\""
|
|
159
|
-
});
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if (c.documentOwner !== void 0 && typeof c.documentOwner !== "string") errors.push({
|
|
163
|
-
path: "context.documentOwner",
|
|
164
|
-
message: "Context documentOwner must be a string"
|
|
165
|
-
});
|
|
166
|
-
if (c.provider === "file" && c.config !== void 0) {
|
|
167
|
-
if (typeof c.config !== "object" || c.config === null) {
|
|
168
|
-
errors.push({
|
|
169
|
-
path: "context.config",
|
|
170
|
-
message: "Context config must be an object"
|
|
171
|
-
});
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
const cfg = c.config;
|
|
175
|
-
if (cfg.dir !== void 0 && cfg.bind !== void 0) {
|
|
176
|
-
errors.push({
|
|
177
|
-
path: "context.config",
|
|
178
|
-
message: "\"dir\" and \"bind\" are mutually exclusive — use one or the other"
|
|
179
|
-
});
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (cfg.dir !== void 0 && typeof cfg.dir !== "string") errors.push({
|
|
183
|
-
path: "context.config.dir",
|
|
184
|
-
message: "Context config dir must be a string"
|
|
185
|
-
});
|
|
186
|
-
if (cfg.bind !== void 0 && typeof cfg.bind !== "string") errors.push({
|
|
187
|
-
path: "context.config.bind",
|
|
188
|
-
message: "Context config bind must be a string path"
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
function validateSetupTask(path, task, errors) {
|
|
193
|
-
if (!task || typeof task !== "object") {
|
|
194
|
-
errors.push({
|
|
195
|
-
path,
|
|
196
|
-
message: "Setup task must be an object"
|
|
197
|
-
});
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const t = task;
|
|
201
|
-
if (!t.shell || typeof t.shell !== "string") errors.push({
|
|
202
|
-
path: `${path}.shell`,
|
|
203
|
-
message: "Setup task requires \"shell\" field as string"
|
|
204
|
-
});
|
|
205
|
-
if (t.as !== void 0 && typeof t.as !== "string") errors.push({
|
|
206
|
-
path: `${path}.as`,
|
|
207
|
-
message: "Setup task \"as\" field must be a string"
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
/** Backends that don't require an explicit model field */
|
|
211
|
-
const CLI_BACKENDS = [
|
|
212
|
-
"claude",
|
|
213
|
-
"cursor",
|
|
214
|
-
"codex",
|
|
215
|
-
"opencode",
|
|
216
|
-
"mock"
|
|
217
|
-
];
|
|
218
|
-
function validateAgent(name, agent, errors) {
|
|
219
|
-
const path = `agents.${name}`;
|
|
220
|
-
if (!agent || typeof agent !== "object") {
|
|
221
|
-
errors.push({
|
|
222
|
-
path,
|
|
223
|
-
message: "Agent must be an object"
|
|
224
|
-
});
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const a = agent;
|
|
228
|
-
const backend = typeof a.backend === "string" ? a.backend : "default";
|
|
229
|
-
if (a.model !== void 0 && typeof a.model !== "string") errors.push({
|
|
230
|
-
path: `${path}.model`,
|
|
231
|
-
message: "Field \"model\" must be a string"
|
|
232
|
-
});
|
|
233
|
-
else if (!a.model && !CLI_BACKENDS.includes(backend)) errors.push({
|
|
234
|
-
path: `${path}.model`,
|
|
235
|
-
message: "Required field \"model\" must be a string (required for default backend)"
|
|
236
|
-
});
|
|
237
|
-
if (a.system_prompt !== void 0 && typeof a.system_prompt !== "string") errors.push({
|
|
238
|
-
path: `${path}.system_prompt`,
|
|
239
|
-
message: "Optional field \"system_prompt\" must be a string"
|
|
240
|
-
});
|
|
241
|
-
if (a.tools !== void 0 && !Array.isArray(a.tools)) errors.push({
|
|
242
|
-
path: `${path}.tools`,
|
|
243
|
-
message: "Optional field \"tools\" must be an array"
|
|
244
|
-
});
|
|
245
|
-
if (a.provider !== void 0) {
|
|
246
|
-
if (typeof a.provider === "string") {} else if (typeof a.provider === "object" && a.provider !== null && !Array.isArray(a.provider)) {
|
|
247
|
-
const p = a.provider;
|
|
248
|
-
if (!p.name || typeof p.name !== "string") errors.push({
|
|
249
|
-
path: `${path}.provider.name`,
|
|
250
|
-
message: "Field \"provider.name\" is required and must be a string"
|
|
251
|
-
});
|
|
252
|
-
if (p.base_url !== void 0 && typeof p.base_url !== "string") errors.push({
|
|
253
|
-
path: `${path}.provider.base_url`,
|
|
254
|
-
message: "Field \"provider.base_url\" must be a string"
|
|
255
|
-
});
|
|
256
|
-
if (p.api_key !== void 0 && typeof p.api_key !== "string") errors.push({
|
|
257
|
-
path: `${path}.provider.api_key`,
|
|
258
|
-
message: "Field \"provider.api_key\" must be a string"
|
|
259
|
-
});
|
|
260
|
-
} else errors.push({
|
|
261
|
-
path: `${path}.provider`,
|
|
262
|
-
message: "Field \"provider\" must be a string or object with { name, base_url?, api_key? }"
|
|
263
|
-
});
|
|
264
|
-
if (CLI_BACKENDS.includes(backend) && backend !== "mock") errors.push({
|
|
265
|
-
path: `${path}.provider`,
|
|
266
|
-
message: `Field "provider" is ignored for CLI backend "${backend}" (only works with default backend)`
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
//#endregion
|
|
272
|
-
export { parseWorkflowFile, runWorkflowWithControllers, shutdownControllers };
|