@cuylabs/agent-core 0.9.0 → 0.10.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 (116) hide show
  1. package/README.md +33 -17
  2. package/dist/chunk-2O4MCSQS.js +780 -0
  3. package/dist/chunk-2TTOLHBT.js +198 -0
  4. package/dist/chunk-5FMSGQVX.js +281 -0
  5. package/dist/chunk-5NVVNXPQ.js +288 -0
  6. package/dist/{chunk-EKR6PKXU.js → chunk-6HZBHFOL.js} +3 -3
  7. package/dist/chunk-CJI7PVS2.js +58 -0
  8. package/dist/{chunk-WKHDSSXG.js → chunk-CMYN2RCB.js} +146 -46
  9. package/dist/chunk-FII65CN7.js +117 -0
  10. package/dist/{chunk-UHCJEM2E.js → chunk-ICZ66572.js} +13 -6
  11. package/dist/chunk-KYLPMBHD.js +316 -0
  12. package/dist/chunk-MXAP4UG6.js +2956 -0
  13. package/dist/{chunk-4QFNWPIF.js → chunk-N3VX7FEE.js} +35 -2
  14. package/dist/{chunk-MAZ5DY5B.js → chunk-NDZWXCBZ.js} +213 -78
  15. package/dist/{chunk-MHKK374K.js → chunk-Q742PSH3.js} +11 -27
  16. package/dist/{chunk-WGZAPU6N.js → chunk-QAL3OMI3.js} +15 -1
  17. package/dist/{chunk-UDCZ673N.js → chunk-RN6WZEUF.js} +27 -23
  18. package/dist/{chunk-ZXAKHMWH.js → chunk-ROTGCYDW.js} +22 -84
  19. package/dist/chunk-SPBFQXOT.js +0 -0
  20. package/dist/chunk-SSFBF3US.js +602 -0
  21. package/dist/chunk-SZ2XBPTW.js +8 -0
  22. package/dist/chunk-T4UIX5D7.js +115 -0
  23. package/dist/{chunk-IYWQOJMQ.js → chunk-TIHPYVAJ.js} +34 -34
  24. package/dist/{chunk-RKEW5WXI.js → chunk-TOTDGK3P.js} +1 -1
  25. package/dist/chunk-V4RFNEET.js +563 -0
  26. package/dist/chunk-VOUEJSW6.js +0 -0
  27. package/dist/{chunk-J4QDGZIA.js → chunk-WBPOZ7CL.js} +659 -275
  28. package/dist/chunk-X4VN4GIJ.js +185 -0
  29. package/dist/dispatch/index.d.ts +93 -0
  30. package/dist/dispatch/index.js +37 -0
  31. package/dist/events/index.d.ts +93 -0
  32. package/dist/events/index.js +6 -0
  33. package/dist/{runtime → execution}/index.d.ts +120 -35
  34. package/dist/{runtime → execution}/index.js +17 -11
  35. package/dist/index.d.ts +489 -115
  36. package/dist/index.js +1665 -462
  37. package/dist/inference/errors/index.js +1 -1
  38. package/dist/inference/index.d.ts +13 -21
  39. package/dist/inference/index.js +15 -12
  40. package/dist/instance-BqV2D5pc.d.ts +5723 -0
  41. package/dist/logger/index.d.ts +50 -0
  42. package/dist/logger/index.js +11 -0
  43. package/dist/mcp/index.d.ts +5 -9
  44. package/dist/mcp/index.js +2 -3
  45. package/dist/middleware/index.d.ts +10 -150
  46. package/dist/middleware/index.js +10 -2
  47. package/dist/model-messages-B4nK9D1-.d.ts +13 -0
  48. package/dist/models/index.d.ts +5 -2
  49. package/dist/models/index.js +2 -1
  50. package/dist/models/reasoning/index.js +2 -1
  51. package/dist/plugin/index.d.ts +55 -11
  52. package/dist/plugin/index.js +1 -1
  53. package/dist/profiles/index.d.ts +55 -0
  54. package/dist/{presets → profiles}/index.js +10 -10
  55. package/dist/prompt/index.d.ts +8 -13
  56. package/dist/safety/index.d.ts +109 -14
  57. package/dist/safety/index.js +59 -3
  58. package/dist/sandbox/index.d.ts +81 -0
  59. package/dist/sandbox/index.js +1 -0
  60. package/dist/skill/index.d.ts +10 -8
  61. package/dist/skill/index.js +2 -2
  62. package/dist/storage/index.d.ts +12 -4
  63. package/dist/storage/index.js +1 -1
  64. package/dist/subagents/index.d.ts +177 -0
  65. package/dist/subagents/index.js +78 -0
  66. package/dist/team/index.d.ts +544 -0
  67. package/dist/team/index.js +41 -0
  68. package/dist/tool/host/index.d.ts +41 -0
  69. package/dist/tool/host/index.js +10 -0
  70. package/dist/tool/index.d.ts +111 -21
  71. package/dist/tool/index.js +20 -13
  72. package/dist/{types-VQgymC1N.d.ts → types-Bj_J8u_W.d.ts} +44 -64
  73. package/dist/{types-CHiPh8U2.d.ts → types-C_LCeYNg.d.ts} +7 -7
  74. package/dist/types-RSCv7nQ4.d.ts +59 -0
  75. package/package.json +46 -47
  76. package/dist/builder-BgZ_j4Vs.d.ts +0 -35
  77. package/dist/chunk-5ARZJWD2.js +0 -259
  78. package/dist/chunk-DXFBQMXP.js +0 -53
  79. package/dist/chunk-H3FUYU52.js +0 -81
  80. package/dist/chunk-JLXG2SH7.js +0 -905
  81. package/dist/chunk-N7P4PN3O.js +0 -84
  82. package/dist/chunk-OFDKHNCX.js +0 -727
  83. package/dist/chunk-SDSBEQXG.js +0 -157
  84. package/dist/chunk-VEKUXUVF.js +0 -41
  85. package/dist/chunk-VNQBHPCT.js +0 -398
  86. package/dist/chunk-WWYYNWEW.js +0 -259
  87. package/dist/context/index.d.ts +0 -259
  88. package/dist/context/index.js +0 -26
  89. package/dist/events-CE72w8W4.d.ts +0 -149
  90. package/dist/host/index.d.ts +0 -45
  91. package/dist/host/index.js +0 -8
  92. package/dist/index-DQuTZ8xL.d.ts +0 -1335
  93. package/dist/messages-BYWGn8TY.d.ts +0 -110
  94. package/dist/presets/index.d.ts +0 -53
  95. package/dist/registry-DwYqsQkX.d.ts +0 -164
  96. package/dist/runner-CI-XeR16.d.ts +0 -91
  97. package/dist/scope/index.d.ts +0 -10
  98. package/dist/scope/index.js +0 -14
  99. package/dist/session-manager-KbYt2WUh.d.ts +0 -282
  100. package/dist/signal/index.d.ts +0 -28
  101. package/dist/signal/index.js +0 -6
  102. package/dist/sub-agent/index.d.ts +0 -24
  103. package/dist/sub-agent/index.js +0 -32
  104. package/dist/tool-CZWN3KbO.d.ts +0 -141
  105. package/dist/tool-DkhSCV2Y.d.ts +0 -145
  106. package/dist/tracker-DClqYqTj.d.ts +0 -96
  107. package/dist/tracking/index.d.ts +0 -111
  108. package/dist/tracking/index.js +0 -20
  109. package/dist/types-BfNpU8NS.d.ts +0 -270
  110. package/dist/types-BlOKk-Bb.d.ts +0 -330
  111. package/dist/types-BlZwmnuW.d.ts +0 -50
  112. package/dist/types-CQL-SvTn.d.ts +0 -29
  113. package/dist/types-CWm-7rvB.d.ts +0 -55
  114. package/dist/types-DTSkxakL.d.ts +0 -651
  115. package/dist/types-DmDwi2zI.d.ts +0 -339
  116. package/dist/types-YuWV4ag7.d.ts +0 -72
@@ -1,27 +1,31 @@
1
1
  import {
2
- DEFAULT_CONTEXT_LIMITS
3
- } from "./chunk-WWYYNWEW.js";
4
- import {
5
- Inference
6
- } from "./chunk-WKHDSSXG.js";
7
- import {
8
- executeAgentToolCall
9
- } from "./chunk-H3FUYU52.js";
10
- import {
11
- LLMError
12
- } from "./chunk-4QFNWPIF.js";
2
+ Inference,
3
+ buildModelCallContext
4
+ } from "./chunk-CMYN2RCB.js";
13
5
  import {
6
+ PRUNE_PROTECTED_TOOLS,
7
+ accumulateUsage,
14
8
  currentScope,
9
+ executeAgentToolCall,
15
10
  snapshotScope,
16
11
  streamWithinScope,
17
12
  withinScope
18
- } from "./chunk-N7P4PN3O.js";
13
+ } from "./chunk-5NVVNXPQ.js";
14
+ import {
15
+ LLMError
16
+ } from "./chunk-N3VX7FEE.js";
19
17
  import {
20
18
  extractModelId,
21
19
  extractProvider
22
20
  } from "./chunk-I6PKJ7XQ.js";
