@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/agent.ts
DELETED
|
@@ -1,790 +0,0 @@
|
|
|
1
|
-
import { NamedError } from "@codeworksh/utils";
|
|
2
|
-
import Type, { type Static, type TSchema } from "typebox";
|
|
3
|
-
import type { Event } from "../event/event";
|
|
4
|
-
import { Message } from "../message/message";
|
|
5
|
-
import { Model } from "../model/model";
|
|
6
|
-
import type { Provider } from "../provider/provider";
|
|
7
|
-
import "../provider/register";
|
|
8
|
-
import { Stream } from "../provider/stream";
|
|
9
|
-
import { Loop } from "./loop";
|
|
10
|
-
|
|
11
|
-
export namespace Agent {
|
|
12
|
-
export const ToolTerminalResultSchema = Type.Union([Message.ToolCompletedSchema, Message.ToolErrorSchema]);
|
|
13
|
-
export const ToolResultSchema = Type.Union([
|
|
14
|
-
Message.ToolRunningSchema,
|
|
15
|
-
Message.ToolCompletedSchema,
|
|
16
|
-
Message.ToolErrorSchema,
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
export type ToolRunningResult<T> = Omit<Static<typeof Message.ToolRunningSchema>, "partial"> & {
|
|
20
|
-
partial?: Omit<Static<typeof Message.ToolRunningPartial>, "details"> & {
|
|
21
|
-
details?: T;
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
export type ToolCompletedResult<T> = Omit<Static<typeof Message.ToolCompletedSchema>, "result"> & {
|
|
25
|
-
result: Omit<Static<typeof Message.ToolSuccessResult>, "details"> & {
|
|
26
|
-
details?: T;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
export type ToolErrorResult<T> = Omit<Static<typeof Message.ToolErrorSchema>, "result"> & {
|
|
30
|
-
result: Omit<Static<typeof Message.ToolErrorResult>, "details"> & {
|
|
31
|
-
details?: T;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
export type ToolTerminalResult<TCompleted = unknown, TError = unknown> =
|
|
35
|
-
| ToolCompletedResult<TCompleted>
|
|
36
|
-
| ToolErrorResult<TError>;
|
|
37
|
-
type InferSchema<T extends TSchema | undefined> = T extends TSchema ? Static<T> : unknown;
|
|
38
|
-
|
|
39
|
-
// callback for tool execution updates.
|
|
40
|
-
// consumers can narrow on status if needed
|
|
41
|
-
// type inference:
|
|
42
|
-
// U - update
|
|
43
|
-
export type ToolUpdateCallback<U = unknown> = (result: ToolRunningResult<U>) => Promise<void> | void;
|
|
44
|
-
|
|
45
|
-
export interface AgentTool<
|
|
46
|
-
TParameters extends TSchema = TSchema,
|
|
47
|
-
TOutput extends TSchema | undefined = undefined,
|
|
48
|
-
TUpdate = unknown,
|
|
49
|
-
TError extends TSchema | undefined = undefined,
|
|
50
|
-
> extends Message.Tool<TParameters> {
|
|
51
|
-
// A human-readable label for the tool for display
|
|
52
|
-
label: string;
|
|
53
|
-
outputSchema?: TOutput;
|
|
54
|
-
errorSchema?: TError;
|
|
55
|
-
execute: (
|
|
56
|
-
callID: string,
|
|
57
|
-
params: Static<TParameters>,
|
|
58
|
-
signal?: AbortSignal,
|
|
59
|
-
onUpdate?: ToolUpdateCallback<TUpdate>,
|
|
60
|
-
) => Promise<ToolTerminalResult<InferSchema<TOutput>, InferSchema<TError>>>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function defineTool<
|
|
64
|
-
TParameters extends TSchema,
|
|
65
|
-
TOutput extends TSchema | undefined = undefined,
|
|
66
|
-
TUpdate = unknown,
|
|
67
|
-
TError extends TSchema | undefined = undefined,
|
|
68
|
-
>(tool: AgentTool<TParameters, TOutput, TUpdate, TError>): AgentTool<TParameters, TOutput, TUpdate, TError> {
|
|
69
|
-
return tool;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export type AnyAgentTool = AgentTool<any, any, any, any>;
|
|
73
|
-
|
|
74
|
-
export interface State {
|
|
75
|
-
name: string;
|
|
76
|
-
systemPrompt: string;
|
|
77
|
-
model: Model.Info;
|
|
78
|
-
thinkingLevel: Stream.ThinkingLevel;
|
|
79
|
-
tools: AnyAgentTool[];
|
|
80
|
-
messages: Message.Message[];
|
|
81
|
-
isStreaming: boolean;
|
|
82
|
-
streamMessage: Message.Message | null;
|
|
83
|
-
pendingToolCalls: Set<string>;
|
|
84
|
-
error?: string;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// AgentContext is like Message.Context but uses AgentTool
|
|
88
|
-
// TODO: @sanchitrk: rename this to Context; Agent.Context
|
|
89
|
-
export interface AgentContext {
|
|
90
|
-
systemPrompt: string;
|
|
91
|
-
messages: Message.Message[];
|
|
92
|
-
tools?: AnyAgentTool[];
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const ToolCallInFlightSchema = Type.Object({
|
|
96
|
-
callID: Type.String(),
|
|
97
|
-
name: Type.String(),
|
|
98
|
-
rawArgs: Type.Record(Type.String(), Type.Unknown()),
|
|
99
|
-
args: Type.Optional(Type.Unknown()), // post validation
|
|
100
|
-
});
|
|
101
|
-
export type ToolCallInFlight = Static<typeof ToolCallInFlightSchema>;
|
|
102
|
-
|
|
103
|
-
export const ModelNotFoundErr = NamedError.create(
|
|
104
|
-
"ModelNotFoundErr",
|
|
105
|
-
Type.Object({
|
|
106
|
-
message: Type.String(),
|
|
107
|
-
provider: Type.String(),
|
|
108
|
-
model: Type.String(),
|
|
109
|
-
}),
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
export const ModelNotConfiguredErr = NamedError.create(
|
|
113
|
-
"ModelNotConfiguredErr",
|
|
114
|
-
Type.Object({
|
|
115
|
-
message: Type.String(),
|
|
116
|
-
}),
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
export interface AgentOptions {
|
|
120
|
-
initialState?: Partial<State>;
|
|
121
|
-
convertToLlm?: (messages: Message.Message[]) => Message.Message[] | Promise<Message.Message[]>;
|
|
122
|
-
/**
|
|
123
|
-
* Optional transform applied to the context before `convertToLlm`.
|
|
124
|
-
*
|
|
125
|
-
* Use this for operations that work at the AgentMessage level:
|
|
126
|
-
* - Context window management (pruning old messages)
|
|
127
|
-
* - Injecting context from external sources
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```typescript
|
|
131
|
-
* transformContext: async (messages) => {
|
|
132
|
-
* if (estimateTokens(messages) > MAX_TOKENS) {
|
|
133
|
-
* return pruneOldMessages(messages);
|
|
134
|
-
* }
|
|
135
|
-
* return messages;
|
|
136
|
-
* }
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
transformContext?: (messages: Message.Message[], signal?: AbortSignal) => Promise<Message.Message[]>;
|
|
140
|
-
/**
|
|
141
|
-
* Steering mode: "all" = send all steering messages at once, "one-at-a-time" = one per turn
|
|
142
|
-
*/
|
|
143
|
-
steeringMode?: "all" | "one-at-a-time";
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Follow-up mode: "all" = send all follow-up messages at once, "one-at-a-time" = one per turn
|
|
147
|
-
*/
|
|
148
|
-
followUpMode?: "all" | "one-at-a-time";
|
|
149
|
-
/**
|
|
150
|
-
* Custom stream function (for proxy backends, etc.). Default uses streamSimple.
|
|
151
|
-
*/
|
|
152
|
-
streamFn?: Loop.StreamFn;
|
|
153
|
-
/**
|
|
154
|
-
* Optional session identifier forwarded to LLM providers.
|
|
155
|
-
* Used by providers that support session-based caching (e.g., OpenAI Codex).
|
|
156
|
-
*/
|
|
157
|
-
sessionId?: string;
|
|
158
|
-
/**
|
|
159
|
-
* Resolves an API key dynamically for each LLM call.
|
|
160
|
-
* Useful for expiring tokens (e.g., GitHub Copilot OAuth).
|
|
161
|
-
*/
|
|
162
|
-
getApiKey?: (provider: Provider.Info) => Promise<string | undefined> | string | undefined;
|
|
163
|
-
/**
|
|
164
|
-
* Inspect or replace provider payloads before they are sent.
|
|
165
|
-
*/
|
|
166
|
-
onPayload?: Stream.SimpleOptions["onPayload"];
|
|
167
|
-
/**
|
|
168
|
-
* Custom token budgets for thinking levels (token-based providers only).
|
|
169
|
-
*/
|
|
170
|
-
thinkingBudgets?: Stream.ThinkingBudgets;
|
|
171
|
-
/**
|
|
172
|
-
* Preferred transport for providers that support multiple transports.
|
|
173
|
-
*/
|
|
174
|
-
transport?: Stream.Transport;
|
|
175
|
-
/**
|
|
176
|
-
* Maximum delay in milliseconds to wait for a retry when the server requests a long wait.
|
|
177
|
-
* If the server's requested delay exceeds this value, the request fails immediately,
|
|
178
|
-
* allowing higher-level retry logic to handle it with user visibility.
|
|
179
|
-
* Default: 60000 (60 seconds). Set to 0 to disable the cap.
|
|
180
|
-
*/
|
|
181
|
-
maxRetryDelayMs?: number;
|
|
182
|
-
/** Tool execution mode. Default: "parallel" */
|
|
183
|
-
toolExecution?: Loop.ToolExecutionMode;
|
|
184
|
-
/** Called before a tool is executed, after arguments have been validated. */
|
|
185
|
-
beforeToolExecution?: (
|
|
186
|
-
context: Loop.BeforeToolExecutionContext,
|
|
187
|
-
signal?: AbortSignal,
|
|
188
|
-
) => Promise<Loop.BeforeToolCallResult | undefined>;
|
|
189
|
-
/** Called after a tool finishes executing, before final tool events are emitted. */
|
|
190
|
-
afterToolExecution?: (
|
|
191
|
-
context: Loop.AfterToolExecutionContext,
|
|
192
|
-
signal?: AbortSignal,
|
|
193
|
-
) => Promise<Agent.ToolTerminalResult<unknown> | undefined>;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export const AgentInStreamingErr = NamedError.create(
|
|
197
|
-
"AgentInStreamingErr",
|
|
198
|
-
Type.Object({
|
|
199
|
-
message: Type.String(),
|
|
200
|
-
name: Type.String(),
|
|
201
|
-
}),
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
export class Instance {
|
|
205
|
-
private _state: State;
|
|
206
|
-
|
|
207
|
-
private listeners = new Set<(e: Event.AgentEvent) => void>();
|
|
208
|
-
private abortController?: AbortController;
|
|
209
|
-
|
|
210
|
-
private convertToLlm: (messages: Message.Message[]) => Message.Message[] | Promise<Message.Message[]>;
|
|
211
|
-
private transformContext?: (messages: Message.Message[], signal?: AbortSignal) => Promise<Message.Message[]>;
|
|
212
|
-
|
|
213
|
-
private steeringQueue: Message.UserMessage[] = [];
|
|
214
|
-
private followUpQueue: Message.UserMessage[] = [];
|
|
215
|
-
private steeringMode: "all" | "one-at-a-time";
|
|
216
|
-
private followUpMode: "all" | "one-at-a-time";
|
|
217
|
-
|
|
218
|
-
public streamFn: Loop.StreamFn;
|
|
219
|
-
|
|
220
|
-
private _sessionId?: string;
|
|
221
|
-
public getApiKey?: (provider: Provider.Info) => Promise<string | undefined> | string | undefined;
|
|
222
|
-
|
|
223
|
-
private _onPayload?: Stream.SimpleOptions["onPayload"];
|
|
224
|
-
|
|
225
|
-
private runningPrompt?: Promise<void>;
|
|
226
|
-
private resolveRunningPrompt?: () => void;
|
|
227
|
-
private _thinkingBudgets?: Stream.ThinkingBudgets;
|
|
228
|
-
private _transport: Stream.Transport;
|
|
229
|
-
private _maxRetryDelayMs?: number;
|
|
230
|
-
private _toolExecution: Loop.ToolExecutionMode;
|
|
231
|
-
|
|
232
|
-
private _beforeToolExecution?: (
|
|
233
|
-
context: Loop.BeforeToolExecutionContext,
|
|
234
|
-
signal?: AbortSignal,
|
|
235
|
-
) => Promise<Loop.BeforeToolCallResult | undefined>;
|
|
236
|
-
private _afterToolExecution?: (
|
|
237
|
-
context: Loop.AfterToolExecutionContext,
|
|
238
|
-
signal?: AbortSignal,
|
|
239
|
-
) => Promise<Agent.ToolTerminalResult<unknown> | undefined>;
|
|
240
|
-
|
|
241
|
-
constructor(name: string, model: Model.Info, opts: AgentOptions = {}) {
|
|
242
|
-
this._state = {
|
|
243
|
-
systemPrompt: "",
|
|
244
|
-
thinkingLevel: "off",
|
|
245
|
-
tools: [],
|
|
246
|
-
messages: [],
|
|
247
|
-
isStreaming: false,
|
|
248
|
-
streamMessage: null,
|
|
249
|
-
pendingToolCalls: new Set<string>(),
|
|
250
|
-
error: undefined,
|
|
251
|
-
...opts.initialState,
|
|
252
|
-
name,
|
|
253
|
-
model,
|
|
254
|
-
};
|
|
255
|
-
/**
|
|
256
|
-
* Transformation logic to prepare messages for the LLM.
|
|
257
|
-
* * @remarks
|
|
258
|
-
* Currently just forwards messages as-is. Modify this logic when
|
|
259
|
-
* specific formatting or filtering is required.
|
|
260
|
-
* A good example is using user message parts with custom content
|
|
261
|
-
* * @default
|
|
262
|
-
* forwards messages as is.
|
|
263
|
-
*/
|
|
264
|
-
this.convertToLlm = opts.convertToLlm || ((messages: Message.Message[]) => messages);
|
|
265
|
-
this.transformContext = opts.transformContext;
|
|
266
|
-
|
|
267
|
-
this.steeringMode = opts.steeringMode || "one-at-a-time";
|
|
268
|
-
this.followUpMode = opts.followUpMode || "one-at-a-time";
|
|
269
|
-
|
|
270
|
-
this.streamFn = opts.streamFn || Stream.streamSimple;
|
|
271
|
-
this._sessionId = opts.sessionId;
|
|
272
|
-
|
|
273
|
-
this.getApiKey = opts.getApiKey;
|
|
274
|
-
this._onPayload = opts.onPayload;
|
|
275
|
-
|
|
276
|
-
this._thinkingBudgets = opts.thinkingBudgets;
|
|
277
|
-
this._transport = opts.transport ?? "sse";
|
|
278
|
-
this._maxRetryDelayMs = opts.maxRetryDelayMs;
|
|
279
|
-
|
|
280
|
-
this._toolExecution = opts.toolExecution ?? "parallel";
|
|
281
|
-
this._beforeToolExecution = opts.beforeToolExecution;
|
|
282
|
-
this._afterToolExecution = opts.afterToolExecution;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Get the current session ID used for provider caching.
|
|
287
|
-
*/
|
|
288
|
-
get sessionId(): string | undefined {
|
|
289
|
-
return this._sessionId;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Set the session ID for provider caching.
|
|
294
|
-
* Call this when switching sessions (new session, branch, resume, etc).
|
|
295
|
-
*/
|
|
296
|
-
set sessionId(value: string | undefined) {
|
|
297
|
-
this._sessionId = value;
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Get the current thinking budgets.
|
|
301
|
-
*/
|
|
302
|
-
get thinkingBudgets(): Stream.ThinkingBudgets | undefined {
|
|
303
|
-
return this._thinkingBudgets;
|
|
304
|
-
}
|
|
305
|
-
/**
|
|
306
|
-
* Set custom thinking budgets for token-based providers.
|
|
307
|
-
*/
|
|
308
|
-
set thinkingBudgets(value: Stream.ThinkingBudgets | undefined) {
|
|
309
|
-
this._thinkingBudgets = value;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Get the current preferred transport.
|
|
314
|
-
*/
|
|
315
|
-
get transport(): Stream.Transport {
|
|
316
|
-
return this._transport;
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Set the preferred transport.
|
|
320
|
-
*/
|
|
321
|
-
setTransport(value: Stream.Transport) {
|
|
322
|
-
this._transport = value;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Get the current max retry delay in milliseconds.
|
|
327
|
-
*/
|
|
328
|
-
get maxRetryDelayMs(): number | undefined {
|
|
329
|
-
return this._maxRetryDelayMs;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Set the maximum delay to wait for server-requested retries.
|
|
334
|
-
* Set to 0 to disable the cap.
|
|
335
|
-
*/
|
|
336
|
-
set maxRetryDelayMs(value: number | undefined) {
|
|
337
|
-
this._maxRetryDelayMs = value;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
get toolExecution(): Loop.ToolExecutionMode {
|
|
341
|
-
return this._toolExecution;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
setToolExecution(value: Loop.ToolExecutionMode) {
|
|
345
|
-
this._toolExecution = value;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
setBeforeToolExecution(
|
|
349
|
-
value:
|
|
350
|
-
| ((
|
|
351
|
-
context: Loop.BeforeToolExecutionContext,
|
|
352
|
-
signal?: AbortSignal,
|
|
353
|
-
) => Promise<Loop.BeforeToolCallResult | undefined>)
|
|
354
|
-
| undefined,
|
|
355
|
-
) {
|
|
356
|
-
this._beforeToolExecution = value;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
setAfterToolExecution(
|
|
360
|
-
value:
|
|
361
|
-
| ((
|
|
362
|
-
context: Loop.AfterToolExecutionContext,
|
|
363
|
-
signal?: AbortSignal,
|
|
364
|
-
) => Promise<Agent.ToolTerminalResult<unknown> | undefined>)
|
|
365
|
-
| undefined,
|
|
366
|
-
) {
|
|
367
|
-
this._afterToolExecution = value;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
get state(): State {
|
|
371
|
-
return this._state;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
subscribe(fn: (e: Event.AgentEvent) => void): () => void {
|
|
375
|
-
this.listeners.add(fn);
|
|
376
|
-
return () => this.listeners.delete(fn);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// State mutators
|
|
380
|
-
setSystemPrompt(v: string) {
|
|
381
|
-
this._state.systemPrompt = v;
|
|
382
|
-
}
|
|
383
|
-
setModel(m: Model.Info) {
|
|
384
|
-
this._state.model = m;
|
|
385
|
-
}
|
|
386
|
-
setThinkingLevel(l: Stream.ThinkingLevel) {
|
|
387
|
-
this._state.thinkingLevel = l;
|
|
388
|
-
}
|
|
389
|
-
setSteeringMode(mode: "all" | "one-at-a-time") {
|
|
390
|
-
this.steeringMode = mode;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
getSteeringMode(): "all" | "one-at-a-time" {
|
|
394
|
-
return this.steeringMode;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
setFollowUpMode(mode: "all" | "one-at-a-time") {
|
|
398
|
-
this.followUpMode = mode;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
getFollowUpMode(): "all" | "one-at-a-time" {
|
|
402
|
-
return this.followUpMode;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
setTools(t: AgentTool<any>[]) {
|
|
406
|
-
this._state.tools = t;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
replaceMessages(ms: Message.Message[]) {
|
|
410
|
-
this._state.messages = ms.slice();
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
appendMessage(m: Message.Message) {
|
|
414
|
-
this._state.messages = [...this._state.messages, m];
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
private findMessageIndexById(messageId: string): number {
|
|
418
|
-
return this._state.messages.findIndex((message) => message.messageId === messageId);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
private replaceMessageById(message: Message.Message): boolean {
|
|
422
|
-
const messageIndex = this.findMessageIndexById(message.messageId);
|
|
423
|
-
if (messageIndex === -1) {
|
|
424
|
-
return false;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const messages = this._state.messages.slice();
|
|
428
|
-
messages[messageIndex] = message;
|
|
429
|
-
this._state.messages = messages;
|
|
430
|
-
return true;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
private upsertMessage(message: Message.Message): void {
|
|
434
|
-
if (this.replaceMessageById(message)) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
this.appendMessage(message);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
private patchStoredAssistantPart(
|
|
441
|
-
messageId: string,
|
|
442
|
-
partIndex: number,
|
|
443
|
-
part: Message.AssistantMessage["parts"][number],
|
|
444
|
-
): boolean {
|
|
445
|
-
const messageIndex = this.findMessageIndexById(messageId);
|
|
446
|
-
if (messageIndex === -1) {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
const messages = this._state.messages.slice();
|
|
451
|
-
const existing = messages[messageIndex];
|
|
452
|
-
if (!existing || existing.role !== "assistant") {
|
|
453
|
-
return false;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const nextParts = existing.parts.slice();
|
|
457
|
-
nextParts[partIndex] = part;
|
|
458
|
-
messages[messageIndex] = {
|
|
459
|
-
...existing,
|
|
460
|
-
parts: nextParts,
|
|
461
|
-
};
|
|
462
|
-
this._state.messages = messages;
|
|
463
|
-
return true;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Queue a steering message while the agent is running.
|
|
468
|
-
* Delivered after the current assistant turn finishes executing its tool calls,
|
|
469
|
-
* before the next LLM call.
|
|
470
|
-
*/
|
|
471
|
-
steer(m: Message.UserMessage) {
|
|
472
|
-
this.steeringQueue.push(m);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
/**
|
|
476
|
-
* Queue a follow-up message to be processed after the agent finishes.
|
|
477
|
-
* Delivered only when agent has no more tool calls or steering messages.
|
|
478
|
-
*/
|
|
479
|
-
followUp(m: Message.UserMessage) {
|
|
480
|
-
this.followUpQueue.push(m);
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
clearSteeringQueue() {
|
|
484
|
-
this.steeringQueue = [];
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
clearFollowUpQueue() {
|
|
488
|
-
this.followUpQueue = [];
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
clearAllQueues() {
|
|
492
|
-
this.steeringQueue = [];
|
|
493
|
-
this.followUpQueue = [];
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
hasQueuedMessages(): boolean {
|
|
497
|
-
return this.steeringQueue.length > 0 || this.followUpQueue.length > 0;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
private dequeueSteeringMessages(): Message.UserMessage[] {
|
|
501
|
-
if (this.steeringMode === "one-at-a-time") {
|
|
502
|
-
if (this.steeringQueue.length > 0) {
|
|
503
|
-
const first = this.steeringQueue[0]!;
|
|
504
|
-
this.steeringQueue = this.steeringQueue.slice(1);
|
|
505
|
-
return [first];
|
|
506
|
-
}
|
|
507
|
-
return [];
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const steering = this.steeringQueue.slice();
|
|
511
|
-
this.steeringQueue = [];
|
|
512
|
-
return steering;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
private dequeueFollowUpMessages(): Message.UserMessage[] {
|
|
516
|
-
if (this.followUpMode === "one-at-a-time") {
|
|
517
|
-
if (this.followUpQueue.length > 0) {
|
|
518
|
-
const first = this.followUpQueue[0]!;
|
|
519
|
-
this.followUpQueue = this.followUpQueue.slice(1);
|
|
520
|
-
return [first];
|
|
521
|
-
}
|
|
522
|
-
return [];
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
const followUp = this.followUpQueue.slice();
|
|
526
|
-
this.followUpQueue = [];
|
|
527
|
-
return followUp;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
clearMessages() {
|
|
531
|
-
this._state.messages = [];
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
abort() {
|
|
535
|
-
this.abortController?.abort();
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
waitForIdle(): Promise<void> {
|
|
539
|
-
return this.runningPrompt ?? Promise.resolve();
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
reset() {
|
|
543
|
-
this._state.messages = [];
|
|
544
|
-
this._state.isStreaming = false;
|
|
545
|
-
this._state.streamMessage = null;
|
|
546
|
-
this._state.pendingToolCalls = new Set<string>();
|
|
547
|
-
this._state.error = undefined;
|
|
548
|
-
this.steeringQueue = [];
|
|
549
|
-
this.followUpQueue = [];
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
getName(): string {
|
|
553
|
-
return this._state.name;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
private ensureIdle() {
|
|
557
|
-
if (this._state.isStreaming) {
|
|
558
|
-
throw new AgentInStreamingErr({
|
|
559
|
-
message:
|
|
560
|
-
"agent is already processing a prompt. Use steer() or followup() to queue messages, or wait for completion.",
|
|
561
|
-
name: this._state.name,
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
private async _runLoop(messages?: Message.UserMessage[], opts?: { skipInitialSteeringPoll?: boolean }) {
|
|
567
|
-
const model = this._state.model;
|
|
568
|
-
if (!model)
|
|
569
|
-
throw new ModelNotConfiguredErr({
|
|
570
|
-
message: "no model configured yet",
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
this.runningPrompt = new Promise<void>((resolve) => {
|
|
574
|
-
this.resolveRunningPrompt = resolve;
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
this.abortController = new AbortController();
|
|
578
|
-
this._state.isStreaming = true;
|
|
579
|
-
this._state.streamMessage = null;
|
|
580
|
-
this._state.error = undefined;
|
|
581
|
-
|
|
582
|
-
const reasoning = this.state.thinkingLevel === "off" ? undefined : this._state.thinkingLevel;
|
|
583
|
-
|
|
584
|
-
const context: Agent.AgentContext = {
|
|
585
|
-
systemPrompt: this._state.systemPrompt,
|
|
586
|
-
messages: this._state.messages.slice(),
|
|
587
|
-
tools: this._state.tools,
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
let skipInitialSteeringPoll = opts?.skipInitialSteeringPoll === true;
|
|
591
|
-
|
|
592
|
-
const config: Loop.Config = {
|
|
593
|
-
model,
|
|
594
|
-
reasoning,
|
|
595
|
-
sessionId: this._sessionId,
|
|
596
|
-
onPayload: this._onPayload,
|
|
597
|
-
transport: this._transport,
|
|
598
|
-
thinkingBudgets: this._thinkingBudgets,
|
|
599
|
-
maxRetryDelayMs: this._maxRetryDelayMs,
|
|
600
|
-
toolExecution: this._toolExecution,
|
|
601
|
-
beforeToolExecution: this._beforeToolExecution,
|
|
602
|
-
afterToolExecution: this._afterToolExecution,
|
|
603
|
-
convertToLlm: this.convertToLlm,
|
|
604
|
-
transformContext: this.transformContext,
|
|
605
|
-
getApiKey: this.getApiKey,
|
|
606
|
-
getSteeringMessages: async () => {
|
|
607
|
-
if (skipInitialSteeringPoll) {
|
|
608
|
-
skipInitialSteeringPoll = false;
|
|
609
|
-
return [];
|
|
610
|
-
}
|
|
611
|
-
return this.dequeueSteeringMessages();
|
|
612
|
-
},
|
|
613
|
-
getFollowUpMessages: async () => this.dequeueFollowUpMessages(),
|
|
614
|
-
};
|
|
615
|
-
|
|
616
|
-
try {
|
|
617
|
-
if (messages) {
|
|
618
|
-
await Loop.runAgentLoop(
|
|
619
|
-
config,
|
|
620
|
-
context,
|
|
621
|
-
messages,
|
|
622
|
-
async (e) => this._processLoopEvent(e),
|
|
623
|
-
this.streamFn,
|
|
624
|
-
this.abortController.signal,
|
|
625
|
-
);
|
|
626
|
-
} else {
|
|
627
|
-
await Loop.runAgentLoopContinue(
|
|
628
|
-
config,
|
|
629
|
-
context,
|
|
630
|
-
async (e) => this._processLoopEvent(e),
|
|
631
|
-
this.streamFn,
|
|
632
|
-
this.abortController.signal,
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
} catch (err: any) {
|
|
636
|
-
const message = Message.createAssistantMessage({
|
|
637
|
-
role: "assistant",
|
|
638
|
-
protocol: model.protocol,
|
|
639
|
-
provider: model.provider,
|
|
640
|
-
model: model.id,
|
|
641
|
-
usage: {
|
|
642
|
-
input: 0,
|
|
643
|
-
output: 0,
|
|
644
|
-
cacheRead: 0,
|
|
645
|
-
cacheWrite: 0,
|
|
646
|
-
totalTokens: 0,
|
|
647
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
648
|
-
},
|
|
649
|
-
stopReason: this.abortController.signal.aborted ? "aborted" : "error",
|
|
650
|
-
errorMessage: err?.message || String(err),
|
|
651
|
-
time: {
|
|
652
|
-
created: Date.now(),
|
|
653
|
-
completed: Date.now(),
|
|
654
|
-
},
|
|
655
|
-
parts: [],
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
this.appendMessage(message);
|
|
659
|
-
this._state.error = err?.message || String(err);
|
|
660
|
-
this.emit({ type: "agent.end", messages: [message] });
|
|
661
|
-
} finally {
|
|
662
|
-
this._state.isStreaming = false;
|
|
663
|
-
this._state.streamMessage = null;
|
|
664
|
-
this._state.pendingToolCalls = new Set<string>();
|
|
665
|
-
this.abortController = undefined;
|
|
666
|
-
this.resolveRunningPrompt?.();
|
|
667
|
-
this.runningPrompt = undefined;
|
|
668
|
-
this.resolveRunningPrompt = undefined;
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
async loop(messages: Message.UserMessage[]): Promise<void> {
|
|
673
|
-
this.ensureIdle();
|
|
674
|
-
await this._runLoop(messages);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
async loopContinue(): Promise<void> {
|
|
678
|
-
this.ensureIdle();
|
|
679
|
-
await this._runLoop();
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
async prompt(messages: Message.TextContent[], images?: Message.ImageContent[]): Promise<void> {
|
|
683
|
-
this.ensureIdle();
|
|
684
|
-
const model = this._state.model;
|
|
685
|
-
if (!model)
|
|
686
|
-
throw new ModelNotConfiguredErr({
|
|
687
|
-
message: "no model configured yet.",
|
|
688
|
-
});
|
|
689
|
-
|
|
690
|
-
const message = Message.createUserMessage({
|
|
691
|
-
role: "user",
|
|
692
|
-
time: {
|
|
693
|
-
created: Date.now(),
|
|
694
|
-
},
|
|
695
|
-
parts: [...messages, ...(images ?? [])],
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
await this.loop([message]);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
private _processLoopEvent(event: Event.AgentEvent): void {
|
|
702
|
-
switch (event.type) {
|
|
703
|
-
case "message.start": {
|
|
704
|
-
this._state.streamMessage = event.message;
|
|
705
|
-
break;
|
|
706
|
-
}
|
|
707
|
-
case "message.part.start":
|
|
708
|
-
case "message.part.update":
|
|
709
|
-
case "message.part.end": {
|
|
710
|
-
this._state.streamMessage = event.message;
|
|
711
|
-
if (event.message.role === "assistant") {
|
|
712
|
-
this.patchStoredAssistantPart(event.message.messageId, event.partIndex, event.part);
|
|
713
|
-
}
|
|
714
|
-
break;
|
|
715
|
-
}
|
|
716
|
-
case "message.update":
|
|
717
|
-
this._state.streamMessage = event.message;
|
|
718
|
-
this.replaceMessageById(event.message);
|
|
719
|
-
break;
|
|
720
|
-
case "message.end":
|
|
721
|
-
this._state.streamMessage = null;
|
|
722
|
-
this.upsertMessage(event.message);
|
|
723
|
-
break;
|
|
724
|
-
case "tool.execution.start": {
|
|
725
|
-
const pendingToolCalls = new Set(this._state.pendingToolCalls);
|
|
726
|
-
pendingToolCalls.add(event.callID);
|
|
727
|
-
this._state.pendingToolCalls = pendingToolCalls;
|
|
728
|
-
break;
|
|
729
|
-
}
|
|
730
|
-
case "tool.execution.end": {
|
|
731
|
-
const pendingToolCalls = new Set(this._state.pendingToolCalls);
|
|
732
|
-
pendingToolCalls.delete(event.callID);
|
|
733
|
-
this._state.pendingToolCalls = pendingToolCalls;
|
|
734
|
-
break;
|
|
735
|
-
}
|
|
736
|
-
case "turn.end":
|
|
737
|
-
if (event.message.role === "assistant" && event.message.parts.length > 0) {
|
|
738
|
-
this._state.error = event.message.errorMessage;
|
|
739
|
-
}
|
|
740
|
-
break;
|
|
741
|
-
case "agent.end":
|
|
742
|
-
this._state.isStreaming = false;
|
|
743
|
-
this._state.streamMessage = null;
|
|
744
|
-
break;
|
|
745
|
-
}
|
|
746
|
-
this.emit(event);
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
private emit(e: Event.AgentEvent) {
|
|
750
|
-
for (const listener of this.listeners) {
|
|
751
|
-
listener(e);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
export async function create<TProvider extends Provider.KnownProvider, TModel extends Model.Info["id"]>(
|
|
757
|
-
options: AgentOptions & {
|
|
758
|
-
provider: TProvider;
|
|
759
|
-
model: TModel;
|
|
760
|
-
name?: string;
|
|
761
|
-
},
|
|
762
|
-
): Promise<Instance>;
|
|
763
|
-
export async function create(options: AgentOptions & { model: Model.Info; name?: string }): Promise<Instance>;
|
|
764
|
-
export async function create(
|
|
765
|
-
options:
|
|
766
|
-
| (AgentOptions & { model: Model.Info; name?: string })
|
|
767
|
-
| (AgentOptions & {
|
|
768
|
-
provider: Provider.KnownProvider;
|
|
769
|
-
model: Model.Info["id"];
|
|
770
|
-
name?: string;
|
|
771
|
-
}),
|
|
772
|
-
): Promise<Instance> {
|
|
773
|
-
let resolvedModel: Model.Info;
|
|
774
|
-
if ("provider" in options) {
|
|
775
|
-
const result = await Model.getModel(options.provider, options.model);
|
|
776
|
-
if (!result) {
|
|
777
|
-
throw new ModelNotFoundErr({
|
|
778
|
-
message: "model not found or not configured yet",
|
|
779
|
-
provider: options.provider,
|
|
780
|
-
model: options.model,
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
resolvedModel = result;
|
|
784
|
-
} else {
|
|
785
|
-
resolvedModel = options.model;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
return new Instance(options.name ?? "main", resolvedModel, options);
|
|
789
|
-
}
|
|
790
|
-
}
|