@cuylabs/agent-core 0.8.0 → 0.9.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 (68) hide show
  1. package/dist/{builder-UpOWQMW3.d.ts → builder-BgZ_j4Vs.d.ts} +2 -1
  2. package/dist/{chunk-RZITT45F.js → chunk-4QFNWPIF.js} +4 -4
  3. package/dist/{chunk-BFM2YHNM.js → chunk-5ARZJWD2.js} +74 -37
  4. package/dist/{chunk-KUVSERLJ.js → chunk-DXFBQMXP.js} +5 -2
  5. package/dist/{chunk-CAA7FHIH.js → chunk-EKR6PKXU.js} +0 -100
  6. package/dist/{chunk-IVUJDISU.js → chunk-GFTW23FV.js} +5 -14
  7. package/dist/{chunk-7VKQ4WPB.js → chunk-H3FUYU52.js} +11 -3
  8. package/dist/chunk-I6PKJ7XQ.js +292 -0
  9. package/dist/chunk-IYWQOJMQ.js +102 -0
  10. package/dist/{chunk-4BDA7DQY.js → chunk-J4QDGZIA.js} +19 -3
  11. package/dist/{chunk-7MUFEN4K.js → chunk-JLXG2SH7.js} +349 -3
  12. package/dist/{chunk-YSLSEQ6B.js → chunk-MAZ5DY5B.js} +18 -30
  13. package/dist/{chunk-P6YF7USR.js → chunk-MHKK374K.js} +12 -11
  14. package/dist/{chunk-VBWWUHWI.js → chunk-OFDKHNCX.js} +4 -1
  15. package/dist/{chunk-YUUJK53A.js → chunk-RKEW5WXI.js} +1 -1
  16. package/dist/{chunk-LRHOS4ZN.js → chunk-SPILYYDF.js} +3 -2
  17. package/dist/{chunk-RFEKJKTO.js → chunk-UDCZ673N.js} +321 -275
  18. package/dist/{chunk-BDBZ3SLK.js → chunk-UHCJEM2E.js} +39 -2
  19. package/dist/chunk-WGZAPU6N.js +929 -0
  20. package/dist/{chunk-N6HWIEEA.js → chunk-WKHDSSXG.js} +140 -23
  21. package/dist/index-BCqEGzBj.d.ts +251 -0
  22. package/dist/{index-CWSchSql.d.ts → index-DQuTZ8xL.d.ts} +290 -13
  23. package/dist/index.d.ts +23 -29
  24. package/dist/index.js +776 -490
  25. package/dist/{errors → inference/errors}/index.d.ts +2 -2
  26. package/dist/{errors → inference/errors}/index.js +1 -1
  27. package/dist/inference/index.d.ts +10 -9
  28. package/dist/inference/index.js +34 -8
  29. package/dist/middleware/index.d.ts +5 -4
  30. package/dist/middleware/index.js +3 -3
  31. package/dist/models/index.d.ts +18 -16
  32. package/dist/models/index.js +47 -11
  33. package/dist/models/reasoning/index.d.ts +4 -0
  34. package/dist/{reasoning → models/reasoning}/index.js +2 -3
  35. package/dist/plugin/index.d.ts +414 -0
  36. package/dist/plugin/index.js +32 -0
  37. package/dist/presets/index.d.ts +3 -3
  38. package/dist/presets/index.js +7 -5
  39. package/dist/prompt/index.d.ts +6 -5
  40. package/dist/prompt/index.js +3 -2
  41. package/dist/runner-CI-XeR16.d.ts +91 -0
  42. package/dist/runtime/index.d.ts +7 -6
  43. package/dist/runtime/index.js +6 -7
  44. package/dist/safety/index.d.ts +1 -1
  45. package/dist/safety/index.js +1 -1
  46. package/dist/{session-manager-B_CWGTsl.d.ts → session-manager-KbYt2WUh.d.ts} +8 -0
  47. package/dist/signal/index.js +1 -1
  48. package/dist/skill/index.d.ts +2 -2
  49. package/dist/skill/index.js +3 -3
  50. package/dist/storage/index.d.ts +2 -2
  51. package/dist/storage/index.js +1 -1
  52. package/dist/sub-agent/index.d.ts +10 -9
  53. package/dist/sub-agent/index.js +21 -4
  54. package/dist/tool/index.d.ts +19 -5
  55. package/dist/tool/index.js +2 -2
  56. package/dist/{tool-BHbyUAy3.d.ts → tool-CZWN3KbO.d.ts} +1 -10
  57. package/dist/{tool-DLXAR9Ce.d.ts → tool-DkhSCV2Y.d.ts} +1 -1
  58. package/dist/tracking/index.d.ts +1 -1
  59. package/dist/tracking/index.js +1 -1
  60. package/dist/{types-KKDrdU9Y.d.ts → types-BlOKk-Bb.d.ts} +9 -4
  61. package/dist/{types-BnpEOYV-.d.ts → types-BlZwmnuW.d.ts} +1 -1
  62. package/dist/{runner-e2YRcUoX.d.ts → types-DTSkxakL.d.ts} +3 -138
  63. package/dist/{types-QKHHQLLq.d.ts → types-DmDwi2zI.d.ts} +7 -4
  64. package/package.json +15 -9
  65. package/dist/chunk-DWYX7ASF.js +0 -26
  66. package/dist/chunk-SQU2AJHO.js +0 -305
  67. package/dist/reasoning/index.d.ts +0 -116
  68. package/dist/types-QA4WhEfz.d.ts +0 -138
package/dist/index.js CHANGED
@@ -1,35 +1,18 @@
1
- import {
2
- FileStorage,
3
- MemoryStorage,
4
- STORAGE_VERSION,
5
- SessionManager,
6
- buildEntryPath,
7
- buildMessagesFromEntries,
8
- configureDefaultSessionManager,
9
- createMessageEntry,
10
- createMetadataEntry,
11
- deserializeMessage,
12
- extractSessionInfo,
13
- generateEntryId,
14
- getDataDir,
15
- getDefaultSessionManager,
16
- getGitRootHash,
17
- getLeafId,
18
- getProjectId,
19
- getProjectSessionsDir,
20
- getSessionsDir,
21
- parseJSONL,
22
- serializeMessage,
23
- toJSONL,
24
- toJSONLBatch
25
- } from "./chunk-BDBZ3SLK.js";
26
1
  import {
27
2
  DEFAULT_MAX_CONCURRENT,
28
3
  DEFAULT_MAX_SPAWN_DEPTH,
29
4
  DEFAULT_SESSION_TITLE_PREFIX,
30
5
  SubAgentTracker,
31
- createSubAgentTools
32
- } from "./chunk-7MUFEN4K.js";
6
+ createSubAgentTools,
7
+ discoverAgentProfiles,
8
+ getProjectAgentsDir,
9
+ getUserAgentsDir,
10
+ isMarkdownProfile,
11
+ parseAgentFrontmatter,
12
+ parseMarkdownAgent,
13
+ parseToolSpec,
14
+ toAgentProfile
15
+ } from "./chunk-JLXG2SH7.js";
33
16
  import {
34
17
  ToolRegistry,
35
18
  defaultRegistry
@@ -39,21 +22,23 @@ import {
39
22
  clearCheckpoints,
40
23
  createCheckpointManager,
41
24
  createTurnTracker
42
- } from "./chunk-VBWWUHWI.js";
25
+ } from "./chunk-OFDKHNCX.js";
43
26
  import {
44
- Presets,
45
27
  applyPreset,
28
+ createPreset,
29
+ filterTools,
30
+ mergePresets
31
+ } from "./chunk-IYWQOJMQ.js";
32
+ import {
33
+ Presets,
46
34
  careful,
47
35
  code,
48
- createPreset,
49
36
  explore,
50
- filterTools,
51
- mergePresets,
52
37
  plan,
53
38
  quick,
54
39
  review,
55
40
  watch
56
- } from "./chunk-CAA7FHIH.js";
41
+ } from "./chunk-EKR6PKXU.js";
57
42
  import {
58
43
  DEFAULT_INSTRUCTION_PATTERNS,
59
44
  DEFAULT_MAX_DEPTH,
@@ -75,7 +60,7 @@ import {
75
60
  getTemplate,
76
61
  loadGlobalInstructions,
77
62
  summarizeEnvironment
78
- } from "./chunk-IVUJDISU.js";
63
+ } from "./chunk-GFTW23FV.js";
79
64
  import {
80
65
  AgentTurnEngine,
81
66
  ContextOverflowError,
@@ -108,15 +93,15 @@ import {
108
93
  runToolBatch,
109
94
  snapshotAgentWorkflowMessage,
110
95
  snapshotAgentWorkflowMessages
111
- } from "./chunk-4BDA7DQY.js";
96
+ } from "./chunk-J4QDGZIA.js";
112
97
  import {
113
98
  LocalSignal
114
- } from "./chunk-KUVSERLJ.js";
99
+ } from "./chunk-DXFBQMXP.js";
115
100
  import {
116
101
  createSkillResourceTool,
117
102
  createSkillTool,
118
103
  createSkillTools
119
- } from "./chunk-YUUJK53A.js";
104
+ } from "./chunk-RKEW5WXI.js";
120
105
  import {
121
106
  MAX_BYTES,
122
107
  MAX_LINES,
@@ -127,7 +112,7 @@ import {
127
112
  formatSize,
128
113
  normalizeToolReplayPolicy,
129
114
  truncateOutput
130
- } from "./chunk-P6YF7USR.js";
115
+ } from "./chunk-MHKK374K.js";
131
116
  import {
132
117
  DEFAULT_EXTERNAL_DIRS,
133
118
  DEFAULT_MAX_SCAN_DEPTH,
@@ -142,7 +127,32 @@ import {
142
127
  loadSkillContent,
143
128
  loadSkillMetadata,
144
129
  parseFrontmatter
145
- } from "./chunk-LRHOS4ZN.js";
130
+ } from "./chunk-SPILYYDF.js";
131
+ import {
132
+ FileStorage,
133
+ MemoryStorage,
134
+ STORAGE_VERSION,
135
+ SessionManager,
136
+ buildEntryPath,
137
+ buildMessagesFromEntries,
138
+ configureDefaultSessionManager,
139
+ createMessageEntry,
140
+ createMetadataEntry,
141
+ deserializeMessage,
142
+ extractSessionInfo,
143
+ generateEntryId,
144
+ getDataDir,
145
+ getDefaultSessionManager,
146
+ getGitRootHash,
147
+ getLeafId,
148
+ getProjectId,
149
+ getProjectSessionsDir,
150
+ getSessionsDir,
151
+ parseJSONL,
152
+ serializeMessage,
153
+ toJSONL,
154
+ toJSONLBatch
155
+ } from "./chunk-UHCJEM2E.js";
146
156
  import {
147
157
  ContextManager,
148
158
  DEFAULT_CONTEXT_LIMITS,
@@ -177,35 +187,23 @@ import {
177
187
  streamOnce,
178
188
  streamStep,
179
189
  withRetry
180
- } from "./chunk-N6HWIEEA.js";
190
+ } from "./chunk-WKHDSSXG.js";
181
191
  import {
182
192
  executeAgentToolCall
183
- } from "./chunk-7VKQ4WPB.js";
193
+ } from "./chunk-H3FUYU52.js";
184
194
  import {
185
195
  extractFilePathsFromArgs,
186
196
  shouldCaptureBaseline,
187
197
  withFileTracking
188
198
  } from "./chunk-VEKUXUVF.js";
