@eminent337/aery-core 0.1.119

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 (102) hide show
  1. package/README.md +488 -0
  2. package/dist/agent-loop.d.ts +24 -0
  3. package/dist/agent-loop.d.ts.map +1 -0
  4. package/dist/agent-loop.js +479 -0
  5. package/dist/agent-loop.js.map +1 -0
  6. package/dist/agent.d.ts +118 -0
  7. package/dist/agent.d.ts.map +1 -0
  8. package/dist/agent.js +402 -0
  9. package/dist/agent.js.map +1 -0
  10. package/dist/harness/agent-harness.d.ts +92 -0
  11. package/dist/harness/agent-harness.d.ts.map +1 -0
  12. package/dist/harness/agent-harness.js +900 -0
  13. package/dist/harness/agent-harness.js.map +1 -0
  14. package/dist/harness/compaction/branch-summarization.d.ts +53 -0
  15. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  16. package/dist/harness/compaction/branch-summarization.js +174 -0
  17. package/dist/harness/compaction/branch-summarization.js.map +1 -0
  18. package/dist/harness/compaction/compaction.d.ts +95 -0
  19. package/dist/harness/compaction/compaction.d.ts.map +1 -0
  20. package/dist/harness/compaction/compaction.js +533 -0
  21. package/dist/harness/compaction/compaction.js.map +1 -0
  22. package/dist/harness/compaction/utils.d.ts +25 -0
  23. package/dist/harness/compaction/utils.d.ts.map +1 -0
  24. package/dist/harness/compaction/utils.js +131 -0
  25. package/dist/harness/compaction/utils.js.map +1 -0
  26. package/dist/harness/env/nodejs.d.ts +51 -0
  27. package/dist/harness/env/nodejs.d.ts.map +1 -0
  28. package/dist/harness/env/nodejs.js +481 -0
  29. package/dist/harness/env/nodejs.js.map +1 -0
  30. package/dist/harness/messages.d.ts +51 -0
  31. package/dist/harness/messages.d.ts.map +1 -0
  32. package/dist/harness/messages.js +102 -0
  33. package/dist/harness/messages.js.map +1 -0
  34. package/dist/harness/prompt-templates.d.ts +48 -0
  35. package/dist/harness/prompt-templates.d.ts.map +1 -0
  36. package/dist/harness/prompt-templates.js +230 -0
  37. package/dist/harness/prompt-templates.js.map +1 -0
  38. package/dist/harness/session/jsonl-repo.d.ts +26 -0
  39. package/dist/harness/session/jsonl-repo.d.ts.map +1 -0
  40. package/dist/harness/session/jsonl-repo.js +101 -0
  41. package/dist/harness/session/jsonl-repo.js.map +1 -0
  42. package/dist/harness/session/jsonl-storage.d.ts +33 -0
  43. package/dist/harness/session/jsonl-storage.d.ts.map +1 -0
  44. package/dist/harness/session/jsonl-storage.js +231 -0
  45. package/dist/harness/session/jsonl-storage.js.map +1 -0
  46. package/dist/harness/session/memory-repo.d.ts +18 -0
  47. package/dist/harness/session/memory-repo.d.ts.map +1 -0
  48. package/dist/harness/session/memory-repo.js +42 -0
  49. package/dist/harness/session/memory-repo.js.map +1 -0
  50. package/dist/harness/session/memory-storage.d.ts +25 -0
  51. package/dist/harness/session/memory-storage.d.ts.map +1 -0
  52. package/dist/harness/session/memory-storage.js +114 -0
  53. package/dist/harness/session/memory-storage.js.map +1 -0
  54. package/dist/harness/session/repo-utils.d.ts +11 -0
  55. package/dist/harness/session/repo-utils.d.ts.map +1 -0
  56. package/dist/harness/session/repo-utils.js +39 -0
  57. package/dist/harness/session/repo-utils.js.map +1 -0
  58. package/dist/harness/session/session.d.ts +32 -0
  59. package/dist/harness/session/session.d.ts.map +1 -0
  60. package/dist/harness/session/session.js +197 -0
  61. package/dist/harness/session/session.js.map +1 -0
  62. package/dist/harness/session/uuid.d.ts +2 -0
  63. package/dist/harness/session/uuid.d.ts.map +1 -0
  64. package/dist/harness/session/uuid.js +50 -0
  65. package/dist/harness/session/uuid.js.map +1 -0
  66. package/dist/harness/skills.d.ts +44 -0
  67. package/dist/harness/skills.d.ts.map +1 -0
  68. package/dist/harness/skills.js +311 -0
  69. package/dist/harness/skills.js.map +1 -0
  70. package/dist/harness/system-prompt.d.ts +3 -0
  71. package/dist/harness/system-prompt.d.ts.map +1 -0
  72. package/dist/harness/system-prompt.js +30 -0
  73. package/dist/harness/system-prompt.js.map +1 -0
  74. package/dist/harness/types.d.ts +613 -0
  75. package/dist/harness/types.d.ts.map +1 -0
  76. package/dist/harness/types.js +100 -0
  77. package/dist/harness/types.js.map +1 -0
  78. package/dist/harness/utils/shell-output.d.ts +14 -0
  79. package/dist/harness/utils/shell-output.d.ts.map +1 -0
  80. package/dist/harness/utils/shell-output.js +126 -0
  81. package/dist/harness/utils/shell-output.js.map +1 -0
  82. package/dist/harness/utils/truncate.d.ts +70 -0
  83. package/dist/harness/utils/truncate.d.ts.map +1 -0
  84. package/dist/harness/utils/truncate.js +288 -0
  85. package/dist/harness/utils/truncate.js.map +1 -0
  86. package/dist/index.d.ts +20 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +25 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/node.d.ts +3 -0
  91. package/dist/node.d.ts.map +1 -0
  92. package/dist/node.js +3 -0
  93. package/dist/node.js.map +1 -0
  94. package/dist/proxy.d.ts +69 -0
  95. package/dist/proxy.d.ts.map +1 -0
  96. package/dist/proxy.js +278 -0
  97. package/dist/proxy.js.map +1 -0
  98. package/dist/types.d.ts +393 -0
  99. package/dist/types.d.ts.map +1 -0
  100. package/dist/types.js +2 -0
  101. package/dist/types.js.map +1 -0
  102. package/package.json +61 -0
