@oh-my-pi/pi-agent-core 18.3.2 → 18.3.3
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 +6 -0
- package/dist/types/agent-loop.d.ts +8 -1
- package/dist/types/agent.d.ts +13 -0
- package/dist/types/types.d.ts +7 -0
- package/package.json +8 -8
- package/src/agent-loop.ts +38 -2
- package/src/agent.ts +76 -22
- package/src/types.ts +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { type AssistantMessage, type Context, EventStream, type ToolResultMessage } from "@oh-my-pi/pi-ai";
|
|
6
6
|
import { type Dialect } from "@oh-my-pi/pi-ai/dialect";
|
|
7
7
|
import { type AgentRunCoverage, type AgentRunSummary } from "./run-collector.js";
|
|
8
|
-
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types.js";
|
|
8
|
+
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, SteeringQueueState, StreamFn } from "./types.js";
|
|
9
9
|
/** Stop-details marker for a provider error after assistant content/tool args already streamed. */
|
|
10
10
|
export declare const STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL = "stream_interrupted_after_content";
|
|
11
11
|
/**
|
|
@@ -106,6 +106,13 @@ export interface NormalizeToolsOptions {
|
|
|
106
106
|
pruneDescriptions?: boolean;
|
|
107
107
|
}
|
|
108
108
|
export declare function normalizeTools(tools: AgentContext["tools"], options: NormalizeToolsOptions): Context["tools"];
|
|
109
|
+
/**
|
|
110
|
+
* Classify the first `count` steering messages for a tool-batch interrupt:
|
|
111
|
+
* any user-authored message wins, then agent-attributed user messages, else
|
|
112
|
+
* system steering (advisor cards, hidden directives). Shared by the agent's
|
|
113
|
+
* queue peek and the loop's live-taken steering.
|
|
114
|
+
*/
|
|
115
|
+
export declare function steeringQueueState(messages: readonly AgentMessage[], count?: number): SteeringQueueState;
|
|
109
116
|
/** Resolve the human-readable reason an abort carried. A caller that aborts via
|
|
110
117
|
* `AbortController.abort(reason)` with a string or a non-`AbortError` `Error`
|
|
111
118
|
* (e.g. the coding agent's user-interrupt label) gets that text surfaced on the
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -411,6 +411,14 @@ export declare class Agent {
|
|
|
411
411
|
addBeforeQueuedMessageDequeueHook(hook: (signal?: AbortSignal) => Promise<void> | void): () => void;
|
|
412
412
|
/** Register an independently removable hook that runs immediately before each model call. */
|
|
413
413
|
addBeforeModelCallHook(hook: (signal?: AbortSignal) => Promise<void> | void): () => void;
|
|
414
|
+
/**
|
|
415
|
+
* Take back live-steered messages ahead of an abort (Esc restores them to the editor):
|
|
416
|
+
* the aborted run then neither records nor requeues them.
|
|
417
|
+
*/
|
|
418
|
+
withdrawLiveSteering(): AgentMessage[];
|
|
419
|
+
/** Steering live steering took for the streaming response; the transcript records it once
|
|
420
|
+
* that response (or its tool batch) ends, which is when the model switches to it. */
|
|
421
|
+
peekLiveSteeredMessages(): AgentMessage[];
|
|
414
422
|
setProviderResponseInterceptor(fn: SimpleStreamOptions["onResponse"] | undefined): void;
|
|
415
423
|
setRawSseEventInterceptor(fn: SimpleStreamOptions["onSseEvent"] | undefined): void;
|
|
416
424
|
setAssistantMessageEventInterceptor(fn: ((message: AssistantMessage, event: AssistantMessageEvent) => void) | undefined): void;
|
|
@@ -476,6 +484,11 @@ export declare class Agent {
|
|
|
476
484
|
* single source of truth. Includes exclusively claimed originals while
|
|
477
485
|
* preparation is pending, so editor restoration can cancel their delivery. */
|
|
478
486
|
peekSteeringQueue(): readonly AgentMessage[];
|
|
487
|
+
/** Dequeued messages not yet in the transcript, e.g. steering a provider
|
|
488
|
+
* took into its in-flight response via live steering. Aborting the run
|
|
489
|
+
* requeues them, so the session's empty-submit interrupt counts them as
|
|
490
|
+
* pending input even though {@link peekSteeringQueue} no longer does. */
|
|
491
|
+
peekUndeliveredQueuedMessages(): AgentMessage[];
|
|
479
492
|
/** Non-consuming view of the pending follow-up queue. See
|
|
480
493
|
* {@link peekSteeringQueue}. */
|
|
481
494
|
peekFollowUpQueue(): readonly AgentMessage[];
|
package/dist/types/types.d.ts
CHANGED
|
@@ -240,6 +240,13 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
240
240
|
* {@link getSteeringMessages}.
|
|
241
241
|
*/
|
|
242
242
|
waitForSteeringMessages?: (signal?: AbortSignal) => Promise<void>;
|
|
243
|
+
/**
|
|
244
|
+
* Called when live steering dequeues messages via {@link getSteeringMessages}
|
|
245
|
+
* for the response being streamed. The loop records them in the transcript
|
|
246
|
+
* after that response (or its tool batch); an abort before then leaves them
|
|
247
|
+
* unrecorded for the host to requeue.
|
|
248
|
+
*/
|
|
249
|
+
onLiveSteeringTaken?: (messages: AgentMessage[]) => void;
|
|
243
250
|
/**
|
|
244
251
|
* Peeks whether IRC messages should interrupt an interruptible waiting tool.
|
|
245
252
|
*
|
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": "18.3.
|
|
4
|
+
"version": "18.3.3",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": {
|
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@oh-my-pi/pi-ai": "18.3.
|
|
42
|
-
"@oh-my-pi/pi-catalog": "18.3.
|
|
43
|
-
"@oh-my-pi/pi-natives": "18.3.
|
|
44
|
-
"@oh-my-pi/pi-utils": "18.3.
|
|
45
|
-
"@oh-my-pi/pi-wire": "18.3.
|
|
46
|
-
"@oh-my-pi/snapcompact": "18.3.
|
|
41
|
+
"@oh-my-pi/pi-ai": "18.3.3",
|
|
42
|
+
"@oh-my-pi/pi-catalog": "18.3.3",
|
|
43
|
+
"@oh-my-pi/pi-natives": "18.3.3",
|
|
44
|
+
"@oh-my-pi/pi-utils": "18.3.3",
|
|
45
|
+
"@oh-my-pi/pi-wire": "18.3.3",
|
|
46
|
+
"@oh-my-pi/snapcompact": "18.3.3",
|
|
47
47
|
"@opentelemetry/api": "^1.9.1"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@oh-my-pi/omptype": "18.3.
|
|
50
|
+
"@oh-my-pi/omptype": "18.3.3",
|
|
51
51
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
52
52
|
"@opentelemetry/sdk-trace-base": "^2.9.0",
|
|
53
53
|
"@types/bun": "^1.3.14"
|
package/src/agent-loop.ts
CHANGED
|
@@ -1247,6 +1247,7 @@ async function runLoopBody(
|
|
|
1247
1247
|
config,
|
|
1248
1248
|
telemetry,
|
|
1249
1249
|
invokeAgentSpan,
|
|
1250
|
+
[],
|
|
1250
1251
|
);
|
|
1251
1252
|
for (const result of executionResult.toolResults) {
|
|
1252
1253
|
currentContext.messages.push(result);
|
|
@@ -1619,6 +1620,7 @@ async function runLoopBody(
|
|
|
1619
1620
|
config,
|
|
1620
1621
|
telemetry,
|
|
1621
1622
|
invokeAgentSpan,
|
|
1623
|
+
[...liveAccepted, ...liveDeferred],
|
|
1622
1624
|
);
|
|
1623
1625
|
toolResults.push(...executionResult.toolResults);
|
|
1624
1626
|
|
|
@@ -1800,6 +1802,27 @@ interface PreparedProviderCall {
|
|
|
1800
1802
|
liveSteering?: LiveSteeringChannel;
|
|
1801
1803
|
}
|
|
1802
1804
|
|
|
1805
|
+
/**
|
|
1806
|
+
* Classify the first `count` steering messages for a tool-batch interrupt:
|
|
1807
|
+
* any user-authored message wins, then agent-attributed user messages, else
|
|
1808
|
+
* system steering (advisor cards, hidden directives). Shared by the agent's
|
|
1809
|
+
* queue peek and the loop's live-taken steering.
|
|
1810
|
+
*/
|
|
1811
|
+
export function steeringQueueState(messages: readonly AgentMessage[], count = messages.length): SteeringQueueState {
|
|
1812
|
+
if (count === 0) return { queued: false };
|
|
1813
|
+
let hasAgentSteering = false;
|
|
1814
|
+
for (let i = 0; i < count; i++) {
|
|
1815
|
+
const message = messages[i];
|
|
1816
|
+
const role = "role" in message ? message.role : undefined;
|
|
1817
|
+
const attribution = "attribution" in message ? message.attribution : undefined;
|
|
1818
|
+
if (attribution === "user") return { queued: true, source: "user" };
|
|
1819
|
+
if (role !== "user") continue;
|
|
1820
|
+
if (attribution !== "agent") return { queued: true, source: "user" };
|
|
1821
|
+
hasAgentSteering = true;
|
|
1822
|
+
}
|
|
1823
|
+
return { queued: true, source: hasAgentSteering ? "agent" : "system" };
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1803
1826
|
/**
|
|
1804
1827
|
* Offer queued steering to a provider that can deliver it into the response it
|
|
1805
1828
|
* is streaming. Latency decides whether steering lands before the model commits
|
|
@@ -1818,7 +1841,11 @@ function openLiveSteering(
|
|
|
1818
1841
|
const bound = (signal: AbortSignal): AbortSignal => (loopSignal ? AbortSignal.any([signal, loopSignal]) : signal);
|
|
1819
1842
|
return new LiveSteeringChannel({
|
|
1820
1843
|
wait: signal => waitForSteeringMessages(bound(signal)),
|
|
1821
|
-
take: signal =>
|
|
1844
|
+
take: async signal => {
|
|
1845
|
+
const messages = await getSteeringMessages(bound(signal));
|
|
1846
|
+
if (messages.length > 0) config.onLiveSteeringTaken?.(messages);
|
|
1847
|
+
return messages;
|
|
1848
|
+
},
|
|
1822
1849
|
toProvider: async (messages, signal) => {
|
|
1823
1850
|
const transformed = config.transformContext
|
|
1824
1851
|
? await config.transformContext(messages, bound(signal))
|
|
@@ -3002,6 +3029,9 @@ async function executeToolCalls(
|
|
|
3002
3029
|
config: AgentLoopConfig,
|
|
3003
3030
|
telemetry: AgentTelemetry | undefined,
|
|
3004
3031
|
invokeAgentSpan: Span | undefined,
|
|
3032
|
+
// Steering the provider took off the queue during the response that emitted
|
|
3033
|
+
// this batch; it injects at this batch's boundary like queued steering.
|
|
3034
|
+
liveSteering: readonly AgentMessage[],
|
|
3005
3035
|
): Promise<{ toolResults: ToolResultMessage[]; additionalContext?: string }> {
|
|
3006
3036
|
const tools = currentContext.tools;
|
|
3007
3037
|
const {
|
|
@@ -3143,7 +3173,10 @@ async function executeToolCalls(
|
|
|
3143
3173
|
// injection boundary below; polling it here would strand or drop messages.
|
|
3144
3174
|
let steeringQueued = false;
|
|
3145
3175
|
let steeringSource: SteeringInterruptSource | undefined;
|
|
3146
|
-
if (
|
|
3176
|
+
if (liveSteering.length > 0) {
|
|
3177
|
+
steeringQueued = true;
|
|
3178
|
+
steeringSource = steeringQueueState(liveSteering).source;
|
|
3179
|
+
} else if (hasSteeringMessages) {
|
|
3147
3180
|
const queuedState = await hasSteeringMessages();
|
|
3148
3181
|
if (typeof queuedState === "boolean") {
|
|
3149
3182
|
steeringQueued = queuedState;
|
|
@@ -3490,6 +3523,9 @@ async function executeToolCalls(
|
|
|
3490
3523
|
const watchSteeringWhileRunning =
|
|
3491
3524
|
(softInterrupts || records.some(record => record.interruptible)) &&
|
|
3492
3525
|
(hasSteeringMessages !== undefined || hasAsidePeek);
|
|
3526
|
+
// Live-taken steering is already pending: interrupt before any call starts
|
|
3527
|
+
// so not-yet-started interruptible waits are skipped outright.
|
|
3528
|
+
if (liveSteering.length > 0) await checkSteering();
|
|
3493
3529
|
const eventDrivenSteeringWatch =
|
|
3494
3530
|
watchSteeringWhileRunning && config.waitForSteeringMessages !== undefined && hasSteeringMessages !== undefined;
|
|
3495
3531
|
const steeringWatchAbortController = new AbortController();
|
package/src/agent.ts
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
normalizeMessagesForProvider,
|
|
35
35
|
normalizeTools,
|
|
36
36
|
resolveOwnedDialectFromEnv,
|
|
37
|
+
steeringQueueState,
|
|
37
38
|
unpairedToolCallTail,
|
|
38
39
|
} from "./agent-loop";
|
|
39
40
|
import type { AppendOnlyContextManager } from "./append-only-context";
|
|
@@ -411,6 +412,14 @@ export class Agent {
|
|
|
411
412
|
#steeringQueue: AgentMessage[] = [];
|
|
412
413
|
#followUpQueue: AgentMessage[] = [];
|
|
413
414
|
#queuedMessageClaims: Partial<Record<QueuedMessageQueue, QueuedMessageClaim>> = {};
|
|
415
|
+
/**
|
|
416
|
+
* Steering live steering took for the in-flight response (`onLiveSteeringTaken`) that the
|
|
417
|
+
* transcript has not recorded yet, whether or not the provider accepted it. Kept apart from
|
|
418
|
+
* {@link #queuedMessageDeliveries}: the loop drops it on abort instead of recording it, so queue
|
|
419
|
+
* replacement must not drop it too; the run's end requeues whatever it did not record, and
|
|
420
|
+
* {@link withdrawLiveSteering} takes it back ahead of an abort.
|
|
421
|
+
*/
|
|
422
|
+
#liveSteered: { message: AgentMessage; controller: AbortController | undefined }[] = [];
|
|
414
423
|
/** Dequeued originals remain recoverable until their transcript events arrive. */
|
|
415
424
|
#queuedMessageDeliveries = new Set<{
|
|
416
425
|
queue: QueuedMessageQueue;
|
|
@@ -992,8 +1001,14 @@ export class Agent {
|
|
|
992
1001
|
}
|
|
993
1002
|
|
|
994
1003
|
#restoreUndeliveredQueuedMessages(controller: AbortController): void {
|
|
995
|
-
if (this.#queuedMessageDeliveries.size === 0) return;
|
|
1004
|
+
if (this.#queuedMessageDeliveries.size === 0 && this.#liveSteered.length === 0) return;
|
|
996
1005
|
const restored: Record<QueuedMessageQueue, AgentMessage[]> = { steering: [], followUp: [] };
|
|
1006
|
+
// Live steering was taken before anything the run still holds undelivered.
|
|
1007
|
+
this.#liveSteered = this.#liveSteered.filter(entry => {
|
|
1008
|
+
if (entry.controller !== controller) return true;
|
|
1009
|
+
restored.steering.push(entry.message);
|
|
1010
|
+
return false;
|
|
1011
|
+
});
|
|
997
1012
|
for (const delivery of this.#queuedMessageDeliveries) {
|
|
998
1013
|
if (delivery.controller !== controller) continue;
|
|
999
1014
|
this.#queuedMessageDeliveries.delete(delivery);
|
|
@@ -1008,6 +1023,40 @@ export class Agent {
|
|
|
1008
1023
|
if (restored.followUp.length > 0) this.#followUpQueue = [...restored.followUp, ...this.#followUpQueue];
|
|
1009
1024
|
}
|
|
1010
1025
|
|
|
1026
|
+
/** Move steering live steering took out of the queue-delivery records into {@link #liveSteered}. */
|
|
1027
|
+
#adoptLiveSteering(taken: readonly AgentMessage[]): void {
|
|
1028
|
+
for (const delivery of this.#queuedMessageDeliveries) {
|
|
1029
|
+
const pending = delivery.messages.slice(delivery.next);
|
|
1030
|
+
const kept = pending.filter(message => !taken.includes(message));
|
|
1031
|
+
if (kept.length === pending.length) continue;
|
|
1032
|
+
for (const message of pending) {
|
|
1033
|
+
if (taken.includes(message)) this.#liveSteered.push({ message, controller: delivery.controller });
|
|
1034
|
+
}
|
|
1035
|
+
if (kept.length === 0) {
|
|
1036
|
+
this.#queuedMessageDeliveries.delete(delivery);
|
|
1037
|
+
} else {
|
|
1038
|
+
delivery.messages = kept;
|
|
1039
|
+
delivery.next = 0;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Take back live-steered messages ahead of an abort (Esc restores them to the editor):
|
|
1046
|
+
* the aborted run then neither records nor requeues them.
|
|
1047
|
+
*/
|
|
1048
|
+
withdrawLiveSteering(): AgentMessage[] {
|
|
1049
|
+
const messages = this.peekLiveSteeredMessages();
|
|
1050
|
+
this.#liveSteered = [];
|
|
1051
|
+
return messages;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/** Steering live steering took for the streaming response; the transcript records it once
|
|
1055
|
+
* that response (or its tool batch) ends, which is when the model switches to it. */
|
|
1056
|
+
peekLiveSteeredMessages(): AgentMessage[] {
|
|
1057
|
+
return this.#liveSteered.map(entry => entry.message);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1011
1060
|
setProviderResponseInterceptor(fn: SimpleStreamOptions["onResponse"] | undefined): void {
|
|
1012
1061
|
this.#onResponse = fn;
|
|
1013
1062
|
}
|
|
@@ -1147,6 +1196,11 @@ export class Agent {
|
|
|
1147
1196
|
|
|
1148
1197
|
appendMessage(m: AgentMessage) {
|
|
1149
1198
|
this.#state.messages.push(m);
|
|
1199
|
+
const live = this.#liveSteered.findIndex(entry => entry.message === m);
|
|
1200
|
+
if (live >= 0) {
|
|
1201
|
+
this.#liveSteered.splice(live, 1);
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1150
1204
|
for (const delivery of this.#queuedMessageDeliveries) {
|
|
1151
1205
|
if (delivery.messages[delivery.next] !== m) continue;
|
|
1152
1206
|
if (++delivery.next === delivery.messages.length) this.#queuedMessageDeliveries.delete(delivery);
|
|
@@ -1202,6 +1256,7 @@ export class Agent {
|
|
|
1202
1256
|
clearAllQueues() {
|
|
1203
1257
|
this.#steeringQueue = [];
|
|
1204
1258
|
this.#followUpQueue = [];
|
|
1259
|
+
this.#liveSteered = [];
|
|
1205
1260
|
this.#cancelQueuedMessagePreparation("steering");
|
|
1206
1261
|
this.#cancelQueuedMessagePreparation("followUp");
|
|
1207
1262
|
this.#notifySteeringWaiters();
|
|
@@ -1227,6 +1282,18 @@ export class Agent {
|
|
|
1227
1282
|
return claim ? [...claim.messages, ...this.#steeringQueue] : this.#steeringQueue;
|
|
1228
1283
|
}
|
|
1229
1284
|
|
|
1285
|
+
/** Dequeued messages not yet in the transcript, e.g. steering a provider
|
|
1286
|
+
* took into its in-flight response via live steering. Aborting the run
|
|
1287
|
+
* requeues them, so the session's empty-submit interrupt counts them as
|
|
1288
|
+
* pending input even though {@link peekSteeringQueue} no longer does. */
|
|
1289
|
+
peekUndeliveredQueuedMessages(): AgentMessage[] {
|
|
1290
|
+
const messages = this.peekLiveSteeredMessages();
|
|
1291
|
+
for (const delivery of this.#queuedMessageDeliveries) {
|
|
1292
|
+
for (let i = delivery.next; i < delivery.messages.length; i++) messages.push(delivery.messages[i]);
|
|
1293
|
+
}
|
|
1294
|
+
return messages;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1230
1297
|
/** Non-consuming view of the pending follow-up queue. See
|
|
1231
1298
|
* {@link peekSteeringQueue}. */
|
|
1232
1299
|
peekFollowUpQueue(): readonly AgentMessage[] {
|
|
@@ -1702,28 +1769,15 @@ export class Agent {
|
|
|
1702
1769
|
}
|
|
1703
1770
|
return this.#dequeueSteeringMessagesAfterHooks(signal ?? loopSignal);
|
|
1704
1771
|
},
|
|
1705
|
-
hasSteeringMessages: () =>
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
const message = this.#steeringQueue[i];
|
|
1713
|
-
const role = "role" in message ? message.role : undefined;
|
|
1714
|
-
const attribution = "attribution" in message ? message.attribution : undefined;
|
|
1715
|
-
if (attribution === "user") {
|
|
1716
|
-
return { queued: true, source: "user" };
|
|
1717
|
-
}
|
|
1718
|
-
if (role !== "user") continue;
|
|
1719
|
-
if (attribution !== "agent") {
|
|
1720
|
-
return { queued: true, source: "user" };
|
|
1721
|
-
}
|
|
1722
|
-
hasAgentSteering = true;
|
|
1723
|
-
}
|
|
1724
|
-
return { queued: true, source: hasAgentSteering ? "agent" : "system" };
|
|
1725
|
-
},
|
|
1772
|
+
hasSteeringMessages: () =>
|
|
1773
|
+
steeringQueueState(
|
|
1774
|
+
this.#steeringQueue,
|
|
1775
|
+
this.#steeringMode === "one-at-a-time"
|
|
1776
|
+
? Math.min(1, this.#steeringQueue.length)
|
|
1777
|
+
: this.#steeringQueue.length,
|
|
1778
|
+
),
|
|
1726
1779
|
waitForSteeringMessages: signal => this.#waitForSteeringMessages(signal),
|
|
1780
|
+
onLiveSteeringTaken: messages => this.#adoptLiveSteering(messages),
|
|
1727
1781
|
hasIrcInterrupts: this.hasIrcInterrupts,
|
|
1728
1782
|
hasBackgroundCompletions: this.hasBackgroundCompletions,
|
|
1729
1783
|
getFollowUpMessages: signal => this.#dequeueFollowUpMessagesAfterHooks(signal ?? loopSignal),
|
package/src/types.ts
CHANGED
|
@@ -298,6 +298,14 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
298
298
|
*/
|
|
299
299
|
waitForSteeringMessages?: (signal?: AbortSignal) => Promise<void>;
|
|
300
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Called when live steering dequeues messages via {@link getSteeringMessages}
|
|
303
|
+
* for the response being streamed. The loop records them in the transcript
|
|
304
|
+
* after that response (or its tool batch); an abort before then leaves them
|
|
305
|
+
* unrecorded for the host to requeue.
|
|
306
|
+
*/
|
|
307
|
+
onLiveSteeringTaken?: (messages: AgentMessage[]) => void;
|
|
308
|
+
|
|
301
309
|
/**
|
|
302
310
|
* Peeks whether IRC messages should interrupt an interruptible waiting tool.
|
|
303
311
|
*
|