189
199
  import {
190
- EXTENDED_LEVELS,
191
- FIXED_LEVELS,
192
- STANDARD_LEVELS,
193
- buildAnthropicOptions,
194
- buildBedrockOptions,
195
- buildGoogleOptions,
196
- buildGroqOptions,
197
- buildOpenAIOptions,
198
- buildOpenRouterOptions,
199
- buildReasoningOptions,
200
- buildReasoningOptionsSync,
201
- buildXAIOptions,
202
- getProviderOptionsKey,
203
- getReasoningConfig,
204
- getReasoningConfigSync,
205
- shouldIncludeReasoningSummary,
206
- supportsReasoning,
207
- supportsReasoningSync
208
- } from "./chunk-SQU2AJHO.js";
200
+ LLMError,
201
+ getErrorCategory,
202
+ getRetryDelay,
203
+ isRetryable,
204
+ isRetryableCategory,
205
+ parseRetryDelay
206
+ } from "./chunk-4QFNWPIF.js";
209
207
  import {
210
208
  createScope,
211
209
  currentScope,
@@ -214,14 +212,6 @@ import {
214
212
  streamWithinScope,
215
213
  withinScope
216
214
  } from "./chunk-N7P4PN3O.js";
217
- import {
218
- LLMError,
219
- getErrorCategory,
220
- getRetryDelay,
221
- isRetryable,
222
- isRetryableCategory,
223
- parseRetryDelay
224
- } from "./chunk-RZITT45F.js";
225
215
  import {
226
216
  createMCPManager,
227
217
  defineServer,
@@ -235,39 +225,187 @@ import {
235
225
  createTelemetryConfig,
236
226
  otelMiddleware,
237
227
  promptCacheMiddleware
238
- } from "./chunk-YSLSEQ6B.js";
228
+ } from "./chunk-MAZ5DY5B.js";
239
229
  import {
240
230
  ApprovalDeniedError,
241
231
  ApprovalTimeoutError,
242
232
  createApprovalHandler,
243
233
  getToolRisk
244
- } from "./chunk-BFM2YHNM.js";
234
+ } from "./chunk-5ARZJWD2.js";
245
235
  import {
246
236
  CacheCapabilitySource,
247
237
  CapabilityCache,
248
- DEFAULT_RESOLVER_OPTIONS,
238
+ EXTENDED_LEVELS,
239
+ FIXED_LEVELS,
249
240
  ModelCapabilityResolver,
250
- PatternCapabilitySource,
251
241
  RemoteCapabilityFetcher,
252
242
  RemoteCapabilitySource,
253
- SourcePriority,
243
+ STANDARD_LEVELS,
254
244
  applyCapabilityOverride,
245
+ buildAnthropicOptions,
246
+ buildBedrockOptions,
247
+ buildGoogleOptions,
248
+ buildGroqOptions,
249
+ buildOpenAIOptions,
250
+ buildOpenRouterOptions,
251
+ buildReasoningOptions,
252
+ buildReasoningOptionsSync,
253
+ buildXAIOptions,
255
254
  configureResolver,
256
255
  createResolver,
257
- extractModelId,
258
- extractProvider,
259
256
  findCapabilityOverride,
260
257
  getDefaultResolver,
261
258
  getNetworkStatus,
259
+ getProviderOptionsKey,
260
+ getReasoningConfig,
261
+ getReasoningConfigSync,
262
+ shouldIncludeReasoningSummary,
263
+ supportsReasoning,
264
+ supportsReasoningSync
265
+ } from "./chunk-UDCZ673N.js";
266
+ import {
267
+ DEFAULT_RESOLVER_OPTIONS,
268
+ PatternCapabilitySource,
269
+ SourcePriority,
270
+ extractModelId,
271
+ extractProvider,
272
+ getModelId,
262
273
  getProviderCompatibility,
274
+ getProviderId,
263
275
  inferContextWindow,
264
276
  inferProvider,
265
277
  likelySupportsReasoning
266
- } from "./chunk-RFEKJKTO.js";
278
+ } from "./chunk-I6PKJ7XQ.js";
267
279
  import {
268
- getModelId,
269
- getProviderId
270
- } from "./chunk-DWYX7ASF.js";
280
+ LayeredSettings,
281
+ NullSettings,
282
+ PluginEventBus,
283
+ PluginRegistry,
284
+ StaticSettings,
285
+ ValidatedSettings,
286
+ definePlugin,
287
+ discoverPlugins,
288
+ getPluginLoader,
289
+ isDefinedPlugin,
290
+ loadPluginModule,
291
+ resetFrameworkAliases,
292
+ resetPluginLoader,
293
+ resolveFrameworkAliases
294
+ } from "./chunk-WGZAPU6N.js";
295
+
296
+ // src/agent/intervention.ts
297
+ var InterventionController = class {
298
+ /** Immediate interventions — applied at the next step boundary */
299
+ immediate = [];
300
+ /** Deferred messages — held until the turn completes */
301
+ deferred = [];
302
+ /**
303
+ * Callback fired when an intervention is wired into a step.
304
+ * Set by the Agent before starting a chat turn, cleared after.
305
+ */
306
+ onApplied;
307
+ // ---------------------------------------------------------------------------
308
+ // Immediate interventions (mid-turn redirect)
309
+ // ---------------------------------------------------------------------------
310
+ /**
311
+ * Inject a message at the next LLM step boundary.
312
+ *
313
+ * The message is appended as a user message to the conversation
314
+ * before the next LLM call in the current multi-step turn. The
315
+ * LLM will see it and can adjust its behavior accordingly.
316
+ *
317
+ * Safe to call from any async context while `chat()` is running.
318
+ * If called when no turn is active, the message will be picked up
319
+ * by the first step of the next `chat()` call.
320
+ *
321
+ * @param message - The user message to inject
322
+ * @returns Intervention ID for tracking
323
+ */
324
+ intervene(message) {
325
+ const id = crypto.randomUUID();
326
+ this.immediate.push(
327
+ Object.freeze({ id, message, createdAt: /* @__PURE__ */ new Date() })
328
+ );
329
+ return id;
330
+ }
331
+ /**
332
+ * Drain and return all pending immediate interventions.
333
+ * The internal queue is cleared atomically.
334
+ *
335
+ * @internal Called by the LLM stream's `prepareStep` hook
336
+ */
337
+ drainImmediate() {
338
+ if (this.immediate.length === 0) return [];
339
+ return this.immediate.splice(0);
340
+ }
341
+ /** Adopt existing immediate interventions without changing their IDs. */
342
+ adoptImmediate(items) {
343
+ if (items.length === 0) return;
344
+ this.immediate.push(...items);
345
+ }
346
+ /** Whether there are pending immediate interventions */
347
+ get hasPending() {
348
+ return this.immediate.length > 0;
349
+ }
350
+ /** Number of pending immediate interventions */
351
+ get pendingCount() {
352
+ return this.immediate.length;
353
+ }
354
+ // ---------------------------------------------------------------------------
355
+ // Deferred messages (after-turn follow-ups)
356
+ // ---------------------------------------------------------------------------
357
+ /**
358
+ * Queue a message for after the current turn completes.
359
+ *
360
+ * Unlike `intervene()`, this does **not** inject mid-turn. The
361
+ * message is held and available via `drainDeferred()` after
362
+ * `chat()` finishes. The consumer decides whether to send it
363
+ * as a new turn.
364
+ *
365
+ * @param message - The message to queue
366
+ * @returns Intervention ID for tracking
367
+ */
368
+ queueNext(message) {
369
+ const id = crypto.randomUUID();
370
+ this.deferred.push(
371
+ Object.freeze({ id, message, createdAt: /* @__PURE__ */ new Date() })
372
+ );
373
+ return id;
374
+ }
375
+ /**
376
+ * Drain and return all deferred messages.
377
+ * The internal queue is cleared atomically.
378
+ */
379
+ drainDeferred() {
380
+ if (this.deferred.length === 0) return [];
381
+ return this.deferred.splice(0);
382
+ }
383
+ /** Adopt existing deferred interventions without changing their IDs. */
384
+ adoptDeferred(items) {
385
+ if (items.length === 0) return;
386
+ this.deferred.push(...items);
387
+ }
388
+ /** Whether there are deferred messages */
389
+ get hasDeferred() {
390
+ return this.deferred.length > 0;
391
+ }
392
+ /** Number of deferred messages */
393
+ get deferredCount() {
394
+ return this.deferred.length;
395
+ }
396
+ // ---------------------------------------------------------------------------
397
+ // Housekeeping
398
+ // ---------------------------------------------------------------------------
399
+ /** Clear all queues (immediate + deferred) */
400
+ clear() {
401
+ this.immediate.length = 0;
402
+ this.deferred.length = 0;
403
+ }
404
+ /** Reset the controller for a new turn (clears onApplied, keeps queues) */
405
+ resetCallbacks() {
406
+ this.onApplied = void 0;
407
+ }
408
+ };
271
409
 