@@ -0,0 +1,900 @@
1
+ import { streamSimple, } from "@eminent337/aery-ai";
2
+ import { runAgentLoop } from "../agent-loop.js";
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";
6
+ import { formatPromptTemplateInvocation } from "./prompt-templates.js";
7
+ import { formatSkillInvocation } from "./skills.js";
8
+ 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 applyStreamOptionsPatch(base, patch) {
54
+ const result = cloneStreamOptions(base);
55
+ if (!patch)
56
+ return result;
57
+ if (Object.hasOwn(patch, "transport"))
58
+ result.transport = patch.transport;
59
+ if (Object.hasOwn(patch, "timeoutMs"))
60
+ result.timeoutMs = patch.timeoutMs;
61
+ if (Object.hasOwn(patch, "maxRetries"))
62
+ result.maxRetries = patch.maxRetries;
63
+ if (Object.hasOwn(patch, "maxRetryDelayMs"))
64
+ result.maxRetryDelayMs = patch.maxRetryDelayMs;
65
+ if (Object.hasOwn(patch, "cacheRetention"))
66
+ result.cacheRetention = patch.cacheRetention;
67
+ if (Object.hasOwn(patch, "headers")) {
68
+ if (patch.headers === undefined) {
69
+ result.headers = undefined;
70
+ }
71
+ else {
72
+ const headers = { ...(result.headers ?? {}) };
73
+ for (const [key, value] of Object.entries(patch.headers)) {
74
+ if (value === undefined)
75
+ delete headers[key];
76
+ else
77
+ headers[key] = value;
78
+ }
79
+ result.headers = Object.keys(headers).length > 0 ? headers : undefined;
80
+ }
81
+ }
82
+ if (Object.hasOwn(patch, "metadata")) {
83
+ if (patch.metadata === undefined) {
84
+ result.metadata = undefined;
85
+ }
86
+ else {
87
+ const metadata = { ...(result.metadata ?? {}) };
88
+ for (const [key, value] of Object.entries(patch.metadata)) {
89
+ if (value === undefined)
90
+ delete metadata[key];
91
+ else
92
+ metadata[key] = value;
93
+ }
94
+ result.metadata = Object.keys(metadata).length > 0 ? metadata : undefined;
95
+ }
96
+ }
97
+ return result;
98
+ }
99
+ const SUBSCRIBER_EVENT_TYPE = "*";
100
+ function normalizeHarnessError(error, fallbackCode) {
101
+ if (error instanceof AgentHarnessError)
102
+ return error;
103
+ const cause = toError(error);
104
+ if (cause instanceof SessionError)
105
+ return new AgentHarnessError("session", cause.message, cause);
106
+ if (cause instanceof CompactionError)
107
+ return new AgentHarnessError("compaction", cause.message, cause);
108
+ if (cause instanceof BranchSummaryError)
109
+ return new AgentHarnessError("branch_summary", cause.message, cause);
110
+ return new AgentHarnessError(fallbackCode, cause.message, cause);
111
+ }
112
+ function normalizeHookError(error) {
113
+ return normalizeHarnessError(error, "hook");
114
+ }
115
+ export class AgentHarness {
116
+ env;
117
+ session;
118
+ phase = "idle";
119
+ runAbortController;
120
+ runPromise;
121
+ pendingSessionWrites = [];
122
+ model;
123
+ thinkingLevel;
124
+ systemPrompt;
125
+ streamOptions;
126
+ getApiKeyAndHeaders;
127
+ resources;
128
+ tools = new Map();
129
+ activeToolNames;
130
+ steerQueue = [];
131
+ steeringQueueMode;
132
+ followUpQueue = [];
133
+ followUpQueueMode;
134
+ nextTurnQueue = [];
135
+ handlers = new Map();
136
+ constructor(options) {
137
+ this.env = options.env;
138
+ this.session = options.session;
139
+ this.resources = options.resources ?? {};
140
+ this.streamOptions = cloneStreamOptions(options.streamOptions);
141
+ this.systemPrompt = options.systemPrompt;
142
+ this.getApiKeyAndHeaders = options.getApiKeyAndHeaders;
143
+ for (const tool of options.tools ?? []) {
144
+ this.tools.set(tool.name, tool);
145
+ }
146
+ this.model = options.model;
147
+ this.thinkingLevel = options.thinkingLevel ?? "off";
148
+ this.activeToolNames = options.activeToolNames ?? (options.tools ?? []).map((tool) => tool.name);
149
+ this.steeringQueueMode = options.steeringMode ?? "one-at-a-time";
150
+ this.followUpQueueMode = options.followUpMode ?? "one-at-a-time";
151
+ }
152
+ getHandlers(type) {
153
+ return this.handlers.get(type);
154
+ }
155
+ async emitOwn(event, signal) {
156
+ for (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {
157
+ try {
158
+ await listener(event, signal);
159
+ }
160
+ catch (error) {
161
+ throw normalizeHookError(error);
162
+ }
163
+ }
164
+ }
165
+ async emitAny(event, signal) {
166
+ for (const listener of this.getHandlers(SUBSCRIBER_EVENT_TYPE) ?? []) {
167
+ try {
168
+ await listener(event, signal);
169
+ }
170
+ catch (error) {
171
+ throw normalizeHookError(error);
172
+ }
173
+ }
174
+ }
175
+ async emitHook(event) {
176
+ const handlers = this.getHandlers(event.type);
177
+ if (!handlers || handlers.size === 0)
178
+ return undefined;
179
+ let lastResult;
180
+ for (const handler of handlers) {
181
+ try {
182
+ const result = await handler(event);
183
+ if (result !== undefined) {
184
+ lastResult = result;
185
+ }
186
+ }
187
+ catch (error) {
188
+ throw normalizeHookError(error);
189
+ }
190
+ }
191
+ return lastResult;
192
+ }
193
+ async emitBeforeProviderRequest(model, sessionId, streamOptions) {
194
+ const handlers = this.getHandlers("before_provider_request");
195
+ let current = cloneStreamOptions(streamOptions);
196
+ if (!handlers || handlers.size === 0)
197
+ return current;
198
+ for (const handler of handlers) {
199
+ try {
200
+ const result = await handler({
201
+ type: "before_provider_request",
202
+ model,
203
+ sessionId,
204
+ streamOptions: cloneStreamOptions(current),
205
+ });
206
+ if (result?.streamOptions) {
207
+ current = applyStreamOptionsPatch(current, result.streamOptions);
208
+ }
209
+ }
210
+ catch (error) {
211
+ throw normalizeHookError(error);
212
+ }
213
+ }
214
+ return current;
215
+ }
216
+ async emitBeforeProviderPayload(model, payload) {
217
+ const handlers = this.getHandlers("before_provider_payload");
218
+ let current = payload;
219
+ if (!handlers || handlers.size === 0)
220
+ return current;
221
+ for (const handler of handlers) {
222
+ try {
223
+ const result = await handler({ type: "before_provider_payload", model, payload: current });
224
+ if (result !== undefined) {
225
+ current = result.payload;
226
+ }
227
+ }
228
+ catch (error) {
229
+ throw normalizeHookError(error);
230
+ }
231
+ }
232
+ return current;
233
+ }
234
+ async emitQueueUpdate() {
235
+ await this.emitOwn({
236
+ type: "queue_update",
237
+ steer: [...this.steerQueue],
238
+ followUp: [...this.followUpQueue],
239
+ nextTurn: [...this.nextTurnQueue],
240
+ });
241
+ }
242
+ startRunPromise() {
243
+ let finish = () => { };
244
+ this.runPromise = new Promise((resolve) => {
245
+ finish = resolve;
246
+ });
247
+ return () => {
248
+ this.runPromise = undefined;
249
+ finish();
250
+ };
251
+ }
252
+ async createTurnState() {
253
+ const context = await this.session.buildContext();
254
+ const resources = this.getResources();
255
+ const sessionMetadata = await this.session.getMetadata();
256
+ const tools = [...this.tools.values()];
257
+ const activeTools = this.activeToolNames
258
+ .map((name) => this.tools.get(name))
259
+ .filter((tool) => tool !== undefined);
260
+ let systemPrompt = "You are a helpful assistant.";
261
+ if (typeof this.systemPrompt === "string") {
262
+ systemPrompt = this.systemPrompt;
263
+ }
264
+ else if (this.systemPrompt) {
265
+ systemPrompt = await this.systemPrompt({
266
+ env: this.env,
267
+ session: this.session,
268
+ model: this.model,
269
+ thinkingLevel: this.thinkingLevel,
270
+ activeTools,
271
+ resources,
272
+ });
273
+ }
274
+ return {
275
+ messages: context.messages,
276
+ resources,
277
+ streamOptions: cloneStreamOptions(this.streamOptions),
278
+ sessionId: sessionMetadata.id,
279
+ systemPrompt,
280
+ model: this.model,
281
+ thinkingLevel: this.thinkingLevel,
282
+ tools,
283
+ activeTools,
284
+ };
285
+ }
286
+ createContext(turnState, systemPrompt) {
287
+ return {
288
+ systemPrompt: systemPrompt ?? turnState.systemPrompt,
289
+ messages: turnState.messages.slice(),
290
+ tools: turnState.activeTools.slice(),
291
+ };
292
+ }
293
+ createStreamFn(getTurnState) {
294
+ return async (model, context, streamOptions) => {
295
+ const turnState = getTurnState();
296
+ const auth = await this.getApiKeyAndHeaders?.(model);
297
+ const snapshotOptions = {
298
+ ...turnState.streamOptions,
299
+ headers: mergeHeaders(turnState.streamOptions.headers, auth?.headers),
300
+ };
301
+ const requestOptions = await this.emitBeforeProviderRequest(model, turnState.sessionId, snapshotOptions);
302
+ return streamSimple(model, context, {
303
+ cacheRetention: requestOptions.cacheRetention,
304
+ headers: requestOptions.headers,
305
+ maxRetries: requestOptions.maxRetries,
306
+ maxRetryDelayMs: requestOptions.maxRetryDelayMs,
307
+ metadata: requestOptions.metadata,
308
+ onPayload: async (payload) => await this.emitBeforeProviderPayload(model, payload),
309
+ onResponse: async (response) => {
310
+ const headers = { ...response.headers };
311
+ await this.emitOwn({ type: "after_provider_response", status: response.status, headers }, streamOptions?.signal);
312
+ },
313
+ reasoning: streamOptions?.reasoning,
314
+ signal: streamOptions?.signal,
315
+ sessionId: turnState.sessionId,
316
+ timeoutMs: requestOptions.timeoutMs,
317
+ transport: requestOptions.transport,
318
+ apiKey: auth?.apiKey,
319
+ });
320
+ };
321
+ }
322
+ async drainQueuedMessages(queue, mode) {
323
+ const messages = mode === "all" ? queue.splice(0) : queue.splice(0, 1);
324
+ if (messages.length === 0)
325
+ return messages;
326
+ try {
327
+ await this.emitQueueUpdate();
328
+ return messages;
329
+ }
330
+ catch (error) {
331
+ queue.unshift(...messages);
332
+ throw normalizeHookError(error);
333
+ }
334
+ }
335
+ createLoopConfig(getTurnState, setTurnState) {
336
+ const turnState = getTurnState();
337
+ return {
338
+ model: turnState.model,
339
+ reasoning: turnState.thinkingLevel === "off" ? undefined : turnState.thinkingLevel,
340
+ convertToLlm,
341
+ transformContext: async (messages) => {
342
+ const result = await this.emitHook({ type: "context", messages: [...messages] });
343
+ return result?.messages ?? messages;
344
+ },
345
+ beforeToolCall: async ({ toolCall, args }) => {
346
+ const result = await this.emitHook({
347
+ type: "tool_call",
348
+ toolCallId: toolCall.id,
349
+ toolName: toolCall.name,
350
+ input: args,
351
+ });
352
+ return result ? { block: result.block, reason: result.reason } : undefined;
353
+ },
354
+ afterToolCall: async ({ toolCall, args, result, isError }) => {
355
+ const patch = await this.emitHook({
356
+ type: "tool_result",
357
+ toolCallId: toolCall.id,
358
+ toolName: toolCall.name,
359
+ input: args,
360
+ content: result.content,
361
+ details: result.details,
362
+ isError,
363
+ });
364
+ return patch
365
+ ? { content: patch.content, details: patch.details, isError: patch.isError, terminate: patch.terminate }
366
+ : undefined;
367
+ },
368
+ prepareNextTurn: async () => {
369
+ await this.flushPendingSessionWrites();
370
+ const nextTurnState = await this.createTurnState();
371
+ setTurnState(nextTurnState);
372
+ return {
373
+ context: this.createContext(nextTurnState),
374
+ model: nextTurnState.model,
375
+ thinkingLevel: nextTurnState.thinkingLevel,
376
+ };
377
+ },
378
+ getSteeringMessages: async () => this.drainQueuedMessages(this.steerQueue, this.steeringQueueMode),
379
+ getFollowUpMessages: async () => this.drainQueuedMessages(this.followUpQueue, this.followUpQueueMode),
380
+ };
381
+ }
382
+ validateToolNames(toolNames, tools = this.tools) {
383
+ const missing = toolNames.filter((name) => !tools.has(name));
384
+ if (missing.length > 0)
385
+ throw new AgentHarnessError("invalid_argument", `Unknown tool(s): ${missing.join(", ")}`);
386
+ }
387
+ async flushPendingSessionWrites() {
388
+ while (this.pendingSessionWrites.length > 0) {
389
+ const write = this.pendingSessionWrites[0];
390
+ if (write.type === "message") {
391
+ await this.session.appendMessage(write.message);
392
+ }
393
+ else if (write.type === "model_change") {
394
+ await this.session.appendModelChange(write.provider, write.modelId);
395
+ }
396
+ else if (write.type === "thinking_level_change") {
397
+ await this.session.appendThinkingLevelChange(write.thinkingLevel);
398
+ }
399
+ else if (write.type === "custom") {
400
+ await this.session.appendCustomEntry(write.customType, write.data);
401
+ }
402
+ else if (write.type === "custom_message") {
403
+ await this.session.appendCustomMessageEntry(write.customType, write.content, write.display, write.details);
404
+ }
405
+ else if (write.type === "label") {
406
+ await this.session.appendLabel(write.targetId, write.label);
407
+ }
408
+ else if (write.type === "session_info") {
409
+ await this.session.appendSessionName(write.name ?? "");
410
+ }
411
+ else if (write.type === "leaf") {
412
+ await this.session.getStorage().setLeafId(write.targetId);
413
+ }
414
+ this.pendingSessionWrites.shift();
415
+ }
416
+ }
417
+ async handleAgentEvent(event, signal) {
418
+ if (event.type === "message_end") {
419
+ await this.session.appendMessage(event.message);
420
+ await this.emitAny(event, signal);
421
+ return;
422
+ }
423
+ if (event.type === "turn_end") {
424
+ let eventError;
425
+ try {
426
+ await this.emitAny(event, signal);
427
+ }
428
+ catch (error) {
429
+ eventError = error;
430
+ }
431
+ const hadPendingMutations = this.pendingSessionWrites.length > 0;
432
+ await this.flushPendingSessionWrites();
433
+ if (eventError)
434
+ throw eventError;
435
+ await this.emitOwn({ type: "save_point", hadPendingMutations });
436
+ return;
437
+ }
438
+ if (event.type === "agent_end") {
439
+ await this.flushPendingSessionWrites();
440
+ this.phase = "idle";
441
+ await this.emitAny(event, signal);
442
+ await this.emitOwn({ type: "settled", nextTurnCount: this.nextTurnQueue.length }, signal);
443
+ return;
444
+ }
445
+ await this.emitAny(event, signal);
446
+ }
447
+ async emitRunFailure(model, error, aborted, signal) {
448
+ const failureMessage = createFailureMessage(model, error, aborted);
449
+ await this.handleAgentEvent({ type: "message_start", message: failureMessage }, signal);
450
+ await this.handleAgentEvent({ type: "message_end", message: failureMessage }, signal);
451
+ await this.handleAgentEvent({ type: "turn_end", message: failureMessage, toolResults: [] }, signal);
452
+ await this.handleAgentEvent({ type: "agent_end", messages: [failureMessage] }, signal);
453
+ return [failureMessage];
454
+ }
455
+ async executeTurn(turnState, text, options) {
456
+ let activeTurnState = turnState;
457
+ let messages = [createUserMessage(text, options?.images)];
458
+ if (this.nextTurnQueue.length > 0) {
459
+ const queuedMessages = this.nextTurnQueue.splice(0);
460
+ try {
461
+ await this.emitQueueUpdate();
462
+ }
463
+ catch (error) {
464
+ this.nextTurnQueue.unshift(...queuedMessages);
465
+ throw normalizeHookError(error);
466
+ }
467
+ messages = [...queuedMessages, messages[0]];
468
+ }
469
+ const beforeResult = await this.emitHook({
470
+ type: "before_agent_start",
471
+ prompt: text,
472
+ images: options?.images,
473
+ systemPrompt: turnState.systemPrompt,
474
+ resources: turnState.resources,
475
+ });
476
+ if (beforeResult?.messages)
477
+ messages = [...messages, ...beforeResult.messages];
478
+ const abortController = new AbortController();
479
+ const getTurnState = () => activeTurnState;
480
+ const setTurnState = (nextTurnState) => {
481
+ activeTurnState = nextTurnState;
482
+ };
483
+ this.runAbortController = abortController;
484
+ const runResultPromise = (async () => {
485
+ try {
486
+ return await runAgentLoop(messages, this.createContext(turnState, beforeResult?.systemPrompt), this.createLoopConfig(getTurnState, setTurnState), (event) => this.handleAgentEvent(event, abortController.signal), abortController.signal, this.createStreamFn(getTurnState));
487
+ }
488
+ catch (error) {
489
+ try {
490
+ return await this.emitRunFailure(activeTurnState.model, error, abortController.signal.aborted, abortController.signal);
491
+ }
492
+ catch (failureError) {
493
+ const cause = new AggregateError([toError(error), toError(failureError)], "Agent run failed and failure reporting failed");
494
+ throw new AgentHarnessError("unknown", cause.message, cause);
495
+ }
496
+ }
497
+ })();
498
+ try {
499
+ const newMessages = await runResultPromise;
500
+ for (let i = newMessages.length - 1; i >= 0; i--) {
501
+ const message = newMessages[i];
502
+ if (message.role === "assistant") {
503
+ return message;
504
+ }
505
+ }
506
+ throw new AgentHarnessError("invalid_state", "AgentHarness prompt completed without an assistant message");
507
+ }
508
+ finally {
509
+ try {
510
+ await this.flushPendingSessionWrites();
511
+ }
512
+ finally {
513
+ this.runAbortController = undefined;
514
+ }
515
+ }
516
+ }
517
+ async prompt(text, options) {
518
+ if (this.phase !== "idle")
519
+ throw new AgentHarnessError("busy", "AgentHarness is busy");
520
+ this.phase = "turn";
521
+ const finishRunPromise = this.startRunPromise();
522
+ try {
523
+ const turnState = await this.createTurnState();
524
+ return await this.executeTurn(turnState, text, options);
525
+ }
526
+ catch (error) {
527
+ this.phase = "idle";
528
+ throw normalizeHarnessError(error, "unknown");
529
+ }
530
+ finally {
531
+ finishRunPromise();
532
+ }
533
+ }
534
+ async skill(name, additionalInstructions) {
535
+ if (this.phase !== "idle")
536
+ throw new AgentHarnessError("busy", "AgentHarness is busy");
537
+ this.phase = "turn";
538
+ const finishRunPromise = this.startRunPromise();
539
+ try {
540
+ const turnState = await this.createTurnState();
541
+ const skill = (turnState.resources.skills ?? []).find((candidate) => candidate.name === name);
542
+ if (!skill)
543
+ throw new AgentHarnessError("invalid_argument", `Unknown skill: ${name}`);
544
+ return await this.executeTurn(turnState, formatSkillInvocation(skill, additionalInstructions));
545
+ }
546
+ catch (error) {
547
+ this.phase = "idle";
548
+ throw normalizeHarnessError(error, "unknown");
549
+ }
550
+ finally {
551
+ finishRunPromise();
552
+ }
553
+ }
554
+ async promptFromTemplate(name, args = []) {
555
+ if (this.phase !== "idle")
556
+ throw new AgentHarnessError("busy", "AgentHarness is busy");
557
+ this.phase = "turn";
558
+ const finishRunPromise = this.startRunPromise();
559
+ try {
560
+ const turnState = await this.createTurnState();
561
+ const template = (turnState.resources.promptTemplates ?? []).find((candidate) => candidate.name === name);
562
+ if (!template)
563
+ throw new AgentHarnessError("invalid_argument", `Unknown prompt template: ${name}`);
564
+ return await this.executeTurn(turnState, formatPromptTemplateInvocation(template, args));
565
+ }
566
+ catch (error) {
567
+ this.phase = "idle";
568
+ throw normalizeHarnessError(error, "unknown");
569
+ }
570
+ finally {
571
+ finishRunPromise();
572
+ }
573
+ }
574
+ async steer(text, options) {
575
+ if (this.phase === "idle")
576
+ throw new AgentHarnessError("invalid_state", "Cannot steer while idle");
577
+ this.steerQueue.push(createUserMessage(text, options?.images));
578
+ await this.emitQueueUpdate();
579
+ }
580
+ async followUp(text, options) {
581
+ if (this.phase === "idle")
582
+ throw new AgentHarnessError("invalid_state", "Cannot follow up while idle");
583
+ this.followUpQueue.push(createUserMessage(text, options?.images));
584
+ await this.emitQueueUpdate();
585
+ }
586
+ async nextTurn(text, options) {
587
+ this.nextTurnQueue.push(createUserMessage(text, options?.images));
588
+ await this.emitQueueUpdate();
589
+ }
590
+ async appendMessage(message) {
591
+ try {
592
+ if (this.phase === "idle") {
593
+ await this.session.appendMessage(message);
594
+ }
595
+ else {
596
+ this.pendingSessionWrites.push({ type: "message", message });
597
+ }
598
+ }
599
+ catch (error) {
600
+ throw normalizeHarnessError(error, "session");
601
+ }
602
+ }
603
+ async compact(customInstructions) {
604
+ if (this.phase !== "idle")
605
+ throw new AgentHarnessError("busy", "compact() requires idle harness");
606
+ this.phase = "compaction";
607
+ try {
608
+ const model = this.model;
609
+ if (!model)
610
+ throw new AgentHarnessError("invalid_state", "No model set for compaction");
611
+ const auth = await this.getApiKeyAndHeaders?.(model);
612
+ if (!auth)
613
+ throw new AgentHarnessError("auth", "No auth available for compaction");
614
+ const branchEntries = await this.session.getBranch();
615
+ const preparationResult = prepareCompaction(branchEntries, DEFAULT_COMPACTION_SETTINGS);
616
+ if (!preparationResult.ok)
617
+ throw preparationResult.error;
618
+ const preparation = preparationResult.value;
619
+ if (!preparation)
620
+ throw new AgentHarnessError("compaction", "Nothing to compact");
621
+ const hookResult = await this.emitHook({
622
+ type: "session_before_compact",
623
+ preparation,
624
+ branchEntries,
625
+ customInstructions,
626
+ signal: new AbortController().signal,
627
+ });
628
+ if (hookResult?.cancel)
629
+ throw new AgentHarnessError("compaction", "Compaction cancelled");
630
+ const provided = hookResult?.compaction;
631
+ const compactResult = provided
632
+ ? { ok: true, value: provided }
633
+ : await compact(preparation, model, auth.apiKey, auth.headers, customInstructions, undefined, this.thinkingLevel);
634
+ if (!compactResult.ok)
635
+ throw compactResult.error;
636
+ const result = compactResult.value;
637
+ const entryId = await this.session.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore, result.details, provided !== undefined);
638
+ const entry = await this.session.getEntry(entryId);
639
+ if (entry?.type === "compaction") {
640
+ await this.emitOwn({ type: "session_compact", compactionEntry: entry, fromHook: provided !== undefined });
641
+ }
642
+ return result;
643
+ }
644
+ catch (error) {
645
+ throw normalizeHarnessError(error, "compaction");
646
+ }
647
+ finally {
648
+ this.phase = "idle";
649
+ }
650
+ }
651
+ async navigateTree(targetId, options) {
652
+ if (this.phase !== "idle")
653
+ throw new AgentHarnessError("busy", "navigateTree() requires idle harness");
654
+ this.phase = "branch_summary";
655
+ try {
656
+ const oldLeafId = await this.session.getLeafId();
657
+ if (oldLeafId === targetId)
658
+ return { cancelled: false };
659
+ const targetEntry = await this.session.getEntry(targetId);
660
+ if (!targetEntry)
661
+ throw new AgentHarnessError("invalid_argument", `Entry ${targetId} not found`);
662
+ const { entries, commonAncestorId } = await collectEntriesForBranchSummary(this.session, oldLeafId, targetId);
663
+ const preparation = {
664
+ targetId,
665
+ oldLeafId,
666
+ commonAncestorId,
667
+ entriesToSummarize: entries,
668
+ userWantsSummary: options?.summarize ?? false,
669
+ customInstructions: options?.customInstructions,
670
+ replaceInstructions: options?.replaceInstructions,
671
+ label: options?.label,
672
+ };
673
+ const signal = new AbortController().signal;
674
+ const hookResult = await this.emitHook({ type: "session_before_tree", preparation, signal });
675
+ if (hookResult?.cancel)
676
+ return { cancelled: true };
677
+ let summaryEntry;
678
+ let summaryText = hookResult?.summary?.summary;
679
+ let summaryDetails = hookResult?.summary?.details;
680
+ if (!summaryText && options?.summarize && entries.length > 0) {
681
+ const model = this.model;
682
+ if (!model)
683
+ throw new AgentHarnessError("invalid_state", "No model set for branch summary");
684
+ const auth = await this.getApiKeyAndHeaders?.(model);
685
+ if (!auth)
686
+ throw new AgentHarnessError("auth", "No auth available for branch summary");
687
+ const branchSummary = await generateBranchSummary(entries, {
688
+ model,
689
+ apiKey: auth.apiKey,
690
+ headers: auth.headers,
691
+ signal: new AbortController().signal,
692
+ customInstructions: hookResult?.customInstructions ?? options?.customInstructions,
693
+ replaceInstructions: hookResult?.replaceInstructions ?? options?.replaceInstructions,
694
+ });
695
+ if (!branchSummary.ok) {
696
+ if (branchSummary.error.code === "aborted")
697
+ return { cancelled: true };
698
+ throw new AgentHarnessError("branch_summary", branchSummary.error.message, branchSummary.error);
699
+ }
700
+ summaryText = branchSummary.value.summary;
701
+ summaryDetails = {
702
+ readFiles: branchSummary.value.readFiles,
703
+ modifiedFiles: branchSummary.value.modifiedFiles,
704
+ };
705
+ }
706
+ let editorText;
707
+ let newLeafId;
708
+ if (targetEntry.type === "message" && targetEntry.message.role === "user") {
709
+ newLeafId = targetEntry.parentId;
710
+ const content = targetEntry.message.content;
711
+ editorText =
712
+ typeof content === "string"
713
+ ? content
714
+ : content
715
+ .filter((c) => c.type === "text")
716
+ .map((c) => c.text)
717
+ .join("");
718
+ }
719
+ else if (targetEntry.type === "custom_message") {
720
+ newLeafId = targetEntry.parentId;
721
+ editorText =
722
+ typeof targetEntry.content === "string"
723
+ ? targetEntry.content
724
+ : targetEntry.content
725
+ .filter((c) => c.type === "text")
726
+ .map((c) => c.text)
727
+ .join("");
728
+ }
729
+ else {
730
+ newLeafId = targetId;
731
+ }
732
+ const summaryId = await this.session.moveTo(newLeafId, summaryText
733
+ ? { summary: summaryText, details: summaryDetails, fromHook: hookResult?.summary !== undefined }
734
+ : undefined);
735
+ if (summaryId) {
736
+ const entry = await this.session.getEntry(summaryId);
737
+ if (entry?.type === "branch_summary")
738
+ summaryEntry = entry;
739
+ }
740
+ await this.emitOwn({
741
+ type: "session_tree",
742
+ newLeafId: await this.session.getLeafId(),
743
+ oldLeafId,
744
+ summaryEntry,
745
+ fromHook: hookResult?.summary !== undefined,
746
+ });
747
+ return { cancelled: false, editorText, summaryEntry };
748
+ }
749
+ catch (error) {
750
+ throw normalizeHarnessError(error, "branch_summary");
751
+ }
752
+ finally {
753
+ this.phase = "idle";
754
+ }
755
+ }
756
+ getModel() {
757
+ return this.model;
758
+ }
759
+ getThinkingLevel() {
760
+ return this.thinkingLevel;
761
+ }
762
+ async setModel(model) {
763
+ try {
764
+ const previousModel = this.model;
765
+ if (this.phase === "idle") {
766
+ await this.session.appendModelChange(model.provider, model.id);
767
+ }
768
+ else {
769
+ this.pendingSessionWrites.push({ type: "model_change", provider: model.provider, modelId: model.id });
770
+ }
771
+ this.model = model;
772
+ await this.emitOwn({ type: "model_select", model, previousModel, source: "set" });
773
+ }
774
+ catch (error) {
775
+ throw normalizeHarnessError(error, "session");
776
+ }
777
+ }
778
+ async setThinkingLevel(level) {
779
+ try {
780
+ const previousLevel = this.thinkingLevel;
781
+ if (this.phase === "idle") {
782
+ await this.session.appendThinkingLevelChange(level);
783
+ }
784
+ else {
785
+ this.pendingSessionWrites.push({ type: "thinking_level_change", thinkingLevel: level });
786
+ }
787
+ this.thinkingLevel = level;
788
+ await this.emitOwn({ type: "thinking_level_select", level, previousLevel });
789
+ }
790
+ catch (error) {
791
+ throw normalizeHarnessError(error, "session");
792
+ }
793
+ }
794
+ async setActiveTools(toolNames) {
795
+ try {
796
+ this.validateToolNames(toolNames);
797
+ this.activeToolNames = [...toolNames];
798
+ }
799
+ catch (error) {
800
+ throw normalizeHarnessError(error, "invalid_argument");
801
+ }
802
+ }
803
+ getSteeringMode() {
804
+ return this.steeringQueueMode;
805
+ }
806
+ async setSteeringMode(mode) {
807
+ this.steeringQueueMode = mode;
808
+ }
809
+ getFollowUpMode() {
810
+ return this.followUpQueueMode;
811
+ }
812
+ async setFollowUpMode(mode) {
813
+ this.followUpQueueMode = mode;
814
+ }
815
+ getResources() {
816
+ return {
817
+ skills: this.resources.skills?.slice(),
818
+ promptTemplates: this.resources.promptTemplates?.slice(),
819
+ };
820
+ }
821
+ async setResources(resources) {
822
+ const previousResources = this.getResources();
823
+ this.resources = {
824
+ skills: resources.skills?.slice(),
825
+ promptTemplates: resources.promptTemplates?.slice(),
826
+ };
827
+ await this.emitOwn({ type: "resources_update", resources: this.getResources(), previousResources });
828
+ }
829
+ getStreamOptions() {
830
+ return cloneStreamOptions(this.streamOptions);
831
+ }
832
+ async setStreamOptions(streamOptions) {
833
+ this.streamOptions = cloneStreamOptions(streamOptions);
834
+ }
835
+ async setTools(tools, activeToolNames) {
836
+ try {
837
+ const nextTools = new Map(tools.map((tool) => [tool.name, tool]));
838
+ const nextActiveToolNames = activeToolNames ? [...activeToolNames] : this.activeToolNames;
839
+ this.validateToolNames(nextActiveToolNames, nextTools);
840
+ this.tools = nextTools;
841
+ this.activeToolNames = [...nextActiveToolNames];
842
+ }
843
+ catch (error) {
844
+ throw normalizeHarnessError(error, "invalid_argument");
845
+ }
846
+ }
847
+ async abort() {
848
+ const clearedSteer = [...this.steerQueue];
849
+ const clearedFollowUp = [...this.followUpQueue];
850
+ this.steerQueue = [];
851
+ this.followUpQueue = [];
852
+ this.runAbortController?.abort();
853
+ const errors = [];
854
+ try {
855
+ await this.emitQueueUpdate();
856
+ }
857
+ catch (error) {
858
+ errors.push(toError(error));
859
+ }
860
+ try {
861
+ await this.waitForIdle();
862
+ }
863
+ catch (error) {
864
+ errors.push(toError(error));
865
+ }
866
+ try {
867
+ await this.emitOwn({ type: "abort", clearedSteer, clearedFollowUp });
868
+ }
869
+ catch (error) {
870
+ errors.push(toError(error));
871
+ }
872
+ if (errors.length > 0) {
873
+ const cause = errors.length === 1 ? errors[0] : new AggregateError(errors, "Abort completed with errors");
874
+ throw normalizeHarnessError(cause, "hook");
875
+ }
876
+ return { clearedSteer, clearedFollowUp };
877
+ }
878
+ async waitForIdle() {
879
+ await this.runPromise;
880
+ }
881
+ subscribe(listener) {
882
+ let handlers = this.handlers.get(SUBSCRIBER_EVENT_TYPE);
883
+ if (!handlers) {
884
+ handlers = new Set();
885
+ this.handlers.set(SUBSCRIBER_EVENT_TYPE, handlers);
886
+ }
887
+ handlers.add(listener);
888
+ return () => handlers.delete(listener);
889
+ }
890
+ on(type, handler) {
891
+ let handlers = this.handlers.get(type);
892
+ if (!handlers) {
893
+ handlers = new Set();
894
+ this.handlers.set(type, handlers);
895
+ }
896
+ handlers.add(handler);
897
+ return () => handlers.delete(handler);
898
+ }
899
+ }
900
+ //# sourceMappingURL=agent-harness.js.map