omk-agent-core 0.98.0 → 0.98.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/harness/agent-harness.d.ts +9 -8
  2. package/dist/harness/agent-harness.d.ts.map +1 -1
  3. package/dist/harness/agent-harness.js +145 -182
  4. package/dist/harness/agent-harness.js.map +1 -1
  5. package/dist/harness/compaction/branch-summarization.d.ts +5 -1
  6. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  7. package/dist/harness/compaction/branch-summarization.js +3 -3
  8. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  9. package/dist/harness/compaction/compaction.d.ts +8 -4
  10. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  11. package/dist/harness/compaction/compaction.js +12 -97
  12. package/dist/harness/compaction/compaction.js.map +1 -1
  13. package/dist/harness/compaction/operation.d.ts +6 -0
  14. package/dist/harness/compaction/operation.d.ts.map +1 -0
  15. package/dist/harness/compaction/operation.js +2 -0
  16. package/dist/harness/compaction/operation.js.map +1 -0
  17. package/dist/harness/compaction/summarization-prompts.d.ts +5 -0
  18. package/dist/harness/compaction/summarization-prompts.d.ts.map +1 -0
  19. package/dist/harness/compaction/summarization-prompts.js +88 -0
  20. package/dist/harness/compaction/summarization-prompts.js.map +1 -0
  21. package/dist/harness/errors.d.ts +15 -0
  22. package/dist/harness/errors.d.ts.map +1 -0
  23. package/dist/harness/errors.js +32 -0
  24. package/dist/harness/errors.js.map +1 -0
  25. package/dist/harness/harness-session.d.ts +105 -0
  26. package/dist/harness/harness-session.d.ts.map +1 -0
  27. package/dist/harness/harness-session.js +116 -0
  28. package/dist/harness/harness-session.js.map +1 -0
  29. package/dist/harness/messages.d.ts +3 -1
  30. package/dist/harness/messages.d.ts.map +1 -1
  31. package/dist/harness/messages.js +26 -0
  32. package/dist/harness/messages.js.map +1 -1
  33. package/dist/harness/name-validation.d.ts +2 -0
  34. package/dist/harness/name-validation.d.ts.map +1 -0
  35. package/dist/harness/name-validation.js +11 -0
  36. package/dist/harness/name-validation.js.map +1 -0
  37. package/dist/harness/stream-options.d.ts +31 -0
  38. package/dist/harness/stream-options.d.ts.map +1 -0
  39. package/dist/harness/stream-options.js +66 -0
  40. package/dist/harness/stream-options.js.map +1 -0
  41. package/dist/harness/summarization-retry.d.ts +29 -0
  42. package/dist/harness/summarization-retry.d.ts.map +1 -0
  43. package/dist/harness/summarization-retry.js +20 -0
  44. package/dist/harness/summarization-retry.js.map +1 -0
  45. package/dist/harness/types.d.ts +36 -41
  46. package/dist/harness/types.d.ts.map +1 -1
  47. package/dist/harness/types.js +1 -32
  48. package/dist/harness/types.js.map +1 -1
  49. package/package.json +2 -2
@@ -1,111 +1,15 @@
1
- import { streamSimple } from "omk-ai";
2
- import { runAgentLoop } from "../agent-loop.js";
1
+ import { isContextOverflow, streamSimple, } from "omk-ai";
2
+ import { runAgentLoop, runAgentLoopContinue } from "../agent-loop.js";
3
3
  import { collectEntriesForBranchSummary, generateBranchSummary } from "./compaction/branch-summarization.js";
4
- import { compact, DEFAULT_COMPACTION_SETTINGS, prepareCompaction } from "./compaction/compaction.js";
5
- import { convertToLlm } from "./messages.js";
4
+ import { compact, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, prepareCompaction, shouldCompact, } from "./compaction/compaction.js";
5
+ import { HarnessSessionFacade } from "./harness-session.js";
6
+ import { convertToLlm, createFailureMessage, createUserMessage } from "./messages.js";
7
+ import { findDuplicateNames } from "./name-validation.js";
6
8
  import { formatPromptTemplateInvocation } from "./prompt-templates.js";
7
9
  import { formatSkillInvocation } from "./skills.js";
10
+ import { applyStreamOptionsPatch, cloneStreamOptions, mergeHeaders } from "./stream-options.js";
11
+ import { createSummarizationRetry } from "./summarization-retry.js";
8
12
  import { AgentHarnessError, BranchSummaryError, CompactionError, SessionError, toError } from "./types.js";
