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.
- package/dist/harness/agent-harness.d.ts +9 -8
- package/dist/harness/agent-harness.d.ts.map +1 -1
- package/dist/harness/agent-harness.js +145 -182
- package/dist/harness/agent-harness.js.map +1 -1
- package/dist/harness/compaction/branch-summarization.d.ts +5 -1
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/harness/compaction/branch-summarization.js +3 -3
- package/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/dist/harness/compaction/compaction.d.ts +8 -4
- package/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/dist/harness/compaction/compaction.js +12 -97
- package/dist/harness/compaction/compaction.js.map +1 -1
- package/dist/harness/compaction/operation.d.ts +6 -0
- package/dist/harness/compaction/operation.d.ts.map +1 -0
- package/dist/harness/compaction/operation.js +2 -0
- package/dist/harness/compaction/operation.js.map +1 -0
- package/dist/harness/compaction/summarization-prompts.d.ts +5 -0
- package/dist/harness/compaction/summarization-prompts.d.ts.map +1 -0
- package/dist/harness/compaction/summarization-prompts.js +88 -0
- package/dist/harness/compaction/summarization-prompts.js.map +1 -0
- package/dist/harness/errors.d.ts +15 -0
- package/dist/harness/errors.d.ts.map +1 -0
- package/dist/harness/errors.js +32 -0
- package/dist/harness/errors.js.map +1 -0
- package/dist/harness/harness-session.d.ts +105 -0
- package/dist/harness/harness-session.d.ts.map +1 -0
- package/dist/harness/harness-session.js +116 -0
- package/dist/harness/harness-session.js.map +1 -0
- package/dist/harness/messages.d.ts +3 -1
- package/dist/harness/messages.d.ts.map +1 -1
- package/dist/harness/messages.js +26 -0
- package/dist/harness/messages.js.map +1 -1
- package/dist/harness/name-validation.d.ts +2 -0
- package/dist/harness/name-validation.d.ts.map +1 -0
- package/dist/harness/name-validation.js +11 -0
- package/dist/harness/name-validation.js.map +1 -0
- package/dist/harness/stream-options.d.ts +31 -0
- package/dist/harness/stream-options.d.ts.map +1 -0
- package/dist/harness/stream-options.js +66 -0
- package/dist/harness/stream-options.js.map +1 -0
- package/dist/harness/summarization-retry.d.ts +29 -0
- package/dist/harness/summarization-retry.d.ts.map +1 -0
- package/dist/harness/summarization-retry.js +20 -0
- package/dist/harness/summarization-retry.js.map +1 -0
- package/dist/harness/types.d.ts +36 -41
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js +1 -32
- package/dist/harness/types.js.map +1 -1
- 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 {
|
|
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.
|
|
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
|
|
78
|
+
throw normalizeHarnessError(error, "hook");
|
|
177
79
|
}
|
|
178
80
|
}
|
|
179
81
|
}
|
|
180
82
|
async emitAny(event, signal) {
|
|
181
|
-
for (const listener of this.
|
|
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
|
|
88
|
+
throw normalizeHarnessError(error, "hook");
|
|
187
89
|
}
|
|
188
90
|
}
|
|
189
91
|
}
|
|
190
92
|
async emitHook(event) {
|
|
191
|
-
const handlers = this.
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
await
|
|
530
|
-
|
|
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
|
-
|
|
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
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
|
662
|
-
if (!
|
|
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
|
-
|
|
941
|
-
|
|
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 {
|