@cuylabs/agent-core 0.3.0 → 0.5.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.
Files changed (66) hide show
  1. package/README.md +216 -41
  2. package/dist/builder-RcTZuYnO.d.ts +34 -0
  3. package/dist/capabilities/index.d.ts +97 -0
  4. package/dist/capabilities/index.js +46 -0
  5. package/dist/chunk-6TDTQJ4P.js +116 -0
  6. package/dist/chunk-7MUFEN4K.js +559 -0
  7. package/dist/chunk-BDBZ3SLK.js +745 -0
  8. package/dist/chunk-DWYX7ASF.js +26 -0
  9. package/dist/chunk-FG4MD5MU.js +54 -0
  10. package/dist/chunk-IMGQOTU2.js +2019 -0
  11. package/dist/chunk-IVUJDISU.js +556 -0
  12. package/dist/chunk-LRHOS4ZN.js +584 -0
  13. package/dist/chunk-OTUGSCED.js +691 -0
  14. package/dist/chunk-P6YF7USR.js +182 -0
  15. package/dist/chunk-QAQADS4X.js +258 -0
  16. package/dist/chunk-QWFMX226.js +879 -0
  17. package/dist/{chunk-6VKLWNRE.js → chunk-SDSBEQXG.js} +1 -132
  18. package/dist/chunk-VBWWUHWI.js +724 -0
  19. package/dist/chunk-VEKUXUVF.js +41 -0
  20. package/dist/chunk-X635CM2F.js +305 -0
  21. package/dist/chunk-YUUJK53A.js +91 -0
  22. package/dist/chunk-ZXAKHMWH.js +283 -0
  23. package/dist/config-D2xeGEHK.d.ts +52 -0
  24. package/dist/context/index.d.ts +259 -0
  25. package/dist/context/index.js +26 -0
  26. package/dist/identifiers-BLUxFqV_.d.ts +12 -0
  27. package/dist/index-p0kOsVsE.d.ts +1067 -0
  28. package/dist/index-tmhaADz5.d.ts +198 -0
  29. package/dist/index.d.ts +185 -4316
  30. package/dist/index.js +1238 -5368
  31. package/dist/mcp/index.d.ts +26 -0
  32. package/dist/mcp/index.js +14 -0
  33. package/dist/messages-BYWGn8TY.d.ts +110 -0
  34. package/dist/middleware/index.d.ts +7 -0
  35. package/dist/middleware/index.js +12 -0
  36. package/dist/models/index.d.ts +33 -0
  37. package/dist/models/index.js +12 -0
  38. package/dist/network-D76DS5ot.d.ts +5 -0
  39. package/dist/prompt/index.d.ts +224 -0
  40. package/dist/prompt/index.js +45 -0
  41. package/dist/reasoning/index.d.ts +71 -0
  42. package/dist/reasoning/index.js +47 -0
  43. package/dist/registry-CuRWWtcT.d.ts +164 -0
  44. package/dist/resolver-DOfZ-xuk.d.ts +254 -0
  45. package/dist/runner-C7aMP_x3.d.ts +596 -0
  46. package/dist/runtime/index.d.ts +357 -0
  47. package/dist/runtime/index.js +64 -0
  48. package/dist/session-manager-Uawm2Le7.d.ts +274 -0
  49. package/dist/skill/index.d.ts +103 -0
  50. package/dist/skill/index.js +39 -0
  51. package/dist/storage/index.d.ts +167 -0
  52. package/dist/storage/index.js +50 -0
  53. package/dist/sub-agent/index.d.ts +14 -0
  54. package/dist/sub-agent/index.js +15 -0
  55. package/dist/tool/index.d.ts +173 -1
  56. package/dist/tool/index.js +12 -3
  57. package/dist/tool-DYp6-cC3.d.ts +239 -0
  58. package/dist/tool-pFAnJc5Y.d.ts +419 -0
  59. package/dist/tracker-DClqYqTj.d.ts +96 -0
  60. package/dist/tracking/index.d.ts +109 -0
  61. package/dist/tracking/index.js +20 -0
  62. package/dist/types-CQaXbRsS.d.ts +47 -0
  63. package/dist/types-MM1JoX5T.d.ts +810 -0
  64. package/dist/types-VQgymC1N.d.ts +156 -0
  65. package/package.json +89 -5
  66. package/dist/index-QR704uRr.d.ts +0 -472