9
- function createUserMessage(text, images) {
10
- const content = [{ type: "text", text }];
11
- if (images)
12
- content.push(...images);
13
- return { role: "user", content, timestamp: Date.now() };
14
- }
15
- function createFailureMessage(model, error, aborted) {
16
- return {
17
- role: "assistant",
18
- content: [{ type: "text", text: "" }],
19
- api: model.api,
20
- provider: model.provider,
21
- model: model.id,
22
- stopReason: aborted ? "aborted" : "error",
23
- errorMessage: error instanceof Error ? error.message : String(error),
24
- timestamp: Date.now(),
25
- usage: {
26
- input: 0,
27
- output: 0,
28
- cacheRead: 0,
29
- cacheWrite: 0,
30
- totalTokens: 0,
31
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
32
- },
33
- };
34
- }
35
- function cloneStreamOptions(streamOptions) {
36
- return {
37
- ...streamOptions,
38
- headers: streamOptions?.headers ? { ...streamOptions.headers } : undefined,
39
- metadata: streamOptions?.metadata ? { ...streamOptions.metadata } : undefined,
40
- };
41
- }
42
- function mergeHeaders(...headers) {
43
- const merged = {};
44
- let hasHeaders = false;
45
- for (const entry of headers) {
46
- if (!entry)
47
- continue;
48
- Object.assign(merged, entry);
49
- hasHeaders = true;
50
- }
51
- return hasHeaders ? merged : undefined;
52
- }
53
- function findDuplicateNames(names) {
54
- const seen = new Set();
55
- const duplicates = new Set();
56
- for (const name of names) {
57
- if (seen.has(name))
58
- duplicates.add(name);
59
- seen.add(name);
60
- }
61
- return [...duplicates];
62
- }
63
- function applyStreamOptionsPatch(base, patch) {
64
- const result = cloneStreamOptions(base);
65
- if (!patch)
66
- return result;
67
- if (Object.hasOwn(patch, "transport"))
68
- result.transport = patch.transport;
69
- if (Object.hasOwn(patch, "timeoutMs"))
70
- result.timeoutMs = patch.timeoutMs;
71
- if (Object.hasOwn(patch, "maxRetries"))
72
- result.maxRetries = patch.maxRetries;
73
- if (Object.hasOwn(patch, "maxRetryDelayMs"))
74
- result.maxRetryDelayMs = patch.maxRetryDelayMs;
75
- if (Object.hasOwn(patch, "cacheRetention"))
76
- result.cacheRetention = patch.cacheRetention;
77
- if (Object.hasOwn(patch, "headers")) {
78
- if (patch.headers === undefined) {
79
- result.headers = undefined;
80
- }
81
- else {
82
- const headers = { ...(result.headers ?? {}) };
83
- for (const [key, value] of Object.entries(patch.headers)) {
84
- if (value === undefined)
85
- delete headers[key];
86
- else
87
- headers[key] = value;
88
- }
89
- result.headers = Object.keys(headers).length > 0 ? headers : undefined;
90
- }
91
- }
92
- if (Object.hasOwn(patch, "metadata")) {
93
- if (patch.metadata === undefined) {
94
- result.metadata = undefined;
95
- }
96
- else {
97
- const metadata = { ...(result.metadata ?? {}) };
98
- for (const [key, value] of Object.entries(patch.metadata)) {
99
- if (value === undefined)
100
- delete metadata[key];
101
- else
102
- metadata[key] = value;
103
- }
104
- result.metadata = Object.keys(metadata).length > 0 ? metadata : undefined;
105
- }
106
- }
107
- return result;
108
- }
109
13
  const SUBSCRIBER_EVENT_TYPE = "*";
