agent-worker 0.9.0 → 0.11.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.
@@ -0,0 +1,446 @@
1
+ import { A as createModelAsync } from "./backends-DLaP0rMW.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
+ cachedAgent = null;
34
+ toolsChanged = false;
35
+ /**
36
+ * Whether this session supports tool management (SDK backend only)
37
+ */
38
+ get supportsTools() {
39
+ return this.backend === null;
40
+ }
41
+ /**
42
+ * Convert AgentMessage[] to ModelMessage[] for AI SDK
43
+ */
44
+ toModelMessages() {
45
+ return this.messages.filter((m) => m.status !== "responding").map((m) => ({
46
+ role: m.role,
47
+ content: m.content
48
+ }));
49
+ }
50
+ constructor(config, restore) {
51
+ if (restore) {
52
+ this.id = restore.id;
53
+ this.createdAt = restore.createdAt;
54
+ this.messages = [...restore.messages];
55
+ this.totalUsage = { ...restore.totalUsage };
56
+ this.pendingApprovals = [...restore.pendingApprovals ?? []];
57
+ } else {
58
+ this.id = crypto.randomUUID();
59
+ this.createdAt = (/* @__PURE__ */ new Date()).toISOString();
60
+ }
61
+ this.model = config.model;
62
+ this.system = config.system;
63
+ this.tools = config.tools ? { ...config.tools } : {};
64
+ this.approval = config.approval ? { ...config.approval } : {};
65
+ this.maxTokens = config.maxTokens ?? 4096;
66
+ this.maxSteps = config.maxSteps ?? 200;
67
+ this.backend = config.backend ?? null;
68
+ }
69
+ /**
70
+ * Check if a tool needs approval for given arguments
71
+ */
72
+ checkApproval(name, args) {
73
+ const check = this.approval[name];
74
+ if (!check) return false;
75
+ if (typeof check === "function") return check(args);
76
+ return check;
77
+ }
78
+ /**
79
+ * Build tools with approval wrapping for ToolLoopAgent
80
+ */
81
+ buildTools(autoApprove) {
82
+ if (Object.keys(this.tools).length === 0) return void 0;
83
+ if (autoApprove || Object.keys(this.approval).length === 0) return this.tools;
84
+ const wrapped = {};
85
+ for (const [name, t] of Object.entries(this.tools)) {
86
+ if (!this.approval[name]) {
87
+ wrapped[name] = t;
88
+ continue;
89
+ }
90
+ wrapped[name] = {
91
+ ...t,
92
+ execute: async (args, options) => {
93
+ if (this.checkApproval(name, args)) {
94
+ const approval = {
95
+ id: crypto.randomUUID(),
96
+ toolName: name,
97
+ toolCallId: crypto.randomUUID(),
98
+ arguments: args,
99
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
100
+ status: "pending"
101
+ };
102
+ this.pendingApprovals.push(approval);
103
+ return {
104
+ __approvalRequired: true,
105
+ approvalId: approval.id
106
+ };
107
+ }
108
+ return t.execute?.(args, options);
109
+ }
110
+ };
111
+ }
112
+ return wrapped;
113
+ }
114
+ /**
115
+ * Get or create cached agent, rebuild if tools changed
116
+ */
117
+ async getAgent(autoApprove) {
118
+ if (!this.cachedAgent || this.toolsChanged || !autoApprove) {
119
+ this.cachedAgent = new ToolLoopAgent({
120
+ model: await createModelAsync(this.model),
121
+ instructions: this.system,
122
+ tools: this.buildTools(autoApprove),
123
+ maxOutputTokens: this.maxTokens,
124
+ stopWhen: stepCountIs(this.maxSteps)
125
+ });
126
+ if (autoApprove) this.toolsChanged = false;
127
+ }
128
+ return this.cachedAgent;
129
+ }
130
+ /**
131
+ * Send a message via CLI backend (non-SDK path)
132
+ */
133
+ async sendViaBackend(content) {
134
+ const startTime = performance.now();
135
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
136
+ this.messages.push({
137
+ role: "user",
138
+ content,
139
+ status: "complete",
140
+ timestamp
141
+ });
142
+ const result = await this.backend.send(content, { system: this.system });
143
+ const latency = Math.round(performance.now() - startTime);
144
+ this.messages.push({
145
+ role: "assistant",
146
+ content: result.content,
147
+ status: "complete",
148
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
149
+ });
150
+ const usage = {
151
+ input: result.usage?.input ?? 0,
152
+ output: result.usage?.output ?? 0,
153
+ total: result.usage?.total ?? 0
154
+ };
155
+ this.totalUsage.input += usage.input;
156
+ this.totalUsage.output += usage.output;
157
+ this.totalUsage.total += usage.total;
158
+ const toolCalls = (result.toolCalls ?? []).map((tc) => ({
159
+ name: tc.name,
160
+ arguments: tc.arguments,
161
+ result: tc.result,
162
+ timing: 0
163
+ }));
164
+ return {
165
+ content: result.content,
166
+ toolCalls,
167
+ pendingApprovals: [],
168
+ usage,
169
+ latency
170
+ };
171
+ }
172
+ /**
173
+ * Send a message and get the agent's response
174
+ */
175
+ async send(content, options = {}) {
176
+ if (this.backend) return this.sendViaBackend(content);
177
+ const { autoApprove = true, onStepFinish } = options;
178
+ const startTime = performance.now();
179
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
180
+ this.messages.push({
181
+ role: "user",
182
+ content,
183
+ status: "complete",
184
+ timestamp
185
+ });
186
+ const agent = await this.getAgent(autoApprove);
187
+ const allToolCalls = [];
188
+ let stepNumber = 0;
189
+ const result = await agent.generate({
190
+ messages: this.toModelMessages(),
191
+ onStepFinish: async ({ usage, toolCalls, toolResults }) => {
192
+ stepNumber++;
193
+ const stepToolCalls = [];
194
+ if (toolCalls) for (const tc of toolCalls) {
195
+ const toolResult = toolResults?.find((tr) => tr.toolCallId === tc.toolCallId);
196
+ const toolCall = {
197
+ name: tc.toolName,
198
+ arguments: tc.input,
199
+ result: toolResult?.output ?? null,
200
+ timing: 0
201
+ };
202
+ stepToolCalls.push(toolCall);
203
+ allToolCalls.push(toolCall);
204
+ }
205
+ if (onStepFinish) {
206
+ const stepUsage = {
207
+ input: usage?.inputTokens ?? 0,
208
+ output: usage?.outputTokens ?? 0,
209
+ total: (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)
210
+ };
211
+ await onStepFinish({
212
+ stepNumber,
213
+ toolCalls: stepToolCalls,
214
+ usage: stepUsage
215
+ });
216
+ }
217
+ }
218
+ });
219
+ const latency = Math.round(performance.now() - startTime);
220
+ this.messages.push({
221
+ role: "assistant",
222
+ content: result.text,
223
+ status: "complete",
224
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
225
+ });
226
+ const usage = {
227
+ input: result.usage?.inputTokens ?? 0,
228
+ output: result.usage?.outputTokens ?? 0,
229
+ total: (result.usage?.inputTokens ?? 0) + (result.usage?.outputTokens ?? 0)
230
+ };
231
+ this.totalUsage.input += usage.input;
232
+ this.totalUsage.output += usage.output;
233
+ this.totalUsage.total += usage.total;
234
+ 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.`);
235
+ const currentPending = this.pendingApprovals.filter((p) => p.status === "pending");
236
+ return {
237
+ content: result.text,
238
+ toolCalls: allToolCalls,
239
+ pendingApprovals: currentPending,
240
+ usage,
241
+ latency
242
+ };
243
+ }
244
+ /**
245
+ * Send a message and stream the response
246
+ */
247
+ async *sendStream(content, options = {}) {
248
+ if (this.backend) {
249
+ const response = await this.sendViaBackend(content);
250
+ yield response.content;
251
+ return response;
252
+ }
253
+ const { autoApprove = true, onStepFinish } = options;
254
+ const startTime = performance.now();
255
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
256
+ this.messages.push({
257
+ role: "user",
258
+ content,
259
+ status: "complete",
260
+ timestamp
261
+ });
262
+ const assistantMsg = {
263
+ role: "assistant",
264
+ content: "",
265
+ status: "responding",
266
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
267
+ };
268
+ this.messages.push(assistantMsg);
269
+ const agent = await this.getAgent(autoApprove);
270
+ const allToolCalls = [];
271
+ let stepNumber = 0;
272
+ const result = await agent.stream({
273
+ messages: this.toModelMessages(),
274
+ onStepFinish: async ({ usage, toolCalls, toolResults }) => {
275
+ stepNumber++;
276
+ const stepToolCalls = [];
277
+ if (toolCalls) for (const tc of toolCalls) {
278
+ const toolResult = toolResults?.find((tr) => tr.toolCallId === tc.toolCallId);
279
+ const toolCall = {
280
+ name: tc.toolName,
281
+ arguments: tc.input,
282
+ result: toolResult?.output ?? null,
283
+ timing: 0
284
+ };
285
+ stepToolCalls.push(toolCall);
286
+ allToolCalls.push(toolCall);
287
+ }
288
+ if (onStepFinish) {
289
+ const stepUsage = {
290
+ input: usage?.inputTokens ?? 0,
291
+ output: usage?.outputTokens ?? 0,
292
+ total: (usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0)
293
+ };
294
+ await onStepFinish({
295
+ stepNumber,
296
+ toolCalls: stepToolCalls,
297
+ usage: stepUsage
298
+ });
299
+ }
300
+ }
301
+ });
302
+ for await (const chunk of result.textStream) {
303
+ assistantMsg.content += chunk;
304
+ yield chunk;
305
+ }
306
+ const latency = Math.round(performance.now() - startTime);
307
+ const text = await result.text;
308
+ assistantMsg.content = text;
309
+ assistantMsg.status = "complete";
310
+ const finalUsage = await result.usage;
311
+ const usage = {
312
+ input: finalUsage?.inputTokens ?? 0,
313
+ output: finalUsage?.outputTokens ?? 0,
314
+ total: (finalUsage?.inputTokens ?? 0) + (finalUsage?.outputTokens ?? 0)
315
+ };
316
+ this.totalUsage.input += usage.input;
317
+ this.totalUsage.output += usage.output;
318
+ this.totalUsage.total += usage.total;
319
+ return {
320
+ content: text,
321
+ toolCalls: allToolCalls,
322
+ pendingApprovals: this.pendingApprovals.filter((p) => p.status === "pending"),
323
+ usage,
324
+ latency
325
+ };
326
+ }
327
+ /**
328
+ * Add an AI SDK tool
329
+ * Only supported for SDK backends (ToolLoopAgent)
330
+ */
331
+ addTool(name, t) {
332
+ if (this.backend) throw new Error("Tool management not supported for CLI backends");
333
+ this.tools[name] = t;
334
+ this.toolsChanged = true;
335
+ this.cachedAgent = null;
336
+ }
337
+ /**
338
+ * Set approval requirement for a tool
339
+ */
340
+ setApproval(name, check) {
341
+ this.approval[name] = check;
342
+ }
343
+ /**
344
+ * Replace a tool's execute function (for testing)
345
+ */
346
+ mockTool(name, mockFn) {
347
+ if (this.backend) throw new Error("Tool management not supported for CLI backends");
348
+ const t = this.tools[name];
349
+ if (!t) throw new Error(`Tool not found: ${name}`);
350
+ this.tools[name] = {
351
+ ...t,
352
+ execute: mockFn
353
+ };
354
+ this.toolsChanged = true;
355
+ this.cachedAgent = null;
356
+ }
357
+ /**
358
+ * Set a static mock response for an existing tool
359
+ */
360
+ setMockResponse(name, response) {
361
+ if (this.backend) throw new Error("Tool management not supported for CLI backends");
362
+ const t = this.tools[name];
363
+ if (!t) throw new Error(`Tool not found: ${name}`);
364
+ this.tools[name] = {
365
+ ...t,
366
+ execute: () => response
367
+ };
368
+ this.toolsChanged = true;
369
+ this.cachedAgent = null;
370
+ }
371
+ /**
372
+ * Get tool info (names, descriptions, approval status)
373
+ */
374
+ getTools() {
375
+ return Object.entries(this.tools).map(([name, t]) => {
376
+ return {
377
+ name,
378
+ description: t?.description,
379
+ needsApproval: !!this.approval[name]
380
+ };
381
+ });
382
+ }
383
+ history() {
384
+ return [...this.messages];
385
+ }
386
+ stats() {
387
+ return {
388
+ messageCount: this.messages.length,
389
+ usage: { ...this.totalUsage }
390
+ };
391
+ }
392
+ export() {
393
+ return {
394
+ sessionId: this.id,
395
+ model: this.model,
396
+ system: this.system,
397
+ messages: [...this.messages],
398
+ totalUsage: { ...this.totalUsage },
399
+ createdAt: this.createdAt
400
+ };
401
+ }
402
+ getState() {
403
+ return {
404
+ id: this.id,
405
+ createdAt: this.createdAt,
406
+ messages: [...this.messages],
407
+ totalUsage: { ...this.totalUsage },
408
+ pendingApprovals: [...this.pendingApprovals]
409
+ };
410
+ }
411
+ getPendingApprovals() {
412
+ return this.pendingApprovals.filter((p) => p.status === "pending");
413
+ }
414
+ async approve(approvalId) {
415
+ const approval = this.pendingApprovals.find((p) => p.id === approvalId);
416
+ if (!approval) throw new Error(`Approval not found: ${approvalId}`);
417
+ if (approval.status !== "pending") throw new Error(`Approval already ${approval.status}: ${approvalId}`);
418
+ const t = this.tools[approval.toolName];
419
+ if (!t) throw new Error(`Tool not found: ${approval.toolName}`);
420
+ let result;
421
+ const tool = t;
422
+ if (typeof tool.execute === "function") result = await tool.execute(approval.arguments);
423
+ else result = { error: "No implementation provided" };
424
+ approval.status = "approved";
425
+ return result;
426
+ }
427
+ deny(approvalId, reason) {
428
+ const approval = this.pendingApprovals.find((p) => p.id === approvalId);
429
+ if (!approval) throw new Error(`Approval not found: ${approvalId}`);
430
+ if (approval.status !== "pending") throw new Error(`Approval already ${approval.status}: ${approvalId}`);
431
+ approval.status = "denied";
432
+ approval.denyReason = reason;
433
+ }
434
+ clear() {
435
+ this.messages = [];
436
+ this.totalUsage = {
437
+ input: 0,
438
+ output: 0,
439
+ total: 0
440
+ };
441
+ this.pendingApprovals = [];
442
+ }
443
+ };
444
+
445
+ //#endregion
446
+ export { AgentWorker as t };
@@ -1,16 +1,16 @@
1
- import { T as parseModel, a as createMockBackend, k as createModelAsync, n as createBackend } from "./backends-BOAkfYyL.mjs";
2
- import { c as CONTEXT_DEFAULTS, i as resolveContextDir, n as createFileContextProvider, t as FileContextProvider } from "./cli/index.mjs";
3
- import { a as createMemoryContextProvider, t as createContextMCPServer } from "./mcp-server-BQCQxv2v.mjs";
4
- import { createChannelLogger, createSilentLogger } from "./logger-C3ekEOzi.mjs";
1
+ import { A as createModelAsync, E as parseModel, a as createMockBackend, n as createBackend } from "./backends-DLaP0rMW.mjs";
2
+ import { T as EventLog, c as CONTEXT_DEFAULTS, i as resolveContextDir, n as createFileContextProvider, t as FileContextProvider, v as createContextMCPServer } from "./cli/index.mjs";
3
+ import { n as createMemoryContextProvider } from "./memory-provider-CBlKMdbJ.mjs";
4
+ import { createChannelLogger, createSilentLogger } from "./logger-CCFaMMV7.mjs";
5
5
  import { generateText, jsonSchema, stepCountIs, tool } from "ai";
6
6
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
7
7
  import { basename, dirname, join, resolve } from "node:path";
8
8
  import { parse } from "yaml";
9
9
  import { tmpdir } from "node:os";
10
10
  import { exec, execSync } from "node:child_process";
11
+ import { randomUUID } from "node:crypto";
11
12
  import { promisify } from "node:util";
12
13
  import { createServer } from "node:http";
13
- import { randomUUID } from "node:crypto";
14
14
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
15
15
  import { MockLanguageModelV3, mockValues } from "ai/test";
16
16
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
@@ -236,14 +236,14 @@ function validateAgent(name, agent, errors) {
236
236
  return;
237
237
  }
238
238
  const a = agent;
239
- const backend = typeof a.backend === "string" ? a.backend : "sdk";
239
+ const backend = typeof a.backend === "string" ? a.backend : "default";
240
240
  if (a.model !== void 0 && typeof a.model !== "string") errors.push({
241
241
  path: `${path}.model`,
242
242
  message: "Field \"model\" must be a string"
243
243
  });
244
244
  else if (!a.model && !CLI_BACKENDS.includes(backend)) errors.push({
245
245
  path: `${path}.model`,
246
- message: "Required field \"model\" must be a string (required for sdk backend)"
246
+ message: "Required field \"model\" must be a string (required for default backend)"
247
247
  });
248
248
  if (a.system_prompt !== void 0 && typeof a.system_prompt !== "string") errors.push({
249
249
  path: `${path}.system_prompt`,
@@ -779,7 +779,7 @@ function createBashTool() {
779
779
  /**
780
780
  * Run an SDK agent with real model + MCP tools + bash.
781
781
  *
782
- * Used by the controller when backend.type === 'sdk'.
782
+ * Used by the controller when backend.type === 'default'.
783
783
  * Unlike the simple SdkBackend.send() (text-only), this runner:
784
784
  * 1. Connects to MCP server for context tools (channel, document)
785
785
  * 2. Adds bash tool for shell access
@@ -813,17 +813,8 @@ async function runSdkAgent(ctx, debugLog) {
813
813
  stopWhen: stepCountIs(ctx.agent.max_steps ?? 200),
814
814
  onStepFinish: (step) => {
815
815
  _stepNum++;
816
- if (step.toolCalls?.length) {
817
- for (const tc of step.toolCalls) if (tc.toolName === "bash") {
818
- const args = formatToolCall(tc);
819
- ctx.provider.appendChannel(ctx.name, args, {
820
- kind: "tool_call",
821
- toolCall: {
822
- name: tc.toolName,
823
- args
824
- }
825
- }).catch(() => {});
826
- }
816
+ if (step.toolCalls?.length && ctx.eventLog) {
817
+ for (const tc of step.toolCalls) if (tc.toolName === "bash") ctx.eventLog.toolCall(ctx.name, tc.toolName, formatToolCall(tc), "sdk");
827
818
  }
828
819
  }
829
820
  });
@@ -832,7 +823,7 @@ async function runSdkAgent(ctx, debugLog) {
832
823
  if (ctx.agent.max_steps && result.steps.length >= ctx.agent.max_steps && (lastStep?.toolCalls?.length ?? 0) > 0) {
833
824
  const warning = `⚠️ Agent reached max_steps limit (${ctx.agent.max_steps}) but wanted to continue. Consider increasing max_steps or removing the limit.`;
834
825
  log(warning);
835
- await ctx.provider.appendChannel(ctx.name, warning, { kind: "log" }).catch(() => {});
826
+ await ctx.provider.appendChannel(ctx.name, warning, { kind: "system" }).catch(() => {});
836
827
  }
837
828
  await mcp.close();
838
829
  return {
@@ -868,7 +859,7 @@ function shouldContinue(state) {
868
859
  * 5. Can be woken early via wake()
869
860
  */
870
861
  function createAgentController(config) {
871
- const { name, agent, contextProvider, mcpUrl, workspaceDir, projectDir, backend, onRunComplete, log = () => {}, feedback } = config;
862
+ const { name, agent, contextProvider, eventLog, mcpUrl, workspaceDir, projectDir, backend, onRunComplete, log = () => {}, feedback } = config;
872
863
  const infoLog = config.infoLog ?? log;
873
864
  const errorLog = config.errorLog ?? log;
874
865
  const pollInterval = config.pollInterval ?? CONTROLLER_DEFAULTS.pollInterval;
@@ -934,6 +925,7 @@ function createAgentController(config) {
934
925
  projectDir,
935
926
  retryAttempt: attempt,
936
927
  provider: contextProvider,
928
+ eventLog,
937
929
  feedback
938
930
  }, log, infoLog);
939
931
  if (lastResult.success) {
@@ -1016,7 +1008,7 @@ function createAgentController(config) {
1016
1008
  async function runAgent(backend, ctx, log, infoLog) {
1017
1009
  const info = infoLog ?? log;
1018
1010
  if (backend.type === "mock") return runMockAgent(ctx, (msg) => log(msg));
1019
- if (backend.type === "sdk") return runSdkAgent(ctx, (msg) => log(msg));
1011
+ if (backend.type === "default") return runSdkAgent(ctx, (msg) => log(msg));
1020
1012
  const startTime = Date.now();
1021
1013
  try {
1022
1014
  if (backend.setWorkspace) {
@@ -1067,8 +1059,7 @@ function getBackendByType(backendType, options) {
1067
1059
  if (backendType === "mock") return createMockBackend(options?.debugLog);
1068
1060
  const backendOptions = {};
1069
1061
  if (options?.timeout) backendOptions.timeout = options.timeout;
1070
- if (options?.debugLog) backendOptions.debugLog = options.debugLog;
1071
- if (options?.messageLog) backendOptions.messageLog = options.messageLog;
1062
+ if (options?.streamCallbacks) backendOptions.streamCallbacks = options.streamCallbacks;
1072
1063
  return createBackend({
1073
1064
  type: backendType,
1074
1065
  model: options?.model,
@@ -1084,7 +1075,7 @@ function getBackendByType(backendType, options) {
1084
1075
  function getBackendForModel(model, options) {
1085
1076
  const { provider } = parseModel(model);
1086
1077
  switch (provider) {
1087
- case "anthropic": return getBackendByType("sdk", {
1078
+ case "anthropic": return getBackendByType("default", {
1088
1079
  ...options,
1089
1080
  model
1090
1081
  });
@@ -1354,9 +1345,11 @@ function startChannelWatcher(config) {
1354
1345
  * Workflow Runner
1355
1346
  *
1356
1347
  * All output flows through the channel:
1357
- * - Operational events (init, setup, connect) → kind="log" (always visible)
1348
+ * - Operational events (init, setup, connect) → kind="system" (always visible)
1358
1349
  * - Debug details (MCP traces, idle checks) → kind="debug" (visible with --debug)
1359
- * - Agent messages → kind=undefined (always visible)
1350
+ * - Agent messages → kind="message" or undefined (always visible)
1351
+ * - Tool calls → kind="tool_call" with structured metadata
1352
+ * - Backend text output → kind="output" (always visible)
1360
1353
  *
1361
1354
  * The display layer (display.ts) handles filtering and formatting.
1362
1355
  */
@@ -1421,6 +1414,8 @@ async function initWorkflow(config) {
1421
1414
  }
1422
1415
  const projectDir = process.cwd();
1423
1416
  let mcpGetFeedback;
1417
+ let mcpToolNames = /* @__PURE__ */ new Set();
1418
+ const eventLog = new EventLog(contextProvider);
1424
1419
  const createMCPServerInstance = () => {
1425
1420
  const mcp = createContextMCPServer({
1426
1421
  provider: contextProvider,
@@ -1432,6 +1427,7 @@ async function initWorkflow(config) {
1432
1427
  debugLog
1433
1428
  });
1434
1429
  mcpGetFeedback = mcp.getFeedback;
1430
+ mcpToolNames = mcp.mcpToolNames;
1435
1431
  return mcp.server;
1436
1432
  };
1437
1433
  const httpMcpServer = await runWithHttp({
@@ -1468,9 +1464,11 @@ async function initWorkflow(config) {
1468
1464
  contextDir,
1469
1465
  projectDir,
1470
1466
  contextProvider,
1467
+ eventLog,
1471
1468
  httpMcpServer,
1472
1469
  mcpUrl: httpMcpServer.url,
1473
1470
  agentNames,
1471
+ mcpToolNames,
1474
1472
  setupResults,
1475
1473
  async sendKickoff() {
1476
1474
  if (!interpolatedKickoff) {
@@ -1574,23 +1572,23 @@ async function runWorkflowWithControllers(config) {
1574
1572
  model: agentDef.model
1575
1573
  });
1576
1574
  const agentLogger = logger.child(agentName);
1577
- const backendDebugLog = (msg) => {
1578
- agentLogger.debug(msg);
1579
- };
1580
- const backendMessageLog = (msg) => {
1581
- runtime.contextProvider.appendChannel(agentName, msg, { kind: "stream" }).catch(() => {});
1575
+ const streamCallbacks = {
1576
+ debugLog: (msg) => agentLogger.debug(msg),
1577
+ outputLog: (msg) => runtime.eventLog.output(agentName, msg),
1578
+ toolCallLog: (name, args) => runtime.eventLog.toolCall(agentName, name, args, "backend"),
1579
+ mcpToolNames: runtime.mcpToolNames
1582
1580
  };
1583
1581
  let backend;
1584
1582
  if (createBackend) backend = createBackend(agentName, agentDef);
1585
1583
  else if (agentDef.backend) backend = getBackendByType(agentDef.backend, {
1586
1584
  model: agentDef.model,
1587
- debugLog: backendDebugLog,
1588
- messageLog: backendMessageLog,
1585
+ debugLog: (msg) => agentLogger.debug(msg),
1586
+ streamCallbacks,
1589
1587
  timeout: agentDef.timeout
1590
1588
  });
1591
1589
  else if (agentDef.model) backend = getBackendForModel(agentDef.model, {
1592
- debugLog: backendDebugLog,
1593
- messageLog: backendMessageLog
1590
+ debugLog: (msg) => agentLogger.debug(msg),
1591
+ streamCallbacks
1594
1592
  });
1595
1593
  else throw new Error(`Agent "${agentName}" requires either a backend or model field`);
1596
1594
  logger.debug(`Using backend: ${backend.type} for ${agentName}`);
@@ -1601,6 +1599,7 @@ async function runWorkflowWithControllers(config) {
1601
1599
  name: agentName,
1602
1600
  agent: agentDef,
1603
1601
  contextProvider: runtime.contextProvider,
1602
+ eventLog: runtime.eventLog,
1604
1603
  mcpUrl: runtime.mcpUrl,
1605
1604
  workspaceDir,
1606
1605
  projectDir: runtime.projectDir,
@@ -1620,7 +1619,7 @@ async function runWorkflowWithControllers(config) {
1620
1619
  logger.debug("Kickoff sent");
1621
1620
  let channelWatcher;
1622
1621
  if (config.prettyDisplay) {
1623
- const { startPrettyDisplay } = await import("./display-pretty-BL9H2ocr.mjs");
1622
+ const { startPrettyDisplay } = await import("./display-pretty-BsCsnPqs.mjs");
1624
1623
  channelWatcher = startPrettyDisplay({
1625
1624
  contextProvider: runtime.contextProvider,
1626
1625
  agentNames: runtime.agentNames,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-worker",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "SDK and CLI for creating and testing agent workers with Vercel AI SDK",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -37,6 +37,8 @@
37
37
  "chalk": "^5.6.2",
38
38
  "commander": "^14.0.3",
39
39
  "execa": "^9.6.1",
40
+ "@hono/node-server": "^1.19.9",
41
+ "hono": "^4.11.9",
40
42
  "just-bash": "^2.8.0",
41
43
  "nanoid": "^5.1.6",
42
44
  "picocolors": "^1.1.1",
@@ -1,3 +0,0 @@
1
- import { C as SDK_MODEL_ALIASES, S as CURSOR_MODEL_MAP, _ as execWithIdleTimeout, a as createMockBackend, b as CLAUDE_MODEL_MAP, c as CodexBackend, d as codexAdapter, f as createStreamParser, g as IdleTimeoutError, h as formatEvent, i as MockAIBackend, l as ClaudeCodeBackend, m as extractCodexResult, n as createBackend, o as SdkBackend, p as extractClaudeResult, r as listBackends, s as CursorBackend, t as checkBackends, u as claudeAdapter, v as DEFAULT_IDLE_TIMEOUT, w as getModelForBackend, x as CODEX_MODEL_MAP, y as BACKEND_DEFAULT_MODELS } from "./backends-BOAkfYyL.mjs";
2
-
3
- export { listBackends };
@@ -1,4 +0,0 @@
1
- import { _ as shouldUseResource, a as FileStorage, c as CONTEXT_DEFAULTS, d as RESOURCE_PREFIX, f as RESOURCE_SCHEME, g as generateResourceId, h as extractMentions, i as resolveContextDir, l as MENTION_PATTERN, m as createResourceRef, n as createFileContextProvider, o as MemoryStorage, p as calculatePriority, r as getDefaultContextDir, s as ContextProviderImpl, t as FileContextProvider, u as MESSAGE_LENGTH_THRESHOLD } from "./cli/index.mjs";
2
- import { a as createMemoryContextProvider, i as MemoryContextProvider, n as formatProposal, r as formatProposalList, t as createContextMCPServer } from "./mcp-server-BQCQxv2v.mjs";
3
-
4
- export { createFileContextProvider };