@standardagents/builder 0.8.5 → 0.9.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/dist/index.d.ts CHANGED
@@ -77,8 +77,9 @@ declare global {
77
77
  */
78
78
  type BeforeClose = () => Promise<void> | void;
79
79
  /**
80
- * Manages multiplexed streaming of content (HTTP) and telemetry (WebSocket)
81
- * Inspired by bod.coach MultiplexedStream pattern
80
+ * Manages HTTP streaming of content chunks.
81
+ * WebSocket telemetry is handled separately by DurableThread.
82
+ * Inspired by bod.coach MultiplexedStream pattern.
82
83
  */
83
84
  declare class StreamManager {
84
85
  /**
@@ -89,10 +90,6 @@ declare class StreamManager {
89
90
  * HTTP ReadableStream for content chunks
90
91
  */
91
92
  httpStream: ReadableStream<Uint8Array>;
92
- /**
93
- * WebSocket connections for telemetry
94
- */
95
- private wsConnections;
96
93
  /**
97
94
  * Active channels
98
95
  */
@@ -126,24 +123,6 @@ declare class StreamManager {
126
123
  * Send content chunk to HTTP stream
127
124
  */
128
125
  sendContent(chunk: string): void;
129
- /**
130
- * Send telemetry event to all WebSocket connections
131
- */
132
- sendTelemetry(event: TelemetryEvent): void;
133
- /**
134
- * Emit a custom event to all WebSocket connections
135
- *
136
- * This sends a custom event message that can be received by frontend
137
- * clients using the onThreadEvent hook.
138
- *
139
- * @param type - The event type name
140
- * @param data - The event payload (any serializable data)
141
- */
142
- emitEvent(type: string, data: unknown): void;
143
- /**
144
- * Add a WebSocket connection for telemetry
145
- */
146
- addWebSocket(ws: WebSocket): void;
147
126
  /**
148
127
  * Add a before close hook
149
128
  */
@@ -473,6 +452,8 @@ interface FlowState {
473
452
  emitLog?: (log: unknown) => void;
474
453
  emitMessage?: (message: unknown) => void;
475
454
  emitMessageChunk?: (messageId: string, chunk: string, depth?: number) => void;
455
+ emitTelemetry?: (event: TelemetryEvent) => void;
456
+ emitEvent?: (type: string, data: unknown) => void;
476
457
  rootState: FlowState;
477
458
  rootMessageId?: string | null;
478
459
  parentLogId?: string | null;
@@ -1085,6 +1066,16 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
1085
1066
  * Does NOT update database - only broadcasts to connected clients
1086
1067
  */
1087
1068
  private broadcastMessageChunk;
1069
+ /**
1070
+ * Broadcast a telemetry event to all connected message WebSocket clients
1071
+ * Used for execution status updates (turn_started, tool_started, etc.)
1072
+ */
1073
+ private broadcastTelemetry;
1074
+ /**
1075
+ * Broadcast a custom event to all connected message WebSocket clients
1076
+ * Used by tools via emitThreadEvent() to send user-defined events
1077
+ */
1078
+ private broadcastEvent;
1088
1079
  /**
1089
1080
  * WebSocket Hibernation API handler for incoming messages
1090
1081
  * Called when a message is received on a hibernated WebSocket
package/dist/index.js CHANGED
@@ -39,10 +39,6 @@ var init_StreamManager = __esm({
39
39
  * HTTP ReadableStream for content chunks
40
40
  */
41
41
  httpStream;
42
- /**
43
- * WebSocket connections for telemetry
44
- */
45
- wsConnections;
46
42
  /**
47
43
  * Active channels
48
44
  */
@@ -74,7 +70,6 @@ var init_StreamManager = __esm({
74
70
  closed = false;
75
71
  constructor(beforeClose = []) {
76
72
  this.encoder = new TextEncoder();
77
- this.wsConnections = /* @__PURE__ */ new Set();
78
73
  this.httpStream = new ReadableStream({
79
74
  start: (controller) => {
80
75
  this.httpController = controller;
@@ -99,73 +94,6 @@ var init_StreamManager = __esm({
99
94
  console.error("Error sending content chunk:", error);
100
95
  }
101
96
  }
102
- /**
103
- * Send telemetry event to all WebSocket connections
104
- */
105
- sendTelemetry(event) {
106
- if (this.closed) {
107
- return;
108
- }
109
- const message = JSON.stringify(event);
110
- for (const ws of this.wsConnections) {
111
- if (ws.readyState !== WebSocket.OPEN) {
112
- this.wsConnections.delete(ws);
113
- }
114
- }
115
- for (const ws of this.wsConnections) {
116
- try {
117
- ws.send(message);
118
- } catch (error) {
119
- console.error("Error sending telemetry:", error);
120
- this.wsConnections.delete(ws);
121
- }
122
- }
123
- }
124
- /**
125
- * Emit a custom event to all WebSocket connections
126
- *
127
- * This sends a custom event message that can be received by frontend
128
- * clients using the onThreadEvent hook.
129
- *
130
- * @param type - The event type name
131
- * @param data - The event payload (any serializable data)
132
- */
133
- emitEvent(type, data) {
134
- if (this.closed) {
135
- return;
136
- }
137
- const message = JSON.stringify({
138
- type: "event",
139
- eventType: type,
140
- data,
141
- timestamp: Date.now()
142
- });
143
- for (const ws of this.wsConnections) {
144
- if (ws.readyState !== WebSocket.OPEN) {
145
- this.wsConnections.delete(ws);
146
- }
147
- }
148
- for (const ws of this.wsConnections) {
149
- try {
150
- ws.send(message);
151
- } catch (error) {
152
- console.error("Error emitting event:", error);
153
- this.wsConnections.delete(ws);
154
- }
155
- }
156
- }
157
- /**
158
- * Add a WebSocket connection for telemetry
159
- */
160
- addWebSocket(ws) {
161
- this.wsConnections.add(ws);
162
- ws.addEventListener("close", () => {
163
- this.wsConnections.delete(ws);
164
- });
165
- ws.addEventListener("error", () => {
166
- this.wsConnections.delete(ws);
167
- });
168
- }
169
97
  /**
170
98
  * Add a before close hook
171
99
  */
@@ -224,16 +152,6 @@ var init_StreamManager = __esm({
224
152
  console.error("Error closing HTTP stream:", error);
225
153
  }
226
154
  }
227
- for (const ws of this.wsConnections) {
228
- try {
229
- if (ws.readyState === WebSocket.OPEN) {
230
- ws.close();
231
- }
232
- } catch (error) {
233
- console.error("Error closing WebSocket:", error);
234
- }
235
- }
236
- this.wsConnections.clear();
237
155
  this.resolver();
238
156
  } catch (error) {
239
157
  console.error("Error during stream close:", error);
@@ -891,7 +809,7 @@ var init_LLMRequest = __esm({
891
809
  } catch (primaryError) {
892
810
  console.error(`Primary model ${context.model} failed:`, primaryError);
893
811
  lastFailedLogId = primaryError?._lastLogId;
894
- state.stream.sendTelemetry({
812
+ state.emitTelemetry?.({
895
813
  type: "fallback_triggered",
896
814
  from: context.model,
897
815
  to: "fetching_fallbacks",
@@ -901,7 +819,7 @@ var init_LLMRequest = __esm({
901
819
  const fallbacks = await this.getFallbacks(context.model, state);
902
820
  for (const fallback of fallbacks) {
903
821
  try {
904
- state.stream.sendTelemetry({
822
+ state.emitTelemetry?.({
905
823
  type: "fallback_triggered",
906
824
  from: context.model,
907
825
  to: fallback.fallback_model_id,
@@ -947,7 +865,7 @@ var init_LLMRequest = __esm({
947
865
  attempts++;
948
866
  try {
949
867
  logId = await this.logRequest(state, modelId, context, previousLogId);
950
- state.stream.sendTelemetry({
868
+ state.emitTelemetry?.({
951
869
  type: "llm_request",
952
870
  model: modelId,
953
871
  attempt: attempts,
@@ -955,7 +873,7 @@ var init_LLMRequest = __esm({
955
873
  });
956
874
  const response = await this.callModel(modelId, context, state, logId);
957
875
  await this.logSuccess(response, state, modelId, startTime, context, logId);
958
- state.stream.sendTelemetry({
876
+ state.emitTelemetry?.({
959
877
  type: "llm_response",
960
878
  tokens: response.usage.total_tokens,
961
879
  latency: Date.now() - startTime,
@@ -1433,7 +1351,7 @@ var init_ToolExecutor = __esm({
1433
1351
  const call = state.sequence.queue.shift();
1434
1352
  const toolMessageId = crypto.randomUUID();
1435
1353
  try {
1436
- state.stream.sendTelemetry({
1354
+ state.emitTelemetry?.({
1437
1355
  type: "tool_started",
1438
1356
  tool: call.function.name,
1439
1357
  timestamp: Date.now()
@@ -1445,7 +1363,7 @@ var init_ToolExecutor = __esm({
1445
1363
  error: validationError
1446
1364
  };
1447
1365
  await this.storeToolResult(call, result2, state, toolMessageId);
1448
- state.stream.sendTelemetry({
1366
+ state.emitTelemetry?.({
1449
1367
  type: "tool_completed",
1450
1368
  tool: call.function.name,
1451
1369
  status: "error",
@@ -1468,7 +1386,7 @@ var init_ToolExecutor = __esm({
1468
1386
  continue;
1469
1387
  }
1470
1388
  await this.storeToolResult(call, hookResult, state, toolMessageId);
1471
- state.stream.sendTelemetry({
1389
+ state.emitTelemetry?.({
1472
1390
  type: "tool_completed",
1473
1391
  tool: call.function.name,
1474
1392
  status: hookResult.status,
@@ -1488,7 +1406,7 @@ var init_ToolExecutor = __esm({
1488
1406
  continue;
1489
1407
  }
1490
1408
  await this.storeToolResult(call, hookResult, state, toolMessageId);
1491
- state.stream.sendTelemetry({
1409
+ state.emitTelemetry?.({
1492
1410
  type: "tool_completed",
1493
1411
  tool: call.function.name,
1494
1412
  status: "error",
@@ -1729,7 +1647,7 @@ var init_ToolExecutor = __esm({
1729
1647
  const propertyValue = args[initUserMessageProperty];
1730
1648
  userMessageContent = typeof propertyValue === "string" ? propertyValue : JSON.stringify(propertyValue);
1731
1649
  }
1732
- const extraMessages = state.extraMessages || [];
1650
+ const extraMessages = [...state.extraMessages || []];
1733
1651
  if (userMessageContent) {
1734
1652
  extraMessages.push({
1735
1653
  id: crypto.randomUUID(),
@@ -2358,7 +2276,7 @@ var init_FlowEngine = __esm({
2358
2276
  status: interruptionMessage.status
2359
2277
  });
2360
2278
  }
2361
- state.stream.sendTelemetry({
2279
+ state.emitTelemetry?.({
2362
2280
  type: "stopped_by_user",
2363
2281
  timestamp: Date.now()
2364
2282
  });
@@ -2389,7 +2307,7 @@ var init_FlowEngine = __esm({
2389
2307
 
2390
2308
  [ERROR] ${errorMessage}
2391
2309
  `);
2392
- state.stream.sendTelemetry({
2310
+ state.emitTelemetry?.({
2393
2311
  type: "stopped",
2394
2312
  reason: `Error: ${errorMessage}`,
2395
2313
  side: state.currentSide,
@@ -2408,7 +2326,7 @@ var init_FlowEngine = __esm({
2408
2326
  "max_turns_reached",
2409
2327
  `Thread ${threadId}`
2410
2328
  );
2411
- state.stream.sendTelemetry({
2329
+ state.emitTelemetry?.({
2412
2330
  type: "stopped",
2413
2331
  reason: "Maximum turns reached",
2414
2332
  side: state.currentSide,
@@ -2524,6 +2442,8 @@ var init_FlowEngine = __esm({
2524
2442
  emitLog: stateInput.emitLog,
2525
2443
  emitMessage: stateInput.emitMessage,
2526
2444
  emitMessageChunk: stateInput.emitMessageChunk,
2445
+ emitTelemetry: stateInput.emitTelemetry,
2446
+ emitEvent: stateInput.emitEvent,
2527
2447
  rootMessageId: stateInput.rootMessageId,
2528
2448
  parentLogId: stateInput.parentLogId,
2529
2449
  currentLogId: stateInput.currentLogId,
@@ -2557,7 +2477,7 @@ var init_FlowEngine = __esm({
2557
2477
  if (maxTurns !== null && currentSideTurnCount >= maxTurns) {
2558
2478
  state.stopped = true;
2559
2479
  state.stoppedBy = state.currentSide;
2560
- state.stream.sendTelemetry({
2480
+ state.emitTelemetry?.({
2561
2481
  type: "stopped",
2562
2482
  reason: `Side ${state.currentSide} reached max turns (${maxTurns})`,
2563
2483
  side: state.currentSide,
@@ -2570,7 +2490,7 @@ var init_FlowEngine = __esm({
2570
2490
  if (completedExchanges >= state.agentConfig.max_session_turns) {
2571
2491
  state.stopped = true;
2572
2492
  state.stoppedBy = state.currentSide;
2573
- state.stream.sendTelemetry({
2493
+ state.emitTelemetry?.({
2574
2494
  type: "stopped",
2575
2495
  reason: `Reached max session turns (${state.agentConfig.max_session_turns} exchanges)`,
2576
2496
  side: state.currentSide,
@@ -2584,7 +2504,7 @@ var init_FlowEngine = __esm({
2584
2504
  } else {
2585
2505
  state.sideBTurnCount++;
2586
2506
  }
2587
- state.stream.sendTelemetry({
2507
+ state.emitTelemetry?.({
2588
2508
  type: "turn_started",
2589
2509
  turn: state.turnCount,
2590
2510
  side: state.currentSide,
@@ -2642,7 +2562,7 @@ var init_FlowEngine = __esm({
2642
2562
  if (state.agentConfig.type === "ai_human" && forcedSide === "b") {
2643
2563
  state.stopped = true;
2644
2564
  state.stoppedBy = "a";
2645
- state.stream.sendTelemetry({
2565
+ state.emitTelemetry?.({
2646
2566
  type: "stopped",
2647
2567
  reason: "Forced turn to human (side_b)",
2648
2568
  side: state.currentSide,
@@ -2669,7 +2589,7 @@ var init_FlowEngine = __esm({
2669
2589
  if (state.agentConfig.type === "dual_ai" && !state.stopped) {
2670
2590
  this.switchSides(state);
2671
2591
  }
2672
- state.stream.sendTelemetry({
2592
+ state.emitTelemetry?.({
2673
2593
  type: "turn_completed",
2674
2594
  turn: state.turnCount,
2675
2595
  stopped: state.stopped,
@@ -2726,7 +2646,7 @@ var init_FlowEngine = __esm({
2726
2646
  state.currentSide = "a";
2727
2647
  state.prompt = state.prompts.sideA;
2728
2648
  state.pendingHandoff = void 0;
2729
- state.stream.sendTelemetry({
2649
+ state.emitTelemetry?.({
2730
2650
  type: "agent_handoff",
2731
2651
  new_agent_id: newAgentName,
2732
2652
  new_agent_title: state.agentConfig.title,
@@ -3626,7 +3546,7 @@ var init_FlowEngine = __esm({
3626
3546
  if (endConversationTool && response.tool_calls?.some((call) => call.function.name === endConversationTool)) {
3627
3547
  state.stopped = true;
3628
3548
  state.stoppedBy = state.currentSide;
3629
- state.stream.sendTelemetry({
3549
+ state.emitTelemetry?.({
3630
3550
  type: "stopped",
3631
3551
  reason: `End conversation tool called: ${endConversationTool}`,
3632
3552
  side: state.currentSide,
@@ -3640,7 +3560,7 @@ var init_FlowEngine = __esm({
3640
3560
  if (stopOnResponse && response.content && !response.tool_calls) {
3641
3561
  state.stopped = true;
3642
3562
  state.stoppedBy = state.currentSide;
3643
- state.stream.sendTelemetry({
3563
+ state.emitTelemetry?.({
3644
3564
  type: "stopped",
3645
3565
  reason: "Returns content (no tool calls)",
3646
3566
  side: state.currentSide,
@@ -3650,7 +3570,7 @@ var init_FlowEngine = __esm({
3650
3570
  if (stopTool && response.tool_calls?.some((call) => call.function.name === stopTool)) {
3651
3571
  state.stopped = true;
3652
3572
  state.stoppedBy = state.currentSide;
3653
- state.stream.sendTelemetry({
3573
+ state.emitTelemetry?.({
3654
3574
  type: "stopped",
3655
3575
  reason: `Stop tool called: ${stopTool}`,
3656
3576
  side: state.currentSide,
@@ -5933,9 +5853,12 @@ function agentbuilder(options = {}) {
5933
5853
  optimizeDeps: {
5934
5854
  // Pre-bundle these deps to prevent reload during startup
5935
5855
  include: ["openai", "zod"],
5936
- // Exclude @standardagents/builder/mcp from pre-bundling (used for MCP server, not needed in worker)
5856
+ // Exclude workspace packages from pre-bundling to prevent "new version of pre-bundle" errors
5857
+ // during first startup when node_modules/.vite cache is empty
5937
5858
  exclude: [
5938
- "@standardagents/builder/mcp"
5859
+ "@standardagents/builder/mcp",
5860
+ "@standardagents/builder/runtime",
5861
+ "@standardagents/builder/built-in-routes"
5939
5862
  ]
5940
5863
  },
5941
5864
  ssr: {
@@ -8815,6 +8738,55 @@ var DurableThread = class extends DurableObject {
8815
8738
  }
8816
8739
  }
8817
8740
  }
8741
+ /**
8742
+ * Broadcast a telemetry event to all connected message WebSocket clients
8743
+ * Used for execution status updates (turn_started, tool_started, etc.)
8744
+ */
8745
+ broadcastTelemetry(event) {
8746
+ const payload = JSON.stringify(event);
8747
+ for (const ws of Array.from(this.messageSockets)) {
8748
+ try {
8749
+ if (ws.readyState === WebSocket.OPEN) {
8750
+ ws.send(payload);
8751
+ } else {
8752
+ this.messageSockets.delete(ws);
8753
+ }
8754
+ } catch (err) {
8755
+ console.error(
8756
+ "[DurableThread] Failed to send telemetry to WebSocket:",
8757
+ err
8758
+ );
8759
+ this.messageSockets.delete(ws);
8760
+ }
8761
+ }
8762
+ }
8763
+ /**
8764
+ * Broadcast a custom event to all connected message WebSocket clients
8765
+ * Used by tools via emitThreadEvent() to send user-defined events
8766
+ */
8767
+ broadcastEvent(type, data) {
8768
+ const payload = JSON.stringify({
8769
+ type: "event",
8770
+ eventType: type,
8771
+ data,
8772
+ timestamp: Date.now()
8773
+ });
8774
+ for (const ws of Array.from(this.messageSockets)) {
8775
+ try {
8776
+ if (ws.readyState === WebSocket.OPEN) {
8777
+ ws.send(payload);
8778
+ } else {
8779
+ this.messageSockets.delete(ws);
8780
+ }
8781
+ } catch (err) {
8782
+ console.error(
8783
+ "[DurableThread] Failed to send event to WebSocket:",
8784
+ err
8785
+ );
8786
+ this.messageSockets.delete(ws);
8787
+ }
8788
+ }
8789
+ }
8818
8790
  /**
8819
8791
  * WebSocket Hibernation API handler for incoming messages
8820
8792
  * Called when a message is received on a hibernated WebSocket
@@ -8963,6 +8935,8 @@ var DurableThread = class extends DurableObject {
8963
8935
  emitLog: (log) => this.broadcastLog(log),
8964
8936
  emitMessage: (message) => this.broadcastMessage(message),
8965
8937
  emitMessageChunk: (messageId, chunk, depth) => this.broadcastMessageChunk(messageId, chunk, depth),
8938
+ emitTelemetry: (event) => this.broadcastTelemetry(event),
8939
+ emitEvent: (type, data2) => this.broadcastEvent(type, data2),
8966
8940
  rootMessageId,
8967
8941
  abortController: this.currentAbortController
8968
8942
  });
@@ -9054,11 +9028,6 @@ var DurableThread = class extends DurableObject {
9054
9028
  });
9055
9029
  const nextSide = role === "assistant" ? "b" : "a";
9056
9030
  const stream = new StreamManager();
9057
- for (const ws of this.messageSockets) {
9058
- if (ws.readyState === WebSocket.OPEN) {
9059
- stream.addWebSocket(ws);
9060
- }
9061
- }
9062
9031
  await FlowEngine2.execute({
9063
9032
  agentConfig: agent,
9064
9033
  storage: this.ctx.storage,
@@ -9075,6 +9044,8 @@ var DurableThread = class extends DurableObject {
9075
9044
  emitLog: (log) => this.broadcastLog(log),
9076
9045
  emitMessage: (message2) => this.broadcastMessage(message2),
9077
9046
  emitMessageChunk: (messageId2, chunk, depth) => this.broadcastMessageChunk(messageId2, chunk, depth),
9047
+ emitTelemetry: (event) => this.broadcastTelemetry(event),
9048
+ emitEvent: (type, data) => this.broadcastEvent(type, data),
9078
9049
  abortController: this.currentAbortController
9079
9050
  });
9080
9051
  } finally {
@@ -10355,11 +10326,11 @@ async function reloadHistory(state) {
10355
10326
  return state.messageHistory;
10356
10327
  }
10357
10328
  function emitThreadEvent(flow, type, data) {
10358
- if (!flow.stream) {
10359
- console.warn("Cannot emit event: stream is not available");
10329
+ if (!flow.emitEvent) {
10330
+ console.warn("Cannot emit event: emitEvent callback is not available");
10360
10331
  return;
10361
10332
  }
10362
- flow.stream.emitEvent(type, data);
10333
+ flow.emitEvent(type, data);
10363
10334
  }
10364
10335
  function forceTurn(flow, side) {
10365
10336
  if (flow.sequence.isHandling) {