272
410
  // src/agent/chat-loop/commit.ts
273
411
  function createChatLoopCommitBatchApplier(params) {
@@ -288,14 +426,240 @@ function createChatLoopCommitBatchApplier(params) {
288
426
  };
289
427
  }
290
428
 
429
+ // src/agent/session.ts
430
+ async function ensureSessionLoaded(options) {
431
+ const { sessionId, sessions, cwd } = options;
432
+ if (sessions.getSessionId() === sessionId) {
433
+ return;
434
+ }
435
+ const exists = await sessions.sessionExists(sessionId);
436
+ if (exists) {
437
+ await sessions.load(sessionId);
438
+ } else {
439
+ await sessions.create({ id: sessionId, cwd });
440
+ }
441
+ }
442
+ async function repairOrphanedToolCalls(sessions) {
443
+ const messages = sessions.getMessages();
444
+ const pendingCallIds = /* @__PURE__ */ new Map();
445
+ for (const message of messages) {
446
+ if (message.role === "assistant" && message.toolCalls) {
447
+ for (const toolCall of message.toolCalls) {
448
+ pendingCallIds.set(toolCall.toolCallId, { toolName: toolCall.toolName });
449
+ }
450
+ }
451
+ if (message.role === "tool" && message.toolCallId) {
452
+ pendingCallIds.delete(message.toolCallId);
453
+ }
454
+ }
455
+ for (const [toolCallId, { toolName }] of pendingCallIds) {
456
+ const toolMessage = {
457
+ id: crypto.randomUUID(),
458
+ role: "tool",
459
+ content: "Error: tool execution failed (result was not recorded)",
460
+ toolCallId,
461
+ toolName,
462
+ result: "Error: tool execution failed (result was not recorded)",
463
+ createdAt: /* @__PURE__ */ new Date()
464
+ };
465
+ await sessions.addMessage(toolMessage);
466
+ }
467
+ }
468
+ async function createSubAgentRunSession(options) {
469
+ const sessionId = options.parentSessionId ? `${options.parentSessionId}:sub:${crypto.randomUUID().slice(0, 8)}` : `sub:${crypto.randomUUID().slice(0, 8)}`;
470
+ await options.sessions.create({
471
+ id: sessionId,
472
+ cwd: options.cwd,
473
+ title: options.title ?? "Sub-agent task",
474
+ parentSessionId: options.parentSessionId
475
+ });
476
+ return sessionId;
477
+ }
478
+ function getVisibleSessionMessages(sessions) {
479
+ const leafId = sessions.getLeafId();
480
+ if (!leafId) {
481
+ return [];
482
+ }
483
+ const path = buildEntryPath(
484
+ sessions.getEntries(),
485
+ leafId
486
+ );
487
+ const visible = [];
488
+ let skipUntilId;
489
+ for (const entry of path) {
490
+ if (entry.type === "compaction") {
491
+ visible.push({
492
+ message: {
493
+ id: entry.id,
494
+ role: "system",
495
+ content: `## Previous Conversation Summary
496
+
497
+ ${entry.summary}`,
498
+ createdAt: new Date(entry.timestamp)
499
+ }
500
+ });
501
+ skipUntilId = entry.firstKeptEntryId;
502
+ continue;
503
+ }
504
+ if (skipUntilId && entry.id !== skipUntilId) {
505
+ continue;
506
+ }
507
+ if (entry.id === skipUntilId) {
508
+ skipUntilId = void 0;
509
+ }
510
+ if (entry.type === "message") {
511
+ visible.push({
512
+ message: deserializeMessage(entry.message)
513
+ });
514
+ }
515
+ }
516
+ return visible;
517
+ }
518
+
519
+ // src/agent/runtime-config.ts
520
+ function createAgentToolRecord(tools) {
521
+ const toolRecord = {};
522
+ for (const [id, tool] of tools) {
523
+ toolRecord[id] = tool;
524
+ }
525
+ return toolRecord;
526
+ }
527
+ function getAgentContextStats(contextManager, messages) {
528
+ return contextManager.getStats(messages);
529
+ }
530
+ function buildFallbackCompactionSummary(messages) {
531
+ const excerpt = messages.map((message) => {
532
+ const role = message.role.toUpperCase();
533
+ const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
534
+ return `[${role}] ${content}`;
535
+ }).join("\n\n");
536
+ const trimmed = excerpt.length > 8e3 ? `${excerpt.slice(0, 8e3)}
537
+
538
+ ...[truncated during compaction]` : excerpt;
539
+ return trimmed.length > 0 ? trimmed : "Earlier conversation context was compacted.";
540
+ }
541
+ async function compactAgentContext(options) {
542
+ const { contextManager, sessions } = options;
543
+ const visibleMessages = getVisibleSessionMessages(sessions);
544
+ const messages = visibleMessages.map((item) => item.message);
545
+ if (!contextManager.shouldPrune(messages)) {
546
+ return {
547
+ removedCount: 0,
548
+ tokensRemoved: 0,
549
+ summarized: false,
550
+ summary: void 0
551
+ };
552
+ }
553
+ const tokensBefore = estimateConversationTokens(messages);
554
+ let removedCount = 0;
555
+ let summarized = false;
556
+ let summary;
557
+ try {
558
+ const result = await contextManager.prune(messages);
559
+ removedCount = result.removedCount;
560
+ summarized = result.summarized;
561
+ summary = result.summary;
562
+ } catch {
563
+ }
564
+ if (removedCount === 0) {
565
+ removedCount = findCutPoint(
566
+ messages,
567
+ contextManager.getLimits().protectedTokens
568
+ );
569
+ }
570
+ if (removedCount === 0) {
571
+ return {
572
+ removedCount: 0,
573
+ tokensRemoved: 0,
574
+ summarized: false,
575
+ summary: void 0
576
+ };
577
+ }
578
+ const summaryText = summary ?? buildFallbackCompactionSummary(messages.slice(0, removedCount));
579
+ const keptMessages = messages.slice(removedCount);
580
+ const persistedMessages = [
581
+ {
582
+ id: "compaction-preview",
583
+ role: "system",
584
+ content: `## Previous Conversation Summary
585
+
586
+ ${summaryText}`,
587
+ createdAt: /* @__PURE__ */ new Date()
588
+ },
589
+ ...keptMessages
590
+ ];
591
+ const tokensAfter = estimateConversationTokens(persistedMessages);
592
+ await sessions.replaceWithCompaction({
593
+ summary: summaryText,
594
+ messages: keptMessages,
595
+ tokensBefore,
596
+ tokensAfter
597
+ });
598
+ return {
599
+ removedCount,
600
+ tokensRemoved: Math.max(0, tokensBefore - tokensAfter),
601
+ summarized,
602
+ summary: summaryText
603
+ };
604
+ }
605
+ async function buildAgentSystemPrompts(options) {
606
+ const {
607
+ config,
608
+ toolIds,
609
+ sessionId,
610
+ override,
611
+ promptBuilder,
612
+ middlewareRunner
613
+ } = options;
614
+ if (promptBuilder) {
615
+ const composedPrompt = await promptBuilder.build(
616
+ {
617
+ cwd: config.cwd,
618
+ model: config.model,
619
+ toolNames: toolIds,
620
+ ...override ? { override } : {},
621
+ sessionId
622
+ },
623
+ middlewareRunner
624
+ );
625
+ return [composedPrompt];
626
+ }
627
+ const prompts = [config.systemPrompt];
628
+ if (override) {
629
+ prompts.push(override);
630
+ }
631
+ return prompts;
632
+ }
633
+ function createAgentTurnRuntimeConfig(options) {
634
+ const { config, telemetrySettings } = options;
635
+ return {
636
+ model: config.model,
637
+ cwd: config.cwd,
638
+ ...config.temperature !== void 0 ? { temperature: config.temperature } : {},
639
+ ...config.topP !== void 0 ? { topP: config.topP } : {},
640
+ ...config.maxOutputTokens !== void 0 ? { maxOutputTokens: config.maxOutputTokens } : {},
641
+ maxSteps: config.maxSteps,
642
+ ...config.doomLoopThreshold !== void 0 ? { doomLoopThreshold: config.doomLoopThreshold } : {},
643
+ ...config.enforceDoomLoop !== void 0 ? { enforceDoomLoop: config.enforceDoomLoop } : {},
644
+ ...config.onDoomLoop ? { onDoomLoop: config.onDoomLoop } : {},
645
+ ...config.contextWindow !== void 0 ? { contextWindow: config.contextWindow } : {},
646
+ ...config.streamProvider ? { streamProvider: config.streamProvider } : {},
647
+ ...telemetrySettings ? { telemetry: telemetrySettings } : {}
648
+ };
649
+ }
650
+
291
651
  // src/agent/chat-loop/compaction.ts
