@oh-my-pi/pi-agent-core 15.10.2 → 15.10.4
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/CHANGELOG.md +21 -0
- package/dist/types/agent.d.ts +7 -1
- package/dist/types/types.d.ts +18 -2
- package/package.json +4 -4
- package/src/agent-loop.ts +27 -6
- package/src/agent.ts +12 -0
- package/src/compaction/messages.ts +1 -1
- package/src/compaction/openai.ts +25 -48
- package/src/types.ts +19 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.10.3] - 2026-06-08
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a non-interrupting "aside" message channel to the agent loop (`AgentLoopConfig.getAsideMessages` / `Agent.setAsideMessageProvider`). Asides are drained at each step boundary (after a tool batch, before the next model call) and at the yield check, so passive notifications (e.g. background-job completions, late LSP diagnostics) reach the model *between requests* without waiting for the agent to stop and without aborting in-flight tools the way steering does.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Changed core custom and hook messages to convert to `developer` messages for provider context.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Fixed the compaction spinner freezing (only repainting on a terminal resize) when compacting very large codex/OpenAI contexts. `buildOpenAiNativeHistory` re-collected the full known/custom tool-call id sets on every history-bearing message, rescanning the entire growing native history each time — O(N²) in history items — which blocked the event loop for seconds and starved the loader's animation timer and render scheduler. The sets are now maintained incrementally (linear), so building the compaction request no longer monopolizes the main thread.
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
- Removed the now-dead `<turn-aborted>` marker from the OpenAI compaction output user-message filter, since `transformMessages` no longer emits that note.
|
|
22
|
+
- Removed stale synthetic user-message tag filters from OpenAI remote compaction output preservation; developer messages are now dropped by role instead.
|
|
23
|
+
- Tool executions now receive the active turn `AbortSignal` unconditionally.
|
|
24
|
+
|
|
25
|
+
|
|
5
26
|
## [15.10.2] - 2026-06-08
|
|
6
27
|
|
|
7
28
|
### Fixed
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ApiKeyResolveContext, type AssistantMessage, type AssistantMessageEvent, type CursorExecHandlers, type CursorToolResultHandler, type Effort, type ImageContent, type Message, type Model, type ProviderSessionState, type ServiceTier, type SimpleStreamOptions, type ThinkingBudgets, type ToolChoice } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import type { AppendOnlyContextManager } from "./append-only-context";
|
|
3
3
|
import type { HarmonyAuditEvent } from "./harmony-leak";
|
|
4
|
-
import type { AgentEvent, AgentLoopConfig, AgentMessage, AgentState, AgentTool, AgentToolContext, StreamFn, ToolCallContext } from "./types";
|
|
4
|
+
import type { AgentEvent, AgentLoopConfig, AgentMessage, AgentState, AgentTool, AgentToolContext, AsideMessage, StreamFn, ToolCallContext } from "./types";
|
|
5
5
|
export declare class AgentBusyError extends Error {
|
|
6
6
|
constructor(message?: string);
|
|
7
7
|
}
|
|
@@ -291,6 +291,12 @@ export declare class Agent {
|
|
|
291
291
|
setRawSseEventInterceptor(fn: SimpleStreamOptions["onSseEvent"] | undefined): void;
|
|
292
292
|
setAssistantMessageEventInterceptor(fn: ((message: AssistantMessage, event: AssistantMessageEvent) => void) | undefined): void;
|
|
293
293
|
setOnBeforeYield(fn: (() => Promise<void> | void) | undefined): void;
|
|
294
|
+
/**
|
|
295
|
+
* Provide a source of non-interrupting "aside" messages (e.g. background-job
|
|
296
|
+
* completions, late LSP diagnostics) drained at each step boundary. Never
|
|
297
|
+
* aborts in-flight tools. See `AgentLoopConfig.getAsideMessages`.
|
|
298
|
+
*/
|
|
299
|
+
setAsideMessageProvider(fn: (() => AsideMessage[] | Promise<AsideMessage[]>) | undefined): void;
|
|
294
300
|
emitExternalEvent(event: AgentEvent): void;
|
|
295
301
|
setSystemPrompt(v: string[]): void;
|
|
296
302
|
setModel(m: Model): void;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -5,6 +5,13 @@ import type { AgentRunCoverage, AgentRunSummary } from "./run-collector";
|
|
|
5
5
|
import type { AgentTelemetryConfig } from "./telemetry";
|
|
6
6
|
/** Stream function - can return sync or Promise for async config lookup */
|
|
7
7
|
export type StreamFn = (...args: Parameters<typeof streamSimple>) => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
|
|
8
|
+
/**
|
|
9
|
+
* An aside entry: a ready {@link AgentMessage}, or a sync thunk evaluated at
|
|
10
|
+
* injection time that returns the message to inject or `null` to skip it. Thunks
|
|
11
|
+
* let the producer make the final inject-or-drop decision against current state
|
|
12
|
+
* (e.g. dropping late diagnostics a newer edit superseded).
|
|
13
|
+
*/
|
|
14
|
+
export type AsideMessage = AgentMessage | (() => AgentMessage | null);
|
|
8
15
|
/**
|
|
9
16
|
* Configuration for the agent loop.
|
|
10
17
|
*/
|
|
@@ -102,6 +109,17 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
102
109
|
* continues with another turn.
|
|
103
110
|
*/
|
|
104
111
|
getFollowUpMessages?: () => Promise<AgentMessage[]>;
|
|
112
|
+
/**
|
|
113
|
+
* Returns non-interrupting "aside" messages to inject at a step boundary.
|
|
114
|
+
*
|
|
115
|
+
* Polled after each tool batch (before the next LLM call) AND at the yield
|
|
116
|
+
* check. Unlike steering, these NEVER abort in-flight tools — they are passive
|
|
117
|
+
* notifications (e.g. background-job completions, late LSP diagnostics) that
|
|
118
|
+
* should reach the model between requests without waiting for the agent to
|
|
119
|
+
* fully stop. Returned messages are appended to the context with normal
|
|
120
|
+
* message events and keep the loop running so the model can react.
|
|
121
|
+
*/
|
|
122
|
+
getAsideMessages?: () => Promise<AsideMessage[]>;
|
|
105
123
|
/**
|
|
106
124
|
* Hook fired right before the loop would exit.
|
|
107
125
|
*
|
|
@@ -352,8 +370,6 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
|
|
|
352
370
|
loadMode?: "essential" | "discoverable";
|
|
353
371
|
/** Short one-line summary used for tool discovery indexes. */
|
|
354
372
|
summary?: string;
|
|
355
|
-
/** If true, tool execution ignores abort signals (runs to completion) */
|
|
356
|
-
nonAbortable?: boolean;
|
|
357
373
|
/**
|
|
358
374
|
* Concurrency mode for tool scheduling when multiple calls are in one turn.
|
|
359
375
|
* - "shared": can run alongside other shared tools (default)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-agent-core",
|
|
4
|
-
"version": "15.10.
|
|
4
|
+
"version": "15.10.4",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"fmt": "biome format --write ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@oh-my-pi/pi-ai": "15.10.
|
|
39
|
-
"@oh-my-pi/pi-natives": "15.10.
|
|
40
|
-
"@oh-my-pi/pi-utils": "15.10.
|
|
38
|
+
"@oh-my-pi/pi-ai": "15.10.4",
|
|
39
|
+
"@oh-my-pi/pi-natives": "15.10.4",
|
|
40
|
+
"@oh-my-pi/pi-utils": "15.10.4",
|
|
41
41
|
"@opentelemetry/api": "^1.9.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -49,6 +49,7 @@ import type {
|
|
|
49
49
|
AgentMessage,
|
|
50
50
|
AgentTool,
|
|
51
51
|
AgentToolResult,
|
|
52
|
+
AsideMessage,
|
|
52
53
|
StreamFn,
|
|
53
54
|
} from "./types";
|
|
54
55
|
import { yieldIfDue } from "./utils/yield";
|
|
@@ -465,6 +466,23 @@ function cloneAssistantMessageForToolCallCap(message: AssistantMessage): Assista
|
|
|
465
466
|
};
|
|
466
467
|
}
|
|
467
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Resolve aside entries at the moment the loop is about to inject them. Each entry
|
|
471
|
+
* is either a ready {@link AgentMessage} or a sync thunk evaluated here so the
|
|
472
|
+
* producer can make the final inject-or-drop decision (return null) against
|
|
473
|
+
* up-to-the-injection state — e.g. dropping late diagnostics a newer edit
|
|
474
|
+
* superseded. Kept sync so it can never stall the loop.
|
|
475
|
+
*/
|
|
476
|
+
function resolveAsides(entries: AsideMessage[] | undefined): AgentMessage[] {
|
|
477
|
+
if (!entries || entries.length === 0) return [];
|
|
478
|
+
const out: AgentMessage[] = [];
|
|
479
|
+
for (const entry of entries) {
|
|
480
|
+
const message = typeof entry === "function" ? entry() : entry;
|
|
481
|
+
if (message) out.push(message);
|
|
482
|
+
}
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
485
|
+
|
|
468
486
|
async function runLoopBody(
|
|
469
487
|
currentContext: AgentContext,
|
|
470
488
|
newMessages: AgentMessage[],
|
|
@@ -647,15 +665,18 @@ async function runLoopBody(
|
|
|
647
665
|
|
|
648
666
|
stream.push({ type: "turn_end", message, toolResults });
|
|
649
667
|
|
|
650
|
-
|
|
668
|
+
const steering = steeringMessagesFromExecution ?? ((await config.getSteeringMessages?.()) || []);
|
|
669
|
+
const asides = resolveAsides(await config.getAsideMessages?.());
|
|
670
|
+
pendingMessages = asides.length > 0 ? [...steering, ...asides] : steering;
|
|
651
671
|
}
|
|
652
672
|
|
|
653
|
-
// Agent would stop here.
|
|
673
|
+
// Agent would stop here. Drain non-interrupting asides + follow-up messages.
|
|
654
674
|
await config.onBeforeYield?.();
|
|
675
|
+
const asideMessages = resolveAsides(await config.getAsideMessages?.());
|
|
655
676
|
const followUpMessages = (await config.getFollowUpMessages?.()) || [];
|
|
656
|
-
if (followUpMessages.length > 0) {
|
|
657
|
-
// Set as pending so inner loop processes them
|
|
658
|
-
pendingMessages = followUpMessages;
|
|
677
|
+
if (asideMessages.length > 0 || followUpMessages.length > 0) {
|
|
678
|
+
// Set as pending so the inner loop processes them before stopping.
|
|
679
|
+
pendingMessages = [...asideMessages, ...followUpMessages];
|
|
659
680
|
continue;
|
|
660
681
|
}
|
|
661
682
|
|
|
@@ -1282,7 +1303,7 @@ async function executeToolCalls(
|
|
|
1282
1303
|
const rawResult = await tool.execute(
|
|
1283
1304
|
toolCall.id,
|
|
1284
1305
|
transformToolCallArguments ? transformToolCallArguments(effectiveArgs, toolCall.name) : effectiveArgs,
|
|
1285
|
-
|
|
1306
|
+
toolSignal,
|
|
1286
1307
|
partialResult => {
|
|
1287
1308
|
stream.push({
|
|
1288
1309
|
type: "tool_execution_update",
|
package/src/agent.ts
CHANGED
|
@@ -33,6 +33,7 @@ import type {
|
|
|
33
33
|
AgentState,
|
|
34
34
|
AgentTool,
|
|
35
35
|
AgentToolContext,
|
|
36
|
+
AsideMessage,
|
|
36
37
|
StreamFn,
|
|
37
38
|
ToolCallContext,
|
|
38
39
|
} from "./types";
|
|
@@ -319,6 +320,7 @@ export class Agent {
|
|
|
319
320
|
#onAssistantMessageEvent?: (message: AssistantMessage, event: AssistantMessageEvent) => void;
|
|
320
321
|
#onHarmonyLeak?: (event: HarmonyAuditEvent) => void | Promise<void>;
|
|
321
322
|
#onBeforeYield?: () => Promise<void> | void;
|
|
323
|
+
#asideMessageProvider?: () => AsideMessage[] | Promise<AsideMessage[]>;
|
|
322
324
|
#telemetry?: AgentLoopConfig["telemetry"];
|
|
323
325
|
#appendOnlyContext?: AppendOnlyContextManager;
|
|
324
326
|
|
|
@@ -629,6 +631,15 @@ export class Agent {
|
|
|
629
631
|
this.#onBeforeYield = fn;
|
|
630
632
|
}
|
|
631
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Provide a source of non-interrupting "aside" messages (e.g. background-job
|
|
636
|
+
* completions, late LSP diagnostics) drained at each step boundary. Never
|
|
637
|
+
* aborts in-flight tools. See `AgentLoopConfig.getAsideMessages`.
|
|
638
|
+
*/
|
|
639
|
+
setAsideMessageProvider(fn: (() => AsideMessage[] | Promise<AsideMessage[]>) | undefined): void {
|
|
640
|
+
this.#asideMessageProvider = fn;
|
|
641
|
+
}
|
|
642
|
+
|
|
632
643
|
emitExternalEvent(event: AgentEvent) {
|
|
633
644
|
switch (event.type) {
|
|
634
645
|
case "message_start":
|
|
@@ -999,6 +1010,7 @@ export class Agent {
|
|
|
999
1010
|
return this.#dequeueSteeringMessages();
|
|
1000
1011
|
},
|
|
1001
1012
|
getFollowUpMessages: async () => this.#dequeueFollowUpMessages(),
|
|
1013
|
+
getAsideMessages: async () => (await this.#asideMessageProvider?.()) ?? [],
|
|
1002
1014
|
onBeforeYield: () => this.#onBeforeYield?.(),
|
|
1003
1015
|
telemetry: this.#telemetry,
|
|
1004
1016
|
};
|
|
@@ -156,7 +156,7 @@ export function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
|
|
|
156
156
|
? [{ type: "text" as const, text: message.content }]
|
|
157
157
|
: message.content;
|
|
158
158
|
return {
|
|
159
|
-
role: "
|
|
159
|
+
role: "developer",
|
|
160
160
|
content,
|
|
161
161
|
attribution: message.attribution,
|
|
162
162
|
timestamp: message.timestamp,
|
package/src/compaction/openai.ts
CHANGED
|
@@ -158,38 +158,10 @@ function shouldTrimOpenAiCompactInputItem(item: Record<string, unknown>): boolea
|
|
|
158
158
|
return item.type === "function_call_output" || (item.type === "message" && item.role === "developer");
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
function shouldKeepOpenAiCompactOutputUserMessage(item: Record<string, unknown>): boolean {
|
|
162
|
-
if (item.role !== "user") return false;
|
|
163
|
-
const content = item.content;
|
|
164
|
-
if (!Array.isArray(content) || content.length === 0) return false;
|
|
165
|
-
const contextualFragmentPatterns = [
|
|
166
|
-
[/^<system-reminder>[\s\S]*<\/system-reminder>$/i, /<system-reminder>/i],
|
|
167
|
-
[/^#\s*AGENTS\.md instructions for\b[\s\S]*<\/INSTRUCTIONS>$/i, /# AGENTS.md instructions/],
|
|
168
|
-
[/^<environment-context>[\s\S]*<\/environment-context>$/i, /<environment-context>/i],
|
|
169
|
-
[/^<skill>[\s\S]*<\/skill>$/i, /<skill>/i],
|
|
170
|
-
[/^<user-shell-command>[\s\S]*<\/user-shell-command>$/i, /<user-shell-command>/i],
|
|
171
|
-
[/^<turn-aborted>[\s\S]*<\/turn-aborted>$/i, /<turn-aborted>/i],
|
|
172
|
-
[/^<subagent-notification>[\s\S]*<\/subagent-notification>$/i, /<subagent-notification>/i],
|
|
173
|
-
] as const;
|
|
174
|
-
return content.every(part => {
|
|
175
|
-
if (!part || typeof part !== "object") return false;
|
|
176
|
-
const candidate = part as { type?: unknown; text?: unknown };
|
|
177
|
-
if (candidate.type === "input_image") return true;
|
|
178
|
-
if (candidate.type !== "input_text" || typeof candidate.text !== "string") return false;
|
|
179
|
-
const trimmed = candidate.text.trim();
|
|
180
|
-
if (trimmed.length === 0) return false;
|
|
181
|
-
return !contextualFragmentPatterns.some(([strictPattern, markerPattern]) => {
|
|
182
|
-
return strictPattern.test(trimmed) || markerPattern.test(trimmed);
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
161
|
function shouldKeepOpenAiCompactOutputItem(item: Record<string, unknown>): boolean {
|
|
188
162
|
if (item.type === "compaction" || item.type === "compaction_summary") return true;
|
|
189
163
|
if (item.type !== "message") return false;
|
|
190
|
-
|
|
191
|
-
if (item.role === "assistant") return true;
|
|
192
|
-
return shouldKeepOpenAiCompactOutputUserMessage(item);
|
|
164
|
+
return item.role === "assistant" || item.role === "user";
|
|
193
165
|
}
|
|
194
166
|
|
|
195
167
|
function trimOpenAiCompactInput(
|
|
@@ -220,24 +192,27 @@ function trimOpenAiCompactInput(
|
|
|
220
192
|
return trimmed;
|
|
221
193
|
}
|
|
222
194
|
|
|
223
|
-
|
|
224
|
-
|
|
195
|
+
// Register every tool-call id in `items` (and the subset using the custom-tool
|
|
196
|
+
// wire shape) into the running sets. The history builder maintains both sets
|
|
197
|
+
// incrementally as native history is appended, so this only scans the
|
|
198
|
+
// newly-added items (or, after a full-snapshot replace, the fresh input) rather
|
|
199
|
+
// than re-scanning the whole growing history per message — the latter was
|
|
200
|
+
// O(N²) and blocked the event loop for seconds while compacting large codex
|
|
201
|
+
// contexts (frozen spinner until the next forced render).
|
|
202
|
+
function addOpenAiCallIds(
|
|
203
|
+
items: Array<Record<string, unknown>>,
|
|
204
|
+
knownCallIds: Set<string>,
|
|
205
|
+
customCallIds: Set<string>,
|
|
206
|
+
): void {
|
|
225
207
|
for (const item of items) {
|
|
226
|
-
if (
|
|
208
|
+
if (typeof item.call_id !== "string") continue;
|
|
209
|
+
if (item.type === "function_call") {
|
|
210
|
+
knownCallIds.add(item.call_id);
|
|
211
|
+
} else if (item.type === "custom_tool_call") {
|
|
227
212
|
knownCallIds.add(item.call_id);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return knownCallIds;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function collectCustomOpenAiCallIds(items: Array<Record<string, unknown>>): Set<string> {
|
|
234
|
-
const customCallIds = new Set<string>();
|
|
235
|
-
for (const item of items) {
|
|
236
|
-
if (item.type === "custom_tool_call" && typeof item.call_id === "string") {
|
|
237
213
|
customCallIds.add(item.call_id);
|
|
238
214
|
}
|
|
239
215
|
}
|
|
240
|
-
return customCallIds;
|
|
241
216
|
}
|
|
242
217
|
|
|
243
218
|
// ============================================================================
|
|
@@ -265,16 +240,16 @@ export function buildOpenAiNativeHistory(
|
|
|
265
240
|
const transformedMessages = transformMessages(messages, model, id => normalizeOpenAiCompactionToolCallId(id));
|
|
266
241
|
|
|
267
242
|
let msgIndex = 0;
|
|
268
|
-
|
|
269
|
-
|
|
243
|
+
const knownCallIds = new Set<string>();
|
|
244
|
+
const customCallIds = new Set<string>();
|
|
245
|
+
addOpenAiCallIds(input, knownCallIds, customCallIds);
|
|
270
246
|
for (const message of transformedMessages) {
|
|
271
247
|
if (message.role === "user" || message.role === "developer") {
|
|
272
248
|
const providerPayload = (message as { providerPayload?: AssistantMessage["providerPayload"] }).providerPayload;
|
|
273
249
|
const historyItems = getOpenAIResponsesHistoryItems(providerPayload, model.provider);
|
|
274
250
|
if (historyItems) {
|
|
275
251
|
input.push(...historyItems);
|
|
276
|
-
knownCallIds
|
|
277
|
-
customCallIds = collectCustomOpenAiCallIds(input);
|
|
252
|
+
addOpenAiCallIds(historyItems, knownCallIds, customCallIds);
|
|
278
253
|
msgIndex++;
|
|
279
254
|
continue;
|
|
280
255
|
}
|
|
@@ -317,11 +292,13 @@ export function buildOpenAiNativeHistory(
|
|
|
317
292
|
if (providerPayload) {
|
|
318
293
|
if (providerPayload.dt) {
|
|
319
294
|
input.push(...providerPayload.items);
|
|
295
|
+
addOpenAiCallIds(providerPayload.items, knownCallIds, customCallIds);
|
|
320
296
|
} else {
|
|
321
297
|
input.splice(0, input.length, ...providerPayload.items);
|
|
298
|
+
knownCallIds.clear();
|
|
299
|
+
customCallIds.clear();
|
|
300
|
+
addOpenAiCallIds(input, knownCallIds, customCallIds);
|
|
322
301
|
}
|
|
323
|
-
knownCallIds = collectKnownOpenAiCallIds(input);
|
|
324
|
-
customCallIds = collectCustomOpenAiCallIds(input);
|
|
325
302
|
msgIndex++;
|
|
326
303
|
continue;
|
|
327
304
|
}
|
package/src/types.ts
CHANGED
|
@@ -26,6 +26,14 @@ export type StreamFn = (
|
|
|
26
26
|
...args: Parameters<typeof streamSimple>
|
|
27
27
|
) => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* An aside entry: a ready {@link AgentMessage}, or a sync thunk evaluated at
|
|
31
|
+
* injection time that returns the message to inject or `null` to skip it. Thunks
|
|
32
|
+
* let the producer make the final inject-or-drop decision against current state
|
|
33
|
+
* (e.g. dropping late diagnostics a newer edit superseded).
|
|
34
|
+
*/
|
|
35
|
+
export type AsideMessage = AgentMessage | (() => AgentMessage | null);
|
|
36
|
+
|
|
29
37
|
/**
|
|
30
38
|
* Configuration for the agent loop.
|
|
31
39
|
*/
|
|
@@ -132,6 +140,17 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
132
140
|
* continues with another turn.
|
|
133
141
|
*/
|
|
134
142
|
getFollowUpMessages?: () => Promise<AgentMessage[]>;
|
|
143
|
+
/**
|
|
144
|
+
* Returns non-interrupting "aside" messages to inject at a step boundary.
|
|
145
|
+
*
|
|
146
|
+
* Polled after each tool batch (before the next LLM call) AND at the yield
|
|
147
|
+
* check. Unlike steering, these NEVER abort in-flight tools — they are passive
|
|
148
|
+
* notifications (e.g. background-job completions, late LSP diagnostics) that
|
|
149
|
+
* should reach the model between requests without waiting for the agent to
|
|
150
|
+
* fully stop. Returned messages are appended to the context with normal
|
|
151
|
+
* message events and keep the loop running so the model can react.
|
|
152
|
+
*/
|
|
153
|
+
getAsideMessages?: () => Promise<AsideMessage[]>;
|
|
135
154
|
/**
|
|
136
155
|
* Hook fired right before the loop would exit.
|
|
137
156
|
*
|
|
@@ -423,8 +442,6 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
|
|
|
423
442
|
loadMode?: "essential" | "discoverable";
|
|
424
443
|
/** Short one-line summary used for tool discovery indexes. */
|
|
425
444
|
summary?: string;
|
|
426
|
-
/** If true, tool execution ignores abort signals (runs to completion) */
|
|
427
|
-
nonAbortable?: boolean;
|
|
428
445
|
/**
|
|
429
446
|
* Concurrency mode for tool scheduling when multiple calls are in one turn.
|
|
430
447
|
* - "shared": can run alongside other shared tools (default)
|