110
14
  function normalizeHarnessError(error, fallbackCode) {
111
15
  if (error instanceof AgentHarnessError)
@@ -119,12 +23,10 @@ function normalizeHarnessError(error, fallbackCode) {
119
23
  return new AgentHarnessError("branch_summary", cause.message, cause);
120
24
  return new AgentHarnessError(fallbackCode, cause.message, cause);
121
25
  }
122
- function normalizeHookError(error) {
123
- return normalizeHarnessError(error, "hook");
124
- }
125
26
  export class AgentHarness {
126
27
  env;
127
28
  session;
29
+ sessionFacade;
128
30
  phase = "idle";
129
31
  runAbortController;
130
32
  runPromise;
@@ -133,6 +35,7 @@ export class AgentHarness {
133
35
  thinkingLevel;
134
36
  systemPrompt;
135
37
  streamOptions;
38
+ compactionSettings;
136
39
  getApiKeyAndHeaders;
137
40
  resources;
138
41
  tools = new Map();
@@ -146,8 +49,10 @@ export class AgentHarness {
146
49
  constructor(options) {
147
50
  this.env = options.env;
148
51
  this.session = options.session;
52
+ this.sessionFacade = new HarnessSessionFacade(this.session, () => this.phase, this.pendingSessionWrites);
149
53
  this.resources = options.resources ?? {};
150
54
  this.streamOptions = cloneStreamOptions(options.streamOptions);
55
+ this.compactionSettings = { ...DEFAULT_COMPACTION_SETTINGS, ...options.compaction };
151
56
  this.systemPrompt = options.systemPrompt;
152
57
  this.getApiKeyAndHeaders = options.getApiKeyAndHeaders;
153
58
  this.validateUniqueNames((options.tools ?? []).map((tool) => tool.name), "Duplicate tool name(s)");
@@ -164,31 +69,28 @@ export class AgentHarness {
164
69
  this.steeringQueueMode = options.steeringMode ?? "one-at-a-time";
165
70
  this.followUpQueueMode = options.followUpMode ?? "one-at-a-time";
166
71
  }
167
- getHandlers(type) {
168
- return this.handlers.get(type);
169
- }
170
72
  async emitOwn(event, signal) {
171
- for (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {
73
+ for (const listener of this.handlers.get(SUBSCRIBER_EVENT_TYPE) ?? []) {
172
74
  try {
173
75
  await listener(event, signal);
174
76
  }
175
77
  catch (error) {
176
- throw normalizeHookError(error);
78
+ throw normalizeHarnessError(error, "hook");
177
79
  }
178
80
  }
179
81
  }
180
82
  async emitAny(event, signal) {
181
- for (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {
83
+ for (const listener of this.handlers.get(SUBSCRIBER_EVENT_TYPE) ?? []) {
182
84
  try {
183
85
  await listener(event, signal);
184
86
  }
185
87
  catch (error) {
186
- throw normalizeHookError(error);
88
+ throw normalizeHarnessError(error, "hook");
187
89
  }
188
90
  }
189
91
  }
190
92
  async emitHook(event) {
191
- const handlers = this.getHandlers(event.type);
93
+ const handlers = this.handlers.get(event.type);
192
94
  if (!handlers || handlers.size === 0)
193
95
  return undefined;
194
96
  let lastResult;
@@ -200,13 +102,13 @@ export class AgentHarness {
200
102
  }
201
103
  }
202
104
  catch (error) {
203
- throw normalizeHookError(error);
105
+ throw normalizeHarnessError(error, "hook");
204
106
  }
205
107
  }
206
108
  return lastResult;
207
109
  }
208
110
  async emitBeforeProviderRequest(model, sessionId, streamOptions) {
209
- const handlers = this.getHandlers("before_provider_request");
111
+ const handlers = this.handlers.get("before_provider_request");
210
112
  let current = cloneStreamOptions(streamOptions);
211
113
  if (!handlers || handlers.size === 0)
212
114
  return current;
@@ -223,13 +125,13 @@ export class AgentHarness {
223
125
  }
224
126
  }
225
127
  catch (error) {
226
- throw normalizeHookError(error);
128
+ throw normalizeHarnessError(error, "hook");
227
129
  }
228
130
  }
229
131
  return current;
230
132
  }
231
133
  async emitBeforeProviderPayload(model, payload) {
232
- const handlers = this.getHandlers("before_provider_payload");
134
+ const handlers = this.handlers.get("before_provider_payload");
233
135
  let current = payload;
234
136
  if (!handlers || handlers.size === 0)
235
137
  return current;
@@ -241,7 +143,7 @@ export class AgentHarness {
241
143
  }
242
144
  }
243
145
  catch (error) {
244
- throw normalizeHookError(error);
146
+ throw normalizeHarnessError(error, "hook");
245
147
  }
246
148
  }
247
149
  return current;
@@ -316,13 +218,14 @@ export class AgentHarness {
316
218
  createStreamFn(getTurnState) {
317
219
  return async (model, context, streamOptions) => {
318
220
  const turnState = getTurnState();
221
+ const requestContext = await this.maybeAutoCompact(model, context, streamOptions?.signal);
319
222
  const auth = await this.getApiKeyAndHeaders?.(model);
320
223
  const snapshotOptions = {
321
224
  ...turnState.streamOptions,
322
225
  headers: mergeHeaders(turnState.streamOptions.headers, auth?.headers),
323
226
  };
324
227
  const requestOptions = await this.emitBeforeProviderRequest(model, turnState.sessionId, snapshotOptions);
325
- return streamSimple(model, context, {
228
+ return streamSimple(model, requestContext, {
326
229
  cacheRetention: requestOptions.cacheRetention,
327
230
  headers: requestOptions.headers,
328
231
  maxRetries: requestOptions.maxRetries,
@@ -352,7 +255,7 @@ export class AgentHarness {
352
255
  }
353
256
  catch (error) {
354
257
  queue.unshift(...messages);
355
- throw normalizeHookError(error);
258
+ throw normalizeHarnessError(error, "hook");
356
259
  }
357
260
  }
358
261
  createLoopConfig(getTurnState, setTurnState) {
@@ -492,7 +395,7 @@ export class AgentHarness {
492
395
  return messages;
493
396
  }
494
397
  async executeTurn(turnState, text, options) {
495
- let activeTurnState = turnState;
398
+ const runOwner = this.runPromise;
496
399
  let messages = [createUserMessage(text, options?.images)];
497
400
  if (this.nextTurnQueue.length > 0) {
498
401
  const queuedMessages = this.nextTurnQueue.splice(0);
@@ -501,7 +404,7 @@ export class AgentHarness {
501
404
  }
502
405
  catch (error) {
503
406
  this.nextTurnQueue.unshift(...queuedMessages);
504
- throw normalizeHookError(error);
407
+ throw normalizeHarnessError(error, "hook");
505
408
  }
506
409
  messages = [...queuedMessages, messages[0]];
507
410
  }
@@ -514,6 +417,11 @@ export class AgentHarness {
514
417
  });
515
418
  if (beforeResult?.messages)
516
419
  messages = [...messages, ...beforeResult.messages];
420
+ const result = await this.executeAgentRun(turnState, this.createContext(turnState, beforeResult?.systemPrompt), messages);
421
+ return await this.recoverContextOverflow(result, runOwner);
422
+ }
423
+ async executeAgentRun(turnState, context, initialMessages) {
424
+ let activeTurnState = turnState;
517
425
  const abortController = new AbortController();
518
426
  const getTurnState = () => activeTurnState;
519
427
  const setTurnState = (nextTurnState) => {
@@ -521,13 +429,18 @@ export class AgentHarness {
521
429
  };
522
430
  this.runAbortController = abortController;
523
431
  const completedMessages = [];
432
+ const emit = async (event) => {
433
+ if (event.type === "message_end")
434
+ completedMessages.push(event.message);
435
+ await this.handleAgentEvent(event, abortController.signal);
436
+ };
524
437
  const runResultPromise = (async () => {
525
438
  try {
526
- return await runAgentLoop(messages, this.createContext(turnState, beforeResult?.systemPrompt), this.createLoopConfig(getTurnState, setTurnState), async (event) => {
527
- if (event.type === "message_end")
528
- completedMessages.push(event.message);
529
- await this.handleAgentEvent(event, abortController.signal);
530
- }, abortController.signal, this.createStreamFn(getTurnState));
439
+ const loopConfig = this.createLoopConfig(getTurnState, setTurnState);
440
+ const streamFn = this.createStreamFn(getTurnState);
441
+ return initialMessages
442
+ ? await runAgentLoop(initialMessages, context, loopConfig, emit, abortController.signal, streamFn)
443
+ : await runAgentLoopContinue(context, loopConfig, emit, abortController.signal, streamFn);
531
444
  }
532
445
  catch (error) {
533
446
  try {
@@ -543,9 +456,8 @@ export class AgentHarness {
543
456
  const newMessages = await runResultPromise;
544
457
  for (let i = newMessages.length - 1; i >= 0; i--) {
545
458
  const message = newMessages[i];
546
- if (message.role === "assistant") {
459
+ if (message.role === "assistant")
547
460
  return message;
548
- }
549
461
  }
550
462
  throw new AgentHarnessError("invalid_state", "AgentHarness prompt completed without an assistant message");
551
463
  }
@@ -554,14 +466,47 @@ export class AgentHarness {
554
466
  await this.flushPendingSessionWrites();
555
467
  }
556
468
  finally {
557
- // Ownership check: a re-entrant run started from a settled/agent_end
558
- // listener may have installed its own controller already.
559
- if (this.runAbortController === abortController) {
469
+ if (this.runAbortController === abortController)
560
470
  this.runAbortController = undefined;
561
- }
562
471
  }
563
472
  }
564
473
  }
474
+ async recoverContextOverflow(message, runOwner) {
475
+ if (this.runPromise !== runOwner ||
476
+ this.phase !== "idle" ||
477
+ !this.compactionSettings.enabled ||
478
+ !isContextOverflow(message, this.model.contextWindow)) {
479
+ return message;
480
+ }
481
+ if (!this.getApiKeyAndHeaders)
482
+ return message;
483
+ const leafId = await this.session.getLeafId();
484
+ if (!leafId)
485
+ return message;
486
+ const leaf = await this.session.getEntry(leafId);
487
+ if (leaf?.type !== "message" ||
488
+ leaf.message.role !== "assistant" ||
489
+ leaf.message.timestamp !== message.timestamp ||
490
+ !isContextOverflow(leaf.message, this.model.contextWindow)) {
491
+ return message;
492
+ }
493
+ await this.session.moveTo(leaf.parentId);
494
+ this.phase = "retry";
495
+ try {
496
+ const compacted = await this.runCompaction({ automatic: true });
497
+ if (!compacted) {
498
+ await this.session.moveTo(leafId);
499
+ this.phase = "idle";
500
+ return message;
501
+ }
502
+ const turnState = await this.createTurnState();
503
+ return await this.executeAgentRun(turnState, this.createContext(turnState));
504
+ }
505
+ catch (error) {
506
+ await this.session.moveTo(leafId);
507
+ throw error;
508
+ }
509
+ }
565
510
  async prompt(text, options) {
566
511
  if (this.phase !== "idle")
567
512
  throw new AgentHarnessError("busy", "AgentHarness is busy");
@@ -640,58 +585,75 @@ export class AgentHarness {
640
585
  this.nextTurnQueue.push(createUserMessage(text, options?.images));
641
586
  await this.emitQueueUpdate();
642
587
  }
588
+ getSession() {
589
+ return this.sessionFacade;
590
+ }
643
591
  async appendMessage(message) {
644
- try {
645
- if (this.phase === "idle") {
646
- await this.session.appendMessage(message);
647
- }
648
- else {
649
- this.pendingSessionWrites.push({ type: "message", message });
650
- }
651
- }
652
- catch (error) {
653
- throw normalizeHarnessError(error, "session");
592
+ await this.sessionFacade.appendMessage(message);
593
+ }
594
+ async runCompaction(options) {
595
+ const model = this.model;
596
+ const auth = await this.getApiKeyAndHeaders?.(model);
597
+ if (!auth) {
598
+ if (options.automatic)
599
+ return undefined;
600
+ throw new AgentHarnessError("auth", "No auth available for compaction");
601
+ }
602
+ const branchEntries = await this.session.getBranch();
603
+ const preparationResult = prepareCompaction(branchEntries, this.compactionSettings);
604
+ if (!preparationResult.ok)
605
+ throw preparationResult.error;
606
+ const preparation = preparationResult.value;
607
+ if (!preparation) {
608
+ if (options.automatic)
609
+ return undefined;
610
+ throw new AgentHarnessError("compaction", "Nothing to compact");
611
+ }
612
+ const signal = options.signal ?? new AbortController().signal;
613
+ const hookResult = await this.emitHook({
614
+ type: "session_before_compact",
615
+ preparation,
616
+ branchEntries,
617
+ customInstructions: options.customInstructions,
618
+ signal,
619
+ });
620
+ if (hookResult?.cancel) {
621
+ if (options.automatic)
622
+ return undefined;
623
+ throw new AgentHarnessError("compaction", "Compaction cancelled");
624
+ }
625
+ const provided = hookResult?.compaction;
626
+ const compactResult = provided
627
+ ? { ok: true, value: provided }
628
+ : await compact(preparation, model, auth.apiKey, auth.headers, options.customInstructions, signal, this.thinkingLevel, createSummarizationRetry("compaction", this.streamOptions.summarizationRetry, (event) => this.emitOwn(event)));
629
+ if (!compactResult.ok)
630
+ throw compactResult.error;
631
+ const result = compactResult.value;
632
+ const entryId = await this.session.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, provided !== undefined);
633
+ const entry = await this.session.getEntry(entryId);
634
+ if (entry?.type === "compaction") {
635
+ await this.emitOwn({ type: "session_compact", compactionEntry: entry, fromHook: provided !== undefined });
654
636
  }
637
+ return result;
638
+ }
639
+ async maybeAutoCompact(model, context, signal) {
640
+ const projected = estimateContextTokens(context.messages).tokens;
641
+ if (!shouldCompact(projected, model.contextWindow ?? 0, this.compactionSettings))
642
+ return context;
643
+ const result = await this.runCompaction({ automatic: true, signal });
644
+ if (!result)
645
+ return context;
646
+ const persisted = await this.session.buildContext();
647
+ return { ...context, messages: convertToLlm(persisted.messages) };
655
648
  }
656
649
  async compact(customInstructions) {
657
650
  if (this.phase !== "idle")
658
651
  throw new AgentHarnessError("busy", "compact() requires idle harness");
659
652
  this.phase = "compaction";
660
653
  try {
661
- const model = this.model;
662
- if (!model)
663
- throw new AgentHarnessError("invalid_state", "No model set for compaction");
664
- const auth = await this.getApiKeyAndHeaders?.(model);
665
- if (!auth)
666
- throw new AgentHarnessError("auth", "No auth available for compaction");
667
- const branchEntries = await this.session.getBranch();
668
- const preparationResult = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);
669
- if (!preparationResult.ok)
670
- throw preparationResult.error;
671
- const preparation = preparationResult.value;
672
- if (!preparation)
654
+ const result = await this.runCompaction({ automatic: false, customInstructions });
655
+ if (!result)
673
656
  throw new AgentHarnessError("compaction", "Nothing to compact");
674
- const hookResult = await this.emitHook({
675
- type: "session_before_compact",
676
- preparation,
677
- branchEntries,
678
- customInstructions,
679
- signal: new AbortController().signal,
680
- });
681
- if (hookResult?.cancel)
682
- throw new AgentHarnessError("compaction", "Compaction cancelled");
683
- const provided = hookResult?.compaction;
684
- const compactResult = provided
685
- ? { ok: true, value: provided }
686
- : await compact(preparation, model, auth.apiKey, auth.headers, customInstructions, undefined, this.thinkingLevel);
687
- if (!compactResult.ok)
688
- throw compactResult.error;
689
- const result = compactResult.value;
690
- const entryId = await this.session.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, provided !== undefined);
691
- const entry = await this.session.getEntry(entryId);
692
- if (entry?.type === "compaction") {
693
- await this.emitOwn({ type: "session_compact", compactionEntry: entry, fromHook: provided !== undefined });
694
- }
695
657
  return result;
696
658
  }
697
659
  catch (error) {
@@ -744,6 +706,7 @@ export class AgentHarness {
744
706
  signal: new AbortController().signal,
745
707
  customInstructions: hookResult?.customInstructions ?? options?.customInstructions,
746
708
  replaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,
709
+ ...createSummarizationRetry("branch_summary", this.streamOptions.summarizationRetry, (event) => this.emitOwn(event)),
747
710
  });
748
711
  if (!branchSummary.ok) {
749
712
  if (branchSummary.error.code === "aborted")
@@ -937,10 +900,10 @@ export class AgentHarness {
937
900
  this.streamOptions = cloneStreamOptions(streamOptions);
938
901
  }
939
902
  async abort() {
940
- const clearedSteer = [...this.steerQueue];
941
- const clearedFollowUp = [...this.followUpQueue];
942
- this.steerQueue = [];
943
- this.followUpQueue = [];
903
+ if (this.phase !== "idle" && this.phase !== "turn")
904
+ throw new AgentHarnessError("invalid_state", `Cannot abort during ${this.phase}`);
905
+ const clearedSteer = this.steerQueue.splice(0);
906
+ const clearedFollowUp = this.followUpQueue.splice(0);
944
907
  this.runAbortController?.abort();
945
908
  const errors = [];
946
909
  try {