21
+ import {
22
+ resolveCapability
23
+ } from "./chunk-FII65CN7.js";
24
+ import {
25
+ silentLogger
26
+ } from "./chunk-T4UIX5D7.js";
23
27
 
24
- // src/runtime/task/observer.ts
28
+ // src/execution/task/observer.ts
25
29
  function defaultAgentTaskCheckpointStrategy(input) {
26
30
  switch (input.event.type) {
27
31
  case "step-finish":
@@ -39,18 +43,20 @@ function defaultAgentTaskCheckpointStrategy(input) {
39
43
  }
40
44
  }
41
45
 
42
- // src/runtime/task/runner.ts
46
+ // src/execution/task/runner.ts
43
47
  import { randomUUID } from "crypto";
44
48
 
45
- // src/runtime/turn-state.ts
49
+ // src/execution/clone-usage.ts
50
+ function cloneUsage(usage) {
51
+ return { ...usage };
52
+ }
53
+
54
+ // src/execution/turn-state.ts
46
55
  var EMPTY_USAGE = {
47
56
  inputTokens: 0,
48
57
  outputTokens: 0,
49
58
  totalTokens: 0
50
59
  };
51
- function cloneUsage(usage) {
52
- return { ...usage };
53
- }
54
60
  function normalizeUsage(usage) {
55
61
  if (!usage) {
56
62
  return cloneUsage(EMPTY_USAGE);
@@ -65,13 +71,14 @@ function removeActiveToolCall(toolCalls, toolCallId) {
65
71
  return toolCalls.filter((toolCall) => toolCall.toolCallId !== toolCallId);
66
72
  }
67
73
  function createAgentTurnState(options) {
74
+ const restore = options.restoreFrom;
68
75
  return {
69
76
  sessionId: options.sessionId,
70
77
  phase: "initializing",
71
- step: 0,
72
- response: "",
73
- usage: cloneUsage(EMPTY_USAGE),
74
- eventCount: 0,
78
+ step: restore?.step ?? 0,
79
+ response: restore?.response ?? "",
80
+ usage: restore?.usage ? cloneUsage(restore.usage) : cloneUsage(EMPTY_USAGE),
81
+ eventCount: restore?.eventCount ?? 0,
75
82
  activeToolCalls: [],
76
83
  resolvedToolCalls: [],
77
84
  startedAt: options.startedAt,
@@ -84,7 +91,8 @@ function advanceAgentTurnState(state, event, updatedAt, options = {}) {
84
91
  usage: cloneUsage(state.usage),
85
92
  activeToolCalls: state.activeToolCalls.map((toolCall) => ({ ...toolCall })),
86
93
  resolvedToolCalls: state.resolvedToolCalls.map((toolCall) => ({
87
- ...toolCall
94
+ ...toolCall,
95
+ ...toolCall.metadata ? { metadata: structuredClone(toolCall.metadata) } : {}
88
96
  })),
89
97
  eventCount: state.eventCount + 1,
90
98
  lastEvent: event,
@@ -129,6 +137,7 @@ function advanceAgentTurnState(state, event, updatedAt, options = {}) {
129
137
  toolName: event.toolName,
130
138
  outcome: "result",
131
139
  value: event.result,
140
+ ...event.metadata ? { metadata: structuredClone(event.metadata) } : {},
132
141
  resolvedAt: updatedAt,
133
142
  ...options.toolReplayPolicy ? { replayPolicy: options.toolReplayPolicy } : {}
134
143
  }
@@ -202,7 +211,8 @@ function failAgentTurnState(state, error, updatedAt) {
202
211
  usage: cloneUsage(state.usage),
203
212
  activeToolCalls: state.activeToolCalls.map((toolCall) => ({ ...toolCall })),
204
213
  resolvedToolCalls: state.resolvedToolCalls.map((toolCall) => ({
205
- ...toolCall
214
+ ...toolCall,
215
+ ...toolCall.metadata ? { metadata: structuredClone(toolCall.metadata) } : {}
206
216
  })),
207
217
  phase: "failed",
208
218
  error: error.message,
@@ -210,7 +220,7 @@ function failAgentTurnState(state, error, updatedAt) {
210
220
  };
211
221
  }
212
222
 
213
- // src/runtime/task/runner.ts
223
+ // src/execution/task/runner.ts
214
224
  function normalizeNonEmpty(value, label) {
215
225
  const normalized = value.trim();
216
226
  if (!normalized) {
@@ -234,15 +244,14 @@ function buildRuntimeSessionId(prefix, key) {
234
244
  function nowIso() {
235
245
  return (/* @__PURE__ */ new Date()).toISOString();
236
246
  }
237
- async function notifyObservers(observers, invoke) {
247
+ async function notifyObservers(observers, invoke, logger) {
238
248
  for (const observer of observers) {
239
249
  try {
240
250
  await invoke(observer);
241
251
  } catch (error) {
242
- console.warn(
243
- "[agent-task-runner] observer error:",
244
- error instanceof Error ? error.message : String(error)
245
- );
252
+ logger.warn("observer error", {
253
+ error: error instanceof Error ? error.message : String(error)
254
+ });
246
255
  }
247
256
  }
248
257
  }
@@ -250,11 +259,13 @@ function createAgentTaskRunner(agent, options = {}) {
250
259
  const prefix = options.sessionPrefix ?? "runtime";
251
260
  const baseObservers = options.observers ?? [];
252
261
  const checkpointStrategy = options.checkpointStrategy ?? defaultAgentTaskCheckpointStrategy;
262
+ const log = options.logger?.child("task-runner") ?? silentLogger;
253
263
  return async (payload, context = {}) => {
254
264
  const message = normalizeNonEmpty(payload.message, "payload.message");
255
265
  const resolvedSessionId = payload.sessionId?.trim() || options.resolveSessionId?.(payload, context)?.trim() || buildRuntimeSessionId(prefix, context.fallbackSessionKey);
256
266
  const sessionId = normalizeNonEmpty(resolvedSessionId, "sessionId");
257
267
  const startedAt = nowIso();
268
+ const executionId = context.executionId?.trim() || `${sessionId}:${startedAt}`;
258
269
  return withinScope(
259
270
  {
260
271
  kind: "task",
@@ -270,13 +281,20 @@ function createAgentTaskRunner(agent, options = {}) {
270
281
  payload,
271
282
  context,
272
283
  sessionId,
273
- startedAt,
284
+ executionId,
285
+ startedAt: context.restoreFrom?.startedAt ?? startedAt,
274
286
  scope: snapshotScope()
275
287
  };
276
- const toolCalls = [];
288
+ const toolCalls = context.restoreFrom?.toolCalls.map((tc) => ({ ...tc })) ?? [];
277
289
  let turnState = createAgentTurnState({
278
290
  sessionId,
279
- startedAt
291
+ startedAt: context.restoreFrom?.startedAt ?? startedAt,
292
+ restoreFrom: context.restoreFrom ? {
293
+ response: context.restoreFrom.response,
294
+ usage: { ...context.restoreFrom.usage },
295
+ step: context.restoreFrom.step,
296
+ eventCount: context.restoreFrom.eventCount
297
+ } : void 0
280
298
  });
281
299
  const createSnapshot = () => ({
282
300
  sessionId,
@@ -305,13 +323,13 @@ function createAgentTaskRunner(agent, options = {}) {
305
323
  };
306
324
  await notifyObservers(baseObservers, async (observer) => {
307
325
  await observer.onCheckpoint?.(checkpoint);
308
- });
326
+ }, log);
309
327
  };
310
328
  await notifyObservers(baseObservers, async (observer) => {
311
329
  await observer.onTaskStart?.(run, createSnapshot());
312
- });
330
+ }, log);
313
331
  await emitCheckpoint("task-start");
314
- const activateCtx = baseObservers.find((o) => o.activateContext)?.activateContext?.bind(void 0, sessionId);
332
+ const activateCtx = baseObservers.find((o) => o.activateContext)?.activateContext?.bind(void 0, sessionId, executionId);
315
333
  try {
316
334
  const processChatStream = async () => {
317
335
  for await (const event of agent.chat(sessionId, message, {
@@ -325,7 +343,7 @@ function createAgentTaskRunner(agent, options = {}) {
325
343
  const snapshot = createSnapshot();
326
344
  await notifyObservers(baseObservers, async (observer) => {
327
345
  await observer.onTaskEvent?.(run, event, snapshot);
328
- });
346
+ }, log);
329
347
  const checkpointReason = checkpointStrategy({
330
348
  run,
331
349
  event,
@@ -352,14 +370,14 @@ function createAgentTaskRunner(agent, options = {}) {
352
370
  };
353
371
  await notifyObservers(baseObservers, async (observer) => {
354
372
  await observer.onTaskComplete?.(run, result, createSnapshot());
355
- });
373
+ }, log);
356
374
  return result;
357
375
  } catch (error) {
358
376
  const normalizedError = error instanceof Error ? error : new Error(String(error));
359
377
  turnState = failAgentTurnState(turnState, normalizedError, nowIso());
360
378
  await notifyObservers(baseObservers, async (observer) => {
361
379
  await observer.onTaskError?.(run, normalizedError, createSnapshot());
362
- });
380
+ }, log);
363
381
  await emitCheckpoint("task-error");
364
382
  throw normalizedError;
365
383
  }
@@ -368,50 +386,7 @@ function createAgentTaskRunner(agent, options = {}) {
368
386
  };
369
387
  }
370
388
 
371
- // src/runtime/model-messages.ts
372
- function convertAgentMessagesToModelMessages(messages) {
373
- return messages.flatMap((message) => {
374
- switch (message.role) {
375
- case "user":
376
- return [{ role: "user", content: message.content }];
377
- case "assistant": {
378
- if (message.toolCalls && message.toolCalls.length > 0) {
379
- const toolCallParts = message.toolCalls.map(
380
- (toolCall) => ({
381
- type: "tool-call",
382
- toolCallId: toolCall.toolCallId,
383
- toolName: toolCall.toolName,
384
- input: toolCall.args
385
- })
386
- );
387
- return [{ role: "assistant", content: toolCallParts }];
388
- }
389
- return [{ role: "assistant", content: message.content }];
390
- }
391
- case "tool":
392
- return [
393
- {
394
- role: "tool",
395
- content: [
396
- {
397
- type: "tool-result",
398
- toolCallId: message.toolCallId ?? "",
399
- toolName: message.toolName ?? "",
400
- output: {
401
- type: "text",
402
- value: typeof message.result === "string" ? message.result : JSON.stringify(message.result)
403
- }
404
- }
405
- ]
406
- }
407
- ];
408
- case "system":
409
- return [{ role: "system", content: message.content }];
410
- }
411
- });
412
- }
413
-
414
- // src/runtime/turn-engine/commit-batch.ts
389
+ // src/execution/turn-engine/commit-batch.ts
415
390
  import { randomUUID as randomUUID2 } from "crypto";
416
391
  function createAgentTurnBoundaryEvent(boundary, metadata = {}) {
417
392
  return {
@@ -440,13 +415,14 @@ function createAgentTurnStepCommitBatch(step, snapshot, options = {}) {
440
415
  createdAt
441
416
  };
442
417
  const toolMessages = snapshot.toolResults.map(
443
- ({ toolCallId, toolName, result }) => ({
418
+ ({ toolCallId, toolName, result, metadata }) => ({
444
419
  id: options.toolMessageIds?.[toolCallId] ?? randomUUID2(),
445
420
  role: "tool",
446
421
  content: typeof result === "string" ? result : JSON.stringify(result),
447
422
  toolCallId,
448
423
  toolName,
449
424
  result,
425
+ ...metadata ? { metadata } : {},
450
426
  createdAt
451
427
  })
452
428
  );
@@ -518,7 +494,7 @@ function createAgentTurnOutputCommit(options) {
518
494
  };
519
495
  }
520
496
 
521
- // src/runtime/turn-engine/engine.ts
497
+ // src/execution/turn-engine/engine.ts
522
498
  var AgentTurnEngine = class {
523
499
  turnState;
524
500
  pendingToolCalls = /* @__PURE__ */ new Map();
@@ -559,6 +535,7 @@ var AgentTurnEngine = class {
559
535
  toolCallId,
560
536
  toolName,
561
537
  result: resultData.result,
538
+ ...resultData.metadata ? { metadata: resultData.metadata } : {},
562
539
  ...replayPolicy ? { replayPolicy } : {}
563
540
  };
564
541
  }).filter((result) => result !== void 0)
@@ -581,6 +558,7 @@ var AgentTurnEngine = class {
581
558
  this.pendingToolResults.set(event.toolCallId, {
582
559
  toolName: event.toolName,
583
560
  result: event.result,
561
+ ...event.metadata ? { metadata: event.metadata } : {},
584
562
  replayPolicy: toolReplayPolicy
585
563
  });
586
564
  break;
@@ -623,7 +601,251 @@ function createAgentTurnEngine(options) {
623
601
  return new AgentTurnEngine(options);
624
602
  }
625
603
 
626
- // src/runtime/turn-runner/prepare.ts
604
+ // src/agent/context/estimation.ts
605
+ function estimateTokens(text) {
606
+ return Math.ceil(text.length / 4);
607
+ }
608
+ function estimateMessageTokens(message) {
609
+ if (typeof message.content === "string") {
610
+ return estimateTokens(message.content);
611
+ }
612
+ if (Array.isArray(message.content)) {
613
+ let total = 0;
614
+ for (const part of message.content) {
615
+ if (typeof part === "string") {
616
+ total += estimateTokens(part);
617
+ } else if ("text" in part && typeof part.text === "string") {
618
+ total += estimateTokens(part.text);
619
+ } else if ("type" in part && part.type === "image") {
620
+ total += 765;
621
+ }
622
+ }
623
+ return total;
624
+ }
625
+ return 0;
626
+ }
627
+ function estimateConversationTokens(messages) {
628
+ let total = 0;
629
+ for (const message of messages) {
630
+ total += estimateMessageTokens(message);
631
+ total += 4;
632
+ }
633
+ return total;
634
+ }
635
+
636
+ // src/agent/context/pruning.ts
637
+ var DEFAULT_CONTEXT_LIMITS = {
638
+ contextWindow: 128e3,
639
+ reserveTokens: 16e3,
640
+ // Reserve for output
641
+ protectedTokens: 4e4,
642
+ // Keep recent 40 k tokens
643
+ pruneMinimum: 2e4
644
+ // Don't prune until 20 k tokens
645
+ };
646
+ function getUsableTokenLimit(limits) {
647
+ return limits.contextWindow - limits.reserveTokens;
648
+ }
649
+ function isContextOverflowing(tokens, limits = DEFAULT_CONTEXT_LIMITS) {
650
+ return tokens > getUsableTokenLimit(limits);
651
+ }
652
+ function shouldPruneContext(tokens, limits = DEFAULT_CONTEXT_LIMITS) {
653
+ if (tokens < limits.pruneMinimum) return false;
654
+ return isContextOverflowing(tokens, limits);
655
+ }
656
+ function findCutPoint(messages, protectedTokens = DEFAULT_CONTEXT_LIMITS.protectedTokens) {
657
+ if (messages.length === 0) return 0;
658
+ let tokensFromEnd = 0;
659
+ let cutIndex = messages.length;
660
+ for (let i = messages.length - 1; i >= 0; i--) {
661
+ tokensFromEnd += estimateMessageTokens(messages[i]);
662
+ if (tokensFromEnd >= protectedTokens) {
663
+ cutIndex = i;
664
+ break;
665
+ }
666
+ }
667
+ if (cutIndex <= 1) return 0;
668
+ const startIndex = cutIndex >= messages.length ? messages.length - 1 : cutIndex;
669
+ for (let i = startIndex; i >= 1; i--) {
670
+ const msg = messages[i];
671
+ const prevMsg = messages[i - 1];
672
+ if (!msg || !prevMsg) continue;
673
+ if (msg.role === "tool") continue;
674
+ if (prevMsg.role === "assistant" || prevMsg.role === "user") {
675
+ return i;
676
+ }
677
+ }
678
+ return 0;
679
+ }
680
+ function pruneToolResults(messages, protectedTokens = DEFAULT_CONTEXT_LIMITS.protectedTokens, options) {
681
+ const protectedToolSet = /* @__PURE__ */ new Set([
682
+ ...PRUNE_PROTECTED_TOOLS,
683
+ ...options?.protectedTools ?? []
684
+ ]);
685
+ let tokensFromEnd = 0;
686
+ const tokenPositions = [];
687
+ for (let i = messages.length - 1; i >= 0; i--) {
688
+ tokensFromEnd += estimateMessageTokens(messages[i]);
689
+ tokenPositions[i] = tokensFromEnd;
690
+ }
691
+ return messages.map((msg, i) => {
692
+ if (tokenPositions[i] < protectedTokens) return msg;
693
+ if (!("role" in msg) || msg.role !== "tool") return msg;
694
+ const toolMsg = msg;
695
+ if ("compactedAt" in toolMsg && toolMsg.compactedAt) return msg;
696
+ if (toolMsg.toolName && protectedToolSet.has(toolMsg.toolName)) return msg;
697
+ const currentTokens = estimateTokens(toolMsg.content);
698
+ if (currentTokens < 500) return msg;
699
+ return {
700
+ ...toolMsg,
701
+ content: `[Output pruned - was ${currentTokens} tokens]`,
702
+ compactedAt: Date.now()
703
+ };
704
+ });
705
+ }
706
+
707
+ // src/agent/context/summarization.ts
708
+ import { generateText } from "ai";
709
+ var DEFAULT_SUMMARY_PROMPT = `You are summarizing a conversation to continue it with context.
710
+
711
+ Create a structured summary that captures:
712
+ 1. **Goal**: What the user is trying to accomplish
713
+ 2. **Progress**: What has been done so far
714
+ 3. **Decisions**: Key decisions made during the conversation
715
+ 4. **Current State**: Where we left off
716
+ 5. **Next Steps**: What should happen next
717
+
718
+ Be concise but comprehensive. Include specific file paths, function names, and technical details that would be lost otherwise.
719
+
720
+ Format as a clear summary that could be given to another assistant to continue the work.`;
721
+ async function generateSummary(messages, options) {
722
+ const conversationText = messages.map((m) => {
723
+ const role = m.role.toUpperCase();
724
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
725
+ return `[${role}]: ${content}`;
726
+ }).join("\n\n");
727
+ const prompt = options.customPrompt || DEFAULT_SUMMARY_PROMPT;
728
+ const { text } = await generateText({
729
+ model: options.model,
730
+ maxOutputTokens: options.maxTokens ?? 2e3,
731
+ system: prompt,
732
+ prompt: `Summarize this conversation:
733
+
734
+ ${conversationText}`
735
+ });
736
+ return text;
737
+ }
738
+ async function pruneContext(messages, options = {}) {
739
+ const limits = options.limits ?? DEFAULT_CONTEXT_LIMITS;
740
+ let currentMessages = [...messages];
741
+ let tokensRemoved = 0;
742
+ let removedCount = 0;
743
+ let summarized = false;
744
+ let summary;
745
+ const initialTokens = estimateConversationTokens(currentMessages);
746
+ if (!shouldPruneContext(initialTokens, limits)) {
747
+ return { messages: currentMessages, removedCount: 0, tokensRemoved: 0, summarized: false };
748
+ }
749
+ const prunedMessages = pruneToolResults(currentMessages, limits.protectedTokens);
750
+ const afterPruneTokens = estimateConversationTokens(prunedMessages);
751
+ tokensRemoved = initialTokens - afterPruneTokens;
752
+ currentMessages = prunedMessages;
753
+ if (!isContextOverflowing(afterPruneTokens, limits)) {
754
+ return { messages: currentMessages, removedCount: 0, tokensRemoved, summarized: false };
755
+ }
756
+ const cutIndex = findCutPoint(currentMessages, limits.protectedTokens);
757
+ if (cutIndex === 0) {
758
+ return { messages: currentMessages, removedCount: 0, tokensRemoved, summarized: false };
759
+ }
760
+ const toSummarize = currentMessages.slice(0, cutIndex);
761
+ const toKeep = currentMessages.slice(cutIndex);
762
+ removedCount = toSummarize.length;
763
+ tokensRemoved += estimateConversationTokens(toSummarize);
764
+ if (options.model) {
765
+ summary = await generateSummary(toSummarize, {
766
+ model: options.model,
767
+ customPrompt: options.summaryPrompt
768
+ });
769
+ summarized = true;
770
+ const summaryMessage = {
771
+ id: crypto.randomUUID(),
772
+ role: "system",
773
+ content: `## Previous Conversation Summary
774
+
775
+ ${summary}`,
776
+ createdAt: /* @__PURE__ */ new Date()
777
+ };
778
+ currentMessages = [summaryMessage, ...toKeep];
779
+ } else {
780
+ currentMessages = toKeep;
781
+ }
782
+ return { messages: currentMessages, removedCount, tokensRemoved, summarized, summary };
783
+ }
784
+
785
+ // src/agent/context/manager.ts
786
+ var ContextManager = class {
787
+ limits;
788
+ model;
789
+ summaryPrompt;
790
+ constructor(options) {
791
+ this.limits = { ...DEFAULT_CONTEXT_LIMITS, ...options?.limits };
792
+ this.model = options?.model;
793
+ this.summaryPrompt = options?.summaryPrompt;
794
+ }
795
+ /** Get a copy of the current context limits. */
796
+ getLimits() {
797
+ return { ...this.limits };
798
+ }
799
+ /** Update context limits (e.g. when switching models). */
800
+ setLimits(limits) {
801
+ this.limits = { ...this.limits, ...limits };
802
+ }
803
+ /** Set the model used for summarisation. */
804
+ setModel(model) {
805
+ this.model = model;
806
+ }
807
+ /** Estimate total tokens for a message array. */
808
+ estimateTokens(messages) {
809
+ return estimateConversationTokens(messages);
810
+ }
811
+ /** Check whether the context is overflowing. */
812
+ isOverflowing(messages) {
813
+ const tokens = this.estimateTokens(messages);
814
+ return isContextOverflowing(tokens, this.limits);
815
+ }
816
+ /** Check whether pruning should be triggered. */
817
+ shouldPrune(messages) {
818
+ const tokens = this.estimateTokens(messages);
819
+ return shouldPruneContext(tokens, this.limits);
820
+ }
821
+ /** Prune context to fit within limits. */
822
+ async prune(messages) {
823
+ return pruneContext(messages, {
824
+ model: this.model,
825
+ limits: this.limits,
826
+ summaryPrompt: this.summaryPrompt
827
+ });
828
+ }
829
+ /**
830
+ * Get a snapshot of token statistics.
831
+ *
832
+ * Useful for dashboards, logging, or deciding whether to prune.
833
+ */
834
+ getStats(messages) {
835
+ const tokens = this.estimateTokens(messages);
836
+ const limit = getUsableTokenLimit(this.limits);
837
+ return {
838
+ tokens,
839
+ limit,
840
+ available: Math.max(0, limit - tokens),
841
+ utilizationPercent: Math.round(tokens / limit * 100),
842
+ isOverflowing: isContextOverflowing(tokens, this.limits),
843
+ shouldPrune: shouldPruneContext(tokens, this.limits)
844
+ };
845
+ }
846
+ };
847
+
848
+ // src/execution/turn-runner/prepare.ts
627
849
  function prepareModelStep(options) {
628
850
  const modelMessages = Array.from(options.toModelMessages(options.messages));
629
851
  return {
@@ -632,6 +854,7 @@ function prepareModelStep(options) {
632
854
  modelMessages,
633
855
  inferenceInput: {
634
856
  sessionID: options.sessionId,
857
+ turnID: options.turnTracker?.getCurrentTurnId?.() ?? void 0,
635
858
  step: options.step,
636
859
  model: options.config.model,
637
860
  system: options.systemPrompts,
@@ -641,6 +864,7 @@ function prepareModelStep(options) {
641
864
  mcpTools: options.mcpTools,
642
865
  cwd: options.config.cwd,
643
866
  host: options.host,
867
+ humanInputController: options.humanInputController,
644
868
  temperature: options.config.temperature,
645
869
  topP: options.config.topP,
646
870
  maxOutputTokens: options.config.maxOutputTokens,
@@ -658,12 +882,15 @@ function prepareModelStep(options) {
658
882
  doomLoopThreshold: options.config.doomLoopThreshold,
659
883
  enforceDoomLoop: options.config.enforceDoomLoop,
660
884
  onDoomLoop: options.config.onDoomLoop,
661
- contextTokenLimit: options.config.contextWindow ? options.config.contextWindow - DEFAULT_CONTEXT_LIMITS.reserveTokens : void 0
885
+ contextTokenLimit: options.config.contextWindow ? getUsableTokenLimit({
886
+ contextWindow: options.config.contextWindow,
887
+ reserveTokens: options.config.reserveTokens ?? DEFAULT_CONTEXT_LIMITS.reserveTokens
888
+ }) : void 0
662
889
  }
663
890
  };
664
891
  }
665
892
 
666
- // src/runtime/step-processing/doom-loop.ts
893
+ // src/execution/step-processing/doom-loop.ts
667
894
  var DEFAULT_DOOM_LOOP_THRESHOLD = 3;
668
895
  var DoomLoopError = class extends Error {
669
896
  toolName;
@@ -740,7 +967,7 @@ async function recordToolCallAndCheckDoomLoop(options) {
740
967
  options.warn?.(`[StepProcessing] ${doomError.message}`);
741
968
  }
742
969
 
743
- // src/runtime/step-processing/overflow.ts
970
+ // src/execution/step-processing/overflow.ts
744
971
  var ContextOverflowError = class extends Error {
745
972
  inputTokens;
746
973
  limit;
@@ -767,12 +994,23 @@ async function handleContextOverflow(options) {
767
994
  }
768
995
  }
769
996
 
770
- // src/runtime/step-processing/process.ts
997
+ // src/execution/step-processing/process.ts
998
+ function normalizeToolResultChunk(output) {
999
+ if (output && typeof output === "object" && "__cuylabsAgentToolResult" in output && output.__cuylabsAgentToolResult === true) {
1000
+ const structured = output;
1001
+ return {
1002
+ result: structured.output,
1003
+ ...structured.metadata ? { metadata: structured.metadata } : {}
1004
+ };
1005
+ }
1006
+ return { result: output };
1007
+ }
771
1008
  async function processStepStream(stream, options) {
772
1009
  const { abort, onEvent } = options;
773
1010
  const normalizeError = options.normalizeError ?? ((error2) => error2 instanceof Error ? error2 : new Error(String(error2)));
774
1011
  const doomLoopThreshold = options.doomLoopThreshold ?? DEFAULT_DOOM_LOOP_THRESHOLD;
775
1012
  const maxSteps = options.maxSteps ?? 50;
1013
+ const log = options.logger;
776
1014
  let stepCount = options.currentStep ?? 1;
777
1015
  let sawStartStep = false;
778
1016
  let text = "";
@@ -787,10 +1025,7 @@ async function processStepStream(stream, options) {
787
1025
  try {
788
1026
  for await (const rawChunk of stream.fullStream) {
789
1027
  const chunk = rawChunk;
790
- if (process.env.DEBUG_PROCESSOR) {
791
- process.stderr.write(`[step-processing] Chunk received: ${chunk.type}
792
- `);
793
- }
1028
+ log?.debug(`Chunk received: ${chunk.type}`);
794
1029
  abort.throwIfAborted();
795
1030
  switch (chunk.type) {
796
1031
  case "start-step":
@@ -846,19 +1081,23 @@ async function processStepStream(stream, options) {
846
1081
  `)
847
1082
  });
848
1083
  break;
849
- case "tool-result":
1084
+ case "tool-result": {
1085
+ const normalized = normalizeToolResultChunk(chunk.output);
850
1086
  toolResults.push({
851
1087
  toolName: chunk.toolName,
852
1088
  toolCallId: chunk.toolCallId,
853
- result: chunk.output
1089
+ result: normalized.result,
1090
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
854
1091
  });
855
1092
  await onEvent({
856
1093
  type: "tool-result",
857
1094
  toolName: chunk.toolName,
858
1095
  toolCallId: chunk.toolCallId,
859
- result: chunk.output
1096
+ result: normalized.result,
1097
+ ...normalized.metadata ? { metadata: normalized.metadata } : {}
860
1098
  });
861
1099
  break;
1100
+ }
862
1101
  case "tool-error":
863
1102
  await onEvent({
864
1103
  type: "tool-error",
@@ -942,21 +1181,15 @@ async function processStepStream(stream, options) {
942
1181
  error
943
1182
  };
944
1183
  }
945
- var processStream = processStepStream;
946
1184
 
947
- // src/runtime/turn-runner/stream-step.ts
948
- function buildModelCallContext(options) {
1185
+ // src/execution/turn-runner/stream-step.ts
1186
+ function buildStepModelCallContext(options) {
949
1187
  const input = options.preparedStep.inferenceInput;
950
- return {
951
- sessionID: input.sessionID,
952
- step: input.step ?? options.preparedStep.step,
953
- cwd: input.cwd,
954
- abort: input.abort,
955
- model: input.model,
956
- toolNames: Object.keys(input.tools),
957
- mcpToolNames: Object.keys(input.mcpTools ?? {}),
958
- scope: snapshotScope()
959
- };
1188
+ const ctx = buildModelCallContext(input);
1189
+ if (input.step == null) {
1190
+ ctx.step = options.preparedStep.step;
1191
+ }
1192
+ return ctx;
960
1193
  }
961
1194
  function normalizeModelStepError(options, error) {
962
1195
  const model = options.preparedStep.inferenceInput.model;
@@ -975,13 +1208,20 @@ async function* runModelStep(options) {
975
1208
  },
976
1209
  (async function* () {
977
1210
  const { preparedStep, turnEngine, applyCommitBatch } = options;
978
- const stream = await Inference.streamStep(preparedStep.inferenceInput);
979
1211
  const eventQueue = [];
980
1212
  let resolveNext = null;
981
1213
  let streamDone = false;
982
1214
  let streamError;
983
1215
  const intervention = preparedStep.inferenceInput.intervention;
984
1216
  const middleware = preparedStep.inferenceInput.middleware;
1217
+ const emitQueuedEvent = async (event) => {
1218
+ middleware?.emitEvent(event);
1219
+ eventQueue.push(event);
1220
+ if (resolveNext) {
1221
+ resolveNext();
1222
+ resolveNext = null;
1223
+ }
1224
+ };
985
1225
  if (intervention) {
986
1226
  intervention.onApplied = (item) => {
987
1227
  eventQueue.push({
@@ -994,7 +1234,22 @@ async function* runModelStep(options) {
994
1234
  resolveNext = null;
995
1235
  }
996
1236
  };
1237
+ intervention.onDeferredQueued = (item) => {
1238
+ eventQueue.push({
1239
+ type: "follow-up-queued",
1240
+ id: item.id,
1241
+ message: item.message
1242
+ });
1243
+ if (resolveNext) {
1244
+ resolveNext();
1245
+ resolveNext = null;
1246
+ }
1247
+ };
997
1248
  }
1249
+ const stream = await Inference.streamStep({
1250
+ ...preparedStep.inferenceInput,
1251
+ onEvent: emitQueuedEvent
1252
+ });
998
1253
  const processPromise = processStepStream(stream, {
999
1254
  sessionID: preparedStep.inferenceInput.sessionID,
1000
1255
  abort: preparedStep.inferenceInput.abort,
@@ -1008,14 +1263,7 @@ async function* runModelStep(options) {
1008
1263
  contextTokenLimit: preparedStep.stepProcessing.contextTokenLimit,
1009
1264
  onContextOverflow: async () => {
1010
1265
  },
1011
- onEvent: async (event) => {
1012
- middleware?.emitEvent(event);
1013
- eventQueue.push(event);
1014
- if (resolveNext) {
1015
- resolveNext();
1016
- resolveNext = null;
1017
- }
1018
- }
1266
+ onEvent: emitQueuedEvent
1019
1267
  }).then((result2) => {
1020
1268
  streamDone = true;
1021
1269
  if (resolveNext) {
@@ -1068,7 +1316,7 @@ async function* runModelStep(options) {
1068
1316
  usage: result.usage,
1069
1317
  finishReason: result.finishReason
1070
1318
  },
1071
- buildModelCallContext(options)
1319
+ buildStepModelCallContext(options)
1072
1320
  );
1073
1321
  result.text = revised.text;
1074
1322
  result.usage = revised.usage;
@@ -1079,8 +1327,7 @@ async function* runModelStep(options) {
1079
1327
  );
1080
1328
  }
1081
1329
 
1082
- // src/runtime/turn-runner/tool-batch.ts
1083
- import { randomUUID as randomUUID3 } from "crypto";
1330
+ // src/execution/turn-runner/tool-batch.ts
1084
1331
  function cloneToolResultRecord(options) {
1085
1332
  return new Map(
1086
1333
  options.snapshot.toolResults.map((toolResult) => [
@@ -1089,105 +1336,164 @@ function cloneToolResultRecord(options) {
1089
1336
  ])
1090
1337
  );
1091
1338
  }
1092
- function createToolBatchErrorResult(options) {
1093
- return {
1094
- type: "tool-error",
1095
- toolName: options.toolName,
1096
- toolCallId: options.toolCallId,
1097
- error: options.errorMessage
1098
- };
1339
+ function recordToolResult(toolCall, event, result, accumulators, metadata) {
1340
+ accumulators.events.push(event);
1341
+ accumulators.toolResultsByCallId.set(toolCall.toolCallId, {
1342
+ toolCallId: toolCall.toolCallId,
1343
+ toolName: toolCall.toolName,
1344
+ result,
1345
+ ...metadata ? { metadata } : {},
1346
+ ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1347
+ });
1348
+ if (accumulators.turnState.value) {
1349
+ accumulators.turnState.value = advanceAgentTurnState(
1350
+ accumulators.turnState.value,
1351
+ event,
1352
+ (/* @__PURE__ */ new Date()).toISOString(),
1353
+ {
1354
+ ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1355
+ }
1356
+ );
1357
+ }
1099
1358
  }
1100
- function createToolBatchResultEvent(options) {
1101
- return {
1102
- type: "tool-result",
1103
- toolName: options.toolName,
1104
- toolCallId: options.toolCallId,
1105
- result: options.result
1106
- };
1359
+ async function isParallelSafe(tool, args, cwd) {
1360
+ const initialized = await tool.init({ cwd });
1361
+ const cap = initialized.capabilities?.parallelSafe;
1362
+ const safe = resolveCapability(
1363
+ cap,
1364
+ args,
1365
+ /* fallback */
1366
+ false
1367
+ );
1368
+ return { safe, initialized };
1369
+ }
1370
+ async function executeBatchEntry(toolCall, options, accumulators, preInitialized) {
1371
+ const tool = options.tools[toolCall.toolName];
1372
+ if (!tool) {
1373
+ const errorMessage = `Tool '${toolCall.toolName}' is not registered`;
1374
+ recordToolResult(
1375
+ toolCall,
1376
+ {
1377
+ type: "tool-error",
1378
+ toolName: toolCall.toolName,
1379
+ toolCallId: toolCall.toolCallId,
1380
+ error: errorMessage
1381
+ },
1382
+ `Error: ${errorMessage}`,
1383
+ accumulators
1384
+ );
1385
+ return;
1386
+ }
1387
+ try {
1388
+ const executed = await executeAgentToolCall({
1389
+ toolName: toolCall.toolName,
1390
+ tool,
1391
+ toolCallId: toolCall.toolCallId,
1392
+ params: toolCall.args,
1393
+ cwd: options.cwd,
1394
+ abort: options.abort,
1395
+ sessionID: options.sessionId,
1396
+ turnID: options.turnTracker?.getCurrentTurnId?.() ?? void 0,
1397
+ messageID: toolCall.toolCallId,
1398
+ ...options.host ? { host: options.host } : {},
1399
+ ...options.humanInputController ? { humanInputController: options.humanInputController } : {},
1400
+ ...options.turnTracker ? { turnTracker: options.turnTracker } : {},
1401
+ ...options.middleware ? { middleware: options.middleware } : {},
1402
+ onEvent: async (event) => {
1403
+ accumulators.events.push(event);
1404
+ },
1405
+ ...preInitialized ? { initialized: preInitialized } : {}
1406
+ });
1407
+ recordToolResult(
1408
+ toolCall,
1409
+ {
1410
+ type: "tool-result",
1411
+ toolName: toolCall.toolName,
1412
+ toolCallId: toolCall.toolCallId,
1413
+ result: executed.output,
1414
+ ...executed.metadata ? { metadata: executed.metadata } : {}
1415
+ },
1416
+ executed.output,
1417
+ accumulators,
1418
+ executed.metadata
1419
+ );
1420
+ } catch (error) {
1421
+ const errorMessage = error instanceof Error ? error.message : String(error);
1422
+ recordToolResult(
1423
+ toolCall,
1424
+ {
1425
+ type: "tool-error",
1426
+ toolName: toolCall.toolName,
1427
+ toolCallId: toolCall.toolCallId,
1428
+ error: errorMessage
1429
+ },
1430
+ `Error: ${errorMessage}`,
1431
+ accumulators
1432
+ );
1433
+ }
1107
1434
  }
1108
1435
  async function runToolBatch(options) {
1109
1436
  const toolResultsByCallId = cloneToolResultRecord(options);
1110
1437
  const events = [];
1111
- let turnState = options.turnState ? structuredClone(options.turnState) : void 0;
1112
- for (const toolCall of options.snapshot.toolCalls) {
1113
- if (toolResultsByCallId.has(toolCall.toolCallId)) {
1114
- continue;
1115
- }
1438
+ const turnState = {
1439
+ value: options.turnState ? structuredClone(options.turnState) : void 0
1440
+ };
1441
+ const accumulators = { events, toolResultsByCallId, turnState };
1442
+ const pending = options.snapshot.toolCalls.filter(
1443
+ (tc) => !toolResultsByCallId.has(tc.toolCallId)
1444
+ );
1445
+ const parallelGroup = [];
1446
+ const serialGroup = [];
1447
+ for (const toolCall of pending) {
1116
1448
  const tool = options.tools[toolCall.toolName];
1117
- const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1118
1449
  if (!tool) {
1119
- const errorMessage = `Tool '${toolCall.toolName}' is not registered`;
1120
- const event = createToolBatchErrorResult({
1121
- toolCallId: toolCall.toolCallId,
1122
- toolName: toolCall.toolName,
1123
- errorMessage,
1124
- replayPolicy: toolCall.replayPolicy
1125
- });
1126
- events.push(event);
1127
- toolResultsByCallId.set(toolCall.toolCallId, {
1128
- toolCallId: toolCall.toolCallId,
1129
- toolName: toolCall.toolName,
1130
- result: `Error: ${errorMessage}`,
1131
- ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1132
- });
1133
- if (turnState) {
1134
- turnState = advanceAgentTurnState(turnState, event, updatedAt, {
1135
- ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1136
- });
1137
- }
1450
+ serialGroup.push({ toolCall });
1138
1451
  continue;
1139
1452
  }
1453
+ let safe = false;
1454
+ let initialized;
1140
1455
  try {
1141
- const executed = await executeAgentToolCall({
1142
- toolName: toolCall.toolName,
1456
+ const result = await isParallelSafe(
1143
1457
  tool,
1144
- params: toolCall.args,
1145
- cwd: options.cwd,
1146
- abort: options.abort,
1147
- sessionID: options.sessionId,
1148
- messageID: randomUUID3(),
1149
- ...options.host ? { host: options.host } : {},
1150
- ...options.turnTracker ? { turnTracker: options.turnTracker } : {},
1151
- ...options.middleware ? { middleware: options.middleware } : {}
1152
- });
1153
- const event = createToolBatchResultEvent({
1154
- toolCallId: toolCall.toolCallId,
1155
- toolName: toolCall.toolName,
1156
- result: executed.output
1157
- });
1158
- events.push(event);
1159
- toolResultsByCallId.set(toolCall.toolCallId, {
1160
- toolCallId: toolCall.toolCallId,
1161
- toolName: toolCall.toolName,
1162
- result: executed.output,
1163
- ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1164
- });
1165
- if (turnState) {
1166
- turnState = advanceAgentTurnState(turnState, event, updatedAt, {
1167
- ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1168
- });
1169
- }
1170
- } catch (error) {
1171
- const errorMessage = error instanceof Error ? error.message : String(error);
1172
- const event = createToolBatchErrorResult({
1173
- toolCallId: toolCall.toolCallId,
1174
- toolName: toolCall.toolName,
1175
- errorMessage,
1176
- replayPolicy: toolCall.replayPolicy
1177
- });
1178
- events.push(event);
1179
- toolResultsByCallId.set(toolCall.toolCallId, {
1180
- toolCallId: toolCall.toolCallId,
1181
- toolName: toolCall.toolName,
1182
- result: `Error: ${errorMessage}`,
1183
- ...toolCall.replayPolicy ? { replayPolicy: toolCall.replayPolicy } : {}
1184
- });
1185
- if (turnState) {
1186
- turnState = advanceAgentTurnState(turnState, event, updatedAt, {
1187
- ...toolCall.replayPolicy ? { toolReplayPolicy: toolCall.replayPolicy } : {}
1188
- });
1458
+ toolCall.args,
1459
+ options.cwd
1460
+ );
1461
+ safe = result.safe;
1462
+ initialized = result.initialized;
1463
+ } catch {
1464
+ }
1465
+ if (safe && initialized) {
1466
+ parallelGroup.push({ toolCall, initialized });
1467
+ } else {
1468
+ serialGroup.push({ toolCall, initialized });
1469
+ }
1470
+ }
1471
+ if (parallelGroup.length > 0) {
1472
+ await Promise.all(
1473
+ parallelGroup.map(
1474
+ ({ toolCall, initialized }) => executeBatchEntry(toolCall, options, accumulators, initialized)
1475
+ )
1476
+ );
1477
+ }
1478
+ for (const { toolCall, initialized } of serialGroup) {
1479
+ if (options.intervention?.hasPending && initialized) {
1480
+ const interruptPolicy = initialized.capabilities?.onInterrupt ?? "finish";
1481
+ if (interruptPolicy === "cancel") {
1482
+ recordToolResult(
1483
+ toolCall,
1484
+ {
1485
+ type: "tool-error",
1486
+ toolName: toolCall.toolName,
1487
+ toolCallId: toolCall.toolCallId,
1488
+ error: "Tool cancelled: user sent a new message"
1489
+ },
1490
+ "Error: Tool cancelled: user sent a new message",
1491
+ accumulators
1492
+ );
1493
+ continue;
1189
1494
  }
1190
1495
  }
1496
+ await executeBatchEntry(toolCall, options, accumulators, initialized);
1191
1497
  }
1192
1498
  return {
1193
1499
  snapshot: {
@@ -1196,12 +1502,12 @@ async function runToolBatch(options) {
1196
1502
  ),
1197
1503
  toolResults: options.snapshot.toolCalls.map((toolCall) => toolResultsByCallId.get(toolCall.toolCallId)).filter((toolResult) => toolResult !== void 0)
1198
1504
  },
1199
- ...turnState ? { turnState } : {},
1505
+ ...turnState.value ? { turnState: turnState.value } : {},
1200
1506
  events
1201
1507
  };
1202
1508
  }
1203
1509
 
1204
- // src/runtime/turn-runner/commit.ts
1510
+ // src/execution/turn-runner/commit.ts
1205
1511
  async function* commitStep(options) {
1206
1512
  return yield* streamWithinScope(
1207
1513
  {
@@ -1242,10 +1548,7 @@ async function* commitOutput(options) {
1242
1548
  );
1243
1549
  }
1244
1550
 
1245
- // src/runtime/workflow-state.ts
1246
- function cloneUsage2(usage) {
1247
- return usage ? { ...usage } : void 0;
1248
- }
1551
+ // src/execution/workflow-state.ts
1249
1552
  function cloneMessageSnapshot(message) {
1250
1553
  return {
1251
1554
  ...message,
@@ -1257,9 +1560,96 @@ function cloneMessageSnapshot(message) {
1257
1560
  } : {},
1258
1561
  ...message.role === "assistant" && message.error ? {
1259
1562
  error: { ...message.error }
1563
+ } : {},
1564
+ ...message.role === "tool" && message.metadata ? {
1565
+ metadata: structuredClone(message.metadata)
1566
+ } : {}
1567
+ };
1568
+ }
1569
+ function createAgentWorkflowTurnState(options) {
1570
+ const pendingInput = options.initialInput ? cloneMessageSnapshot(options.initialInput) : void 0;
1571
+ return {
1572
+ sessionId: options.sessionId,
1573
+ systemPrompts: [...options.systemPrompts ?? []],
1574
+ messages: (options.initialMessages ?? []).map(cloneMessageSnapshot),
1575
+ phase: pendingInput ? "input-commit" : "model-step",
1576
+ ...pendingInput ? { pendingInput } : {},
1577
+ step: options.initialStep ?? 1,
1578
+ maxSteps: options.maxSteps,
1579
+ replayDecisions: {},
1580
+ startedAt: options.startedAt,
1581
+ updatedAt: options.startedAt
1582
+ };
1583
+ }
1584
+ function cloneAgentWorkflowTurnState(state) {
1585
+ return {
1586
+ ...state,
1587
+ systemPrompts: [...state.systemPrompts],
1588
+ messages: state.messages.map(cloneMessageSnapshot),
1589
+ ...state.pendingInput ? { pendingInput: cloneMessageSnapshot(state.pendingInput) } : {},
1590
+ ...state.usage ? { usage: cloneUsage(state.usage) } : {},
1591
+ ...state.turnState ? { turnState: structuredClone(state.turnState) } : {},
1592
+ ...state.lastModelStep ? { lastModelStep: structuredClone(state.lastModelStep) } : {},
1593
+ replayDecisions: Object.fromEntries(
1594
+ Object.entries(state.replayDecisions).map(([toolCallId, decision]) => [
1595
+ toolCallId,
1596
+ { ...decision }
1597
+ ])
1598
+ ),
1599
+ ...state.pendingInterventions?.length ? {
1600
+ pendingInterventions: state.pendingInterventions.map(
1601
+ (intervention) => ({ ...intervention })
1602
+ )
1603
+ } : {},
1604
+ ...state.queuedFollowUps?.length ? {
1605
+ queuedFollowUps: state.queuedFollowUps.map((intervention) => ({
1606
+ ...intervention
1607
+ }))
1260
1608
  } : {}
1261
1609
  };
1262
1610
  }
1611
+ function applyWorkflowInterventions(state, interventions, updatedAt) {
1612
+ if (interventions.length === 0) {
1613
+ return state;
1614
+ }
1615
+ const injected = interventions.map((intervention) => ({
1616
+ id: intervention.id,
1617
+ role: "user",
1618
+ createdAt: intervention.createdAt,
1619
+ content: intervention.message
1620
+ }));
1621
+ return {
1622
+ ...state,
1623
+ messages: [...state.messages, ...injected],
1624
+ pendingInterventions: void 0,
1625
+ updatedAt
1626
+ };
1627
+ }
1628
+ function drainWorkflowInterventions(state, updatedAt) {
1629
+ if (!state.pendingInterventions?.length) {
1630
+ return state;
1631
+ }
1632
+ return applyWorkflowInterventions(
1633
+ state,
1634
+ state.pendingInterventions,
1635
+ updatedAt
1636
+ );
1637
+ }
1638
+ function queueWorkflowFollowUps(state, followUps, updatedAt) {
1639
+ if (followUps.length === 0) {
1640
+ return state;
1641
+ }
1642
+ return {
1643
+ ...state,
1644
+ queuedFollowUps: [
1645
+ ...state.queuedFollowUps ?? [],
1646
+ ...followUps.map((followUp) => ({ ...followUp }))
1647
+ ],
1648
+ updatedAt
1649
+ };
1650
+ }
1651
+
1652
+ // src/execution/workflow-snapshot.ts
1263
1653
  function snapshotAgentWorkflowMessage(message) {
1264
1654
  switch (message.role) {
1265
1655
  case "user":
@@ -1297,6 +1687,7 @@ function snapshotAgentWorkflowMessage(message) {
1297
1687
  toolCallId: message.toolCallId,
1298
1688
  toolName: message.toolName,
1299
1689
  result: message.result,
1690
+ ...message.metadata ? { metadata: structuredClone(message.metadata) } : {},
1300
1691
  ...message.compactedAt !== void 0 ? { compactedAt: message.compactedAt } : {}
1301
1692
  };
1302
1693
  case "system":
@@ -1309,7 +1700,7 @@ function snapshotAgentWorkflowMessage(message) {
1309
1700
  }
1310
1701
  }
1311
1702
  function snapshotAgentWorkflowMessages(messages) {
1312
- return messages.map(snapshotAgentWorkflowMessage);
1703
+ return messages.map((message) => snapshotAgentWorkflowMessage(message));
1313
1704
  }
1314
1705
  function restoreAgentWorkflowMessage(snapshot) {
1315
1706
  const createdAt = new Date(snapshot.createdAt);
@@ -1349,6 +1740,7 @@ function restoreAgentWorkflowMessage(snapshot) {
1349
1740
  toolCallId: snapshot.toolCallId,
1350
1741
  toolName: snapshot.toolName,
1351
1742
  result: snapshot.result,
1743
+ ...snapshot.metadata ? { metadata: structuredClone(snapshot.metadata) } : {},
1352
1744
  ...snapshot.compactedAt !== void 0 ? { compactedAt: snapshot.compactedAt } : {}
1353
1745
  };
1354
1746
  case "system":
@@ -1363,52 +1755,8 @@ function restoreAgentWorkflowMessage(snapshot) {
1363
1755
  function restoreAgentWorkflowMessages(snapshots) {
1364
1756
  return snapshots.map(restoreAgentWorkflowMessage);
1365
1757
  }
1366
- function createAgentWorkflowTurnState(options) {
1367
- return {
1368
- sessionId: options.sessionId,
1369
- systemPrompts: [...options.systemPrompts ?? []],
1370
- messages: (options.initialMessages ?? []).map(cloneMessageSnapshot),
1371
- phase: "model-step",
1372
- step: options.initialStep ?? 1,
1373
- maxSteps: options.maxSteps,
1374
- replayDecisions: {},
1375
- startedAt: options.startedAt,
1376
- updatedAt: options.startedAt
1377
- };
1378
- }
1379
- function cloneAgentWorkflowTurnState(state) {
1380
- return {
1381
- ...state,
1382
- systemPrompts: [...state.systemPrompts],
1383
- messages: state.messages.map(cloneMessageSnapshot),
1384
- ...state.usage ? { usage: cloneUsage2(state.usage) } : {},
1385
- ...state.turnState ? { turnState: structuredClone(state.turnState) } : {},
1386
- ...state.lastModelStep ? { lastModelStep: structuredClone(state.lastModelStep) } : {},
1387
- replayDecisions: Object.fromEntries(
1388
- Object.entries(state.replayDecisions).map(([toolCallId, decision]) => [
1389
- toolCallId,
1390
- { ...decision }
1391
- ])
1392
- )
1393
- };
1394
- }
1395
1758
 
1396
- // src/runtime/workflow-planner/helpers.ts
1397
- function accumulateWorkflowUsage(current, next) {
1398
- if (!next) {
1399
- return current ? { ...current } : void 0;
1400
- }
1401
- if (!current) {
1402
- return { ...next };
1403
- }
1404
- return {
1405
- inputTokens: (current.inputTokens ?? 0) + (next.inputTokens ?? 0),
1406
- outputTokens: (current.outputTokens ?? 0) + (next.outputTokens ?? 0),
1407
- totalTokens: (current.totalTokens ?? 0) + (next.totalTokens ?? 0),
1408
- cacheReadTokens: (current.cacheReadTokens ?? 0) + (next.cacheReadTokens ?? 0),
1409
- cacheWriteTokens: (current.cacheWriteTokens ?? 0) + (next.cacheWriteTokens ?? 0)
1410
- };
1411
- }
1759
+ // src/execution/workflow-planner/helpers.ts
1412
1760
  function cloneRelevantReplayDecisions(state, snapshot) {
1413
1761
  return Object.fromEntries(
1414
1762
  snapshot.toolCalls.flatMap((toolCall) => {
@@ -1417,18 +1765,29 @@ function cloneRelevantReplayDecisions(state, snapshot) {
1417
1765
  })
1418
1766
  );
1419
1767
  }
1420
- function findNextUnresolvedToolCall(snapshot) {
1768
+ function findAllUnresolvedToolCalls(snapshot) {
1421
1769
  const resolvedIds = new Set(
1422
1770
  snapshot.toolResults.map((toolResult) => toolResult.toolCallId)
1423
1771
  );
1424
- return snapshot.toolCalls.find(
1772
+ return snapshot.toolCalls.filter(
1425
1773
  (toolCall) => !resolvedIds.has(toolCall.toolCallId)
1426
1774
  );
1427
1775
  }
1428
1776
 
1429
- // src/runtime/workflow-planner/plan.ts
1777
+ // src/execution/workflow-planner/plan.ts
1430
1778
  function planNextAgentWorkflowOperation(state) {
1431
1779
  switch (state.phase) {
1780
+ case "input-commit":
1781
+ if (!state.pendingInput) {
1782
+ throw new Error(
1783
+ `Workflow state for session '${state.sessionId}' is in input-commit phase without a pending input message`
1784
+ );
1785
+ }
1786
+ return {
1787
+ kind: "input-commit",
1788
+ sessionId: state.sessionId,
1789
+ messages: [structuredClone(state.pendingInput)]
1790
+ };
1432
1791
  case "model-step":
1433
1792
  return {
1434
1793
  kind: "model-step",
@@ -1444,24 +1803,37 @@ function planNextAgentWorkflowOperation(state) {
1444
1803
  `Workflow state for session '${state.sessionId}' is in tool-batch phase without a stepCommit snapshot`
1445
1804
  );
1446
1805
  }
1447
- const nextToolCall = findNextUnresolvedToolCall(
1806
+ const allUnresolved = findAllUnresolvedToolCalls(
1448
1807
  state.lastModelStep.stepCommit
1449
1808
  );
1450
- if (!nextToolCall) {
1809
+ if (allUnresolved.length === 0) {
1451
1810
  throw new Error(
1452
1811
  `Workflow state for session '${state.sessionId}' is in tool-batch phase without unresolved tool calls`
1453
1812
  );
1454
1813
  }
1814
+ if (allUnresolved.length === 1) {
1815
+ return {
1816
+ kind: "tool-call",
1817
+ step: state.step,
1818
+ sessionId: state.sessionId,
1819
+ toolCall: structuredClone(allUnresolved[0]),
1820
+ turnState: state.turnState ? structuredClone(state.turnState) : void 0,
1821
+ replayDecision: cloneRelevantReplayDecisions(
1822
+ state,
1823
+ state.lastModelStep.stepCommit
1824
+ )[allUnresolved[0].toolCallId]
1825
+ };
1826
+ }
1455
1827
  return {
1456
- kind: "tool-call",
1828
+ kind: "tool-batch",
1457
1829
  step: state.step,
1458
1830
  sessionId: state.sessionId,
1459
- toolCall: structuredClone(nextToolCall),
1831
+ toolCalls: allUnresolved.map((tc) => structuredClone(tc)),
1460
1832
  turnState: state.turnState ? structuredClone(state.turnState) : void 0,
1461
- replayDecision: cloneRelevantReplayDecisions(
1833
+ replayDecisions: cloneRelevantReplayDecisions(
1462
1834
  state,
1463
1835
  state.lastModelStep.stepCommit
1464
- )[nextToolCall.toolCallId]
1836
+ )
1465
1837
  };
1466
1838
  }
1467
1839
  case "step-commit":
@@ -1494,12 +1866,12 @@ function planNextAgentWorkflowOperation(state) {
1494
1866
  }
1495
1867
  }
1496
1868
 
1497
- // src/runtime/workflow-planner/apply.ts
1869
+ // src/execution/workflow-planner/apply.ts
1498
1870
  function applyAgentWorkflowModelStepResult(state, result, updatedAt) {
1499
1871
  const next = cloneAgentWorkflowTurnState(state);
1500
1872
  next.lastModelStep = structuredClone(result);
1501
1873
  next.turnState = structuredClone(result.turnState);
1502
- next.usage = accumulateWorkflowUsage(next.usage, result.usage);
1874
+ next.usage = accumulateUsage(next.usage, result.usage);
1503
1875
  next.updatedAt = updatedAt;
1504
1876
  next.phase = result.finishReason === "tool-calls" ? "tool-batch" : "output-commit";
1505
1877
  return next;
@@ -1566,6 +1938,10 @@ function applyAgentWorkflowCommitResult(state, result, updatedAt) {
1566
1938
  ];
1567
1939
  next.updatedAt = updatedAt;
1568
1940
  switch (state.phase) {
1941
+ case "input-commit":
1942
+ next.phase = "model-step";
1943
+ delete next.pendingInput;
1944
+ return next;
1569
1945
  case "step-commit":
1570
1946
  next.step = state.step + 1;
1571
1947
  next.phase = "model-step";
@@ -1597,30 +1973,38 @@ function failAgentWorkflowTurnState(state, error, updatedAt) {
1597
1973
  }
1598
1974
 
1599
1975
  export {
1600
- convertAgentMessagesToModelMessages,
1601
1976
  createAgentTurnStepCommitBatch,
1602
1977
  createAgentTurnState,
1603
1978
  advanceAgentTurnState,
1604
1979
  failAgentTurnState,
1605
1980
  AgentTurnEngine,
1606
1981
  createAgentTurnEngine,
1982
+ estimateTokens,
1983
+ estimateMessageTokens,
1984
+ estimateConversationTokens,
1985
+ DEFAULT_CONTEXT_LIMITS,
1986
+ getUsableTokenLimit,
1987
+ findCutPoint,
1988
+ ContextManager,
1607
1989
  prepareModelStep,
1608
1990
  DoomLoopError,
1609
1991
  ContextOverflowError,
1610
1992
  processStepStream,
1611
- processStream,
1612
1993
  runModelStep,
1613
1994
  runToolBatch,
1614
1995
  commitStep,
1615
1996
  commitOutput,
1616
1997
  defaultAgentTaskCheckpointStrategy,
1617
1998
  createAgentTaskRunner,
1999
+ createAgentWorkflowTurnState,
2000
+ cloneAgentWorkflowTurnState,
2001
+ applyWorkflowInterventions,
2002
+ drainWorkflowInterventions,
2003
+ queueWorkflowFollowUps,
1618
2004
  snapshotAgentWorkflowMessage,
1619
2005
  snapshotAgentWorkflowMessages,
1620
2006
  restoreAgentWorkflowMessage,
1621
2007
  restoreAgentWorkflowMessages,
1622
- createAgentWorkflowTurnState,
1623
- cloneAgentWorkflowTurnState,
1624
2008
  planNextAgentWorkflowOperation,
1625
2009
  applyAgentWorkflowModelStepResult,
1626
2010
  applyAgentWorkflowToolBatchResult,