292
652
  async function runAutoCompaction(params) {
293
- const { contextManager, messages } = params;
653
+ const { contextManager, sessions } = params;
654
+ const messages = sessions.getMessages();
294
655
  if (!contextManager.shouldPrune(messages)) {
295
656
  return [];
296
657
  }
297
658
  const events = [{ type: "status", status: "processing" }];
298
- const pruneResult = await contextManager.prune(messages);
659
+ const pruneResult = await compactAgentContext({
660
+ contextManager,
661
+ sessions
662
+ });
299
663
  if (pruneResult.removedCount > 0 || pruneResult.summarized) {
300
664
  const limits = contextManager.getLimits();
301
665
  events.push({
@@ -309,32 +673,14 @@ async function runAutoCompaction(params) {
309
673
 
310
674
  // src/agent/chat-loop/prompts.ts
311
675
  async function buildChatSystemPrompts(params) {
312
- const {
313
- promptBuilder,
314
- middlewareRunner,
315
- systemOverride,
316
- sessionId,
317
- config,
318
- tools
319
- } = params;
320
- if (promptBuilder) {
321
- const composedPrompt = await promptBuilder.build(
322
- {
323
- cwd: config.cwd,
324
- model: config.model,
325
- toolNames: Object.keys(tools),
326
- override: systemOverride,
327
- sessionId
328
- },
329
- middlewareRunner
330
- );
331
- return [composedPrompt];
332
- }
333
- const prompts = [config.systemPrompt];
334
- if (systemOverride) {
335
- prompts.push(systemOverride);
336
- }
337
- return prompts;
676
+ return buildAgentSystemPrompts({
677
+ config: params.config,
678
+ toolIds: Object.keys(params.tools),
679
+ sessionId: params.sessionId,
680
+ override: params.systemOverride,
681
+ promptBuilder: params.promptBuilder,
682
+ middlewareRunner: params.middlewareRunner
683
+ });
338
684
  }
339
685
 
340
686
  // src/agent/chat-loop/usage.ts
@@ -483,7 +829,7 @@ async function* runChatLoop(deps) {
483
829
  if (config.compaction?.auto !== false) {
484
830
  const compactionEvents = await runAutoCompaction({
485
831
  contextManager,
486
- messages: sessions.getMessages()
832
+ sessions
487
833
  });
488
834
  for (const event of compactionEvents) {
489
835
  yield event;
@@ -513,6 +859,12 @@ async function* runChatLoop(deps) {
513
859
  };
514
860
  } catch (error) {
515
861
  chatError = error instanceof Error ? error : new Error(String(error));
862
+ const statusEvent = { type: "status", status: "error" };
863
+ middlewareRunner.emitEvent(statusEvent);
864
+ yield statusEvent;
865
+ const errorEvent = { type: "error", error: chatError };
866
+ middlewareRunner.emitEvent(errorEvent);
867
+ yield errorEvent;
516
868
  throw error;
517
869
  } finally {
518
870
  setIsStreaming(false);
@@ -552,255 +904,100 @@ function resolveChildPromptConfig(options) {
552
904
  if (promptBuilder) {
553
905
  return { prompt: parentPromptConfig ?? {} };
554
906
  }
555
- return { systemPrompt: parentSystemPrompt };
556
- }
557
- function resolveForkMiddleware(options, middlewareRunner) {
558
- if (options.middleware) {
559
- return options.middleware;
560
- }
561
- if (options.additionalMiddleware) {
562
- return [
563
- ...middlewareRunner.getMiddleware(),
564
- ...options.additionalMiddleware
565
- ];
566
- }
567
- return [...middlewareRunner.getMiddleware()];
568
- }
569
- function createForkedAgentConfig(options) {
570
- const {
571
- forkOptions,
572
- parentConfig,
573
- parentTools,
574
- reasoningLevel,
575
- host,
576
- sessions,
577
- mcpManager,
578
- middlewareRunner,
579
- promptBuilder
580
- } = options;
581
- const tools = forkOptions.tools ?? parentTools;
582
- const childPrompt = resolveChildPromptConfig({
583
- forkOptions,
584
- promptBuilder,
585
- parentPromptConfig: parentConfig.prompt,
586
- parentSystemPrompt: parentConfig.systemPrompt
587
- });
588
- return {
589
- model: forkOptions.model ?? parentConfig.model,
590
- cwd: parentConfig.cwd,
591
- host,
592
- maxOutputTokens: parentConfig.maxOutputTokens,
593
- maxSteps: forkOptions.maxSteps ?? parentConfig.maxSteps,
594
- temperature: forkOptions.temperature ?? parentConfig.temperature,
595
- topP: parentConfig.topP,
596
- reasoningLevel: forkOptions.reasoningLevel ?? reasoningLevel,
597
- ...childPrompt,
598
- sessionManager: new SessionManager(sessions.getStorage()),
599
- tools,
600
- onDoomLoop: parentConfig.onDoomLoop,
601
- enforceDoomLoop: parentConfig.enforceDoomLoop,
602
- doomLoopThreshold: parentConfig.doomLoopThreshold,
603
- compaction: parentConfig.compaction,
604
- contextWindow: parentConfig.contextWindow,
605
- mcp: mcpManager,
606
- middleware: resolveForkMiddleware(forkOptions, middlewareRunner)
607
- };
608
- }
609
-
610
- // src/agent/mcp-bridge.ts
611
- async function ensureMcpTools(state) {
612
- if (!state.manager) {
613
- return { connected: false, cachedTools: void 0 };
614
- }
615
- if (state.connected && state.cachedTools) {
616
- return {
617
- connected: state.connected,
618
- cachedTools: state.cachedTools
619
- };
620
- }
621
- const statuses = await state.manager.connect();
622
- let connectedCount = 0;
623
- for (const [name, status] of statuses) {
624
- if (status.status === "connected") {
625
- connectedCount++;
626
- } else if (status.status === "error") {
627
- console.warn(`[mcp] Failed to connect to ${name}: ${status.error}`);
628
- }
629
- }
630
- if (connectedCount === 0 && statuses.size > 0) {
631
- console.warn("[mcp] No MCP servers connected successfully");
632
- }
633
- const cachedTools = await state.manager.getTools();
634
- return {
635
- connected: true,
636
- cachedTools
637
- };
638
- }
639
- async function closeMcpManager(manager) {
640
- if (manager) {
641
- await manager.close();
642
- }
643
- return {
644
- connected: false,
645
- cachedTools: void 0
646
- };
647
- }
648
-
649
- // src/agent/session.ts
650
- async function ensureSessionLoaded(options) {
651
- const { sessionId, sessions, loadedSessions, cwd } = options;
652
- if (loadedSessions.has(sessionId) && sessions.getSessionId() === sessionId) {
653
- return;
654
- }
655
- const exists = await sessions.sessionExists(sessionId);
656
- if (exists) {
657
- await sessions.load(sessionId);
658
- } else {
659
- await sessions.create({ id: sessionId, cwd });
660
- }
661
- loadedSessions.add(sessionId);
662
- }
663
- async function repairOrphanedToolCalls(sessions) {
664
- const messages = sessions.getMessages();
665
- const pendingCallIds = /* @__PURE__ */ new Map();
666
- for (const message of messages) {
667
- if (message.role === "assistant" && message.toolCalls) {
668
- for (const toolCall of message.toolCalls) {
669
- pendingCallIds.set(toolCall.toolCallId, { toolName: toolCall.toolName });
670
- }
671
- }
672
- if (message.role === "tool" && message.toolCallId) {
673
- pendingCallIds.delete(message.toolCallId);
674
- }
675
- }
676
- for (const [toolCallId, { toolName }] of pendingCallIds) {
677
- const toolMessage = {
678
- id: crypto.randomUUID(),
679
- role: "tool",
680
- content: "Error: tool execution failed (result was not recorded)",
681
- toolCallId,
682
- toolName,
683
- result: "Error: tool execution failed (result was not recorded)",
684
- createdAt: /* @__PURE__ */ new Date()
685
- };
686
- await sessions.addMessage(toolMessage);
687
- }
688
- }
689
- async function createSubAgentRunSession(options) {
690
- const sessionId = options.parentSessionId ? `${options.parentSessionId}:sub:${crypto.randomUUID().slice(0, 8)}` : `sub:${crypto.randomUUID().slice(0, 8)}`;
691
- await options.sessions.create({
692
- id: sessionId,
693
- cwd: options.cwd,
694
- title: options.title ?? "Sub-agent task",
695
- parentSessionId: options.parentSessionId
696
- });
697
- options.loadedSessions.add(sessionId);
698
- return sessionId;
699
- }
700
-
701
- // src/agent/intervention.ts
702
- var InterventionController = class {
703
- /** Immediate interventions — applied at the next step boundary */
704
- immediate = [];
705
- /** Deferred messages — held until the turn completes */
706
- deferred = [];
707
- /**
708
- * Callback fired when an intervention is wired into a step.
709
- * Set by the Agent before starting a chat turn, cleared after.
710
- */
711
- onApplied;
712
- // ---------------------------------------------------------------------------
713
- // Immediate interventions (mid-turn redirect)
714
- // ---------------------------------------------------------------------------
715
- /**
716
- * Inject a message at the next LLM step boundary.
717
- *
718
- * The message is appended as a user message to the conversation
719
- * before the next LLM call in the current multi-step turn. The
720
- * LLM will see it and can adjust its behavior accordingly.
721
- *
722
- * Safe to call from any async context while `chat()` is running.
723
- * If called when no turn is active, the message will be picked up
724
- * by the first step of the next `chat()` call.
725
- *
726
- * @param message - The user message to inject
727
- * @returns Intervention ID for tracking
728
- */
729
- intervene(message) {
730
- const id = crypto.randomUUID();
731
- this.immediate.push(
732
- Object.freeze({ id, message, createdAt: /* @__PURE__ */ new Date() })
733
- );
734
- return id;
735
- }
736
- /**
737
- * Drain and return all pending immediate interventions.
738
- * The internal queue is cleared atomically.
739
- *
740
- * @internal Called by the LLM stream's `prepareStep` hook
741
- */
742
- drainImmediate() {
743
- if (this.immediate.length === 0) return [];
744
- return this.immediate.splice(0);
745
- }
746
- /** Whether there are pending immediate interventions */
747
- get hasPending() {
748
- return this.immediate.length > 0;
749
- }
750
- /** Number of pending immediate interventions */
751
- get pendingCount() {
752
- return this.immediate.length;
907
+ return { systemPrompt: parentSystemPrompt };
908
+ }
909
+ function resolveForkMiddleware(options, middlewareRunner) {
910
+ if (options.middleware) {
911
+ return options.middleware;
753
912
  }
754
- // ---------------------------------------------------------------------------
755
- // Deferred messages (after-turn follow-ups)
756
- // ---------------------------------------------------------------------------
757
- /**
758
- * Queue a message for after the current turn completes.
759
- *
760
- * Unlike `intervene()`, this does **not** inject mid-turn. The
761
- * message is held and available via `drainDeferred()` after
762
- * `chat()` finishes. The consumer decides whether to send it
763
- * as a new turn.
764
- *
765
- * @param message - The message to queue
766
- * @returns Intervention ID for tracking
767
- */
768
- queueNext(message) {
769
- const id = crypto.randomUUID();
770
- this.deferred.push(
771
- Object.freeze({ id, message, createdAt: /* @__PURE__ */ new Date() })
772
- );
773
- return id;
913
+ if (options.additionalMiddleware) {
914
+ return [
915
+ ...middlewareRunner.getMiddleware(),
916
+ ...options.additionalMiddleware
917
+ ];
774
918
  }
775
- /**
776
- * Drain and return all deferred messages.
777
- * The internal queue is cleared atomically.
778
- */
779
- drainDeferred() {
780
- if (this.deferred.length === 0) return [];
781
- return this.deferred.splice(0);
919
+ return [...middlewareRunner.getMiddleware()];
920
+ }
921
+ function createForkedAgentConfig(options) {
922
+ const {
923
+ forkOptions,
924
+ parentConfig,
925
+ parentTools,
926
+ reasoningLevel,
927
+ host,
928
+ ownsHost,
929
+ sessions,
930
+ mcpManager,
931
+ middlewareRunner,
932
+ promptBuilder
933
+ } = options;
934
+ const tools = forkOptions.tools ?? parentTools;
935
+ const childPrompt = resolveChildPromptConfig({
936
+ forkOptions,
937
+ promptBuilder,
938
+ parentPromptConfig: parentConfig.prompt,
939
+ parentSystemPrompt: parentConfig.systemPrompt
940
+ });
941
+ return {
942
+ model: forkOptions.model ?? parentConfig.model,
943
+ cwd: parentConfig.cwd,
944
+ ...ownsHost ? {} : { host },
945
+ maxOutputTokens: parentConfig.maxOutputTokens,
946
+ maxSteps: forkOptions.maxSteps ?? parentConfig.maxSteps,
947
+ temperature: forkOptions.temperature ?? parentConfig.temperature,
948
+ topP: parentConfig.topP,
949
+ reasoningLevel: forkOptions.reasoningLevel ?? reasoningLevel,
950
+ ...childPrompt,
951
+ sessionManager: new SessionManager(sessions.getStorage()),
952
+ tools,
953
+ onDoomLoop: parentConfig.onDoomLoop,
954
+ enforceDoomLoop: parentConfig.enforceDoomLoop,
955
+ doomLoopThreshold: parentConfig.doomLoopThreshold,
956
+ compaction: parentConfig.compaction,
957
+ contextWindow: parentConfig.contextWindow,
958
+ mcp: mcpManager,
959
+ middleware: resolveForkMiddleware(forkOptions, middlewareRunner)
960
+ };
961
+ }
962
+
963
+ // src/agent/mcp-bridge.ts
964
+ async function ensureMcpTools(state) {
965
+ if (!state.manager) {
966
+ return { connected: false, cachedTools: void 0 };
782
967
  }
783
- /** Whether there are deferred messages */
784
- get hasDeferred() {
785
- return this.deferred.length > 0;
968
+ if (state.connected && state.cachedTools) {
969
+ return {
970
+ connected: state.connected,
971
+ cachedTools: state.cachedTools
972
+ };
786
973
  }
787
- /** Number of deferred messages */
788
- get deferredCount() {
789
- return this.deferred.length;
974
+ const statuses = await state.manager.connect();
975
+ let connectedCount = 0;
976
+ for (const [name, status] of statuses) {
977
+ if (status.status === "connected") {
978
+ connectedCount++;
979
+ } else if (status.status === "error") {
980
+ console.warn(`[mcp] Failed to connect to ${name}: ${status.error}`);
981
+ }
790
982
  }
791
- // ---------------------------------------------------------------------------
792
- // Housekeeping
793
- // ---------------------------------------------------------------------------
794
- /** Clear all queues (immediate + deferred) */
795
- clear() {
796
- this.immediate.length = 0;
797
- this.deferred.length = 0;
983
+ if (connectedCount === 0 && statuses.size > 0) {
984
+ console.warn("[mcp] No MCP servers connected successfully");
798
985
  }
799
- /** Reset the controller for a new turn (clears onApplied, keeps queues) */
800
- resetCallbacks() {
801
- this.onApplied = void 0;
986
+ const cachedTools = await state.manager.getTools();
987
+ return {
988
+ connected: true,
989
+ cachedTools
990
+ };
991
+ }
992
+ async function closeMcpManager(manager) {
993
+ if (manager) {
994
+ await manager.close();
802
995
  }
803
- };
996
+ return {
997
+ connected: false,
998
+ cachedTools: void 0
999
+ };
1000
+ }
804
1001
 
805
1002
  // src/agent/stream-provider.ts
806
1003
  var DEFAULT_CUSTOM_STREAM_MODELS = [
@@ -808,7 +1005,7 @@ var DEFAULT_CUSTOM_STREAM_MODELS = [
808
1005
  "computer-use-preview-2025-03-11"
809
1006
  ];
810
1007
  function needsCustomStreamProvider(model, customPatterns) {
811
- const modelId = getModelId(model);
1008
+ const modelId = extractModelId(model);
812
1009
  if (!modelId || modelId === "[object Object]") return false;
813
1010
  const patterns = customPatterns ?? DEFAULT_CUSTOM_STREAM_MODELS;
814
1011
  return patterns.some((pattern) => modelId.includes(pattern));
@@ -824,7 +1021,7 @@ function autoDetectStreamProvider(model, tools, explicitProvider) {
824
1021
  const enhancedTools = tools;
825
1022
  const customPatterns = enhancedTools.__customStreamModels;
826
1023
  if (needsCustomStreamProvider(model, customPatterns) && hasStreamProviderFactory(enhancedTools)) {
827
- const modelId = getModelId(model);
1024
+ const modelId = extractModelId(model);
828
1025
  if (modelId) {
829
1026
  const streamConfig = {
830
1027
  apiKey: process.env.OPENAI_API_KEY,
@@ -900,24 +1097,39 @@ function createAgentState(config) {
900
1097
  }
901
1098
  function createAgentContextManager(config) {
902
1099
  const compactionConfig = config.compaction ?? {};
903
- const modelId = getModelId(config.model);
1100
+ const modelId = extractModelId(config.model);
904
1101
  const inferredWindow = inferContextWindow(modelId);
905
1102
  const contextWindow = config.contextWindow ?? inferredWindow ?? DEFAULT_CONTEXT_LIMITS.contextWindow;
906
- const reserveTokens = Math.min(
907
- 32e3,
908
- Math.max(8e3, Math.round(contextWindow * 0.12))
909
- );
910
1103
  return new ContextManager({
911
1104
  limits: {
912
1105
  contextWindow,
913
- reserveTokens,
914
- protectedTokens: compactionConfig.protectedTokens ?? DEFAULT_CONTEXT_LIMITS.protectedTokens,
915
- pruneMinimum: compactionConfig.pruneMinimum ?? DEFAULT_CONTEXT_LIMITS.pruneMinimum
1106
+ ...resolveAgentContextLimits(contextWindow, compactionConfig)
916
1107
  },
917
1108
  model: compactionConfig.summaryModel ?? config.model,
918
1109
  summaryPrompt: compactionConfig.summaryPrompt
919
1110
  });
920
1111
  }
1112
+ function resolveAgentContextLimits(contextWindow, compactionConfig = {}) {
1113
+ const proportionalReserve = Math.round(contextWindow * 0.12);
1114
+ const reserveTokens = contextWindow >= 64e3 ? Math.min(32e3, Math.max(8e3, proportionalReserve)) : Math.min(
1115
+ Math.max(1024, Math.round(contextWindow * 0.25)),
1116
+ Math.max(512, proportionalReserve)
1117
+ );
1118
+ const usableWindow = Math.max(512, contextWindow - reserveTokens);
1119
+ const requestedProtected = compactionConfig.protectedTokens ?? DEFAULT_CONTEXT_LIMITS.protectedTokens;
1120
+ const requestedPruneMinimum = compactionConfig.pruneMinimum ?? DEFAULT_CONTEXT_LIMITS.pruneMinimum;
1121
+ return {
1122
+ reserveTokens,
1123
+ protectedTokens: Math.min(
1124
+ requestedProtected,
1125
+ Math.max(512, Math.round(usableWindow * 0.6))
1126
+ ),
1127
+ pruneMinimum: Math.min(
1128
+ requestedPruneMinimum,
1129
+ Math.max(256, Math.round(usableWindow * 0.5))
1130
+ )
1131
+ };
1132
+ }
921
1133
  function createMiddlewareSetup(input, config) {
922
1134
  let effectiveMiddleware = input.middleware;
923
1135
  let telemetrySettings;
@@ -962,78 +1174,13 @@ function createAgentSetup(input) {
962
1174
  ...promptBuilder ? { promptBuilder } : {},
963
1175
  interventionCtrl: new InterventionController(),
964
1176
  host: input.host ?? localHost(config.cwd),
1177
+ ownsHost: input.host === void 0,
965
1178
  middlewareRunner: middlewareSetup.middlewareRunner,
966
1179
  ...middlewareSetup.telemetrySettings ? { telemetrySettings: middlewareSetup.telemetrySettings } : {},
967
1180
  ...middlewareSetup.tracingShutdown ? { tracingShutdown: middlewareSetup.tracingShutdown } : {}
968
1181
  };
969
1182
  }
970
1183
 
971
- // src/agent/runtime-config.ts
972
- function createAgentToolRecord(tools) {
973
- const toolRecord = {};
974
- for (const [id, tool] of tools) {
975
- toolRecord[id] = tool;
976
- }
977
- return toolRecord;
978
- }
979
- function getAgentContextStats(contextManager, messages) {
980
- return contextManager.getStats(messages);
981
- }
982
- async function compactAgentContext(contextManager, messages) {
983
- const result = await contextManager.prune(messages);
984
- return {
985
- removedCount: result.removedCount,
986
- tokensRemoved: result.tokensRemoved,
987
- summarized: result.summarized,
988
- summary: result.summary
989
- };
990
- }
991
- async function buildAgentSystemPrompts(options) {
992
- const {
993
- config,
994
- toolIds,
995
- sessionId,
996
- override,
997
- promptBuilder,
998
- middlewareRunner
999
- } = options;
1000
- if (promptBuilder) {
1001
- const composedPrompt = await promptBuilder.build(
1002
- {
1003
- cwd: config.cwd,
1004
- model: config.model,
1005
- toolNames: toolIds,
1006
- ...override ? { override } : {},
1007
- sessionId
1008
- },
1009
- middlewareRunner
1010
- );
1011
- return [composedPrompt];
1012
- }
1013
- const prompts = [config.systemPrompt];
1014
- if (override) {
1015
- prompts.push(override);
1016
- }
1017
- return prompts;
1018
- }
1019
- function createAgentTurnRuntimeConfig(options) {
1020
- const { config, telemetrySettings } = options;
1021
- return {
1022
- model: config.model,
1023
- cwd: config.cwd,
1024
- ...config.temperature !== void 0 ? { temperature: config.temperature } : {},
1025
- ...config.topP !== void 0 ? { topP: config.topP } : {},
1026
- ...config.maxOutputTokens !== void 0 ? { maxOutputTokens: config.maxOutputTokens } : {},
1027
- maxSteps: config.maxSteps,
1028
- ...config.doomLoopThreshold !== void 0 ? { doomLoopThreshold: config.doomLoopThreshold } : {},
1029
- ...config.enforceDoomLoop !== void 0 ? { enforceDoomLoop: config.enforceDoomLoop } : {},
1030
- ...config.onDoomLoop ? { onDoomLoop: config.onDoomLoop } : {},
1031
- ...config.contextWindow !== void 0 ? { contextWindow: config.contextWindow } : {},
1032
- ...config.streamProvider ? { streamProvider: config.streamProvider } : {},
1033
- ...telemetrySettings ? { telemetry: telemetrySettings } : {}
1034
- };
1035
- }
1036
-
1037
1184
  // src/agent/instance.ts
1038
1185
  function createAgent(config) {
1039
1186
  return new Agent(config);
@@ -1042,7 +1189,6 @@ var Agent = class _Agent {
1042
1189
  config;
1043
1190
  tools;
1044
1191
  sessions;
1045
- loadedSessions = /* @__PURE__ */ new Set();
1046
1192
  state;
1047
1193
  /** Context manager for overflow detection and compaction */
1048
1194
  contextManager;
@@ -1066,6 +1212,8 @@ var Agent = class _Agent {
1066
1212
  interventionCtrl;
1067
1213
  /** Execution environment for tool operations */
1068
1214
  host;
1215
+ /** Whether the agent owns the host and can safely recreate it on cwd changes */
1216
+ ownsHost;
1069
1217
  /** Middleware runner for lifecycle hooks */
1070
1218
  middlewareRunner;
1071
1219
  /** AI SDK telemetry settings (auto-created from `tracing` config) */
@@ -1074,6 +1222,12 @@ var Agent = class _Agent {
1074
1222
  tracingShutdown;
1075
1223
  /** Multi-consumer event dispatch */
1076
1224
  _signal;
1225
+ /** Number of active turns across all sessions */
1226
+ activeTurnCount = 0;
1227
+ /** Active turn intervention controllers, keyed by turn id */
1228
+ activeTurns = /* @__PURE__ */ new Map();
1229
+ /** Per-session turn queue to prevent concurrent writes to the same history */
1230
+ sessionLocks = /* @__PURE__ */ new Map();
1077
1231
  constructor(config) {
1078
1232
  const setup = createAgentSetup(config);
1079
1233
  this.config = setup.config;
@@ -1086,6 +1240,7 @@ var Agent = class _Agent {
1086
1240
  this.promptBuilder = setup.promptBuilder;
1087
1241
  this.interventionCtrl = setup.interventionCtrl;
1088
1242
  this.host = setup.host;
1243
+ this.ownsHost = setup.ownsHost;
1089
1244
  this.middlewareRunner = setup.middlewareRunner;
1090
1245
  this.telemetrySettings = setup.telemetrySettings;
1091
1246
  this.tracingShutdown = setup.tracingShutdown;
@@ -1119,7 +1274,7 @@ var Agent = class _Agent {
1119
1274
  }
1120
1275
  /** Is currently streaming */
1121
1276
  get isStreaming() {
1122
- return this.state.isStreaming;
1277
+ return this.activeTurnCount > 0;
1123
1278
  }
1124
1279
  /** Current reasoning level */
1125
1280
  get reasoningLevel() {
@@ -1162,6 +1317,74 @@ var Agent = class _Agent {
1162
1317
  }
1163
1318
  }
1164
1319
  }
1320
+ resetPromptScopedTools() {
1321
+ this.tools.delete("skill");
1322
+ this.tools.delete("skill_resource");
1323
+ this.skillToolsResolved = false;
1324
+ }
1325
+ createSessionManager() {
1326
+ return new SessionManager(this.sessions.getStorage());
1327
+ }
1328
+ async acquireSessionLock(sessionId) {
1329
+ const previous = this.sessionLocks.get(sessionId) ?? Promise.resolve();
1330
+ let releaseLock;
1331
+ const current = new Promise((resolve) => {
1332
+ releaseLock = resolve;
1333
+ });
1334
+ const chain = previous.catch(() => void 0).then(() => current);
1335
+ this.sessionLocks.set(sessionId, chain);
1336
+ await previous.catch(() => void 0);
1337
+ let released = false;
1338
+ return () => {
1339
+ if (released) return;
1340
+ released = true;
1341
+ releaseLock();
1342
+ if (this.sessionLocks.get(sessionId) === chain) {
1343
+ this.sessionLocks.delete(sessionId);
1344
+ }
1345
+ };
1346
+ }
1347
+ getActiveInterventionController() {
1348
+ const active = Array.from(this.activeTurns.values());
1349
+ if (active.length === 0) {
1350
+ return void 0;
1351
+ }
1352
+ if (active.length > 1) {
1353
+ throw new Error(
1354
+ "Interventions are ambiguous while multiple agent turns are active. Use separate Agent instances or wait for one turn to finish."
1355
+ );
1356
+ }
1357
+ return active[0].interventionCtrl;
1358
+ }
1359
+ getInterventionControllerForTurn() {
1360
+ const activeController = this.getActiveInterventionController();
1361
+ return activeController ?? this.interventionCtrl;
1362
+ }
1363
+ createTurnInterventionController() {
1364
+ return this.activeTurns.size === 0 ? this.interventionCtrl : new InterventionController();
1365
+ }
1366
+ releaseTurnInterventions(controller) {
1367
+ if (controller === this.interventionCtrl) {
1368
+ controller.resetCallbacks();
1369
+ return;
1370
+ }
1371
+ this.interventionCtrl.adoptImmediate(controller.drainImmediate());
1372
+ this.interventionCtrl.adoptDeferred(controller.drainDeferred());
1373
+ controller.resetCallbacks();
1374
+ }
1375
+ syncStreamingState(active) {
1376
+ this.activeTurnCount = Math.max(
1377
+ 0,
1378
+ this.activeTurnCount + (active ? 1 : -1)
1379
+ );
1380
+ this.state.isStreaming = this.activeTurnCount > 0;
1381
+ }
1382
+ async syncSessionView(sessionId) {
1383
+ if (!await this.sessions.sessionExists(sessionId)) {
1384
+ return;
1385
+ }
1386
+ await this.sessions.load(sessionId);
1387
+ }
1165
1388
  /**
1166
1389
  * Ensure MCP is connected and return tools
1167
1390
  * Lazy initialization - only connects on first use
@@ -1185,8 +1408,8 @@ var Agent = class _Agent {
1185
1408
  * `MissingToolResultsError`. This method detects orphaned tool-call IDs
1186
1409
  * and adds placeholder `tool` messages so the history is valid again.
1187
1410
  */
1188
- async repairOrphanedToolCalls() {
1189
- await repairOrphanedToolCalls(this.sessions);
1411
+ async repairOrphanedToolCalls(sessions) {
1412
+ await repairOrphanedToolCalls(sessions);
1190
1413
  }
1191
1414
  /**
1192
1415
  * Convert internal {@link Message} array to Vercel AI SDK {@link ModelMessage} format.
@@ -1212,52 +1435,75 @@ var Agent = class _Agent {
1212
1435
  * @yields {AgentEvent} Events as they occur during processing
1213
1436
  */
1214
1437
  async *chat(sessionId, message, options) {
1215
- await this.ensureSession(sessionId);
1216
- await this.ensureSkillTools();
1217
- const abort = options?.abort ?? new AbortController().signal;
1438
+ const releaseSessionLock = await this.acquireSessionLock(sessionId);
1439
+ const sessions = this.createSessionManager();
1218
1440
  const turnId = `${sessionId}-turn-${++this.turnCounter}`;
1219
- this.turnTracker.startTurn(turnId);
1220
- await this.repairOrphanedToolCalls();
1221
- const mcpTools = await this.ensureMCPConnected();
1222
- const loop = runChatLoop({
1223
- sessionId,
1224
- message,
1225
- abort,
1226
- systemOverride: options?.system,
1227
- sessions: this.sessions,
1228
- tools: createAgentToolRecord(this.tools),
1229
- config: this.config,
1230
- turnTracker: this.turnTracker,
1231
- interventionCtrl: this.interventionCtrl,
1232
- middlewareRunner: this.middlewareRunner,
1233
- contextManager: this.contextManager,
1234
- rememberedDoomLoopTools: this.rememberedDoomLoopTools,
1235
- reasoningLevel: this.state.reasoningLevel,
1236
- promptBuilder: this.promptBuilder,
1237
- host: this.host,
1238
- mcpTools,
1239
- telemetrySettings: this.telemetrySettings,
1240
- toModelMessages: this.toModelMessages.bind(this),
1241
- setIsStreaming: (v) => {
1242
- this.state.isStreaming = v;
1441
+ let turnTracker;
1442
+ let interventionCtrl = this.interventionCtrl;
1443
+ let streamingStateActive = false;
1444
+ let turnRegistered = false;
1445
+ try {
1446
+ await this.ensureSkillTools();
1447
+ const abort = options?.abort ?? new AbortController().signal;
1448
+ turnTracker = createTurnTracker({ cwd: this.config.cwd });
1449
+ interventionCtrl = this.createTurnInterventionController();
1450
+ this.activeTurns.set(turnId, { sessionId, interventionCtrl });
1451
+ turnRegistered = true;
1452
+ await ensureSessionLoaded({
1453
+ sessionId,
1454
+ sessions,
1455
+ cwd: this.config.cwd
1456
+ });
1457
+ turnTracker.startTurn(turnId);
1458
+ await this.repairOrphanedToolCalls(sessions);
1459
+ const mcpTools = await this.ensureMCPConnected();
1460
+ const loop = runChatLoop({
1461
+ sessionId,
1462
+ message,
1463
+ abort,
1464
+ systemOverride: options?.system,
1465
+ sessions,
1466
+ tools: createAgentToolRecord(this.tools),
1467
+ config: this.config,
1468
+ turnTracker,
1469
+ interventionCtrl,
1470
+ middlewareRunner: this.middlewareRunner,
1471
+ contextManager: this.contextManager,
1472
+ rememberedDoomLoopTools: this.rememberedDoomLoopTools,
1473
+ reasoningLevel: this.state.reasoningLevel,
1474
+ promptBuilder: this.promptBuilder,
1475
+ host: this.host,
1476
+ mcpTools,
1477
+ telemetrySettings: this.telemetrySettings,
1478
+ toModelMessages: this.toModelMessages.bind(this),
1479
+ setIsStreaming: (value) => {
1480
+ if (value === streamingStateActive) return;
1481
+ streamingStateActive = value;
1482
+ this.syncStreamingState(value);
1483
+ }
1484
+ });
1485
+ for await (const event of loop) {
1486
+ this._signal.emit(event);
1487
+ yield event;
1488
+ }
1489
+ } finally {
1490
+ try {
1491
+ if (streamingStateActive) {
1492
+ this.syncStreamingState(false);
1493
+ }
1494
+ if (turnRegistered) {
1495
+ this.activeTurns.delete(turnId);
1496
+ }
1497
+ this.releaseTurnInterventions(interventionCtrl);
1498
+ if (turnRegistered && turnTracker) {
1499
+ this.turnTracker = turnTracker;
1500
+ }
1501
+ await this.syncSessionView(sessionId);
1502
+ } finally {
1503
+ releaseSessionLock();
1243
1504
  }
1244
- });
1245
- for await (const event of loop) {
1246
- this._signal.emit(event);
1247
- yield event;
1248
1505
  }
1249
1506
  }
1250
- /**
1251
- * Ensure a session is loaded or created
1252
- */
1253
- async ensureSession(sessionId) {
1254
- await ensureSessionLoaded({
1255
- sessionId,
1256
- sessions: this.sessions,
1257
- loadedSessions: this.loadedSessions,
1258
- cwd: this.config.cwd
1259
- });
1260
- }
1261
1507
  /**
1262
1508
  * Send a message and wait for the complete response (non-streaming).
1263
1509
  *
@@ -1332,7 +1578,6 @@ var Agent = class _Agent {
1332
1578
  }
1333
1579
  /** Delete a session */
1334
1580
  async deleteSession(sessionId) {
1335
- this.loadedSessions.delete(sessionId);
1336
1581
  return this.sessions.deleteSession(sessionId);
1337
1582
  }
1338
1583
  /** List all sessions */
@@ -1382,10 +1627,10 @@ var Agent = class _Agent {
1382
1627
  * @returns Pruning result with details about what was removed/summarized
1383
1628
  */
1384
1629
  async compactContext() {
1385
- return await compactAgentContext(
1386
- this.contextManager,
1387
- this.sessions.getMessages()
1388
- );
1630
+ return await compactAgentContext({
1631
+ contextManager: this.contextManager,
1632
+ sessions: this.sessions
1633
+ });
1389
1634
  }
1390
1635
  /**
1391
1636
  * Clear remembered doom loop tools.
@@ -1415,6 +1660,8 @@ var Agent = class _Agent {
1415
1660
  *
1416
1661
  * If called when no turn is active, the message will be picked up
1417
1662
  * by the first step of the next `chat()` call.
1663
+ * If multiple turns are active concurrently, this throws because the
1664
+ * target turn would be ambiguous.
1418
1665
  *
1419
1666
  * @param message - The user message to inject mid-turn
1420
1667
  * @returns Intervention ID for tracking
@@ -1436,7 +1683,7 @@ var Agent = class _Agent {
1436
1683
  * ```
1437
1684
  */
1438
1685
  intervene(message) {
1439
- return this.interventionCtrl.intervene(message);
1686
+ return this.getInterventionControllerForTurn().intervene(message);
1440
1687
  }
1441
1688
  /**
1442
1689
  * Queue a message for after the current turn completes.
@@ -1446,6 +1693,9 @@ var Agent = class _Agent {
1446
1693
  * `chat()` finishes. The consumer decides whether to send it as a
1447
1694
  * new turn.
1448
1695
  *
1696
+ * If multiple turns are active concurrently, this throws because the
1697
+ * target turn would be ambiguous.
1698
+ *
1449
1699
  * @param message - The message to queue
1450
1700
  * @returns Intervention ID for tracking
1451
1701
  *
@@ -1468,15 +1718,15 @@ var Agent = class _Agent {
1468
1718
  * ```
1469
1719
  */
1470
1720
  queueNext(message) {
1471
- return this.interventionCtrl.queueNext(message);
1721
+ return this.getInterventionControllerForTurn().queueNext(message);
1472
1722
  }
1473
1723
  /** Whether there are deferred messages queued for after the turn */
1474
1724
  hasQueuedNext() {
1475
- return this.interventionCtrl.hasDeferred;
1725
+ return this.getInterventionControllerForTurn().hasDeferred;
1476
1726
  }
1477
1727
  /** Drain and return all deferred messages (clears the queue) */
1478
1728
  drainQueuedNext() {
1479
- return this.interventionCtrl.drainDeferred();
1729
+ return this.getInterventionControllerForTurn().drainDeferred();
1480
1730
  }
1481
1731
  /**
1482
1732
  * Get the raw intervention controller for advanced use cases.
@@ -1488,7 +1738,7 @@ var Agent = class _Agent {
1488
1738
  * @internal
1489
1739
  */
1490
1740
  getInterventionController() {
1491
- return this.interventionCtrl;
1741
+ return this.getInterventionControllerForTurn();
1492
1742
  }
1493
1743
  // ============================================================================
1494
1744
  // Turn Tracking
@@ -1565,26 +1815,37 @@ var Agent = class _Agent {
1565
1815
  this.config.systemPrompt = prompt;
1566
1816
  this.state.systemPrompt = prompt;
1567
1817
  this.promptBuilder = void 0;
1818
+ this.resetPromptScopedTools();
1568
1819
  }
1569
1820
  /** Update working directory */
1570
1821
  setCwd(cwd) {
1571
1822
  this.config.cwd = cwd;
1572
1823
  this.state.cwd = cwd;
1824
+ this.turnTracker = createTurnTracker({ cwd });
1825
+ if (this.ownsHost) {
1826
+ this.host = localHost(cwd);
1827
+ }
1828
+ this.resetPromptScopedTools();
1573
1829
  this.promptBuilder?.clearCache();
1574
1830
  }
1575
1831
  /** Update model */
1576
1832
  setModel(model) {
1577
1833
  this.config.model = model;
1578
1834
  this.state.model = model;
1835
+ if (this.config.compaction?.summaryModel === void 0) {
1836
+ this.contextManager.setModel(model);
1837
+ }
1579
1838
  if (this.config.contextWindow === void 0) {
1580
- const modelId = getModelId(model);
1839
+ const modelId = extractModelId(model);
1581
1840
  const inferred = inferContextWindow(modelId);
1582
1841
  if (inferred) {
1583
- const reserveTokens = Math.min(
1584
- 32e3,
1585
- Math.max(8e3, Math.round(inferred * 0.12))
1586
- );
1587
- this.contextManager.setLimits({ contextWindow: inferred, reserveTokens });
1842
+ this.contextManager.setLimits({
1843
+ contextWindow: inferred,
1844
+ ...resolveAgentContextLimits(
1845
+ inferred,
1846
+ this.config.compaction
1847
+ )
1848
+ });
1588
1849
  }
1589
1850
  }
1590
1851
  }
@@ -1624,6 +1885,7 @@ var Agent = class _Agent {
1624
1885
  * runtimes can start durable turns without duplicating prompt assembly.
1625
1886
  */
1626
1887
  async buildSystemPrompts(sessionId, override) {
1888
+ await this.ensureSkillTools();
1627
1889
  return await buildAgentSystemPrompts({
1628
1890
  config: this.config,
1629
1891
  toolIds: Array.from(this.tools.keys()),
@@ -1699,6 +1961,7 @@ var Agent = class _Agent {
1699
1961
  parentTools: Array.from(this.tools.values()),
1700
1962
  reasoningLevel: this.state.reasoningLevel,
1701
1963
  host: this.host,
1964
+ ownsHost: this.ownsHost,
1702
1965
  sessions: this.sessions,
1703
1966
  mcpManager: this.mcpManager,
1704
1967
  middlewareRunner: this.middlewareRunner,
@@ -1750,9 +2013,9 @@ var Agent = class _Agent {
1750
2013
  * ```
1751
2014
  */
1752
2015
  async run(options) {
2016
+ const sessions = this.createSessionManager();
1753
2017
  const sessionId = await createSubAgentRunSession({
1754
- sessions: this.sessions,
1755
- loadedSessions: this.loadedSessions,
2018
+ sessions,
1756
2019
  cwd: this.config.cwd,
1757
2020
  parentSessionId: options.parentSessionId,
1758
2021
  title: options.title
@@ -1821,6 +2084,7 @@ async function runConcurrent(tasks, options) {
1821
2084
  let currentIndex = 0;
1822
2085
  async function runNext() {
1823
2086
  while (currentIndex < tasks.length) {
2087
+ if (options?.abort?.aborted) break;
1824
2088
  const index = currentIndex++;
1825
2089
  const task = tasks[index];
1826
2090
  results[index] = await task.agent.run({
@@ -1871,12 +2135,14 @@ export {
1871
2135
  InterventionController,
1872
2136
  LLM,
1873
2137
  LLMError,
2138
+ LayeredSettings,
1874
2139
  LocalSignal,
1875
2140
  MAX_BYTES,
1876
2141
  MAX_LINES,
1877
2142
  MemoryStorage,
1878
2143
  MiddlewareRunner,
1879
2144
  ModelCapabilityResolver,
2145
+ NullSettings,
1880
2146
  OUTPUT_TOKEN_MAX,
1881
2147
  PRIORITY_BASE,
1882
2148
  PRIORITY_CUSTOM,
@@ -1886,6 +2152,8 @@ export {
1886
2152
  PRIORITY_SKILLS,
1887
2153
  PRUNE_PROTECTED_TOOLS,
1888
2154
  PatternCapabilitySource,
2155
+ PluginEventBus,
2156
+ PluginRegistry,
1889
2157
  Presets,
1890
2158
  PromptBuilder,
1891
2159
  RemoteCapabilityFetcher,
@@ -1896,12 +2164,14 @@ export {
1896
2164
  SessionManager,
1897
2165
  SkillRegistry,
1898
2166
  SourcePriority,
2167
+ StaticSettings,
1899
2168
  SubAgentTracker,
1900
2169
  TRUNCATE_DIR,
1901
2170
  TRUNCATE_GLOB,
1902
2171
  Tool,
1903
2172
  ToolRegistry,
1904
2173
  TurnChangeTracker,
2174
+ ValidatedSettings,
1905
2175
  advanceAgentTurnState,
1906
2176
  applyAgentWorkflowCommitResult,
1907
2177
  applyAgentWorkflowModelStepResult,
@@ -1960,11 +2230,14 @@ export {
1960
2230
  currentScope,
1961
2231
  defaultAgentTaskCheckpointStrategy,
1962
2232
  defaultRegistry,
2233
+ definePlugin,
1963
2234
  defineServer,
1964
2235
  defineTool,
1965
2236
  deserializeMessage,
1966
2237
  detectModelFamily,
2238
+ discoverAgentProfiles,
1967
2239
  discoverInstructions,
2240
+ discoverPlugins,
1968
2241
  discoverSkills,
1969
2242
  dockerHost,
1970
2243
  emptySkillRegistry,
@@ -1997,6 +2270,8 @@ export {
1997
2270
  getLeafId,
1998
2271
  getModelId,
1999
2272
  getNetworkStatus,
2273
+ getPluginLoader,
2274
+ getProjectAgentsDir,
2000
2275
  getProjectId,
2001
2276
  getProjectSessionsDir,
2002
2277
  getProviderCompatibility,
@@ -2008,16 +2283,20 @@ export {
2008
2283
  getSessionsDir,
2009
2284
  getTemplate,
2010
2285
  getToolRisk,
2286
+ getUserAgentsDir,
2011
2287
  hasStreamProviderFactory,
2012
2288
  httpServer,
2013
2289
  inferContextWindow,
2014
2290
  inferProvider,
2015
2291
  inferResourceType,
2016
2292
  isContextOverflowing,
2293
+ isDefinedPlugin,
2294
+ isMarkdownProfile,
2017
2295
  isRetryable,
2018
2296
  isRetryableCategory,
2019
2297
  likelySupportsReasoning,
2020
2298
  loadGlobalInstructions,
2299
+ loadPluginModule,
2021
2300
  loadResourceContent,
2022
2301
  loadSkillContent,
2023
2302
  loadSkillMetadata,
@@ -2026,9 +2305,12 @@ export {
2026
2305
  needsCustomStreamProvider,
2027
2306
  normalizeToolReplayPolicy,
2028
2307
  otelMiddleware,
2308
+ parseAgentFrontmatter,
2029
2309
  parseFrontmatter,
2030
2310
  parseJSONL,
2311
+ parseMarkdownAgent,
2031
2312
  parseRetryDelay,
2313
+ parseToolSpec,
2032
2314
  plan,
2033
2315
  planNextAgentWorkflowOperation,
2034
2316
  prepareModelStep,
@@ -2039,6 +2321,9 @@ export {
2039
2321
  pruneToolResults,
2040
2322
  quick,
2041
2323
  recordAgentWorkflowReplayDecision,
2324
+ resetFrameworkAliases,
2325
+ resetPluginLoader,
2326
+ resolveFrameworkAliases,
2042
2327
  restoreAgentWorkflowMessage,
2043
2328
  restoreAgentWorkflowMessages,
2044
2329
  restoreScope,
@@ -2064,6 +2349,7 @@ export {
2064
2349
  summarizeEnvironment,
2065
2350
  supportsReasoning,
2066
2351
  supportsReasoningSync,
2352
+ toAgentProfile,
2067
2353
  toJSONL,
2068
2354
  toJSONLBatch,
2069
2355
  truncateOutput,