@@ -0,0 +1,2019 @@
1
+ import {
2
+ executeAgentToolCall
3
+ } from "./chunk-FG4MD5MU.js";
4
+ import {
5
+ DEFAULT_CONTEXT_LIMITS
6
+ } from "./chunk-QAQADS4X.js";
7
+ import {
8
+ buildReasoningOptionsSync
9
+ } from "./chunk-X635CM2F.js";
10
+
11
+ // src/runtime/observer.ts
12
+ function defaultAgentTaskCheckpointStrategy(input) {
13
+ switch (input.event.type) {
14
+ case "step-finish":
15
+ return "step-finish";
16
+ case "tool-result":
17
+ return "tool-result";
18
+ case "tool-error":
19
+ return "tool-error";
20
+ case "turn-boundary":
21
+ return input.event.boundary;
22
+ case "complete":
23
+ return "task-complete";
24
+ default:
25
+ return void 0;
26
+ }
27
+ }
28
+
29
+ // src/runtime/task-runner.ts
30
+ import { randomUUID } from "crypto";
31
+
32
+ // src/runtime/turn-state.ts
33
+ var EMPTY_USAGE = {
34
+ inputTokens: 0,
35
+ outputTokens: 0,
36
+ totalTokens: 0
37
+ };
38
+ function cloneUsage(usage) {
39
+ return { ...usage };
40
+ }
41
+ function normalizeUsage(usage) {
42
+ if (!usage) {
43
+ return cloneUsage(EMPTY_USAGE);
44
+ }
45
+ return {
46
+ inputTokens: usage.inputTokens ?? 0,
47
+ outputTokens: usage.outputTokens ?? 0,
48
+ totalTokens: usage.totalTokens ?? 0
49
+ };
50
+ }
51
+ function removeActiveToolCall(toolCalls, toolCallId) {
52
+ return toolCalls.filter((toolCall) => toolCall.toolCallId !== toolCallId);
53
+ }
54
+ function createAgentTurnState(options) {
55
+ return {
56
+ sessionId: options.sessionId,
57
+ phase: "initializing",
58
+ step: 0,
59
+ response: "",
60
+ usage: cloneUsage(EMPTY_USAGE),
61
+ eventCount: 0,
62
+ activeToolCalls: [],
63
+ resolvedToolCalls: [],
64
+ startedAt: options.startedAt,
65
+ updatedAt: options.startedAt
66
+ };
67
+ }
68
+ function advanceAgentTurnState(state, event, updatedAt, options = {}) {
69
+ const next = {
70
+ ...state,
71
+ usage: cloneUsage(state.usage),
72
+ activeToolCalls: state.activeToolCalls.map((toolCall) => ({ ...toolCall })),
73
+ resolvedToolCalls: state.resolvedToolCalls.map((toolCall) => ({
74
+ ...toolCall
75
+ })),
76
+ eventCount: state.eventCount + 1,
77
+ lastEvent: event,
78
+ updatedAt
79
+ };
80
+ switch (event.type) {
81
+ case "step-start":
82
+ next.phase = "running-model";
83
+ next.step = event.step;
84
+ next.maxSteps = event.maxSteps;
85
+ next.activeToolCalls = [];
86
+ next.resolvedToolCalls = [];
87
+ next.lastFinishReason = void 0;
88
+ delete next.error;
89
+ break;
90
+ case "text-delta":
91
+ next.response += event.text;
92
+ break;
93
+ case "tool-start":
94
+ next.phase = "running-tools";
95
+ next.activeToolCalls = [
96
+ ...removeActiveToolCall(next.activeToolCalls, event.toolCallId),
97
+ {
98
+ toolCallId: event.toolCallId,
99
+ toolName: event.toolName,
100
+ input: event.input,
101
+ startedAt: updatedAt,
102
+ ...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
103
+ }
104
+ ];
105
+ break;
106
+ case "tool-result":
107
+ next.phase = "running-tools";
108
+ next.activeToolCalls = removeActiveToolCall(
109
+ next.activeToolCalls,
110
+ event.toolCallId
111
+ );
112
+ next.resolvedToolCalls = [
113
+ ...next.resolvedToolCalls,
114
+ {
115
+ toolCallId: event.toolCallId,
116
+ toolName: event.toolName,
117
+ outcome: "result",
118
+ value: event.result,
119
+ resolvedAt: updatedAt,
120
+ ...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
121
+ }
122
+ ];
123
+ break;
124
+ case "tool-error":
125
+ next.phase = "running-tools";
126
+ next.activeToolCalls = removeActiveToolCall(
127
+ next.activeToolCalls,
128
+ event.toolCallId
129
+ );
130
+ next.resolvedToolCalls = [
131
+ ...next.resolvedToolCalls,
132
+ {
133
+ toolCallId: event.toolCallId,
134
+ toolName: event.toolName,
135
+ outcome: "error",
136
+ value: event.error,
137
+ resolvedAt: updatedAt,
138
+ ...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
139
+ }
140
+ ];
141
+ break;
142
+ case "step-finish":
143
+ next.phase = "committing-step";
144
+ next.step = event.step;
145
+ next.usage = normalizeUsage(event.usage ?? next.usage);
146
+ next.lastFinishReason = event.finishReason;
147
+ break;
148
+ case "turn-boundary":
149
+ next.lastBoundary = {
150
+ kind: event.boundary,
151
+ createdAt: updatedAt,
152
+ ...event.step !== void 0 ? { step: event.step } : {},
153
+ ...event.messageRole ? { messageRole: event.messageRole } : {},
154
+ ...event.pendingToolCallCount !== void 0 ? { pendingToolCallCount: event.pendingToolCallCount } : {}
155
+ };
156
+ switch (event.boundary) {
157
+ case "input-commit-start":
158
+ case "input-commit-finish":
159
+ case "intervention-commit-start":
160
+ case "intervention-commit-finish":
161
+ next.phase = "committing-input";
162
+ break;
163
+ case "step-commit-start":
164
+ case "step-commit-finish":
165
+ next.phase = "committing-step";
166
+ break;
167
+ case "output-commit-start":
168
+ case "output-commit-finish":
169
+ next.phase = "committing-output";
170
+ break;
171
+ }
172
+ break;
173
+ case "complete":
174
+ next.phase = "completed";
175
+ next.usage = normalizeUsage(event.usage ?? next.usage);
176
+ break;
177
+ case "error":
178
+ next.phase = "failed";
179
+ next.error = event.error.message;
180
+ break;
181
+ default:
182
+ break;
183
+ }
184
+ return next;
185
+ }
186
+ function failAgentTurnState(state, error, updatedAt) {
187
+ return {
188
+ ...state,
189
+ usage: cloneUsage(state.usage),
190
+ activeToolCalls: state.activeToolCalls.map((toolCall) => ({ ...toolCall })),
191
+ resolvedToolCalls: state.resolvedToolCalls.map((toolCall) => ({
192
+ ...toolCall
193
+ })),
194
+ phase: "failed",
195
+ error: error.message,
196
+ updatedAt
197
+ };
198
+ }
199
+
200
+ // src/runtime/task-runner.ts
201
+ function normalizeNonEmpty(value, label) {
202
+ const normalized = value.trim();
203
+ if (!normalized) {
204
+ throw new Error(`${label} must not be empty`);
205
+ }
206
+ return normalized;
207
+ }
208
+ function sanitizeSegment(input) {
209
+ return input.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
210
+ }
211
+ function buildRuntimeSessionId(prefix, key) {
212
+ const timestamp = Date.now().toString(36);
213
+ const suffix = randomUUID().slice(0, 8);
214
+ const normalizedPrefix = sanitizeSegment(prefix) || "runtime";
215
+ const normalizedKey = key ? sanitizeSegment(key) : "";
216
+ if (normalizedKey) {
217
+ return `${normalizedPrefix}:${normalizedKey}:${timestamp}:${suffix}`;
218
+ }
219
+ return `${normalizedPrefix}:${timestamp}:${suffix}`;
220
+ }
221
+ function nowIso() {
222
+ return (/* @__PURE__ */ new Date()).toISOString();
223
+ }
224
+ async function notifyObservers(observers, invoke) {
225
+ for (const observer of observers) {
226
+ try {
227
+ await invoke(observer);
228
+ } catch (error) {
229
+ console.warn(
230
+ "[agent-task-runner] observer error:",
231
+ error instanceof Error ? error.message : String(error)
232
+ );
233
+ }
234
+ }
235
+ }
236
+ function createAgentTaskRunner(agent, options = {}) {
237
+ const prefix = options.sessionPrefix ?? "runtime";
238
+ const baseObservers = options.observers ?? [];
239
+ const checkpointStrategy = options.checkpointStrategy ?? defaultAgentTaskCheckpointStrategy;
240
+ return async (payload, context = {}) => {
241
+ const message = normalizeNonEmpty(payload.message, "payload.message");
242
+ const resolvedSessionId = payload.sessionId?.trim() || options.resolveSessionId?.(payload, context)?.trim() || buildRuntimeSessionId(prefix, context.fallbackSessionKey);
243
+ const sessionId = normalizeNonEmpty(resolvedSessionId, "sessionId");
244
+ const startedAt = nowIso();
245
+ const run = {
246
+ payload,
247
+ context,
248
+ sessionId,
249
+ startedAt
250
+ };
251
+ const toolCalls = [];
252
+ let turnState = createAgentTurnState({
253
+ sessionId,
254
+ startedAt
255
+ });
256
+ const createSnapshot = () => ({
257
+ sessionId,
258
+ response: turnState.response,
259
+ usage: { ...turnState.usage },
260
+ toolCalls: toolCalls.map((toolCall) => ({ ...toolCall })),
261
+ eventCount: turnState.eventCount,
262
+ activeStep: turnState.step > 0 ? turnState.step : void 0,
263
+ lastEvent: turnState.lastEvent,
264
+ error: turnState.error,
265
+ startedAt: turnState.startedAt,
266
+ updatedAt: turnState.updatedAt,
267
+ turnState
268
+ });
269
+ const emitCheckpoint = async (reason, event) => {
270
+ if (baseObservers.length === 0) {
271
+ return;
272
+ }
273
+ const checkpoint = {
274
+ run,
275
+ reason,
276
+ snapshot: createSnapshot(),
277
+ event,
278
+ createdAt: turnState.updatedAt
279
+ };
280
+ await notifyObservers(baseObservers, async (observer) => {
281
+ await observer.onCheckpoint?.(checkpoint);
282
+ });
283
+ };
284
+ await notifyObservers(baseObservers, async (observer) => {
285
+ await observer.onTaskStart?.(run, createSnapshot());
286
+ });
287
+ await emitCheckpoint("task-start");
288
+ const activateCtx = baseObservers.find((o) => o.activateContext)?.activateContext?.bind(void 0, sessionId);
289
+ try {
290
+ const processChatStream = async () => {
291
+ for await (const event of agent.chat(sessionId, message, {
292
+ abort: context.signal,
293
+ system: payload.system
294
+ })) {
295
+ turnState = advanceAgentTurnState(turnState, event, nowIso());
296
+ if (event.type === "tool-result") {
297
+ toolCalls.push({ name: event.toolName, result: event.result });
298
+ }
299
+ const snapshot = createSnapshot();
300
+ await notifyObservers(baseObservers, async (observer) => {
301
+ await observer.onTaskEvent?.(run, event, snapshot);
302
+ });
303
+ const checkpointReason = checkpointStrategy({
304
+ run,
305
+ event,
306
+ snapshot
307
+ });
308
+ if (checkpointReason) {
309
+ await emitCheckpoint(checkpointReason, event);
310
+ }
311
+ if (event.type === "error") {
312
+ throw event.error;
313
+ }
314
+ }
315
+ };
316
+ if (activateCtx) {
317
+ await activateCtx(processChatStream);
318
+ } else {
319
+ await processChatStream();
320
+ }
321
+ const result = {
322
+ response: turnState.response,
323
+ sessionId,
324
+ usage: { ...turnState.usage },
325
+ toolCalls
326
+ };
327
+ await notifyObservers(baseObservers, async (observer) => {
328
+ await observer.onTaskComplete?.(run, result, createSnapshot());
329
+ });
330
+ return result;
331
+ } catch (error) {
332
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
333
+ turnState = failAgentTurnState(turnState, normalizedError, nowIso());
334
+ await notifyObservers(baseObservers, async (observer) => {
335
+ await observer.onTaskError?.(run, normalizedError, createSnapshot());
336
+ });
337
+ await emitCheckpoint("task-error");
338
+ throw normalizedError;
339
+ }
340
+ };
341
+ }
342
+
343
+ // src/runtime/model-messages.ts
344
+ function convertAgentMessagesToModelMessages(messages) {
345
+ return messages.flatMap((message) => {
346
+ switch (message.role) {
347
+ case "user":
348
+ return [{ role: "user", content: message.content }];
349
+ case "assistant": {
350
+ if (message.toolCalls && message.toolCalls.length > 0) {
351
+ const toolCallParts = message.toolCalls.map(
352
+ (toolCall) => ({
353
+ type: "tool-call",
354
+ toolCallId: toolCall.toolCallId,
355
+ toolName: toolCall.toolName,
356
+ input: toolCall.args
357
+ })
358
+ );
359
+ return [{ role: "assistant", content: toolCallParts }];
360
+ }
361
+ return [{ role: "assistant", content: message.content }];
362
+ }
363
+ case "tool":
364
+ return [
365
+ {
366
+ role: "tool",
367
+ content: [
368
+ {
369
+ type: "tool-result",
370
+ toolCallId: message.toolCallId ?? "",
371
+ toolName: message.toolName ?? "",
372
+ output: {
373
+ type: "text",
374
+ value: typeof message.result === "string" ? message.result : JSON.stringify(message.result)
375
+ }
376
+ }
377
+ ]
378
+ }
379
+ ];
380
+ case "system":
381
+ return [{ role: "system", content: message.content }];
382
+ }
383
+ });
384
+ }
385
+
386
+ // src/runtime/turn-engine/commit-batch.ts
387
+ import { randomUUID as randomUUID2 } from "crypto";
388
+ function createAgentTurnBoundaryEvent(boundary, metadata = {}) {
389
+ return {
390
+ type: "turn-boundary",
391
+ boundary,
392
+ ...metadata.step !== void 0 ? { step: metadata.step } : {},
393
+ ...metadata.messageRole ? { messageRole: metadata.messageRole } : {},
394
+ ...metadata.pendingToolCallCount !== void 0 ? { pendingToolCallCount: metadata.pendingToolCallCount } : {}
395
+ };
396
+ }
397
+ function createAgentTurnStepCommitBatch(step, snapshot, options = {}) {
398
+ if (snapshot.toolCalls.length === 0) {
399
+ return void 0;
400
+ }
401
+ const createdAt = options.createdAt ?? /* @__PURE__ */ new Date();
402
+ const assistantMessage = {
403
+ id: options.assistantMessageId ?? randomUUID2(),
404
+ role: "assistant",
405
+ content: "",
406
+ finish: "tool-calls",
407
+ toolCalls: snapshot.toolCalls.map(({ toolCallId, toolName, args }) => ({
408
+ toolCallId,
409
+ toolName,
410
+ args
411
+ })),
412
+ createdAt
413
+ };
414
+ const toolMessages = snapshot.toolResults.map(
415
+ ({ toolCallId, toolName, result }) => ({
416
+ id: options.toolMessageIds?.[toolCallId] ?? randomUUID2(),
417
+ role: "tool",
418
+ content: typeof result === "string" ? result : JSON.stringify(result),
419
+ toolCallId,
420
+ toolName,
421
+ result,
422
+ createdAt
423
+ })
424
+ );
425
+ return {
426
+ startBoundary: createAgentTurnBoundaryEvent("step-commit-start", {
427
+ step,
428
+ pendingToolCallCount: snapshot.toolCalls.length
429
+ }),
430
+ finishBoundary: createAgentTurnBoundaryEvent("step-commit-finish", {
431
+ step
432
+ }),
433
+ messages: [assistantMessage, ...toolMessages]
434
+ };
435
+ }
436
+ function createAgentTurnInputCommit(options) {
437
+ const message = {
438
+ id: options.id ?? randomUUID2(),
439
+ role: "user",
440
+ content: options.content,
441
+ createdAt: options.createdAt ?? /* @__PURE__ */ new Date(),
442
+ ...options.system ? { system: options.system } : {}
443
+ };
444
+ return {
445
+ startBoundary: createAgentTurnBoundaryEvent("input-commit-start", {
446
+ messageRole: "user"
447
+ }),
448
+ finishBoundary: createAgentTurnBoundaryEvent("input-commit-finish", {
449
+ messageRole: "user"
450
+ }),
451
+ messages: [message]
452
+ };
453
+ }
454
+ function createAgentTurnInterventionCommit(options) {
455
+ const message = {
456
+ id: options.id,
457
+ role: "user",
458
+ content: options.content,
459
+ createdAt: options.createdAt ?? /* @__PURE__ */ new Date()
460
+ };
461
+ return {
462
+ startBoundary: createAgentTurnBoundaryEvent("intervention-commit-start", {
463
+ messageRole: "user"
464
+ }),
465
+ finishBoundary: createAgentTurnBoundaryEvent("intervention-commit-finish", {
466
+ messageRole: "user"
467
+ }),
468
+ messages: [message]
469
+ };
470
+ }
471
+ function createAgentTurnOutputCommit(options) {
472
+ if (!options.text) {
473
+ return void 0;
474
+ }
475
+ const message = {
476
+ id: options.id ?? randomUUID2(),
477
+ role: "assistant",
478
+ content: options.text,
479
+ ...options.usage ? { tokens: options.usage } : {},
480
+ createdAt: options.createdAt ?? /* @__PURE__ */ new Date()
481
+ };
482
+ return {
483
+ startBoundary: createAgentTurnBoundaryEvent("output-commit-start", {
484
+ messageRole: "assistant"
485
+ }),
486
+ finishBoundary: createAgentTurnBoundaryEvent("output-commit-finish", {
487
+ messageRole: "assistant"
488
+ }),
489
+ messages: [message]
490
+ };
491
+ }
492
+
493
+ // src/runtime/turn-engine/engine.ts
494
+ var AgentTurnEngine = class {
495
+ turnState;
496
+ pendingToolCalls = /* @__PURE__ */ new Map();
497
+ pendingToolResults = /* @__PURE__ */ new Map();
498
+ getToolReplayPolicy;
499
+ constructor(options) {
500
+ this.turnState = createAgentTurnState({
501
+ sessionId: options.sessionId,
502
+ startedAt: options.startedAt
503
+ });
504
+ this.getToolReplayPolicy = options.getToolReplayPolicy;
505
+ }
506
+ getState() {
507
+ return structuredClone(this.turnState);
508
+ }
509
+ hasPendingToolCalls() {
510
+ return this.pendingToolCalls.size > 0;
511
+ }
512
+ createStepCommitSnapshot() {
513
+ if (this.pendingToolCalls.size === 0) {
514
+ return void 0;
515
+ }
516
+ return {
517
+ toolCalls: Array.from(this.pendingToolCalls.entries()).map(
518
+ ([toolCallId, { toolName, input, replayPolicy }]) => ({
519
+ toolCallId,
520
+ toolName,
521
+ args: input,
522
+ ...replayPolicy ? { replayPolicy } : {}
523
+ })
524
+ ),
525
+ toolResults: Array.from(this.pendingToolCalls.entries()).map(([toolCallId, { toolName, replayPolicy }]) => {
526
+ const resultData = this.pendingToolResults.get(toolCallId);
527
+ if (!resultData) {
528
+ return void 0;
529
+ }
530
+ return {
531
+ toolCallId,
532
+ toolName,
533
+ result: resultData.result,
534
+ ...replayPolicy ? { replayPolicy } : {}
535
+ };
536
+ }).filter((result) => result !== void 0)
537
+ };
538
+ }
539
+ recordEvent(event, updatedAt) {
540
+ const toolReplayPolicy = "toolName" in event ? this.getToolReplayPolicy?.(event.toolName) : void 0;
541
+ this.turnState = advanceAgentTurnState(this.turnState, event, updatedAt, {
542
+ toolReplayPolicy
543
+ });
544
+ switch (event.type) {
545
+ case "tool-start":
546
+ this.pendingToolCalls.set(event.toolCallId, {
547
+ toolName: event.toolName,
548
+ input: event.input,
549
+ replayPolicy: toolReplayPolicy
550
+ });
551
+ break;
552
+ case "tool-result":
553
+ this.pendingToolResults.set(event.toolCallId, {
554
+ toolName: event.toolName,
555
+ result: event.result,
556
+ replayPolicy: toolReplayPolicy
557
+ });
558
+ break;
559
+ case "tool-error":
560
+ this.pendingToolResults.set(event.toolCallId, {
561
+ toolName: event.toolName,
562
+ result: `Error: ${event.error}`,
563
+ replayPolicy: toolReplayPolicy
564
+ });
565
+ break;
566
+ default:
567
+ break;
568
+ }
569
+ return this.getState();
570
+ }
571
+ createBoundaryEvent(boundary, metadata = {}) {
572
+ return createAgentTurnBoundaryEvent(boundary, metadata);
573
+ }
574
+ createInputCommit(options) {
575
+ return createAgentTurnInputCommit(options);
576
+ }
577
+ createInterventionCommit(options) {
578
+ return createAgentTurnInterventionCommit(options);
579
+ }
580
+ consumeStepCommit(step) {
581
+ const snapshot = this.createStepCommitSnapshot();
582
+ const batch = snapshot ? createAgentTurnStepCommitBatch(step, snapshot) : void 0;
583
+ if (!batch) {
584
+ return void 0;
585
+ }
586
+ this.pendingToolCalls.clear();
587
+ this.pendingToolResults.clear();
588
+ return batch;
589
+ }
590
+ createOutputCommit(options) {
591
+ return createAgentTurnOutputCommit(options);
592
+ }
593
+ };
594
+ function createAgentTurnEngine(options) {
595
+ return new AgentTurnEngine(options);
596
+ }
597
+
598
+ // src/runtime/turn-runner/prepare.ts
599
+ function prepareModelStep(options) {
600
+ const modelMessages = Array.from(options.toModelMessages(options.messages));
601
+ return {
602
+ step: options.step,
603
+ messages: options.messages,
604
+ modelMessages,
605
+ llmInput: {
606
+ sessionID: options.sessionId,
607
+ model: options.config.model,
608
+ system: options.systemPrompts,
609
+ messages: modelMessages,
610
+ abort: options.abort,
611
+ tools: options.tools,
612
+ mcpTools: options.mcpTools,
613
+ cwd: options.config.cwd,
614
+ host: options.host,
615
+ temperature: options.config.temperature,
616
+ topP: options.config.topP,
617
+ maxOutputTokens: options.config.maxOutputTokens,
618
+ maxSteps: options.config.maxSteps,
619
+ reasoningLevel: options.reasoningLevel,
620
+ customStreamProvider: options.config.streamProvider,
621
+ turnTracker: options.turnTracker,
622
+ intervention: options.intervention,
623
+ middleware: options.middleware,
624
+ toolExecutionMode: options.toolExecutionMode,
625
+ telemetry: options.config.telemetry
626
+ },
627
+ processor: {
628
+ maxSteps: options.config.maxSteps,
629
+ doomLoopThreshold: options.config.doomLoopThreshold,
630
+ enforceDoomLoop: options.config.enforceDoomLoop,
631
+ onDoomLoop: options.config.onDoomLoop,
632
+ contextTokenLimit: options.config.contextWindow ? options.config.contextWindow - DEFAULT_CONTEXT_LIMITS.reserveTokens : void 0
633
+ }
634
+ };
635
+ }
636
+
637
+ // src/execution/llm/toolset.ts
638
+ import { tool, zodSchema } from "ai";
639
+ async function buildToolSet(options) {
640
+ const toolSet = {};
641
+ const executionMode = options.executionMode ?? "auto";
642
+ for (const [id, info] of Object.entries(options.tools)) {
643
+ const initialized = await info.init({ cwd: options.cwd });
644
+ toolSet[id] = executionMode === "auto" ? tool({
645
+ description: initialized.description,
646
+ inputSchema: zodSchema(initialized.parameters),
647
+ execute: async (params) => (await executeAgentToolCall({
648
+ toolName: id,
649
+ tool: info,
650
+ params,
651
+ cwd: options.cwd,
652
+ abort: options.abort,
653
+ sessionID: options.sessionID,
654
+ messageID: options.messageID,
655
+ ...options.host ? { host: options.host } : {},
656
+ ...options.turnTracker ? { turnTracker: options.turnTracker } : {},
657
+ ...options.middleware ? { middleware: options.middleware } : {}
658
+ })).output
659
+ }) : tool({
660
+ description: initialized.description,
661
+ inputSchema: zodSchema(initialized.parameters)
662
+ });
663
+ }
664
+ return toolSet;
665
+ }
666
+
667
+ // src/execution/llm/stream.ts
668
+ import {
669
+ stepCountIs,
670
+ streamText
671
+ } from "ai";
672
+
673
+ // src/errors/classify.ts
674
+ function isRetryableCategory(category) {
675
+ switch (category) {
676
+ case "rate_limit":
677
+ case "overloaded":
678
+ case "network":
679
+ case "timeout":
680
+ return true;
681
+ case "auth":
682
+ case "invalid_request":
683
+ case "context_overflow":
684
+ case "content_filter":
685
+ case "cancelled":
686
+ case "unknown":
687
+ return false;
688
+ }
689
+ }
690
+ function classifyFromStatusAndMessage(status, message) {
691
+ const lowerMessage = message.toLowerCase();
692
+ if (status) {
693
+ if (status === 429) return "rate_limit";
694
+ if (status === 401 || status === 403) return "auth";
695
+ if (status === 400) {
696
+ if (lowerMessage.includes("context") || lowerMessage.includes("token")) {
697
+ return "context_overflow";
698
+ }
699
+ return "invalid_request";
700
+ }
701
+ if (status === 503 || status === 502) return "overloaded";
702
+ if (status >= 500) return "network";
703
+ }
704
+ if (lowerMessage.includes("rate") && lowerMessage.includes("limit")) {
705
+ return "rate_limit";
706
+ }
707
+ if (lowerMessage.includes("overload") || lowerMessage.includes("capacity")) {
708
+ return "overloaded";
709
+ }
710
+ if (lowerMessage.includes("too_many_requests")) {
711
+ return "rate_limit";
712
+ }
713
+ if (lowerMessage.includes("unauthorized") || lowerMessage.includes("invalid api key")) {
714
+ return "auth";
715
+ }
716
+ if (lowerMessage.includes("context") && lowerMessage.includes("length")) {
717
+ return "context_overflow";
718
+ }
719
+ if (lowerMessage.includes("content") && lowerMessage.includes("filter")) {
720
+ return "content_filter";
721
+ }
722
+ if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out")) {
723
+ return "timeout";
724
+ }
725
+ if (lowerMessage.includes("network") || lowerMessage.includes("econnrefused") || lowerMessage.includes("econnreset")) {
726
+ return "network";
727
+ }
728
+ return "unknown";
729
+ }
730
+ function parseRetryDelay(headers) {
731
+ const retryAfterMs = headers["retry-after-ms"];
732
+ if (retryAfterMs) {
733
+ const parsed = parseFloat(retryAfterMs);
734
+ if (!Number.isNaN(parsed) && parsed > 0) {
735
+ return parsed;
736
+ }
737
+ }
738
+ const retryAfter = headers["retry-after"];
739
+ if (retryAfter) {
740
+ const seconds = parseFloat(retryAfter);
741
+ if (!Number.isNaN(seconds) && seconds > 0) {
742
+ return Math.ceil(seconds * 1e3);
743
+ }
744
+ const dateMs = Date.parse(retryAfter);
745
+ if (!Number.isNaN(dateMs)) {
746
+ const delayMs = dateMs - Date.now();
747
+ if (delayMs > 0) {
748
+ return Math.ceil(delayMs);
749
+ }
750
+ }
751
+ }
752
+ return void 0;
753
+ }
754
+
755
+ // src/errors/extract.ts
756
+ function extractFromAISDKError(error) {
757
+ const result = {};
758
+ const anyError = error;
759
+ if (typeof anyError.status === "number") {
760
+ result.status = anyError.status;
761
+ } else if (typeof anyError.statusCode === "number") {
762
+ result.status = anyError.statusCode;
763
+ }
764
+ if (anyError.responseHeaders && typeof anyError.responseHeaders === "object") {
765
+ result.headers = anyError.responseHeaders;
766
+ } else if (anyError.headers && typeof anyError.headers === "object") {
767
+ result.headers = anyError.headers;
768
+ }
769
+ if (anyError.data && typeof anyError.data === "object") {
770
+ const data = anyError.data;
771
+ if (data.type === "error" && typeof data.error === "object") {
772
+ const innerError = data.error;
773
+ if (innerError.type === "too_many_requests") {
774
+ result.category = "rate_limit";
775
+ } else if (innerError.type === "overloaded") {
776
+ result.category = "overloaded";
777
+ }
778
+ }
779
+ if (typeof data.isRetryable === "boolean" && !data.isRetryable && !result.category) {
780
+ result.category = "invalid_request";
781
+ }
782
+ }
783
+ return result;
784
+ }
785
+
786
+ // src/errors/llm-error.ts
787
+ var LLMError = class _LLMError extends Error {
788
+ category;
789
+ status;
790
+ headers;
791
+ provider;
792
+ model;
793
+ isRetryable;
794
+ retryDelayMs;
795
+ constructor(options) {
796
+ super(options.message, { cause: options.cause });
797
+ this.name = "LLMError";
798
+ this.status = options.status;
799
+ this.headers = options.headers;
800
+ this.provider = options.provider;
801
+ this.model = options.model;
802
+ this.category = options.category ?? classifyFromStatusAndMessage(
803
+ options.status,
804
+ options.message
805
+ );
806
+ this.isRetryable = isRetryableCategory(this.category);
807
+ this.retryDelayMs = this.headers ? parseRetryDelay(this.headers) : void 0;
808
+ }
809
+ static from(error, context) {
810
+ if (error instanceof _LLMError) {
811
+ return error;
812
+ }
813
+ if (error instanceof Error) {
814
+ if (error.name === "AbortError" || error.message.includes("abort")) {
815
+ return new _LLMError({
816
+ message: error.message,
817
+ category: "cancelled",
818
+ cause: error,
819
+ ...context
820
+ });
821
+ }
822
+ return new _LLMError({
823
+ message: error.message,
824
+ cause: error,
825
+ ...extractFromAISDKError(error),
826
+ ...context
827
+ });
828
+ }
829
+ return new _LLMError({
830
+ message: String(error),
831
+ category: "unknown",
832
+ ...context
833
+ });
834
+ }
835
+ get description() {
836
+ const parts = [this.message];
837
+ if (this.provider) parts.unshift(`[${this.provider}]`);
838
+ if (this.status) parts.push(`(HTTP ${this.status})`);
839
+ if (this.isRetryable && this.retryDelayMs) {
840
+ parts.push(`retry in ${Math.ceil(this.retryDelayMs / 1e3)}s`);
841
+ }
842
+ return parts.join(" ");
843
+ }
844
+ };
845
+
846
+ // src/errors/utils.ts
847
+ function isRetryable(error) {
848
+ if (error instanceof LLMError) {
849
+ return error.isRetryable;
850
+ }
851
+ return LLMError.from(error).isRetryable;
852
+ }
853
+ function getRetryDelay(error) {
854
+ if (error instanceof LLMError) {
855
+ return error.isRetryable ? error.retryDelayMs : void 0;
856
+ }
857
+ const llmError = LLMError.from(error);
858
+ return llmError.isRetryable ? llmError.retryDelayMs : void 0;
859
+ }
860
+ function getErrorCategory(error) {
861
+ if (error instanceof LLMError) {
862
+ return error.category;
863
+ }
864
+ return LLMError.from(error).category;
865
+ }
866
+
867
+ // src/retry.ts
868
+ var DEFAULT_RETRY_CONFIG = {
869
+ maxAttempts: 3,
870
+ initialDelayMs: 2e3,
871
+ backoffFactor: 2,
872
+ maxDelayMs: 3e4,
873
+ jitter: true
874
+ };
875
+ function createRetryState() {
876
+ return {
877
+ attempt: 0,
878
+ errors: [],
879
+ canRetry: true,
880
+ nextDelayMs: void 0
881
+ };
882
+ }
883
+ function calculateDelay(attempt, error, config) {
884
+ if (error?.retryDelayMs) {
885
+ return error.retryDelayMs;
886
+ }
887
+ const baseDelay = config.initialDelayMs * Math.pow(config.backoffFactor, attempt - 1);
888
+ const cappedDelay = Math.min(baseDelay, config.maxDelayMs);
889
+ if (config.jitter) {
890
+ const jitterRange = cappedDelay * 0.25;
891
+ const jitter = (Math.random() - 0.5) * 2 * jitterRange;
892
+ return Math.max(0, Math.round(cappedDelay + jitter));
893
+ }
894
+ return Math.round(cappedDelay);
895
+ }
896
+ async function sleep(ms, signal) {
897
+ return new Promise((resolve, reject) => {
898
+ if (signal?.aborted) {
899
+ reject(new DOMException("Aborted", "AbortError"));
900
+ return;
901
+ }
902
+ const timeoutId = setTimeout(() => {
903
+ cleanup();
904
+ resolve();
905
+ }, ms);
906
+ const abortHandler = () => {
907
+ clearTimeout(timeoutId);
908
+ cleanup();
909
+ reject(new DOMException("Aborted", "AbortError"));
910
+ };
911
+ const cleanup = () => {
912
+ signal?.removeEventListener("abort", abortHandler);
913
+ };
914
+ signal?.addEventListener("abort", abortHandler, { once: true });
915
+ });
916
+ }
917
+ async function withRetry(fn, config, signal) {
918
+ const mergedConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
919
+ const state = createRetryState();
920
+ while (true) {
921
+ state.attempt++;
922
+ try {
923
+ return await fn(state.attempt);
924
+ } catch (error) {
925
+ const llmError = LLMError.from(error);
926
+ state.errors.push(llmError);
927
+ const shouldRetry2 = state.attempt < mergedConfig.maxAttempts && isRetryable(llmError) && !signal?.aborted;
928
+ if (!shouldRetry2) {
929
+ throw new LLMError({
930
+ message: `Failed after ${state.attempt} attempt(s): ${llmError.message}`,
931
+ category: llmError.category,
932
+ status: llmError.status,
933
+ headers: llmError.headers,
934
+ provider: llmError.provider,
935
+ model: llmError.model,
936
+ cause: llmError
937
+ });
938
+ }
939
+ const delayMs = calculateDelay(state.attempt, llmError, mergedConfig);
940
+ state.nextDelayMs = delayMs;
941
+ config?.onRetry?.(state.attempt, delayMs, llmError);
942
+ await sleep(delayMs, signal);
943
+ }
944
+ }
945
+ }
946
+ function createRetryHandler(options) {
947
+ const config = options ?? {};
948
+ const signal = options?.signal;
949
+ return async (createStream) => {
950
+ return withRetry(createStream, config, signal);
951
+ };
952
+ }
953
+ function shouldRetry(error, attempt, maxAttempts = DEFAULT_RETRY_CONFIG.maxAttempts) {
954
+ if (attempt >= maxAttempts) return false;
955
+ return isRetryable(error);
956
+ }
957
+
958
+ // src/execution/llm/types.ts
959
+ var OUTPUT_TOKEN_MAX = 32e3;
960
+
961
+ // src/execution/llm/stream.ts
962
+ async function createCustomStream(input) {
963
+ const system = input.system.filter(Boolean).join("\n");
964
+ return input.customStreamProvider({
965
+ system,
966
+ messages: input.messages,
967
+ abortSignal: input.abort,
968
+ maxSteps: input.maxSteps
969
+ });
970
+ }
971
+ function getModelInfo(input) {
972
+ return {
973
+ provider: typeof input.model === "object" && "provider" in input.model ? String(input.model.provider) : void 0,
974
+ model: typeof input.model === "object" && "modelId" in input.model ? String(input.model.modelId) : String(input.model)
975
+ };
976
+ }
977
+ async function callStreamTextWithOtelContext(options) {
978
+ const { input, allTools, system, providerOptions } = options;
979
+ const callStreamText = () => streamText({
980
+ model: input.model,
981
+ system,
982
+ messages: input.messages,
983
+ tools: allTools,
984
+ stopWhen: stepCountIs(input.maxSteps ?? 50),
985
+ maxOutputTokens: input.maxOutputTokens ?? OUTPUT_TOKEN_MAX,
986
+ temperature: input.temperature,
987
+ topP: input.topP,
988
+ abortSignal: input.abort,
989
+ providerOptions,
990
+ experimental_telemetry: input.telemetry,
991
+ prepareStep: input.intervention ? async ({ messages }) => {
992
+ const pending = input.intervention.drainImmediate();
993
+ if (pending.length === 0) {
994
+ return void 0;
995
+ }
996
+ const injected = pending.map((item) => ({
997
+ role: "user",
998
+ content: item.message
999
+ }));
1000
+ for (const item of pending) {
1001
+ input.intervention.onApplied?.(item);
1002
+ }
1003
+ return { messages: [...messages, ...injected] };
1004
+ } : void 0,
1005
+ onStepFinish: async (step) => {
1006
+ if (!input.onStepFinish) {
1007
+ return;
1008
+ }
1009
+ await input.onStepFinish({
1010
+ toolResults: step.toolResults?.map((toolResult) => ({
1011
+ toolName: toolResult.toolName,
1012
+ toolCallId: toolResult.toolCallId,
1013
+ output: toolResult.output
1014
+ })),
1015
+ usage: step.usage,
1016
+ finishReason: step.finishReason
1017
+ });
1018
+ }
1019
+ });
1020
+ const otelCtx = input.middleware?.getOtelContext(input.sessionID);
1021
+ if (!otelCtx) {
1022
+ return callStreamText();
1023
+ }
1024
+ try {
1025
+ const otelApi = await import("@opentelemetry/api");
1026
+ return otelApi.context.with(
1027
+ otelCtx,
1028
+ callStreamText
1029
+ );
1030
+ } catch {
1031
+ return callStreamText();
1032
+ }
1033
+ }
1034
+ async function stream(input) {
1035
+ const messageID = crypto.randomUUID();
1036
+ const system = input.system.filter(Boolean).join("\n");
1037
+ if (input.customStreamProvider) {
1038
+ const runCustomStream = async () => await createCustomStream(input);
1039
+ if (!input.retry || input.retry.maxAttempts === 0) {
1040
+ return await runCustomStream();
1041
+ }
1042
+ return await withRetry(
1043
+ async () => await runCustomStream(),
1044
+ input.retry,
1045
+ input.abort
1046
+ );
1047
+ }
1048
+ const toolSet = await buildToolSet({
1049
+ tools: input.tools,
1050
+ cwd: input.cwd,
1051
+ sessionID: input.sessionID,
1052
+ messageID,
1053
+ abort: input.abort,
1054
+ turnTracker: input.turnTracker,
1055
+ host: input.host,
1056
+ middleware: input.middleware,
1057
+ executionMode: input.toolExecutionMode
1058
+ });
1059
+ const allTools = {
1060
+ ...toolSet,
1061
+ ...input.mcpTools ?? {}
1062
+ };
1063
+ const providerOptions = input.reasoningLevel ? buildReasoningOptionsSync(input.model, input.reasoningLevel) : void 0;
1064
+ const modelInfo = getModelInfo(input);
1065
+ const createStream = async () => {
1066
+ try {
1067
+ return await callStreamTextWithOtelContext({
1068
+ input,
1069
+ allTools,
1070
+ system,
1071
+ providerOptions
1072
+ });
1073
+ } catch (error) {
1074
+ throw LLMError.from(error, modelInfo);
1075
+ }
1076
+ };
1077
+ if (!input.retry || input.retry.maxAttempts === 0) {
1078
+ return await createStream();
1079
+ }
1080
+ return await withRetry(
1081
+ async () => await createStream(),
1082
+ input.retry,
1083
+ input.abort
1084
+ );
1085
+ }
1086
+ async function streamOnce(input) {
1087
+ return await stream({ ...input, retry: void 0 });
1088
+ }
1089
+ async function streamStep(input) {
1090
+ return await stream({
1091
+ ...input,
1092
+ maxSteps: 1
1093
+ });
1094
+ }
1095
+
1096
+ // src/execution/llm/index.ts
1097
+ var LLM = {
1098
+ buildToolSet,
1099
+ stream,
1100
+ streamOnce,
1101
+ streamStep
1102
+ };
1103
+
1104
+ // src/execution/processor/doom-loop.ts
1105
+ var DEFAULT_DOOM_LOOP_THRESHOLD = 3;
1106
+ var DoomLoopError = class extends Error {
1107
+ toolName;
1108
+ repeatCount;
1109
+ input;
1110
+ constructor(toolName, repeatCount, input) {
1111
+ super(
1112
+ `Doom loop detected: "${toolName}" called ${repeatCount} times with identical input`
1113
+ );
1114
+ this.name = "DoomLoopError";
1115
+ this.toolName = toolName;
1116
+ this.repeatCount = repeatCount;
1117
+ this.input = input;
1118
+ }
1119
+ };
1120
+ function hashInput(input) {
1121
+ return JSON.stringify(input);
1122
+ }
1123
+ async function recordToolCallAndCheckDoomLoop(options) {
1124
+ const {
1125
+ recentToolCalls,
1126
+ toolName,
1127
+ input,
1128
+ processorOptions,
1129
+ threshold,
1130
+ emitEvent
1131
+ } = options;
1132
+ recentToolCalls.push({
1133
+ toolName,
1134
+ inputHash: hashInput(input)
1135
+ });
1136
+ if (recentToolCalls.length > threshold) {
1137
+ recentToolCalls.shift();
1138
+ }
1139
+ if (recentToolCalls.length !== threshold) {
1140
+ return;
1141
+ }
1142
+ const first = recentToolCalls[0];
1143
+ const isLoop = recentToolCalls.every(
1144
+ (call) => call.toolName === first.toolName && call.inputHash === first.inputHash
1145
+ );
1146
+ if (!isLoop) {
1147
+ return;
1148
+ }
1149
+ if (processorOptions.rememberedDoomLoopTools?.has(toolName)) {
1150
+ return;
1151
+ }
1152
+ const doomError = new DoomLoopError(toolName, threshold, input);
1153
+ await emitEvent({
1154
+ type: "doom-loop",
1155
+ toolName,
1156
+ repeatCount: threshold
1157
+ });
1158
+ if (processorOptions.onDoomLoop) {
1159
+ const action = await processorOptions.onDoomLoop({
1160
+ tool: toolName,
1161
+ repeatCount: threshold,
1162
+ input,
1163
+ sessionId: processorOptions.sessionID ?? "unknown"
1164
+ });
1165
+ switch (action) {
1166
+ case "allow":
1167
+ return;
1168
+ case "remember":
1169
+ processorOptions.rememberedDoomLoopTools?.add(toolName);
1170
+ return;
1171
+ case "deny":
1172
+ throw doomError;
1173
+ }
1174
+ }
1175
+ if (processorOptions.enforceDoomLoop ?? true) {
1176
+ throw doomError;
1177
+ }
1178
+ options.warn?.(`[Processor] ${doomError.message}`);
1179
+ }
1180
+
1181
+ // src/execution/processor/overflow.ts
1182
+ var ContextOverflowError = class extends Error {
1183
+ inputTokens;
1184
+ limit;
1185
+ constructor(inputTokens, limit) {
1186
+ super(`Context overflow: ${inputTokens} tokens exceeds limit of ${limit}`);
1187
+ this.name = "ContextOverflowError";
1188
+ this.inputTokens = inputTokens;
1189
+ this.limit = limit;
1190
+ }
1191
+ };
1192
+ async function handleContextOverflow(options) {
1193
+ const limit = options.processorOptions.contextTokenLimit;
1194
+ const inputTokens = options.usage?.inputTokens;
1195
+ if (!limit || !inputTokens || inputTokens <= limit) {
1196
+ return;
1197
+ }
1198
+ await options.emitEvent({
1199
+ type: "context-overflow",
1200
+ inputTokens,
1201
+ limit
1202
+ });
1203
+ if (options.processorOptions.onContextOverflow) {
1204
+ await options.processorOptions.onContextOverflow(inputTokens, limit);
1205
+ }
1206
+ }
1207
+
1208
+ // src/execution/processor/process.ts
1209
+ async function processStream(stream2, options) {
1210
+ const { abort, onEvent } = options;
1211
+ const doomLoopThreshold = options.doomLoopThreshold ?? DEFAULT_DOOM_LOOP_THRESHOLD;
1212
+ const maxSteps = options.maxSteps ?? 50;
1213
+ let stepCount = options.currentStep ?? 1;
1214
+ let sawStartStep = false;
1215
+ let text = "";
1216
+ const toolResults = [];
1217
+ const recentToolCalls = [];
1218
+ let usage;
1219
+ let finishReason;
1220
+ let error;
1221
+ let hasToolCalls = false;
1222
+ await onEvent({ type: "step-start", step: stepCount, maxSteps });
1223
+ await onEvent({ type: "status", status: "processing" });
1224
+ try {
1225
+ for await (const rawChunk of stream2.fullStream) {
1226
+ const chunk = rawChunk;
1227
+ if (process.env.DEBUG_PROCESSOR) {
1228
+ process.stderr.write(`[processor] Chunk received: ${chunk.type}
1229
+ `);
1230
+ }
1231
+ abort.throwIfAborted();
1232
+ switch (chunk.type) {
1233
+ case "start-step":
1234
+ if (!sawStartStep) {
1235
+ sawStartStep = true;
1236
+ break;
1237
+ }
1238
+ stepCount++;
1239
+ await onEvent({ type: "step-start", step: stepCount, maxSteps });
1240
+ await onEvent({ type: "status", status: "processing" });
1241
+ break;
1242
+ case "text-start":
1243
+ await onEvent({ type: "text-start" });
1244
+ break;
1245
+ case "text-delta":
1246
+ text += chunk.text;
1247
+ await onEvent({ type: "text-delta", text: chunk.text });
1248
+ break;
1249
+ case "text-end":
1250
+ await onEvent({ type: "text-end" });
1251
+ break;
1252
+ case "reasoning-start":
1253
+ await onEvent({ type: "status", status: "reasoning" });
1254
+ await onEvent({ type: "reasoning-start", id: chunk.id });
1255
+ break;
1256
+ case "reasoning-delta":
1257
+ await onEvent({
1258
+ type: "reasoning-delta",
1259
+ id: chunk.id,
1260
+ text: chunk.text
1261
+ });
1262
+ break;
1263
+ case "reasoning-end":
1264
+ await onEvent({ type: "reasoning-end", id: chunk.id });
1265
+ break;
1266
+ case "tool-call":
1267
+ hasToolCalls = true;
1268
+ await onEvent({ type: "status", status: "calling-tool" });
1269
+ await onEvent({
1270
+ type: "tool-start",
1271
+ toolName: chunk.toolName,
1272
+ toolCallId: chunk.toolCallId,
1273
+ input: chunk.input
1274
+ });
1275
+ await recordToolCallAndCheckDoomLoop({
1276
+ recentToolCalls,
1277
+ toolName: chunk.toolName,
1278
+ input: chunk.input,
1279
+ processorOptions: options,
1280
+ threshold: doomLoopThreshold,
1281
+ emitEvent: onEvent,
1282
+ warn: (message) => process.stderr.write(`${message}
1283
+ `)
1284
+ });
1285
+ break;
1286
+ case "tool-result":
1287
+ toolResults.push({
1288
+ toolName: chunk.toolName,
1289
+ toolCallId: chunk.toolCallId,
1290
+ result: chunk.output
1291
+ });
1292
+ await onEvent({
1293
+ type: "tool-result",
1294
+ toolName: chunk.toolName,
1295
+ toolCallId: chunk.toolCallId,
1296
+ result: chunk.output
1297
+ });
1298
+ break;
1299
+ case "tool-error":
1300
+ await onEvent({
1301
+ type: "tool-error",
1302
+ toolName: chunk.toolName,
1303
+ toolCallId: chunk.toolCallId,
1304
+ error: String(chunk.error)
1305
+ });
1306
+ break;
1307
+ case "computer-call":
1308
+ hasToolCalls = true;
1309
+ await onEvent({ type: "status", status: "calling-tool" });
1310
+ await onEvent({
1311
+ type: "computer-call",
1312
+ callId: chunk.callId,
1313
+ action: chunk.action,
1314
+ pendingSafetyChecks: chunk.pendingSafetyChecks
1315
+ });
1316
+ break;
1317
+ case "finish-step":
1318
+ finishReason = chunk.finishReason;
1319
+ if (chunk.usage) {
1320
+ usage = {
1321
+ inputTokens: chunk.usage.inputTokens,
1322
+ outputTokens: chunk.usage.outputTokens,
1323
+ totalTokens: chunk.usage.totalTokens
1324
+ };
1325
+ await handleContextOverflow({
1326
+ usage,
1327
+ processorOptions: options,
1328
+ emitEvent: onEvent
1329
+ });
1330
+ }
1331
+ await onEvent({
1332
+ type: "step-finish",
1333
+ step: stepCount,
1334
+ usage,
1335
+ finishReason
1336
+ });
1337
+ if (!sawStartStep) {
1338
+ stepCount++;
1339
+ }
1340
+ break;
1341
+ case "finish":
1342
+ if (chunk.totalUsage) {
1343
+ usage = {
1344
+ inputTokens: chunk.totalUsage.inputTokens,
1345
+ outputTokens: chunk.totalUsage.outputTokens,
1346
+ totalTokens: chunk.totalUsage.totalTokens
1347
+ };
1348
+ }
1349
+ break;
1350
+ case "step-usage":
1351
+ usage = {
1352
+ inputTokens: chunk.usage.inputTokens,
1353
+ outputTokens: chunk.usage.outputTokens,
1354
+ totalTokens: chunk.usage.totalTokens
1355
+ };
1356
+ break;
1357
+ case "error":
1358
+ throw chunk.error;
1359
+ }
1360
+ }
1361
+ } catch (caught) {
1362
+ error = caught instanceof Error ? caught : new Error(String(caught));
1363
+ await onEvent({ type: "status", status: "error" });
1364
+ await onEvent({ type: "error", error });
1365
+ }
1366
+ if (!error) {
1367
+ await onEvent({ type: "status", status: "idle" });
1368
+ }
1369
+ let result = "continue";
1370
+ if (error || !hasToolCalls) {
1371
+ result = "stop";
1372
+ }
1373
+ return {
1374
+ result,
1375
+ text,
1376
+ toolResults,
1377
+ usage,
1378
+ finishReason,
1379
+ error
1380
+ };
1381
+ }
1382
+
1383
+ // src/runtime/turn-runner/stream-step.ts
1384
+ async function* runModelStep(options) {
1385
+ const { preparedStep, turnEngine, applyCommitBatch } = options;
1386
+ const stream2 = await LLM.streamStep(preparedStep.llmInput);
1387
+ const eventQueue = [];
1388
+ let resolveNext = null;
1389
+ let streamDone = false;
1390
+ let streamError;
1391
+ const intervention = preparedStep.llmInput.intervention;
1392
+ const middleware = preparedStep.llmInput.middleware;
1393
+ if (intervention) {
1394
+ intervention.onApplied = (item) => {
1395
+ eventQueue.push({
1396
+ type: "intervention-applied",
1397
+ id: item.id,
1398
+ message: item.message
1399
+ });
1400
+ if (resolveNext) {
1401
+ resolveNext();
1402
+ resolveNext = null;
1403
+ }
1404
+ };
1405
+ }
1406
+ const processPromise = processStream(stream2, {
1407
+ sessionID: preparedStep.llmInput.sessionID,
1408
+ abort: preparedStep.llmInput.abort,
1409
+ currentStep: preparedStep.step,
1410
+ maxSteps: preparedStep.processor.maxSteps,
1411
+ doomLoopThreshold: preparedStep.processor.doomLoopThreshold ?? 3,
1412
+ enforceDoomLoop: preparedStep.processor.enforceDoomLoop ?? true,
1413
+ onDoomLoop: preparedStep.processor.onDoomLoop,
1414
+ rememberedDoomLoopTools: options.rememberedDoomLoopTools,
1415
+ contextTokenLimit: preparedStep.processor.contextTokenLimit,
1416
+ onContextOverflow: async () => {
1417
+ },
1418
+ onEvent: async (event) => {
1419
+ middleware?.emitEvent(event);
1420
+ eventQueue.push(event);
1421
+ if (resolveNext) {
1422
+ resolveNext();
1423
+ resolveNext = null;
1424
+ }
1425
+ }
1426
+ }).then((result2) => {
1427
+ streamDone = true;
1428
+ if (resolveNext) {
1429
+ resolveNext();
1430
+ resolveNext = null;
1431
+ }
1432
+ return result2;
1433
+ }).catch((error) => {
1434
+ streamError = error instanceof Error ? error : new Error(String(error));
1435
+ streamDone = true;
1436
+ if (resolveNext) {
1437
+ resolveNext();
1438
+ resolveNext = null;
1439
+ }
1440
+ return null;
1441
+ });
1442
+ while (!streamDone || eventQueue.length > 0) {
1443
+ while (eventQueue.length > 0) {
1444
+ const event = eventQueue.shift();
1445
+ turnEngine.recordEvent(event, (/* @__PURE__ */ new Date()).toISOString());
1446
+ yield event;
1447
+ if (event.type === "intervention-applied") {
1448
+ yield* applyCommitBatch(
1449
+ turnEngine.createInterventionCommit({
1450
+ id: event.id,
1451
+ content: event.message
1452
+ })
1453
+ );
1454
+ }
1455
+ }
1456
+ if (!streamDone) {
1457
+ await new Promise((resolve) => {
1458
+ resolveNext = resolve;
1459
+ });
1460
+ }
1461
+ }
1462
+ if (streamError) {
1463
+ throw streamError;
1464
+ }
1465
+ const result = await processPromise;
1466
+ if (!result) {
1467
+ throw new Error(
1468
+ `Agent step ${preparedStep.step} produced no processor result`
1469
+ );
1470
+ }
1471
+ return result;
1472
+ }
1473
+
1474
+ // src/runtime/turn-runner/tool-batch.ts
1475
+ import { randomUUID as randomUUID3 } from "crypto";
1476
+ function cloneToolResultRecord(options) {
1477
+ return new Map(
1478
+ options.snapshot.toolResults.map((toolResult) => [
1479
+ toolResult.toolCallId,
1480
+ structuredClone(toolResult)
1481
+ ])
1482
+ );
1483
+ }
1484
+ function createToolBatchErrorResult(options) {
1485
+ return {
1486
+ type: "tool-error",
1487
+ toolName: options.toolName,
1488
+ toolCallId: options.toolCallId,
1489
+ error: options.errorMessage
1490
+ };
1491
+ }
1492
+ function createToolBatchResultEvent(options) {
1493
+ return {
1494
+ type: "tool-result",
1495
+ toolName: options.toolName,
1496
+ toolCallId: options.toolCallId,
1497
+ result: options.result
1498
+ };
1499
+ }
1500
+ async function runToolBatch(options) {
1501
+ const toolResultsByCallId = cloneToolResultRecord(options);
1502
+ const events = [];
1503
+ let turnState = options.turnState ? structuredClone(options.turnState) : void 0;
1504
+ for (const toolCall of options.snapshot.toolCalls) {
1505
+ if (toolResultsByCallId.has(toolCall.toolCallId)) {
1506
+ continue;
1507
+ }
1508
+ const tool2 = options.tools[toolCall.toolName];
1509
+ const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1510
+ if (!tool2) {
1511
+ const errorMessage = `Tool '${toolCall.toolName}' is not registered`;
1512
+ const event = createToolBatchErrorResult({
1513
+ toolCallId: toolCall.toolCallId,
1514
+ toolName: toolCall.toolName,
1515
+ errorMessage,
1516
+ replayPolicy: toolCall.replayPolicy
1517
+ });
1518
+ events.push(event);
1519
+ toolResultsByCallId.set(toolCall.toolCallId, {
1520
+ toolCallId: toolCall.toolCallId,
1521
+ toolName: toolCall.toolName,
1522
+ result: `Error: ${errorMessage}`,
1523
+ ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1524
+ });
1525
+ if (turnState) {
1526
+ turnState = advanceAgentTurnState(turnState, event, updatedAt, {
1527
+ ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1528
+ });
1529
+ }
1530
+ continue;
1531
+ }
1532
+ try {
1533
+ const executed = await executeAgentToolCall({
1534
+ toolName: toolCall.toolName,
1535
+ tool: tool2,
1536
+ params: toolCall.args,
1537
+ cwd: options.cwd,
1538
+ abort: options.abort,
1539
+ sessionID: options.sessionId,
1540
+ messageID: randomUUID3(),
1541
+ ...options.host ? { host: options.host } : {},
1542
+ ...options.turnTracker ? { turnTracker: options.turnTracker } : {},
1543
+ ...options.middleware ? { middleware: options.middleware } : {}
1544
+ });
1545
+ const event = createToolBatchResultEvent({
1546
+ toolCallId: toolCall.toolCallId,
1547
+ toolName: toolCall.toolName,
1548
+ result: executed.output
1549
+ });
1550
+ events.push(event);
1551
+ toolResultsByCallId.set(toolCall.toolCallId, {
1552
+ toolCallId: toolCall.toolCallId,
1553
+ toolName: toolCall.toolName,
1554
+ result: executed.output,
1555
+ ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1556
+ });
1557
+ if (turnState) {
1558
+ turnState = advanceAgentTurnState(turnState, event, updatedAt, {
1559
+ ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1560
+ });
1561
+ }
1562
+ } catch (error) {
1563
+ const errorMessage = error instanceof Error ? error.message : String(error);
1564
+ const event = createToolBatchErrorResult({
1565
+ toolCallId: toolCall.toolCallId,
1566
+ toolName: toolCall.toolName,
1567
+ errorMessage,
1568
+ replayPolicy: toolCall.replayPolicy
1569
+ });
1570
+ events.push(event);
1571
+ toolResultsByCallId.set(toolCall.toolCallId, {
1572
+ toolCallId: toolCall.toolCallId,
1573
+ toolName: toolCall.toolName,
1574
+ result: `Error: ${errorMessage}`,
1575
+ ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1576
+ });
1577
+ if (turnState) {
1578
+ turnState = advanceAgentTurnState(turnState, event, updatedAt, {
1579
+ ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1580
+ });
1581
+ }
1582
+ }
1583
+ }
1584
+ return {
1585
+ snapshot: {
1586
+ toolCalls: options.snapshot.toolCalls.map(
1587
+ (toolCall) => structuredClone(toolCall)
1588
+ ),
1589
+ toolResults: options.snapshot.toolCalls.map((toolCall) => toolResultsByCallId.get(toolCall.toolCallId)).filter((toolResult) => toolResult !== void 0)
1590
+ },
1591
+ ...turnState ? { turnState } : {},
1592
+ events
1593
+ };
1594
+ }
1595
+
1596
+ // src/runtime/turn-runner/commit.ts
1597
+ async function* commitStep(options) {
1598
+ if (options.finishReason !== "tool-calls") {
1599
+ return false;
1600
+ }
1601
+ const batch = options.turnEngine.consumeStepCommit(options.step);
1602
+ if (!batch) {
1603
+ return false;
1604
+ }
1605
+ yield* options.applyCommitBatch(batch);
1606
+ return true;
1607
+ }
1608
+ async function* commitOutput(options) {
1609
+ const batch = options.turnEngine.createOutputCommit({
1610
+ text: options.text,
1611
+ usage: options.usage
1612
+ });
1613
+ if (!batch) {
1614
+ return false;
1615
+ }
1616
+ yield* options.applyCommitBatch(batch, { emitMessages: true });
1617
+ return true;
1618
+ }
1619
+
1620
+ // src/runtime/workflow-state.ts
1621
+ function cloneUsage2(usage) {
1622
+ return usage ? { ...usage } : void 0;
1623
+ }
1624
+ function cloneMessageSnapshot(message) {
1625
+ return {
1626
+ ...message,
1627
+ ...message.role === "assistant" && message.toolCalls ? {
1628
+ toolCalls: message.toolCalls.map((toolCall) => ({ ...toolCall }))
1629
+ } : {},
1630
+ ...message.role === "assistant" && message.tokens ? {
1631
+ tokens: { ...message.tokens }
1632
+ } : {},
1633
+ ...message.role === "assistant" && message.error ? {
1634
+ error: { ...message.error }
1635
+ } : {}
1636
+ };
1637
+ }
1638
+ function snapshotAgentWorkflowMessage(message) {
1639
+ switch (message.role) {
1640
+ case "user":
1641
+ return {
1642
+ id: message.id,
1643
+ role: "user",
1644
+ createdAt: message.createdAt.toISOString(),
1645
+ content: message.content,
1646
+ ...message.system ? { system: message.system } : {}
1647
+ };
1648
+ case "assistant":
1649
+ return {
1650
+ id: message.id,
1651
+ role: "assistant",
1652
+ createdAt: message.createdAt.toISOString(),
1653
+ content: message.content,
1654
+ ...message.finish ? { finish: message.finish } : {},
1655
+ ...message.tokens ? { tokens: { ...message.tokens } } : {},
1656
+ ...message.cost !== void 0 ? { cost: message.cost } : {},
1657
+ ...message.error ? { error: { ...message.error } } : {},
1658
+ ...message.toolCalls ? {
1659
+ toolCalls: message.toolCalls.map((toolCall) => ({
1660
+ toolCallId: toolCall.toolCallId,
1661
+ toolName: toolCall.toolName,
1662
+ args: toolCall.args
1663
+ }))
1664
+ } : {}
1665
+ };
1666
+ case "tool":
1667
+ return {
1668
+ id: message.id,
1669
+ role: "tool",
1670
+ createdAt: message.createdAt.toISOString(),
1671
+ content: message.content,
1672
+ toolCallId: message.toolCallId,
1673
+ toolName: message.toolName,
1674
+ result: message.result,
1675
+ ...message.compactedAt !== void 0 ? { compactedAt: message.compactedAt } : {}
1676
+ };
1677
+ case "system":
1678
+ return {
1679
+ id: message.id,
1680
+ role: "system",
1681
+ createdAt: message.createdAt.toISOString(),
1682
+ content: message.content
1683
+ };
1684
+ }
1685
+ }
1686
+ function snapshotAgentWorkflowMessages(messages) {
1687
+ return messages.map(snapshotAgentWorkflowMessage);
1688
+ }
1689
+ function restoreAgentWorkflowMessage(snapshot) {
1690
+ const createdAt = new Date(snapshot.createdAt);
1691
+ switch (snapshot.role) {
1692
+ case "user":
1693
+ return {
1694
+ id: snapshot.id,
1695
+ role: "user",
1696
+ createdAt,
1697
+ content: snapshot.content,
1698
+ ...snapshot.system ? { system: snapshot.system } : {}
1699
+ };
1700
+ case "assistant":
1701
+ return {
1702
+ id: snapshot.id,
1703
+ role: "assistant",
1704
+ createdAt,
1705
+ content: snapshot.content,
1706
+ ...snapshot.finish ? { finish: snapshot.finish } : {},
1707
+ ...snapshot.tokens ? { tokens: { ...snapshot.tokens } } : {},
1708
+ ...snapshot.cost !== void 0 ? { cost: snapshot.cost } : {},
1709
+ ...snapshot.error ? { error: { ...snapshot.error } } : {},
1710
+ ...snapshot.toolCalls ? {
1711
+ toolCalls: snapshot.toolCalls.map((toolCall) => ({
1712
+ toolCallId: toolCall.toolCallId,
1713
+ toolName: toolCall.toolName,
1714
+ args: toolCall.args
1715
+ }))
1716
+ } : {}
1717
+ };
1718
+ case "tool":
1719
+ return {
1720
+ id: snapshot.id,
1721
+ role: "tool",
1722
+ createdAt,
1723
+ content: snapshot.content,
1724
+ toolCallId: snapshot.toolCallId,
1725
+ toolName: snapshot.toolName,
1726
+ result: snapshot.result,
1727
+ ...snapshot.compactedAt !== void 0 ? { compactedAt: snapshot.compactedAt } : {}
1728
+ };
1729
+ case "system":
1730
+ return {
1731
+ id: snapshot.id,
1732
+ role: "system",
1733
+ createdAt,
1734
+ content: snapshot.content
1735
+ };
1736
+ }
1737
+ }
1738
+ function restoreAgentWorkflowMessages(snapshots) {
1739
+ return snapshots.map(restoreAgentWorkflowMessage);
1740
+ }
1741
+ function createAgentWorkflowTurnState(options) {
1742
+ return {
1743
+ sessionId: options.sessionId,
1744
+ systemPrompts: [...options.systemPrompts ?? []],
1745
+ messages: (options.initialMessages ?? []).map(cloneMessageSnapshot),
1746
+ phase: "model-step",
1747
+ step: options.initialStep ?? 1,
1748
+ maxSteps: options.maxSteps,
1749
+ replayDecisions: {},
1750
+ startedAt: options.startedAt,
1751
+ updatedAt: options.startedAt
1752
+ };
1753
+ }
1754
+ function cloneAgentWorkflowTurnState(state) {
1755
+ return {
1756
+ ...state,
1757
+ systemPrompts: [...state.systemPrompts],
1758
+ messages: state.messages.map(cloneMessageSnapshot),
1759
+ ...state.usage ? { usage: cloneUsage2(state.usage) } : {},
1760
+ ...state.turnState ? { turnState: structuredClone(state.turnState) } : {},
1761
+ ...state.lastModelStep ? { lastModelStep: structuredClone(state.lastModelStep) } : {},
1762
+ replayDecisions: Object.fromEntries(
1763
+ Object.entries(state.replayDecisions).map(([toolCallId, decision]) => [
1764
+ toolCallId,
1765
+ { ...decision }
1766
+ ])
1767
+ )
1768
+ };
1769
+ }
1770
+
1771
+ // src/runtime/workflow-planner/helpers.ts
1772
+ function accumulateWorkflowUsage(current, next) {
1773
+ if (!next) {
1774
+ return current ? { ...current } : void 0;
1775
+ }
1776
+ if (!current) {
1777
+ return { ...next };
1778
+ }
1779
+ return {
1780
+ inputTokens: (current.inputTokens ?? 0) + (next.inputTokens ?? 0),
1781
+ outputTokens: (current.outputTokens ?? 0) + (next.outputTokens ?? 0),
1782
+ totalTokens: (current.totalTokens ?? 0) + (next.totalTokens ?? 0),
1783
+ cacheReadTokens: (current.cacheReadTokens ?? 0) + (next.cacheReadTokens ?? 0),
1784
+ cacheWriteTokens: (current.cacheWriteTokens ?? 0) + (next.cacheWriteTokens ?? 0)
1785
+ };
1786
+ }
1787
+ function cloneRelevantReplayDecisions(state, snapshot) {
1788
+ return Object.fromEntries(
1789
+ snapshot.toolCalls.flatMap((toolCall) => {
1790
+ const decision = state.replayDecisions[toolCall.toolCallId];
1791
+ return decision ? [[toolCall.toolCallId, { ...decision }]] : [];
1792
+ })
1793
+ );
1794
+ }
1795
+ function findNextUnresolvedToolCall(snapshot) {
1796
+ const resolvedIds = new Set(
1797
+ snapshot.toolResults.map((toolResult) => toolResult.toolCallId)
1798
+ );
1799
+ return snapshot.toolCalls.find(
1800
+ (toolCall) => !resolvedIds.has(toolCall.toolCallId)
1801
+ );
1802
+ }
1803
+
1804
+ // src/runtime/workflow-planner/plan.ts
1805
+ function planNextAgentWorkflowOperation(state) {
1806
+ switch (state.phase) {
1807
+ case "model-step":
1808
+ return {
1809
+ kind: "model-step",
1810
+ sessionId: state.sessionId,
1811
+ step: state.step,
1812
+ maxSteps: state.maxSteps,
1813
+ systemPrompts: [...state.systemPrompts],
1814
+ messages: state.messages.map((message) => structuredClone(message))
1815
+ };
1816
+ case "tool-batch": {
1817
+ if (!state.lastModelStep?.stepCommit) {
1818
+ throw new Error(
1819
+ `Workflow state for session '${state.sessionId}' is in tool-batch phase without a stepCommit snapshot`
1820
+ );
1821
+ }
1822
+ const nextToolCall = findNextUnresolvedToolCall(
1823
+ state.lastModelStep.stepCommit
1824
+ );
1825
+ if (!nextToolCall) {
1826
+ throw new Error(
1827
+ `Workflow state for session '${state.sessionId}' is in tool-batch phase without unresolved tool calls`
1828
+ );
1829
+ }
1830
+ return {
1831
+ kind: "tool-call",
1832
+ step: state.step,
1833
+ sessionId: state.sessionId,
1834
+ toolCall: structuredClone(nextToolCall),
1835
+ turnState: state.turnState ? structuredClone(state.turnState) : void 0,
1836
+ replayDecision: cloneRelevantReplayDecisions(
1837
+ state,
1838
+ state.lastModelStep.stepCommit
1839
+ )[nextToolCall.toolCallId]
1840
+ };
1841
+ }
1842
+ case "step-commit":
1843
+ if (!state.lastModelStep?.stepCommit) {
1844
+ throw new Error(
1845
+ `Workflow state for session '${state.sessionId}' is in step-commit phase without a stepCommit snapshot`
1846
+ );
1847
+ }
1848
+ return {
1849
+ kind: "step-commit",
1850
+ sessionId: state.sessionId,
1851
+ step: state.step,
1852
+ snapshot: structuredClone(state.lastModelStep.stepCommit)
1853
+ };
1854
+ case "output-commit":
1855
+ if (!state.lastModelStep) {
1856
+ throw new Error(
1857
+ `Workflow state for session '${state.sessionId}' is in output-commit phase without a last model step`
1858
+ );
1859
+ }
1860
+ return {
1861
+ kind: "output-commit",
1862
+ sessionId: state.sessionId,
1863
+ text: state.lastModelStep.text,
1864
+ ...state.lastModelStep.usage ? { usage: { ...state.lastModelStep.usage } } : {}
1865
+ };
1866
+ case "completed":
1867
+ case "failed":
1868
+ return void 0;
1869
+ }
1870
+ }
1871
+
1872
+ // src/runtime/workflow-planner/apply.ts
1873
+ function applyAgentWorkflowModelStepResult(state, result, updatedAt) {
1874
+ const next = cloneAgentWorkflowTurnState(state);
1875
+ next.lastModelStep = structuredClone(result);
1876
+ next.turnState = structuredClone(result.turnState);
1877
+ next.usage = accumulateWorkflowUsage(next.usage, result.usage);
1878
+ next.updatedAt = updatedAt;
1879
+ next.phase = result.finishReason === "tool-calls" ? "tool-batch" : "output-commit";
1880
+ return next;
1881
+ }
1882
+ function applyAgentWorkflowToolBatchResult(state, result, updatedAt) {
1883
+ if (state.phase !== "tool-batch") {
1884
+ throw new Error(
1885
+ `Cannot apply a tool-batch result while workflow state is in '${state.phase}' phase`
1886
+ );
1887
+ }
1888
+ const next = cloneAgentWorkflowTurnState(state);
1889
+ next.updatedAt = updatedAt;
1890
+ next.phase = "step-commit";
1891
+ if (!next.lastModelStep) {
1892
+ throw new Error(
1893
+ `Workflow state for session '${state.sessionId}' has no model step to update after tool-batch`
1894
+ );
1895
+ }
1896
+ next.lastModelStep.stepCommit = structuredClone(result.stepCommit);
1897
+ if (result.turnState) {
1898
+ next.turnState = structuredClone(result.turnState);
1899
+ }
1900
+ return next;
1901
+ }
1902
+ function applyAgentWorkflowToolCallResult(state, result, updatedAt) {
1903
+ if (state.phase !== "tool-batch") {
1904
+ throw new Error(
1905
+ `Cannot apply a tool-call result while workflow state is in '${state.phase}' phase`
1906
+ );
1907
+ }
1908
+ if (!state.lastModelStep?.stepCommit) {
1909
+ throw new Error(
1910
+ `Workflow state for session '${state.sessionId}' has no stepCommit snapshot in tool-batch phase`
1911
+ );
1912
+ }
1913
+ const next = cloneAgentWorkflowTurnState(state);
1914
+ const snapshot = structuredClone(state.lastModelStep.stepCommit);
1915
+ const existingResultIndex = snapshot.toolResults.findIndex(
1916
+ (toolResult) => toolResult.toolCallId === result.toolResult.toolCallId
1917
+ );
1918
+ if (existingResultIndex >= 0) {
1919
+ snapshot.toolResults[existingResultIndex] = structuredClone(
1920
+ result.toolResult
1921
+ );
1922
+ } else {
1923
+ snapshot.toolResults.push(structuredClone(result.toolResult));
1924
+ }
1925
+ next.lastModelStep = {
1926
+ ...next.lastModelStep,
1927
+ stepCommit: snapshot
1928
+ };
1929
+ next.updatedAt = updatedAt;
1930
+ if (result.turnState) {
1931
+ next.turnState = structuredClone(result.turnState);
1932
+ }
1933
+ next.phase = snapshot.toolResults.length >= snapshot.toolCalls.length ? "step-commit" : "tool-batch";
1934
+ return next;
1935
+ }
1936
+ function applyAgentWorkflowCommitResult(state, result, updatedAt) {
1937
+ const next = cloneAgentWorkflowTurnState(state);
1938
+ next.messages = [
1939
+ ...next.messages,
1940
+ ...result.messages.map((message) => structuredClone(message))
1941
+ ];
1942
+ next.updatedAt = updatedAt;
1943
+ switch (state.phase) {
1944
+ case "step-commit":
1945
+ next.step = state.step + 1;
1946
+ next.phase = "model-step";
1947
+ delete next.lastModelStep;
1948
+ return next;
1949
+ case "output-commit":
1950
+ next.phase = "completed";
1951
+ next.finalResponse = state.lastModelStep?.text ?? state.finalResponse;
1952
+ delete next.lastModelStep;
1953
+ return next;
1954
+ default:
1955
+ throw new Error(
1956
+ `Cannot apply a commit result while workflow state is in '${state.phase}' phase`
1957
+ );
1958
+ }
1959
+ }
1960
+ function recordAgentWorkflowReplayDecision(state, decision, updatedAt) {
1961
+ const next = cloneAgentWorkflowTurnState(state);
1962
+ next.replayDecisions[decision.toolCallId] = { ...decision };
1963
+ next.updatedAt = updatedAt;
1964
+ return next;
1965
+ }
1966
+ function failAgentWorkflowTurnState(state, error, updatedAt) {
1967
+ const next = cloneAgentWorkflowTurnState(state);
1968
+ next.phase = "failed";
1969
+ next.error = error.message;
1970
+ next.updatedAt = updatedAt;
1971
+ return next;
1972
+ }
1973
+
1974
+ export {
1975
+ convertAgentMessagesToModelMessages,
1976
+ createAgentTurnStepCommitBatch,
1977
+ createAgentTurnState,
1978
+ advanceAgentTurnState,
1979
+ failAgentTurnState,
1980
+ AgentTurnEngine,
1981
+ createAgentTurnEngine,
1982
+ prepareModelStep,
1983
+ isRetryableCategory,
1984
+ LLMError,
1985
+ isRetryable,
1986
+ getRetryDelay,
1987
+ getErrorCategory,
1988
+ DEFAULT_RETRY_CONFIG,
1989
+ createRetryState,
1990
+ calculateDelay,
1991
+ sleep,
1992
+ withRetry,
1993
+ createRetryHandler,
1994
+ shouldRetry,
1995
+ OUTPUT_TOKEN_MAX,
1996
+ LLM,
1997
+ DoomLoopError,
1998
+ ContextOverflowError,
1999
+ processStream,
2000
+ runModelStep,
2001
+ runToolBatch,
2002
+ commitStep,
2003
+ commitOutput,
2004
+ defaultAgentTaskCheckpointStrategy,
2005
+ createAgentTaskRunner,
2006
+ snapshotAgentWorkflowMessage,
2007
+ snapshotAgentWorkflowMessages,
2008
+ restoreAgentWorkflowMessage,
2009
+ restoreAgentWorkflowMessages,
2010
+ createAgentWorkflowTurnState,
2011
+ cloneAgentWorkflowTurnState,
2012
+ planNextAgentWorkflowOperation,
2013
+ applyAgentWorkflowModelStepResult,
2014
+ applyAgentWorkflowToolBatchResult,
2015
+ applyAgentWorkflowToolCallResult,
2016
+ applyAgentWorkflowCommitResult,
2017
+ recordAgentWorkflowReplayDecision,
2018
+ failAgentWorkflowTurnState
2019
+ };