@codeworksh/aikit 0.4.0 → 0.4.1-dev.20260530133433
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/LICENSE +21 -0
- package/README.md +109 -137
- package/cli.d.mts +1 -0
- package/cli.mjs +308 -0
- package/cli.mjs.map +1 -0
- package/codex-DfYk740t.mjs +521 -0
- package/codex-DfYk740t.mjs.map +1 -0
- package/index.d.mts +7197 -0
- package/index.mjs +1478 -0
- package/index.mjs.map +1 -0
- package/oauth/openai/codex.d.mts +107 -0
- package/oauth/openai/codex.mjs +2 -0
- package/package.json +28 -33
- package/{dist/pack/index.mjs → registry-CYfzJ6lk.mjs} +210 -3300
- package/registry-CYfzJ6lk.mjs.map +1 -0
- package/dist/pack/agent/codemode/drivers/drivers.d.mts +0 -7
- package/dist/pack/agent/codemode/drivers/drivers.mjs +0 -122
- package/dist/pack/agent/codemode/drivers/drivers.mjs.map +0 -1
- package/dist/pack/codemode-BEDXG7WN.d.mts +0 -8736
- package/dist/pack/index.d.mts +0 -71
- package/dist/pack/index.mjs.map +0 -1
- package/src/agent/agent.ts +0 -790
- package/src/agent/codemode/codemode.ts +0 -561
- package/src/agent/codemode/drivers/drivers.ts +0 -1
- package/src/agent/codemode/drivers/quickjs-wasi-driver.ts +0 -149
- package/src/agent/codemode/index.ts +0 -1
- package/src/agent/loop.ts +0 -903
- package/src/agent.ts +0 -11
- package/src/event/event.ts +0 -277
- package/src/index.ts +0 -6
- package/src/llm.ts +0 -31
- package/src/message/message.ts +0 -311
- package/src/model/catalog.ts +0 -94
- package/src/model/model.ts +0 -250
- package/src/model/transform.ts +0 -59
- package/src/provider/provider.ts +0 -60
- package/src/provider/providers/anthropic/index.ts +0 -792
- package/src/provider/providers/github/github.ts +0 -34
- package/src/provider/providers/openai/completions.ts +0 -953
- package/src/provider/providers/utils.ts +0 -7
- package/src/provider/register.ts +0 -6
- package/src/provider/stream.ts +0 -278
- package/src/stream.ts +0 -72
- package/src/utils/eventstream.ts +0 -95
- package/src/utils/helpers.ts +0 -24
- package/src/utils/jsonparse.ts +0 -32
- package/src/utils/overflow.ts +0 -121
- package/src/utils/sanitize.ts +0 -25
- package/src/utils/validation.ts +0 -87
package/src/agent/loop.ts
DELETED
|
@@ -1,903 +0,0 @@
|
|
|
1
|
-
import { NamedError } from "@codeworksh/utils";
|
|
2
|
-
import Type, { type TSchema } from "typebox";
|
|
3
|
-
import type { Event } from "../event/event";
|
|
4
|
-
import type { Message } from "../message/message";
|
|
5
|
-
import type { Model } from "../model/model";
|
|
6
|
-
import type { Provider } from "../provider/provider";
|
|
7
|
-
import { Stream } from "../provider/stream";
|
|
8
|
-
import { EventStream } from "../utils/eventstream";
|
|
9
|
-
import { validateToolArguments } from "../utils/validation";
|
|
10
|
-
import type { Agent } from "./agent";
|
|
11
|
-
|
|
12
|
-
export namespace Loop {
|
|
13
|
-
/** Stream function used by the core agent loop
|
|
14
|
-
*
|
|
15
|
-
* Contract:
|
|
16
|
-
* - Must not throw or return a rejected promise for request/model/runtime (LLM) failures.
|
|
17
|
-
* - Must return an AssistantMessageEventStream see: packages/aikit/src/utils/eventstream.ts
|
|
18
|
-
* - Failures must be encoded in the returned stream via protocol events and a
|
|
19
|
-
* final Message.AssistantMessage with stopReason "error" or "aborted" and errorMessage.
|
|
20
|
-
*/
|
|
21
|
-
export type StreamFn = (
|
|
22
|
-
...args: Parameters<typeof Stream.streamSimple>
|
|
23
|
-
) => ReturnType<typeof Stream.streamSimple> | Promise<ReturnType<typeof Stream.streamSimple>>;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Configuration for how tool calls from a single assistant message are executed.
|
|
27
|
-
*
|
|
28
|
-
* - "sequential": each tool call is prepared, executed, and finalized before the next one starts.
|
|
29
|
-
* - "parallel": tool calls are prepared sequentially, then allowed tools execute concurrently.
|
|
30
|
-
* Final tool results are still emitted in assistant source order.
|
|
31
|
-
*/
|
|
32
|
-
export type ToolExecutionMode = "sequential" | "parallel";
|
|
33
|
-
|
|
34
|
-
/** Context passed to `beforeToolExecution`. */
|
|
35
|
-
export interface BeforeToolExecutionContext {
|
|
36
|
-
/** The current context at the time the tool call is prepared */
|
|
37
|
-
context: Agent.AgentContext;
|
|
38
|
-
/** The assistant message that requested the tool call. */
|
|
39
|
-
assistantMessage: Message.AssistantMessage;
|
|
40
|
-
/** The validated in-flight tool call with validated arguments */
|
|
41
|
-
toolCall: Agent.ToolCallInFlight;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Context passed to `afterToolExecution`. */
|
|
45
|
-
export interface AfterToolExecutionContext {
|
|
46
|
-
/** Current agent context at the time the tool call is finalized. */
|
|
47
|
-
context: Agent.AgentContext;
|
|
48
|
-
/** The assistant message that requested the tool call. */
|
|
49
|
-
assistantMessage: Message.AssistantMessage;
|
|
50
|
-
/** The validated in-flight tool call with validated arguments */
|
|
51
|
-
toolCall: Agent.ToolCallInFlight;
|
|
52
|
-
/** The executed tool result before any `afterToolExecution` overrides are applied. */
|
|
53
|
-
result: Agent.ToolTerminalResult<unknown>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Result returned from `beforeToolExecution`.
|
|
58
|
-
*
|
|
59
|
-
* Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.
|
|
60
|
-
* `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.
|
|
61
|
-
*/
|
|
62
|
-
export interface BeforeToolCallResult {
|
|
63
|
-
block?: boolean;
|
|
64
|
-
reason?: string;
|
|
65
|
-
details?: unknown;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Configuration for the agent loop. */
|
|
69
|
-
export interface Config extends Stream.SimpleOptions {
|
|
70
|
-
model: Model.Info;
|
|
71
|
-
convertToLlm: (messages: Message.Message[]) => Message.Message[] | Promise<Message.Message[]>;
|
|
72
|
-
/**
|
|
73
|
-
* Optional transform applied to the context before `convertToLlm`.
|
|
74
|
-
*
|
|
75
|
-
* Use this for operations that work at the AgentMessage level:
|
|
76
|
-
* - Context window management (pruning old messages)
|
|
77
|
-
* - Injecting context from external sources
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```typescript
|
|
81
|
-
* transformContext: async (messages) => {
|
|
82
|
-
* if (estimateTokens(messages) > MAX_TOKENS) {
|
|
83
|
-
* return pruneOldMessages(messages);
|
|
84
|
-
* }
|
|
85
|
-
* return messages;
|
|
86
|
-
* }
|
|
87
|
-
* ```
|
|
88
|
-
*/
|
|
89
|
-
transformContext?: (messages: Message.Message[], signal?: AbortSignal) => Promise<Message.Message[]>;
|
|
90
|
-
/**
|
|
91
|
-
* Resolves an API key dynamically for each LLM call.
|
|
92
|
-
*
|
|
93
|
-
* Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire
|
|
94
|
-
* during long-running tool execution phases.
|
|
95
|
-
*/
|
|
96
|
-
getApiKey?: (provider: Provider.Info) => Promise<string | undefined> | string | undefined;
|
|
97
|
-
/**
|
|
98
|
-
* Returns steering messages to inject into the conversation mid-run.
|
|
99
|
-
*
|
|
100
|
-
* Called after each tool execution to check for user interruptions.
|
|
101
|
-
* If messages are returned, remaining tool calls are skipped and
|
|
102
|
-
* these messages are added to the context before the next LLM call.
|
|
103
|
-
*
|
|
104
|
-
* Use this for "steering" the agent while it's working.
|
|
105
|
-
*/
|
|
106
|
-
getSteeringMessages?: () => Promise<Message.UserMessage[]>;
|
|
107
|
-
/**
|
|
108
|
-
* Returns follow-up messages to process after the agent would otherwise stop.
|
|
109
|
-
*
|
|
110
|
-
* Called when the agent has no more tool calls and no steering messages.
|
|
111
|
-
* If messages are returned, they're added to the context and the agent
|
|
112
|
-
* continues with another turn.
|
|
113
|
-
*
|
|
114
|
-
* Use this for follow-up messages that should wait until the agent finishes.
|
|
115
|
-
*/
|
|
116
|
-
getFollowUpMessages?: () => Promise<Message.UserMessage[]>;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Tool execution mode.
|
|
120
|
-
* - "sequential": execute tool calls one by one
|
|
121
|
-
* - "parallel": preflight tool calls sequentially, then execute allowed tools concurrently
|
|
122
|
-
*
|
|
123
|
-
* Default: "parallel"
|
|
124
|
-
*/
|
|
125
|
-
toolExecution?: ToolExecutionMode;
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Called before a tool is executed, after arguments have been validated.
|
|
129
|
-
*
|
|
130
|
-
* Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.
|
|
131
|
-
* The hook receives the agent abort signal and is responsible for honoring it.
|
|
132
|
-
*/
|
|
133
|
-
beforeToolExecution?: (
|
|
134
|
-
context: BeforeToolExecutionContext,
|
|
135
|
-
signal?: AbortSignal,
|
|
136
|
-
) => Promise<BeforeToolCallResult | undefined>;
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Called after a tool finishes executing, before final tool events are emitted.
|
|
140
|
-
*
|
|
141
|
-
* Any omitted fields keep their original values. No deep merge is performed.
|
|
142
|
-
* The hook receives the agent abort signal and is responsible for honoring it.
|
|
143
|
-
*/
|
|
144
|
-
afterToolExecution?: (
|
|
145
|
-
context: AfterToolExecutionContext,
|
|
146
|
-
signal?: AbortSignal,
|
|
147
|
-
) => Promise<Agent.ToolTerminalResult<unknown> | undefined>;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function createAgentStream(): EventStream<Event.AgentEvent, Message.Message[]> {
|
|
151
|
-
return new EventStream<Event.AgentEvent, Message.Message[]>(
|
|
152
|
-
(event: Event.AgentEvent) => event.type === "agent.end",
|
|
153
|
-
(event: Event.AgentEvent) => (event.type === "agent.end" ? event.messages : []),
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
type AgentEventSink = (event: Event.AgentEvent) => Promise<void> | void;
|
|
158
|
-
|
|
159
|
-
type AssistantPart = Message.AssistantMessage["parts"][number];
|
|
160
|
-
|
|
161
|
-
type ToolCallPart = Message.ToolCallPendingPart & { partIndex: number }; // runtime toolcall pending part
|
|
162
|
-
|
|
163
|
-
type RuntimeAgentTool = {
|
|
164
|
-
name: string;
|
|
165
|
-
parameters: TSchema;
|
|
166
|
-
execute: (
|
|
167
|
-
callID: string,
|
|
168
|
-
params: unknown,
|
|
169
|
-
signal?: AbortSignal,
|
|
170
|
-
onUpdate?: Agent.ToolUpdateCallback<unknown>,
|
|
171
|
-
) => Promise<Agent.ToolTerminalResult<unknown>>;
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
type ToolCallPrepared =
|
|
175
|
-
| {
|
|
176
|
-
error: {
|
|
177
|
-
kind: "error" | "blocked";
|
|
178
|
-
message: string;
|
|
179
|
-
details?: unknown;
|
|
180
|
-
};
|
|
181
|
-
runnable: Agent.ToolCallInFlight;
|
|
182
|
-
}
|
|
183
|
-
| {
|
|
184
|
-
error?: never;
|
|
185
|
-
runnable: Agent.ToolCallInFlight;
|
|
186
|
-
tool: RuntimeAgentTool;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
type ToolCallRunnable = {
|
|
190
|
-
toolCall: ToolCallPart;
|
|
191
|
-
runnable: Agent.ToolCallInFlight;
|
|
192
|
-
tool: RuntimeAgentTool;
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
type ToolCallExecutionResult =
|
|
196
|
-
| { result: Agent.ToolTerminalResult<unknown>; error?: never }
|
|
197
|
-
| { result?: never; error: { message: string; details?: unknown } };
|
|
198
|
-
|
|
199
|
-
export const AgentContextError = NamedError.create(
|
|
200
|
-
"AgentContextError",
|
|
201
|
-
Type.Object({
|
|
202
|
-
message: Type.String(),
|
|
203
|
-
}),
|
|
204
|
-
);
|
|
205
|
-
export type AgentContextError = InstanceType<typeof AgentContextError>;
|
|
206
|
-
|
|
207
|
-
function snapshotAssistantMessage(message: Message.AssistantMessage): Message.AssistantMessage {
|
|
208
|
-
return structuredClone(message);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function snapshotAssistantPartEvent(
|
|
212
|
-
message: Message.AssistantMessage,
|
|
213
|
-
partIndex: number,
|
|
214
|
-
): { message: Message.AssistantMessage; part: AssistantPart } {
|
|
215
|
-
const snapshot = snapshotAssistantMessage(message);
|
|
216
|
-
const part = snapshot.parts[partIndex];
|
|
217
|
-
if (!part) {
|
|
218
|
-
throw new Error(`Assistant message part at index ${partIndex} was not found`);
|
|
219
|
-
}
|
|
220
|
-
return { message: snapshot, part };
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
async function emitEvent(emit: AgentEventSink, event: Event.AgentEvent): Promise<void> {
|
|
224
|
-
await emit(event);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async function streamAssistantResponse(
|
|
228
|
-
config: Config,
|
|
229
|
-
context: Agent.AgentContext,
|
|
230
|
-
emit: AgentEventSink,
|
|
231
|
-
streamFn?: StreamFn,
|
|
232
|
-
signal?: AbortSignal,
|
|
233
|
-
): Promise<Message.AssistantMessage> {
|
|
234
|
-
// FIXME @sanchitrk:
|
|
235
|
-
// Have something like a pipeline with Message.Message[] transformation
|
|
236
|
-
// caller will inject transfomers[](async functions) that is then invoked in order
|
|
237
|
-
|
|
238
|
-
// Apply context transform if configured (Message.Message[] → Message.Message[])
|
|
239
|
-
let messages = context.messages;
|
|
240
|
-
if (config.transformContext) {
|
|
241
|
-
messages = await config.transformContext(messages, signal);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Convert to LLM-compatible messages (Message.Message[] → Message.Message[])
|
|
245
|
-
const llmMessages = await config.convertToLlm(messages);
|
|
246
|
-
|
|
247
|
-
// Build LLM context, subset of Agent.AgentContext
|
|
248
|
-
const llmContext: Agent.AgentContext = {
|
|
249
|
-
systemPrompt: context.systemPrompt,
|
|
250
|
-
messages: llmMessages,
|
|
251
|
-
tools: context.tools,
|
|
252
|
-
};
|
|
253
|
-
const streamFunction = streamFn || Stream.streamSimple;
|
|
254
|
-
// Resolve API key (important for expiring tokens)
|
|
255
|
-
// TODO @sanchitrk:
|
|
256
|
-
// improve passing of credentials (key/oauth), provider also has resolve api key,
|
|
257
|
-
// for now its dummy implementation.
|
|
258
|
-
const resolvedApiKey =
|
|
259
|
-
(config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;
|
|
260
|
-
|
|
261
|
-
const events = await streamFunction(config.model, llmContext, {
|
|
262
|
-
...config,
|
|
263
|
-
apiKey: resolvedApiKey,
|
|
264
|
-
signal,
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
let partialMessage: Message.AssistantMessage | null = null;
|
|
268
|
-
let started = false;
|
|
269
|
-
|
|
270
|
-
const ensureMessageStarted = async (message: Message.AssistantMessage): Promise<void> => {
|
|
271
|
-
if (started) return;
|
|
272
|
-
started = true;
|
|
273
|
-
await emitEvent(emit, { type: "message.start", message: snapshotAssistantMessage(message) });
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
const emitMessageUpdate = async (message: Message.AssistantMessage): Promise<void> => {
|
|
277
|
-
await emitEvent(emit, { type: "message.update", message: snapshotAssistantMessage(message) });
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
const emitPartStart = async (message: Message.AssistantMessage, partIndex: number): Promise<void> => {
|
|
281
|
-
const snapshot = snapshotAssistantPartEvent(message, partIndex);
|
|
282
|
-
await emitEvent(emit, { type: "message.part.start", partIndex, ...snapshot });
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const emitPartUpdate = async (message: Message.AssistantMessage, partIndex: number): Promise<void> => {
|
|
286
|
-
const snapshot = snapshotAssistantPartEvent(message, partIndex);
|
|
287
|
-
await emitEvent(emit, { type: "message.part.update", partIndex, ...snapshot, source: "llm" });
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
const emitPartEnd = async (message: Message.AssistantMessage, partIndex: number): Promise<void> => {
|
|
291
|
-
const snapshot = snapshotAssistantPartEvent(message, partIndex);
|
|
292
|
-
await emitEvent(emit, { type: "message.part.end", partIndex, ...snapshot });
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
// NOTE: Do not mutate context directly
|
|
296
|
-
// once all the events are done return message
|
|
297
|
-
// mutate partialMessage and corresponding parts as required
|
|
298
|
-
// return the final built message from llm events
|
|
299
|
-
for await (const event of events) {
|
|
300
|
-
switch (event.type) {
|
|
301
|
-
case "start": {
|
|
302
|
-
partialMessage = event.partial;
|
|
303
|
-
await ensureMessageStarted(partialMessage);
|
|
304
|
-
break;
|
|
305
|
-
}
|
|
306
|
-
case "text.start":
|
|
307
|
-
case "thinking.start":
|
|
308
|
-
case "toolcall.start": {
|
|
309
|
-
partialMessage = event.partial;
|
|
310
|
-
await ensureMessageStarted(partialMessage);
|
|
311
|
-
await emitPartStart(partialMessage, event.partIndex);
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
case "text.delta":
|
|
315
|
-
case "thinking.delta":
|
|
316
|
-
case "toolcall.delta": {
|
|
317
|
-
partialMessage = event.partial;
|
|
318
|
-
await ensureMessageStarted(partialMessage);
|
|
319
|
-
await emitPartUpdate(partialMessage, event.partIndex);
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
case "text.end":
|
|
323
|
-
case "thinking.end":
|
|
324
|
-
case "toolcall.end": {
|
|
325
|
-
partialMessage = event.partial;
|
|
326
|
-
await ensureMessageStarted(partialMessage);
|
|
327
|
-
await emitPartEnd(partialMessage, event.partIndex);
|
|
328
|
-
await emitMessageUpdate(partialMessage);
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
case "done": {
|
|
332
|
-
await ensureMessageStarted(event.message);
|
|
333
|
-
await emitEvent(emit, { type: "message.end", message: snapshotAssistantMessage(event.message) });
|
|
334
|
-
return event.message;
|
|
335
|
-
}
|
|
336
|
-
case "error": {
|
|
337
|
-
await ensureMessageStarted(event.error);
|
|
338
|
-
await emitEvent(emit, { type: "message.end", message: snapshotAssistantMessage(event.error) });
|
|
339
|
-
return event.error;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const finalMessage = await events.result();
|
|
345
|
-
await ensureMessageStarted(finalMessage);
|
|
346
|
-
await emitEvent(emit, { type: "message.end", message: snapshotAssistantMessage(finalMessage) });
|
|
347
|
-
return finalMessage;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function executeToolCalls(
|
|
351
|
-
config: Config,
|
|
352
|
-
currentContext: Agent.AgentContext,
|
|
353
|
-
message: Message.AssistantMessage,
|
|
354
|
-
toolCalls: ToolCallPart[],
|
|
355
|
-
emit: AgentEventSink,
|
|
356
|
-
signal?: AbortSignal,
|
|
357
|
-
): Promise<void> {
|
|
358
|
-
if (config.toolExecution === "sequential")
|
|
359
|
-
return executeToolCallsSequential(config, currentContext, message, toolCalls, emit, signal);
|
|
360
|
-
return executeToolCallsParallel(config, currentContext, message, toolCalls, emit, signal);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
async function executeToolCallsSequential(
|
|
364
|
-
config: Config,
|
|
365
|
-
currentContext: Agent.AgentContext,
|
|
366
|
-
message: Message.AssistantMessage,
|
|
367
|
-
toolCalls: ToolCallPart[],
|
|
368
|
-
emit: AgentEventSink,
|
|
369
|
-
signal?: AbortSignal,
|
|
370
|
-
): Promise<void> {
|
|
371
|
-
for (const toolCall of toolCalls) {
|
|
372
|
-
const toolCallInFlight: Agent.ToolCallInFlight = {
|
|
373
|
-
callID: toolCall.callID,
|
|
374
|
-
name: toolCall.name,
|
|
375
|
-
rawArgs: toolCall.arguments,
|
|
376
|
-
};
|
|
377
|
-
await emitEvent(emit, {
|
|
378
|
-
type: "tool.execution.start",
|
|
379
|
-
...toolCallInFlight,
|
|
380
|
-
});
|
|
381
|
-
//
|
|
382
|
-
// Prepares the tool call for execution.
|
|
383
|
-
// Invokes a pre-execution callback with the current context, allowing
|
|
384
|
-
// the callback to either continue or block (short-circuiting the execution).
|
|
385
|
-
// Mutates `message.parts` in-place to update the pending tool call with its result.
|
|
386
|
-
const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
|
|
387
|
-
if (prepared.error) {
|
|
388
|
-
const error: Agent.ToolErrorResult<unknown> = {
|
|
389
|
-
status: "error",
|
|
390
|
-
result: {
|
|
391
|
-
content: [{ type: "text", text: prepared.error.message }],
|
|
392
|
-
details: prepared.error.details,
|
|
393
|
-
isError: true,
|
|
394
|
-
},
|
|
395
|
-
};
|
|
396
|
-
await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
|
|
397
|
-
} else {
|
|
398
|
-
const runnable: ToolCallRunnable = {
|
|
399
|
-
toolCall,
|
|
400
|
-
runnable: prepared.runnable,
|
|
401
|
-
tool: prepared.tool,
|
|
402
|
-
};
|
|
403
|
-
const executed = await executeRunnableToolCall(runnable, message, emit, signal);
|
|
404
|
-
if (executed.error) {
|
|
405
|
-
// tool call invocation failed with error
|
|
406
|
-
// create tool error result
|
|
407
|
-
const error: Agent.ToolErrorResult<unknown> = {
|
|
408
|
-
status: "error",
|
|
409
|
-
result: {
|
|
410
|
-
content: [{ type: "text", text: executed.error.message }],
|
|
411
|
-
details: executed.error.details,
|
|
412
|
-
isError: true,
|
|
413
|
-
},
|
|
414
|
-
};
|
|
415
|
-
await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
|
|
416
|
-
} else {
|
|
417
|
-
// finalize results from tool invocation
|
|
418
|
-
// invokes after tool exection callback
|
|
419
|
-
await finalizeExecutedToolCall(
|
|
420
|
-
config,
|
|
421
|
-
currentContext,
|
|
422
|
-
runnable.toolCall,
|
|
423
|
-
runnable.runnable,
|
|
424
|
-
message,
|
|
425
|
-
emit,
|
|
426
|
-
executed.result,
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
async function executeToolCallsParallel(
|
|
434
|
-
config: Config,
|
|
435
|
-
currentContext: Agent.AgentContext,
|
|
436
|
-
message: Message.AssistantMessage,
|
|
437
|
-
toolCalls: ToolCallPart[],
|
|
438
|
-
emit: AgentEventSink,
|
|
439
|
-
signal?: AbortSignal,
|
|
440
|
-
): Promise<void> {
|
|
441
|
-
const runnables: ToolCallRunnable[] = [];
|
|
442
|
-
|
|
443
|
-
for (const toolCall of toolCalls) {
|
|
444
|
-
const toolCallInFlight: Agent.ToolCallInFlight = {
|
|
445
|
-
callID: toolCall.callID,
|
|
446
|
-
name: toolCall.name,
|
|
447
|
-
rawArgs: toolCall.arguments,
|
|
448
|
-
};
|
|
449
|
-
await emitEvent(emit, {
|
|
450
|
-
type: "tool.execution.start",
|
|
451
|
-
...toolCallInFlight,
|
|
452
|
-
});
|
|
453
|
-
//
|
|
454
|
-
// Prepares the tool call for execution.
|
|
455
|
-
// Invokes a pre-execution callback with the current context, allowing
|
|
456
|
-
// the callback to either continue or block (short-circuiting the execution).
|
|
457
|
-
// Mutates `message.parts` in-place to update the pending tool call with its result.
|
|
458
|
-
const prepared = await prepareToolCall(config, currentContext, message, toolCallInFlight, signal);
|
|
459
|
-
if (prepared.error) {
|
|
460
|
-
const error: Agent.ToolErrorResult<unknown> = {
|
|
461
|
-
status: "error",
|
|
462
|
-
result: {
|
|
463
|
-
content: [{ type: "text", text: prepared.error.message }],
|
|
464
|
-
details: prepared.error.details,
|
|
465
|
-
isError: true,
|
|
466
|
-
},
|
|
467
|
-
};
|
|
468
|
-
await emitToolCallTerminalResult(toolCall, prepared.runnable, message, emit, error);
|
|
469
|
-
} else {
|
|
470
|
-
runnables.push({
|
|
471
|
-
toolCall,
|
|
472
|
-
runnable: prepared.runnable,
|
|
473
|
-
tool: prepared.tool,
|
|
474
|
-
});
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const runningExecutions = runnables.map((runnable) => ({
|
|
479
|
-
runnable,
|
|
480
|
-
execution: executeRunnableToolCall(runnable, message, emit, signal),
|
|
481
|
-
}));
|
|
482
|
-
|
|
483
|
-
for (const running of runningExecutions) {
|
|
484
|
-
const executed = await running.execution;
|
|
485
|
-
const { runnable } = running;
|
|
486
|
-
if (executed.error) {
|
|
487
|
-
// tool call invocation failed with error
|
|
488
|
-
// create tool error result
|
|
489
|
-
const error: Agent.ToolErrorResult<unknown> = {
|
|
490
|
-
status: "error",
|
|
491
|
-
result: {
|
|
492
|
-
content: [{ type: "text", text: executed.error.message }],
|
|
493
|
-
details: executed.error.details,
|
|
494
|
-
isError: true,
|
|
495
|
-
},
|
|
496
|
-
};
|
|
497
|
-
await emitToolCallTerminalResult(runnable.toolCall, runnable.runnable, message, emit, error);
|
|
498
|
-
} else {
|
|
499
|
-
// finalize results from tool invocation
|
|
500
|
-
// invokes after tool exection callback
|
|
501
|
-
await finalizeExecutedToolCall(
|
|
502
|
-
config,
|
|
503
|
-
currentContext,
|
|
504
|
-
runnable.toolCall,
|
|
505
|
-
runnable.runnable,
|
|
506
|
-
message,
|
|
507
|
-
emit,
|
|
508
|
-
executed.result,
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
async function prepareToolCall(
|
|
515
|
-
config: Config,
|
|
516
|
-
currentContext: Agent.AgentContext,
|
|
517
|
-
assistantMessage: Message.AssistantMessage,
|
|
518
|
-
toolCallInFlight: Agent.ToolCallInFlight,
|
|
519
|
-
signal?: AbortSignal,
|
|
520
|
-
): Promise<ToolCallPrepared> {
|
|
521
|
-
const tool = currentContext.tools?.find((t) => t.name === toolCallInFlight.name);
|
|
522
|
-
if (!tool) {
|
|
523
|
-
return {
|
|
524
|
-
error: {
|
|
525
|
-
kind: "error",
|
|
526
|
-
message: `Tool ${toolCallInFlight.name} Not Found`,
|
|
527
|
-
},
|
|
528
|
-
runnable: toolCallInFlight,
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
try {
|
|
533
|
-
//
|
|
534
|
-
// first validate toolcall args, before invoking callback
|
|
535
|
-
// appends validated args
|
|
536
|
-
toolCallInFlight.args = validateToolArguments(tool, toolCallInFlight);
|
|
537
|
-
if (config.beforeToolExecution) {
|
|
538
|
-
const beforeResult = await config.beforeToolExecution(
|
|
539
|
-
{
|
|
540
|
-
context: currentContext,
|
|
541
|
-
assistantMessage,
|
|
542
|
-
toolCall: toolCallInFlight,
|
|
543
|
-
},
|
|
544
|
-
signal,
|
|
545
|
-
);
|
|
546
|
-
if (beforeResult?.block) {
|
|
547
|
-
return {
|
|
548
|
-
error: {
|
|
549
|
-
kind: "blocked",
|
|
550
|
-
message: beforeResult?.reason || "Tool Execution was Blocked",
|
|
551
|
-
details: beforeResult?.details,
|
|
552
|
-
},
|
|
553
|
-
runnable: toolCallInFlight,
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return {
|
|
558
|
-
runnable: toolCallInFlight,
|
|
559
|
-
tool: tool as RuntimeAgentTool,
|
|
560
|
-
};
|
|
561
|
-
} catch (err) {
|
|
562
|
-
return {
|
|
563
|
-
error: {
|
|
564
|
-
kind: "error",
|
|
565
|
-
message: err instanceof Error ? err.message : String(err),
|
|
566
|
-
},
|
|
567
|
-
runnable: toolCallInFlight,
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
async function executeRunnableToolCall(
|
|
573
|
-
toolCallRunnable: ToolCallRunnable,
|
|
574
|
-
message: Message.AssistantMessage,
|
|
575
|
-
emit: AgentEventSink,
|
|
576
|
-
signal?: AbortSignal,
|
|
577
|
-
): Promise<ToolCallExecutionResult> {
|
|
578
|
-
const { toolCall, runnable, tool } = toolCallRunnable;
|
|
579
|
-
const { partIndex, ...pendingPart } = toolCall;
|
|
580
|
-
try {
|
|
581
|
-
const result = await tool.execute(runnable.callID, runnable.args, signal, async (result) => {
|
|
582
|
-
const runningResult: Agent.ToolRunningResult<unknown> = {
|
|
583
|
-
status: "running",
|
|
584
|
-
partial: result.partial,
|
|
585
|
-
};
|
|
586
|
-
const toolExecutionUpdate: Event.ToolExecutionUpdate = {
|
|
587
|
-
type: "tool.execution.update",
|
|
588
|
-
...runnable,
|
|
589
|
-
...runningResult,
|
|
590
|
-
};
|
|
591
|
-
await emitEvent(emit, { ...toolExecutionUpdate });
|
|
592
|
-
|
|
593
|
-
const runningPart: Message.ToolCallRunningPart = {
|
|
594
|
-
...pendingPart,
|
|
595
|
-
...runningResult,
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
message.parts[partIndex] = runningPart; // in-place mutate; add error part
|
|
599
|
-
|
|
600
|
-
await emitEvent(emit, {
|
|
601
|
-
type: "message.part.update",
|
|
602
|
-
message,
|
|
603
|
-
partIndex,
|
|
604
|
-
part: runningPart,
|
|
605
|
-
source: "tool",
|
|
606
|
-
});
|
|
607
|
-
});
|
|
608
|
-
return {
|
|
609
|
-
result,
|
|
610
|
-
};
|
|
611
|
-
} catch (err) {
|
|
612
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
613
|
-
return {
|
|
614
|
-
error: {
|
|
615
|
-
message,
|
|
616
|
-
},
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
async function emitToolCallTerminalResult(
|
|
622
|
-
toolCall: ToolCallPart,
|
|
623
|
-
runnable: Agent.ToolCallInFlight,
|
|
624
|
-
message: Message.AssistantMessage,
|
|
625
|
-
emit: AgentEventSink,
|
|
626
|
-
result: Agent.ToolTerminalResult<unknown>,
|
|
627
|
-
): Promise<void> {
|
|
628
|
-
const { partIndex, ...pendingPart } = toolCall;
|
|
629
|
-
const { time } = pendingPart;
|
|
630
|
-
const toolExecutionEnd: Event.ToolExecutionEnd = {
|
|
631
|
-
type: "tool.execution.end",
|
|
632
|
-
...runnable,
|
|
633
|
-
...result,
|
|
634
|
-
};
|
|
635
|
-
await emitEvent(emit, { ...toolExecutionEnd });
|
|
636
|
-
|
|
637
|
-
const part: Message.ToolCallErrorPart | Message.ToolCallCompletedPart = {
|
|
638
|
-
...pendingPart,
|
|
639
|
-
...result,
|
|
640
|
-
time: {
|
|
641
|
-
start: time.start,
|
|
642
|
-
end: Date.now(),
|
|
643
|
-
},
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
message.parts[partIndex] = part; // in-place mutate; add error part
|
|
647
|
-
|
|
648
|
-
await emitEvent(emit, { type: "message.part.start", message: message, partIndex: partIndex, part: part });
|
|
649
|
-
await emitEvent(emit, { type: "message.part.end", message: message, partIndex: partIndex, part: part });
|
|
650
|
-
await emitEvent(emit, { type: "message.update", message: message });
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
async function finalizeExecutedToolCall(
|
|
654
|
-
config: Config,
|
|
655
|
-
currentContext: Agent.AgentContext,
|
|
656
|
-
toolCall: ToolCallPart,
|
|
657
|
-
runnable: Agent.ToolCallInFlight,
|
|
658
|
-
message: Message.AssistantMessage,
|
|
659
|
-
emit: AgentEventSink,
|
|
660
|
-
result: Agent.ToolTerminalResult<unknown>,
|
|
661
|
-
signal?: AbortSignal,
|
|
662
|
-
): Promise<void> {
|
|
663
|
-
if (config.afterToolExecution) {
|
|
664
|
-
const afterResult = await config.afterToolExecution(
|
|
665
|
-
{
|
|
666
|
-
context: currentContext,
|
|
667
|
-
assistantMessage: message,
|
|
668
|
-
toolCall: runnable,
|
|
669
|
-
result,
|
|
670
|
-
},
|
|
671
|
-
signal,
|
|
672
|
-
);
|
|
673
|
-
if (afterResult) {
|
|
674
|
-
await emitToolCallTerminalResult(toolCall, runnable, message, emit, afterResult);
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
await emitToolCallTerminalResult(toolCall, runnable, message, emit, result);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Main core loop shared by run and runContinue.
|
|
683
|
-
*/
|
|
684
|
-
async function runLoop(
|
|
685
|
-
config: Config,
|
|
686
|
-
currentContext: Agent.AgentContext,
|
|
687
|
-
newMessages: Message.Message[],
|
|
688
|
-
emit: AgentEventSink,
|
|
689
|
-
streamFn?: StreamFn,
|
|
690
|
-
signal?: AbortSignal,
|
|
691
|
-
): Promise<void> {
|
|
692
|
-
// initial `turn.start` is already emitted
|
|
693
|
-
// avoiding duplicate `turn.start` emits
|
|
694
|
-
let firstTurn = true;
|
|
695
|
-
// Check for steering messages at start (user may have submitted while waiting)
|
|
696
|
-
let pendingMessages: Message.UserMessage[] = (await config.getSteeringMessages?.()) || [];
|
|
697
|
-
|
|
698
|
-
// Outer loop: continues when queued follow-up messages arrive after agent would stop
|
|
699
|
-
while (true) {
|
|
700
|
-
let hasMoreToolCalls = true;
|
|
701
|
-
|
|
702
|
-
// Inner loop: process tool calls and steering messages
|
|
703
|
-
// Note: turn.start for full tool calls / pending messages.
|
|
704
|
-
while (hasMoreToolCalls || pendingMessages.length > 0) {
|
|
705
|
-
// flip firstTurn, so next iterations starts emitting `turn.start` event
|
|
706
|
-
if (!firstTurn) {
|
|
707
|
-
await emitEvent(emit, { type: "turn.start" });
|
|
708
|
-
} else {
|
|
709
|
-
firstTurn = false;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// Process pending messages (inject before next assistant response)
|
|
713
|
-
if (pendingMessages.length > 0) {
|
|
714
|
-
for (const message of pendingMessages) {
|
|
715
|
-
await emitEvent(emit, { type: "message.start", message: message });
|
|
716
|
-
for (const [partIndex, part] of message.parts.entries()) {
|
|
717
|
-
await emitEvent(emit, { type: "message.part.start", message: message, partIndex, part });
|
|
718
|
-
await emitEvent(emit, { type: "message.part.end", message: message, partIndex, part });
|
|
719
|
-
await emitEvent(emit, { type: "message.update", message: message });
|
|
720
|
-
}
|
|
721
|
-
await emitEvent(emit, { type: "message.end", message: message });
|
|
722
|
-
currentContext.messages.push(message);
|
|
723
|
-
newMessages.push(message);
|
|
724
|
-
}
|
|
725
|
-
pendingMessages = [];
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// Run llm and emit assistant response
|
|
729
|
-
const message: Message.AssistantMessage = await streamAssistantResponse(
|
|
730
|
-
config,
|
|
731
|
-
currentContext,
|
|
732
|
-
emit,
|
|
733
|
-
streamFn,
|
|
734
|
-
signal,
|
|
735
|
-
);
|
|
736
|
-
|
|
737
|
-
// terminal state
|
|
738
|
-
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
739
|
-
currentContext.messages.push(message); // append the terminal message into the context
|
|
740
|
-
newMessages.push(message); // append the terminal message
|
|
741
|
-
|
|
742
|
-
await emitEvent(emit, { type: "turn.end", message });
|
|
743
|
-
await emitEvent(emit, { type: "agent.end", messages: newMessages });
|
|
744
|
-
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// check for pending tool calls
|
|
749
|
-
// use partIndex for in-place mutation for tool call result parts in message.parts
|
|
750
|
-
const toolCalls: ToolCallPart[] = message.parts
|
|
751
|
-
.map((part, index) => ({ ...part, partIndex: index }))
|
|
752
|
-
.filter((part) => part.type === "toolCall" && part.status === "pending");
|
|
753
|
-
|
|
754
|
-
hasMoreToolCalls = toolCalls.length > 0;
|
|
755
|
-
if (hasMoreToolCalls) {
|
|
756
|
-
await executeToolCalls(config, currentContext, message, toolCalls, emit, signal);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
currentContext.messages.push(message);
|
|
760
|
-
newMessages.push(message);
|
|
761
|
-
|
|
762
|
-
await emitEvent(emit, { type: "turn.end", message });
|
|
763
|
-
|
|
764
|
-
//
|
|
765
|
-
// check for steering messages while turn loop was working.
|
|
766
|
-
pendingMessages = (await config.getSteeringMessages?.()) || [];
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// Agent would stop here. Check for follow-up messages.
|
|
770
|
-
const followUpMessages = (await config.getFollowUpMessages?.()) || [];
|
|
771
|
-
if (followUpMessages.length > 0) {
|
|
772
|
-
// Set as pending so inner loop processes them
|
|
773
|
-
pendingMessages = followUpMessages;
|
|
774
|
-
continue;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// No more messages, exit
|
|
778
|
-
break;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
await emitEvent(emit, { type: "agent.end", messages: newMessages });
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
export async function runAgentLoop(
|
|
785
|
-
config: Config,
|
|
786
|
-
context: Agent.AgentContext,
|
|
787
|
-
prompts: Message.UserMessage[],
|
|
788
|
-
emit: AgentEventSink,
|
|
789
|
-
streamFn?: StreamFn,
|
|
790
|
-
signal?: AbortSignal,
|
|
791
|
-
) {
|
|
792
|
-
/**
|
|
793
|
-
* maintain 2 arrays to represent state, `context` the actual full state sent to the LLM,
|
|
794
|
-
* `newMessages` for intermediate current loop state.
|
|
795
|
-
* think of newMessages as subset of context.messages for the running current loop.
|
|
796
|
-
* they do move at the same speed staring with initial user prompts.
|
|
797
|
-
* */
|
|
798
|
-
const newMessages: Message.Message[] = [...prompts];
|
|
799
|
-
const currentContext: Agent.AgentContext = {
|
|
800
|
-
...context,
|
|
801
|
-
messages: [...context.messages, ...prompts],
|
|
802
|
-
};
|
|
803
|
-
|
|
804
|
-
await emitEvent(emit, { type: "agent.start" });
|
|
805
|
-
await emitEvent(emit, { type: "turn.start" });
|
|
806
|
-
for (const prompt of prompts) {
|
|
807
|
-
await emitEvent(emit, { type: "message.start", message: prompt });
|
|
808
|
-
for (const [partIndex, part] of prompt.parts.entries()) {
|
|
809
|
-
await emitEvent(emit, { type: "message.part.start", message: prompt, partIndex, part });
|
|
810
|
-
await emitEvent(emit, { type: "message.part.end", message: prompt, partIndex, part });
|
|
811
|
-
}
|
|
812
|
-
await emitEvent(emit, { type: "message.end", message: prompt });
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
|
|
816
|
-
return newMessages;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
/**
|
|
820
|
-
* **Important:** The last message in context must not be `assistant` message, convert
|
|
821
|
-
* via `convertToLlm`. If it doesn't, the LLM provider will reject the request.
|
|
822
|
-
* This cannot be validated here since `convertToLlm` is only called once per turn.
|
|
823
|
-
*/
|
|
824
|
-
export async function runAgentLoopContinue(
|
|
825
|
-
config: Config,
|
|
826
|
-
context: Agent.AgentContext,
|
|
827
|
-
emit: AgentEventSink,
|
|
828
|
-
streamFn?: StreamFn,
|
|
829
|
-
signal?: AbortSignal,
|
|
830
|
-
) {
|
|
831
|
-
if (context.messages.length === 0) {
|
|
832
|
-
throw new AgentContextError({ message: "connot continue: no messages in context" });
|
|
833
|
-
}
|
|
834
|
-
if (context.messages[context.messages.length - 1]?.role === "assistant") {
|
|
835
|
-
throw new AgentContextError({ message: "cannot continue from message role: assistant" });
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
const newMessages: Message.Message[] = [];
|
|
839
|
-
const currentContext: Agent.AgentContext = { ...context };
|
|
840
|
-
|
|
841
|
-
await emitEvent(emit, { type: "agent.start" });
|
|
842
|
-
await emitEvent(emit, { type: "turn.start" });
|
|
843
|
-
|
|
844
|
-
await runLoop(config, currentContext, newMessages, emit, streamFn, signal);
|
|
845
|
-
return newMessages;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
/**
|
|
849
|
-
* Start an agent loop with a new prompt message.
|
|
850
|
-
* The prompt is added to the context and events are emitted for it.
|
|
851
|
-
*/
|
|
852
|
-
export function run(
|
|
853
|
-
config: Config,
|
|
854
|
-
context: Agent.AgentContext,
|
|
855
|
-
prompts: Message.UserMessage[],
|
|
856
|
-
streamFn?: StreamFn,
|
|
857
|
-
signal?: AbortSignal,
|
|
858
|
-
): EventStream<Event.AgentEvent, Message.Message[]> {
|
|
859
|
-
const stream = createAgentStream();
|
|
860
|
-
|
|
861
|
-
void runAgentLoop(
|
|
862
|
-
config,
|
|
863
|
-
context,
|
|
864
|
-
prompts,
|
|
865
|
-
async (event) => {
|
|
866
|
-
stream.push(event);
|
|
867
|
-
},
|
|
868
|
-
streamFn,
|
|
869
|
-
signal,
|
|
870
|
-
).then((messages) => {
|
|
871
|
-
stream.end(messages);
|
|
872
|
-
});
|
|
873
|
-
|
|
874
|
-
return stream;
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
* Continue an agent loop from the current context without adding a new message.
|
|
879
|
-
* Used for retries - context already has user message or tool results.
|
|
880
|
-
*/
|
|
881
|
-
export function runContinue(
|
|
882
|
-
config: Config,
|
|
883
|
-
context: Agent.AgentContext,
|
|
884
|
-
streamFn?: StreamFn,
|
|
885
|
-
signal?: AbortSignal,
|
|
886
|
-
): EventStream<Event.AgentEvent, Message.Message[]> {
|
|
887
|
-
const stream = createAgentStream();
|
|
888
|
-
|
|
889
|
-
void runAgentLoopContinue(
|
|
890
|
-
config,
|
|
891
|
-
context,
|
|
892
|
-
async (event) => {
|
|
893
|
-
stream.push(event);
|
|
894
|
-
},
|
|
895
|
-
streamFn,
|
|
896
|
-
signal,
|
|
897
|
-
).then((messages) => {
|
|
898
|
-
stream.end(messages);
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
return stream;
|
|
902
|
-
}
|
|
903
|
-
}
|