@cline/agents 0.0.38 → 0.0.39

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.
@@ -1,1440 +0,0 @@
1
- import { createGateway } from "@cline/llms";
2
- import type {
3
- AgentAfterToolResult,
4
- AgentBeforeModelResult,
5
- AgentBeforeToolResult,
6
- AgentMessage,
7
- AgentMessagePart,
8
- AgentModel,
9
- AgentModelFinishReason,
10
- AgentModelRequest,
11
- AgentRunResult,
12
- AgentRuntimeEvent,
13
- AgentRuntimeHooks,
14
- AgentRuntimeStateSnapshot,
15
- AgentStopControl,
16
- AgentTool,
17
- AgentToolCallPart,
18
- AgentToolDefinition,
19
- AgentToolResult,
20
- AgentUsage,
21
- AgentRuntimeConfig as BaseAgentRuntimeConfig,
22
- ToolApprovalResult,
23
- ToolPolicy,
24
- } from "@cline/shared";
25
- import { nanoid } from "nanoid";
26
-
27
- // Local `createUID` helper. The clinee source imports this from
28
- // `@cline/shared` (see `packages/shared/dist/identifier.ts`), but
29
- // sdk-re's shared package does not expose it yet. Inlining here keeps
30
- // PLAN.md Step 1 scoped to `packages/agents/src/` and matches the
31
- // exact clinee implementation (`${prefix}_${nanoid(length)}`).
32
- function createUID(prefix: string, length = 8): string {
33
- return `${prefix}_${nanoid(length)}`;
34
- }
35
-
36
- export type AgentRunInput = string | AgentMessage | readonly AgentMessage[];
37
- export type AgentEventListener = (event: AgentRuntimeEvent) => void;
38
-
39
- /**
40
- * Advanced form: caller supplies a pre-built `AgentModel`. Used by
41
- * `@cline/core`, which constructs models itself to share gateway/telemetry
42
- * wiring with the rest of the session runtime.
43
- */
44
- export interface AgentRuntimeConfigWithModel extends BaseAgentRuntimeConfig {
45
- model: AgentModel;
46
- }
47
-
48
- /**
49
- * Friendly form: caller supplies provider/model IDs and credentials, and the
50
- * runtime builds an `AgentModel` internally via `@cline/llms`. This is the
51
- * entry point most standalone users want.
52
- */
53
- export interface AgentRuntimeConfigWithProvider
54
- extends Omit<BaseAgentRuntimeConfig, "model"> {
55
- /** Provider ID (e.g., "anthropic", "openai") */
56
- providerId: string;
57
- /** Model ID to use */
58
- modelId: string;
59
- /** API key for the provider */
60
- apiKey?: string;
61
- /** Custom base URL for the API */
62
- baseUrl?: string;
63
- /** Additional headers for API requests */
64
- headers?: Record<string, string>;
65
- }
66
-
67
- /**
68
- * Config accepted by `new AgentRuntime(...)` / `createAgentRuntime(...)` /
69
- * `new Agent(...)` / `createAgent(...)`. Either supply a pre-built `model`
70
- * (advanced) or `providerId` + `modelId` (+ credentials) and the runtime will
71
- * construct the model itself via `@cline/llms`.
72
- */
73
- export type AgentRuntimeConfig =
74
- | AgentRuntimeConfigWithModel
75
- | AgentRuntimeConfigWithProvider;
76
-
77
- function hasPrebuiltModel(
78
- config: AgentRuntimeConfig,
79
- ): config is AgentRuntimeConfigWithModel {
80
- return (config as AgentRuntimeConfigWithModel).model !== undefined;
81
- }
82
-
83
- function resolveRuntimeConfig(
84
- config: AgentRuntimeConfig,
85
- ): BaseAgentRuntimeConfig {
86
- if (hasPrebuiltModel(config)) {
87
- return config;
88
- }
89
- const { providerId, modelId, apiKey, baseUrl, headers, ...rest } = config;
90
- const gateway = createGateway({
91
- providerConfigs: [{ providerId, apiKey, baseUrl, headers }],
92
- });
93
- const model = gateway.createAgentModel({ providerId, modelId });
94
- return { ...rest, model };
95
- }
96
-
97
- function resolveToolPolicy(
98
- toolName: string,
99
- policies: BaseAgentRuntimeConfig["toolPolicies"],
100
- ): ToolPolicy {
101
- return {
102
- ...(policies?.["*"] ?? {}),
103
- ...(policies?.[toolName] ?? {}),
104
- };
105
- }
106
-
107
- interface PendingToolAssembly {
108
- toolCallId: string;
109
- toolName?: string;
110
- inputText: string;
111
- inputValue?: unknown;
112
- metadata?: unknown;
113
- parseError?: string;
114
- }
115
-
116
- interface InvalidToolCall {
117
- toolCallId: string;
118
- toolName?: string;
119
- input: Record<string, unknown>;
120
- reason: "missing_name" | "missing_arguments" | "invalid_arguments";
121
- }
122
-
123
- interface PreparedToolExecution {
124
- toolCall: AgentToolCallPart;
125
- tool?: AgentTool;
126
- input: unknown;
127
- skipReason?: string;
128
- }
129
-
130
- interface HookBag {
131
- beforeRun: NonNullable<AgentRuntimeHooks["beforeRun"]>[];
132
- afterRun: NonNullable<AgentRuntimeHooks["afterRun"]>[];
133
- beforeModel: NonNullable<AgentRuntimeHooks["beforeModel"]>[];
134
- afterModel: NonNullable<AgentRuntimeHooks["afterModel"]>[];
135
- beforeTool: NonNullable<AgentRuntimeHooks["beforeTool"]>[];
136
- afterTool: NonNullable<AgentRuntimeHooks["afterTool"]>[];
137
- onEvent: NonNullable<AgentRuntimeHooks["onEvent"]>[];
138
- }
139
-
140
- class ControlledStopError extends Error {
141
- readonly reason?: string;
142
-
143
- constructor(reason?: string) {
144
- super(reason ?? "Run stopped by runtime control");
145
- this.name = "ControlledStopError";
146
- this.reason = reason;
147
- }
148
- }
149
-
150
- const DEFAULT_USAGE: AgentUsage = {
151
- inputTokens: 0,
152
- outputTokens: 0,
153
- cacheReadTokens: 0,
154
- cacheWriteTokens: 0,
155
- };
156
-
157
- function createMessage(
158
- role: AgentMessage["role"],
159
- content: AgentMessagePart[],
160
- metadata?: Record<string, unknown>,
161
- ): AgentMessage {
162
- return {
163
- id: createUID("msg"),
164
- role,
165
- content,
166
- createdAt: Date.now(),
167
- metadata,
168
- };
169
- }
170
-
171
- function cloneUsage(usage: AgentUsage): AgentUsage {
172
- return { ...usage };
173
- }
174
-
175
- function cloneMessages(messages: readonly AgentMessage[]): AgentMessage[] {
176
- return messages.map((message) => ({
177
- ...message,
178
- content: message.content.map((part: AgentMessagePart) => ({ ...part })),
179
- metadata: message.metadata ? { ...message.metadata } : undefined,
180
- modelInfo: message.modelInfo ? { ...message.modelInfo } : undefined,
181
- metrics: message.metrics ? { ...message.metrics } : undefined,
182
- }));
183
- }
184
-
185
- function usageDelta(
186
- start: AgentUsage,
187
- end: AgentUsage,
188
- ): NonNullable<AgentMessage["metrics"]> | undefined {
189
- const inputTokens = Math.max(
190
- 0,
191
- (end.inputTokens ?? 0) - (start.inputTokens ?? 0),
192
- );
193
- const outputTokens = Math.max(
194
- 0,
195
- (end.outputTokens ?? 0) - (start.outputTokens ?? 0),
196
- );
197
- const cacheReadTokens = Math.max(
198
- 0,
199
- (end.cacheReadTokens ?? 0) - (start.cacheReadTokens ?? 0),
200
- );
201
- const cacheWriteTokens = Math.max(
202
- 0,
203
- (end.cacheWriteTokens ?? 0) - (start.cacheWriteTokens ?? 0),
204
- );
205
- const startCost = start.totalCost ?? 0;
206
- const endCost = end.totalCost ?? 0;
207
- const cost = Math.max(0, endCost - startCost);
208
- if (
209
- inputTokens === 0 &&
210
- outputTokens === 0 &&
211
- cacheReadTokens === 0 &&
212
- cacheWriteTokens === 0 &&
213
- cost === 0
214
- ) {
215
- return undefined;
216
- }
217
- return {
218
- inputTokens: inputTokens > 0 ? inputTokens : 0,
219
- outputTokens: outputTokens > 0 ? outputTokens : 0,
220
- cacheReadTokens: cacheReadTokens > 0 ? cacheReadTokens : 0,
221
- cacheWriteTokens: cacheWriteTokens > 0 ? cacheWriteTokens : 0,
222
- ...(cost > 0 ? { cost } : {}),
223
- };
224
- }
225
-
226
- function textFromMessage(message: AgentMessage | undefined): string {
227
- if (!message) {
228
- return "";
229
- }
230
- return message.content
231
- .filter(
232
- (
233
- part: AgentMessagePart,
234
- ): part is Extract<AgentMessagePart, { type: "text" }> =>
235
- part.type === "text",
236
- )
237
- .map((part: Extract<AgentMessagePart, { type: "text" }>) => part.text)
238
- .join("");
239
- }
240
-
241
- function textFromToolMessage(message: AgentMessage | undefined): string {
242
- const result = message?.content.find(
243
- (part): part is Extract<AgentMessagePart, { type: "tool-result" }> =>
244
- part.type === "tool-result",
245
- );
246
- if (!result || result.isError) {
247
- return "";
248
- }
249
- if (typeof result.output === "string") {
250
- return result.output;
251
- }
252
- try {
253
- return JSON.stringify(result.output);
254
- } catch {
255
- return String(result.output);
256
- }
257
- }
258
-
259
- function normalizeInput(input: AgentRunInput): AgentMessage[] {
260
- if (typeof input === "string") {
261
- return [createMessage("user", [{ type: "text", text: input }])];
262
- }
263
- if (Array.isArray(input)) {
264
- return cloneMessages(input);
265
- }
266
- return cloneMessages([input as AgentMessage]);
267
- }
268
-
269
- export class AgentRuntime {
270
- private config: Required<Pick<BaseAgentRuntimeConfig, "toolExecution">> &
271
- BaseAgentRuntimeConfig;
272
- private readonly listeners = new Set<AgentEventListener>();
273
- // biome-ignore lint/suspicious/noExplicitAny: tool input/output types vary per tool
274
- private readonly tools = new Map<string, AgentTool<any, any>>();
275
- private hooks: HookBag = {
276
- beforeRun: [],
277
- afterRun: [],
278
- beforeModel: [],
279
- afterModel: [],
280
- beforeTool: [],
281
- afterTool: [],
282
- onEvent: [],
283
- };
284
- private readonly state = {
285
- agentId: "",
286
- agentRole: undefined as string | undefined,
287
- parentAgentId: undefined as string | null | undefined,
288
- runId: undefined as string | undefined,
289
- status: "idle" as AgentRuntimeStateSnapshot["status"],
290
- iteration: 0,
291
- messages: [] as AgentMessage[],
292
- pendingToolCalls: [] as string[],
293
- usage: cloneUsage(DEFAULT_USAGE),
294
- lastError: undefined as string | undefined,
295
- };
296
- private initialization?: Promise<void>;
297
- private abortController?: AbortController;
298
-
299
- constructor(config: AgentRuntimeConfig) {
300
- const resolved = resolveRuntimeConfig(config);
301
- this.config = {
302
- ...resolved,
303
- toolExecution: resolved.toolExecution ?? "sequential",
304
- };
305
- this.state.agentId = resolved.agentId ?? createUID("agent");
306
- this.state.agentRole = resolved.agentRole;
307
- this.state.parentAgentId = resolved.parentAgentId;
308
- this.state.messages = cloneMessages(resolved.initialMessages ?? []);
309
- }
310
-
311
- async run(input: AgentRunInput): Promise<AgentRunResult> {
312
- return this.execute(input);
313
- }
314
-
315
- async continue(input?: AgentRunInput): Promise<AgentRunResult> {
316
- return this.execute(input);
317
- }
318
-
319
- abort(reason?: string): void {
320
- if (!this.abortController) {
321
- return;
322
- }
323
- this.state.lastError = reason ?? "Run aborted";
324
- this.abortController.abort(new Error(reason ?? "Run aborted"));
325
- }
326
-
327
- subscribe(listener: AgentEventListener): () => void {
328
- this.listeners.add(listener);
329
- return () => {
330
- this.listeners.delete(listener);
331
- };
332
- }
333
-
334
- /**
335
- * Replace the conversation with a fresh set of messages, discarding any
336
- * in-flight run and usage state while preserving the underlying model,
337
- * tools, hooks, plugins, and active event subscribers.
338
- *
339
- * Useful for standalone callers that persist conversations externally and
340
- * want to re-seed the runtime from storage without recreating subscribers.
341
- */
342
- restore(messages: readonly AgentMessage[]): void {
343
- this.abort("Agent state restored");
344
- // Reset state that is not carried across restores. Keep `listeners`,
345
- // tools, hooks, plugins, model, and agent identity so external event
346
- // subscribers continue to receive events after restore().
347
- this.state.runId = undefined;
348
- this.state.status = "idle";
349
- this.state.iteration = 0;
350
- this.state.pendingToolCalls = [];
351
- this.state.usage = cloneUsage(DEFAULT_USAGE);
352
- this.state.lastError = undefined;
353
- this.state.messages = cloneMessages(messages);
354
- this.config = {
355
- ...this.config,
356
- initialMessages: cloneMessages(messages),
357
- };
358
- }
359
-
360
- snapshot(): AgentRuntimeStateSnapshot {
361
- return {
362
- agentId: this.state.agentId,
363
- agentRole: this.state.agentRole,
364
- parentAgentId: this.state.parentAgentId,
365
- conversationId: this.config.conversationId?.trim() || undefined,
366
- runId: this.state.runId,
367
- status: this.state.status,
368
- iteration: this.state.iteration,
369
- messages: cloneMessages(this.state.messages),
370
- pendingToolCalls: [...this.state.pendingToolCalls],
371
- usage: cloneUsage(this.state.usage),
372
- lastError: this.state.lastError,
373
- };
374
- }
375
-
376
- private async ensureInitialized(): Promise<void> {
377
- this.initialization ??= this.initialize();
378
- await this.initialization;
379
- }
380
-
381
- private async initialize(): Promise<void> {
382
- this.registerHooks(this.config.hooks);
383
- for (const tool of this.config.tools ?? []) {
384
- this.tools.set(tool.name, tool);
385
- }
386
- for (const plugin of this.config.plugins ?? []) {
387
- const setup = await plugin.setup?.({
388
- agentId: this.state.agentId,
389
- agentRole: this.state.agentRole,
390
- systemPrompt: this.config.systemPrompt,
391
- });
392
- for (const tool of setup?.tools ?? []) {
393
- this.tools.set(tool.name, tool);
394
- }
395
- this.registerHooks(setup?.hooks);
396
- }
397
- }
398
-
399
- private registerHooks(hooks: Partial<AgentRuntimeHooks> | undefined): void {
400
- if (!hooks) {
401
- return;
402
- }
403
- if (hooks.beforeRun) this.hooks.beforeRun.push(hooks.beforeRun);
404
- if (hooks.afterRun) this.hooks.afterRun.push(hooks.afterRun);
405
- if (hooks.beforeModel) this.hooks.beforeModel.push(hooks.beforeModel);
406
- if (hooks.afterModel) this.hooks.afterModel.push(hooks.afterModel);
407
- if (hooks.beforeTool) this.hooks.beforeTool.push(hooks.beforeTool);
408
- if (hooks.afterTool) this.hooks.afterTool.push(hooks.afterTool);
409
- if (hooks.onEvent) this.hooks.onEvent.push(hooks.onEvent);
410
- }
411
-
412
- private getRequiredCompletionToolNames(): string[] {
413
- if (this.config.completionPolicy?.requireCompletionTool !== true) {
414
- return [];
415
- }
416
- return [...this.tools.values()]
417
- .filter((tool) => tool.lifecycle?.completesRun === true)
418
- .map((tool) => tool.name)
419
- .sort();
420
- }
421
-
422
- private getCompletionToolReminderMessage(): string | undefined {
423
- const terminalToolNames = this.getRequiredCompletionToolNames();
424
- if (terminalToolNames.length === 0) {
425
- return undefined;
426
- }
427
- return `[SYSTEM] This run is not complete until you call one of these terminal completion tools: ${terminalToolNames.join(
428
- ", ",
429
- )}. Continue working if requirements are not met. If the task is complete, call the appropriate terminal completion tool now.`;
430
- }
431
-
432
- private getCompletionReminderMessages(): string[] {
433
- return [
434
- this.getCompletionToolReminderMessage(),
435
- this.config.completionPolicy?.completionGuard?.(),
436
- ].filter((message): message is string => Boolean(message));
437
- }
438
-
439
- private async addUserReminderMessage(text: string): Promise<AgentMessage> {
440
- const reminderMessage = createMessage("user", [{ type: "text", text }]);
441
- this.state.messages.push(reminderMessage);
442
- await this.emit({
443
- type: "message-added",
444
- snapshot: this.snapshot(),
445
- message: reminderMessage,
446
- });
447
- return reminderMessage;
448
- }
449
-
450
- private async execute(input?: AgentRunInput): Promise<AgentRunResult> {
451
- await this.ensureInitialized();
452
- if (this.state.status === "running") {
453
- throw new Error("Agent runtime is already running");
454
- }
455
-
456
- this.abortController = new AbortController();
457
- this.state.runId = createUID("run");
458
- this.state.status = "running";
459
- this.state.iteration = 0;
460
- this.state.pendingToolCalls = [];
461
- this.state.lastError = undefined;
462
-
463
- try {
464
- await this.callBeforeRunHooks();
465
- await this.emit({ type: "run-started", snapshot: this.snapshot() });
466
-
467
- for (const message of input ? normalizeInput(input) : []) {
468
- this.state.messages.push(message);
469
- await this.emit({
470
- type: "message-added",
471
- snapshot: this.snapshot(),
472
- message,
473
- });
474
- }
475
-
476
- const completionToolReminder = this.getCompletionToolReminderMessage();
477
- if (completionToolReminder) {
478
- await this.addUserReminderMessage(completionToolReminder);
479
- }
480
-
481
- let finalAssistantMessage: AgentMessage | undefined;
482
-
483
- while (
484
- this.config.maxIterations === undefined ||
485
- this.state.iteration < this.config.maxIterations
486
- ) {
487
- this.throwIfAborted();
488
-
489
- this.state.iteration += 1;
490
- await this.emit({
491
- type: "turn-started",
492
- snapshot: this.snapshot(),
493
- iteration: this.state.iteration,
494
- });
495
-
496
- const { message, finishReason } = await this.generateAssistantMessage();
497
- finalAssistantMessage = message;
498
- this.state.messages.push(message);
499
- await this.emit({
500
- type: "message-added",
501
- snapshot: this.snapshot(),
502
- message,
503
- });
504
- await this.emit({
505
- type: "assistant-message",
506
- snapshot: this.snapshot(),
507
- iteration: this.state.iteration,
508
- message,
509
- finishReason,
510
- });
511
-
512
- if (finishReason === "aborted") {
513
- throw this.normalizeAbortError();
514
- }
515
-
516
- const toolCalls = message.content.filter(
517
- (part: AgentMessagePart): part is AgentToolCallPart =>
518
- part.type === "tool-call",
519
- );
520
- if (finishReason === "error" && toolCalls.length === 0) {
521
- throw new Error(this.state.lastError ?? "Model stream failed");
522
- }
523
- this.state.pendingToolCalls = toolCalls.map((part) => part.toolCallId);
524
-
525
- if (toolCalls.length === 0) {
526
- await this.emit({
527
- type: "turn-finished",
528
- snapshot: this.snapshot(),
529
- iteration: this.state.iteration,
530
- toolCallCount: 0,
531
- });
532
- const completionReminderMessages =
533
- this.getCompletionReminderMessages();
534
- if (completionReminderMessages.length > 0) {
535
- for (const reminderMessage of completionReminderMessages) {
536
- await this.addUserReminderMessage(reminderMessage);
537
- }
538
- continue;
539
- }
540
- const result = this.finishRun("completed", finalAssistantMessage);
541
- await this.callAfterRunHooks(result);
542
- await this.emit({
543
- type: "run-finished",
544
- snapshot: this.snapshot(),
545
- result,
546
- });
547
- return result;
548
- }
549
-
550
- const toolMessages = await this.executeToolCalls(toolCalls);
551
- this.state.pendingToolCalls = [];
552
- for (const toolMessage of toolMessages) {
553
- this.state.messages.push(toolMessage);
554
- await this.emit({
555
- type: "message-added",
556
- snapshot: this.snapshot(),
557
- message: toolMessage,
558
- });
559
- }
560
- await this.emit({
561
- type: "turn-finished",
562
- snapshot: this.snapshot(),
563
- iteration: this.state.iteration,
564
- toolCallCount: toolCalls.length,
565
- });
566
- const terminalToolMessage = this.findCompletingToolMessage(
567
- toolCalls,
568
- toolMessages,
569
- );
570
- if (terminalToolMessage) {
571
- const result = this.finishRun(
572
- "completed",
573
- finalAssistantMessage,
574
- textFromToolMessage(terminalToolMessage) || undefined,
575
- );
576
- await this.callAfterRunHooks(result);
577
- await this.emit({
578
- type: "run-finished",
579
- snapshot: this.snapshot(),
580
- result,
581
- });
582
- return result;
583
- }
584
- }
585
-
586
- throw new Error(
587
- `Agent runtime exceeded maxIterations (${this.config.maxIterations})`,
588
- );
589
- } catch (error) {
590
- const normalized =
591
- error instanceof Error ? error : new Error(String(error));
592
- const isControlledStop = normalized instanceof ControlledStopError;
593
- const status =
594
- this.abortController.signal.aborted || isControlledStop
595
- ? "aborted"
596
- : "failed";
597
- this.state.status = status;
598
- this.state.lastError = normalized.message;
599
- const result: AgentRunResult = {
600
- agentId: this.state.agentId,
601
- agentRole: this.state.agentRole,
602
- runId: this.state.runId ?? createUID("run"),
603
- status,
604
- iterations: this.state.iteration,
605
- outputText: textFromMessage(this.findLastAssistantMessage()),
606
- messages: cloneMessages(this.state.messages),
607
- usage: cloneUsage(this.state.usage),
608
- error: status === "failed" ? normalized : undefined,
609
- };
610
- await this.callAfterRunHooks(result);
611
- if (status === "failed") {
612
- await this.emit({
613
- type: "run-failed",
614
- snapshot: this.snapshot(),
615
- error: normalized,
616
- });
617
- } else {
618
- await this.emit({
619
- type: "run-finished",
620
- snapshot: this.snapshot(),
621
- result,
622
- });
623
- }
624
- return result;
625
- } finally {
626
- this.abortController = undefined;
627
- }
628
- }
629
-
630
- private async callBeforeRunHooks(): Promise<void> {
631
- for (const hook of this.hooks.beforeRun) {
632
- const control = (await hook({
633
- snapshot: this.snapshot(),
634
- })) as AgentStopControl | undefined;
635
- this.applyStopControl(control);
636
- }
637
- }
638
-
639
- private async callAfterRunHooks(result: AgentRunResult): Promise<void> {
640
- for (const hook of this.hooks.afterRun) {
641
- await hook({ snapshot: this.snapshot(), result });
642
- }
643
- }
644
-
645
- private async generateAssistantMessage(): Promise<{
646
- message: AgentMessage;
647
- finishReason: AgentModelFinishReason;
648
- }> {
649
- const usageBeforeModel = cloneUsage(this.state.usage);
650
- let request: AgentModelRequest = {
651
- systemPrompt: this.config.systemPrompt,
652
- messages: cloneMessages(this.state.messages),
653
- tools: [...this.tools.values()].map<AgentToolDefinition>((tool) => ({
654
- name: tool.name,
655
- description: tool.description,
656
- inputSchema: tool.inputSchema,
657
- })),
658
- signal: this.abortController?.signal,
659
- options: this.config.modelOptions,
660
- };
661
-
662
- request = await this.prepareTurnForModelRequest(request);
663
-
664
- if (this.state.iteration > 1) {
665
- if (await this.consumePendingUserMessage()) {
666
- request = { ...request, messages: cloneMessages(this.state.messages) };
667
- }
668
- }
669
-
670
- for (const hook of this.hooks.beforeModel) {
671
- const result = (await hook({
672
- snapshot: this.snapshot(),
673
- request,
674
- })) as AgentBeforeModelResult | undefined;
675
- this.applyStopControl(result);
676
- if (result?.messages) {
677
- request = { ...request, messages: cloneMessages(result.messages) };
678
- }
679
- if (result?.tools) {
680
- request = { ...request, tools: [...result.tools] };
681
- }
682
- if (result?.options) {
683
- request = {
684
- ...request,
685
- options: { ...(request.options ?? {}), ...result.options },
686
- };
687
- }
688
- }
689
-
690
- const stream = await this.config.model.stream(request);
691
- const content: AgentMessagePart[] = [];
692
- const toolAssemblies = new Map<string, PendingToolAssembly>();
693
- const invalidToolCalls: InvalidToolCall[] = [];
694
- const sequence: Array<
695
- { type: "tool"; key: string } | { type: "part"; part: AgentMessagePart }
696
- > = [];
697
- let nextToolIndex = 0;
698
- let finishReason: AgentModelFinishReason = "stop";
699
- let accumulatedText = "";
700
- let accumulatedReasoning = "";
701
-
702
- for await (const event of stream) {
703
- this.throwIfAborted();
704
- switch (event.type) {
705
- case "text-delta": {
706
- accumulatedText += event.text;
707
- const last = sequence.at(-1);
708
- if (last?.type === "part" && last.part.type === "text") {
709
- last.part.text += event.text;
710
- } else {
711
- sequence.push({
712
- type: "part",
713
- part: { type: "text", text: event.text },
714
- });
715
- }
716
- await this.emit({
717
- type: "assistant-text-delta",
718
- snapshot: this.snapshot(),
719
- iteration: this.state.iteration,
720
- text: event.text,
721
- accumulatedText,
722
- });
723
- break;
724
- }
725
- case "reasoning-delta": {
726
- accumulatedReasoning += event.text;
727
- const last = sequence.at(-1);
728
- if (last?.type === "part" && last.part.type === "reasoning") {
729
- last.part.text += event.text;
730
- last.part.redacted = event.redacted ?? last.part.redacted;
731
- last.part.metadata = event.metadata ?? last.part.metadata;
732
- } else {
733
- sequence.push({
734
- type: "part",
735
- part: {
736
- type: "reasoning",
737
- text: event.text,
738
- redacted: event.redacted,
739
- metadata: event.metadata,
740
- },
741
- });
742
- }
743
- await this.emit({
744
- type: "assistant-reasoning-delta",
745
- snapshot: this.snapshot(),
746
- iteration: this.state.iteration,
747
- text: event.text,
748
- accumulatedText: accumulatedReasoning,
749
- redacted: event.redacted,
750
- metadata: event.metadata,
751
- });
752
- break;
753
- }
754
- case "tool-call-delta": {
755
- const key =
756
- event.toolCallId ?? `tool_${event.index ?? nextToolIndex}`;
757
- if (event.index == null && event.toolCallId == null) {
758
- nextToolIndex += 1;
759
- }
760
- let assembly = toolAssemblies.get(key);
761
- if (!assembly) {
762
- assembly = {
763
- toolCallId: event.toolCallId ?? createUID("tool"),
764
- inputText: "",
765
- };
766
- toolAssemblies.set(key, assembly);
767
- sequence.push({ type: "tool", key });
768
- }
769
- if (event.toolCallId) {
770
- assembly.toolCallId = event.toolCallId;
771
- }
772
- if (event.toolName) {
773
- assembly.toolName = event.toolName;
774
- }
775
- if (event.input !== undefined) {
776
- assembly.inputValue = event.input;
777
- }
778
- if (event.metadata !== undefined) {
779
- assembly.metadata = mergeToolMetadata(
780
- assembly.metadata,
781
- event.metadata,
782
- );
783
- }
784
- if (event.inputText) {
785
- assembly.inputText = mergeToolInputText(
786
- assembly.inputText,
787
- event.inputText,
788
- );
789
- }
790
- break;
791
- }
792
- case "usage": {
793
- await this.updateUsage(event.usage);
794
- break;
795
- }
796
- case "finish": {
797
- finishReason = event.reason;
798
- if (event.error) {
799
- this.state.lastError = event.error;
800
- }
801
- break;
802
- }
803
- }
804
- }
805
-
806
- for (const item of sequence) {
807
- if (item.type === "part") {
808
- content.push(item.part);
809
- continue;
810
- }
811
- const assembly = toolAssemblies.get(item.key);
812
- if (!assembly?.toolName) {
813
- invalidToolCalls.push({
814
- toolCallId: assembly?.toolCallId ?? item.key,
815
- input: buildInvalidToolInput(assembly?.inputText ?? ""),
816
- reason: "missing_name",
817
- });
818
- continue;
819
- }
820
- const parsed = parseToolInput(assembly);
821
- if (parsed.reason) {
822
- invalidToolCalls.push({
823
- toolCallId: assembly.toolCallId,
824
- toolName: assembly.toolName,
825
- input: parsed.invalidInput,
826
- reason: parsed.reason,
827
- });
828
- }
829
- content.push({
830
- type: "tool-call",
831
- toolCallId: assembly.toolCallId,
832
- toolName: assembly.toolName,
833
- input: parsed.input,
834
- metadata: parsed.parseError
835
- ? mergeToolMetadata(assembly.metadata, {
836
- inputParseError: parsed.parseError,
837
- rawInputText: assembly.inputText,
838
- })
839
- : assembly.metadata,
840
- });
841
- }
842
-
843
- const message = createMessage(
844
- "assistant",
845
- content,
846
- invalidToolCalls.length > 0 ? { invalidToolCalls } : undefined,
847
- );
848
- const metrics = usageDelta(usageBeforeModel, this.state.usage);
849
- if (metrics) {
850
- message.metrics = metrics;
851
- }
852
- if (this.config.messageModelInfo) {
853
- message.modelInfo = { ...this.config.messageModelInfo };
854
- }
855
- for (const hook of this.hooks.afterModel) {
856
- const control = (await hook({
857
- snapshot: this.snapshot(),
858
- assistantMessage: message,
859
- finishReason,
860
- })) as AgentStopControl | undefined;
861
- this.applyStopControl(control);
862
- }
863
-
864
- return { message, finishReason };
865
- }
866
-
867
- private async prepareTurnForModelRequest(
868
- request: AgentModelRequest,
869
- ): Promise<AgentModelRequest> {
870
- if (!this.config.prepareTurn) {
871
- return request;
872
- }
873
-
874
- const result = await this.config.prepareTurn({
875
- agentId: this.state.agentId,
876
- conversationId: this.config.conversationId,
877
- parentAgentId: this.state.parentAgentId ?? null,
878
- iteration: this.state.iteration,
879
- messages: request.messages,
880
- systemPrompt: request.systemPrompt,
881
- tools: request.tools,
882
- model: {
883
- id: this.config.messageModelInfo?.id,
884
- provider: this.config.messageModelInfo?.provider,
885
- },
886
- signal: request.signal,
887
- emitStatusNotice: (message, metadata) => {
888
- void this.emit({
889
- type: "status-notice",
890
- snapshot: this.snapshot(),
891
- message,
892
- metadata,
893
- });
894
- },
895
- });
896
- if (!result) {
897
- return request;
898
- }
899
-
900
- let next = request;
901
- if (result.messages) {
902
- const preparedMessages = cloneMessages(result.messages);
903
- this.state.messages = preparedMessages;
904
- next = { ...next, messages: cloneMessages(preparedMessages) };
905
- }
906
- if (result.systemPrompt !== undefined) {
907
- next = { ...next, systemPrompt: result.systemPrompt };
908
- }
909
- return next;
910
- }
911
-
912
- private async consumePendingUserMessage(): Promise<boolean> {
913
- const consumePendingUserMessage = this.config.consumePendingUserMessage;
914
- if (!consumePendingUserMessage) {
915
- return false;
916
- }
917
- const pending = (await consumePendingUserMessage())?.trim();
918
- if (!pending) {
919
- return false;
920
- }
921
- const message = createMessage("user", [{ type: "text", text: pending }]);
922
- this.state.messages.push(message);
923
- await this.emit({
924
- type: "message-added",
925
- snapshot: this.snapshot(),
926
- message,
927
- });
928
- return true;
929
- }
930
-
931
- private async updateUsage(usage: Partial<AgentUsage>): Promise<void> {
932
- this.state.usage = {
933
- inputTokens: this.state.usage.inputTokens + (usage.inputTokens ?? 0),
934
- outputTokens: this.state.usage.outputTokens + (usage.outputTokens ?? 0),
935
- cacheReadTokens:
936
- this.state.usage.cacheReadTokens + (usage.cacheReadTokens ?? 0),
937
- cacheWriteTokens:
938
- this.state.usage.cacheWriteTokens + (usage.cacheWriteTokens ?? 0),
939
- totalCost: (this.state.usage.totalCost ?? 0) + (usage.totalCost ?? 0),
940
- };
941
- await this.emit({
942
- type: "usage-updated",
943
- snapshot: this.snapshot(),
944
- usage: cloneUsage(this.state.usage),
945
- });
946
- }
947
-
948
- private async executeToolCalls(
949
- toolCalls: AgentToolCallPart[],
950
- ): Promise<AgentMessage[]> {
951
- const prepared: PreparedToolExecution[] = [];
952
- for (const toolCall of toolCalls) {
953
- prepared.push(await this.prepareToolExecution(toolCall));
954
- }
955
-
956
- if (this.config.toolExecution === "parallel") {
957
- return Promise.all(
958
- prepared.map((execution) => this.executePreparedTool(execution)),
959
- );
960
- }
961
-
962
- const results: AgentMessage[] = [];
963
- for (const execution of prepared) {
964
- results.push(await this.executePreparedTool(execution));
965
- }
966
- return results;
967
- }
968
-
969
- private findCompletingToolMessage(
970
- toolCalls: AgentToolCallPart[],
971
- toolMessages: AgentMessage[],
972
- ): AgentMessage | undefined {
973
- for (let index = 0; index < toolCalls.length; index += 1) {
974
- const toolCall = toolCalls[index];
975
- if (this.tools.get(toolCall.toolName)?.lifecycle?.completesRun !== true) {
976
- continue;
977
- }
978
- const toolMessage = toolMessages[index];
979
- const result = toolMessage?.content.find(
980
- (part): part is Extract<AgentMessagePart, { type: "tool-result" }> =>
981
- part.type === "tool-result" &&
982
- part.toolCallId === toolCall.toolCallId,
983
- );
984
- if (result && !result.isError) {
985
- return toolMessage;
986
- }
987
- }
988
- return undefined;
989
- }
990
-
991
- private async prepareToolExecution(
992
- toolCall: AgentToolCallPart,
993
- ): Promise<PreparedToolExecution> {
994
- const tool = this.tools.get(toolCall.toolName);
995
- let input = toolCall.input;
996
- let skipReason: string | undefined;
997
- const metadata =
998
- toolCall.metadata &&
999
- typeof toolCall.metadata === "object" &&
1000
- !Array.isArray(toolCall.metadata)
1001
- ? (toolCall.metadata as Record<string, unknown>)
1002
- : undefined;
1003
-
1004
- if (typeof metadata?.inputParseError === "string") {
1005
- skipReason = metadata.inputParseError;
1006
- }
1007
-
1008
- const toolSource =
1009
- metadata?.toolSource &&
1010
- typeof metadata.toolSource === "object" &&
1011
- !Array.isArray(metadata.toolSource)
1012
- ? (metadata.toolSource as Record<string, unknown>)
1013
- : undefined;
1014
- if (toolSource?.executionMode === "provider") {
1015
- const providerId =
1016
- typeof toolSource.providerId === "string"
1017
- ? toolSource.providerId
1018
- : "provider";
1019
- skipReason = `Tool execution is disabled for provider ${providerId}`;
1020
- }
1021
-
1022
- if (tool && !skipReason) {
1023
- for (const hook of this.hooks.beforeTool) {
1024
- const result = (await hook({
1025
- snapshot: this.snapshot(),
1026
- tool,
1027
- toolCall,
1028
- input,
1029
- })) as AgentBeforeToolResult | undefined;
1030
- if (result?.input !== undefined) {
1031
- input = result.input;
1032
- }
1033
- this.applyStopControl(result);
1034
- if (result?.skip) {
1035
- skipReason =
1036
- result.reason ?? `Tool ${tool.name} was blocked by a runtime hook`;
1037
- break;
1038
- }
1039
- }
1040
- }
1041
-
1042
- if (tool && !skipReason) {
1043
- const policy = resolveToolPolicy(
1044
- toolCall.toolName,
1045
- this.config.toolPolicies,
1046
- );
1047
- if (policy.enabled === false) {
1048
- skipReason = `Tool "${toolCall.toolName}" is disabled by policy`;
1049
- } else if (policy.autoApprove === false) {
1050
- const approval = await this.requestToolApproval(
1051
- toolCall,
1052
- input,
1053
- policy,
1054
- );
1055
- if (!approval.approved) {
1056
- skipReason =
1057
- approval.reason ?? `Tool "${toolCall.toolName}" was not approved`;
1058
- }
1059
- }
1060
- }
1061
-
1062
- return {
1063
- toolCall: { ...toolCall, input },
1064
- tool,
1065
- input,
1066
- skipReason,
1067
- };
1068
- }
1069
-
1070
- private async requestToolApproval(
1071
- toolCall: AgentToolCallPart,
1072
- input: unknown,
1073
- policy: ToolPolicy,
1074
- ): Promise<ToolApprovalResult> {
1075
- const requestApproval = this.config.requestToolApproval;
1076
- if (!requestApproval) {
1077
- return {
1078
- approved: false,
1079
- reason: `Tool "${toolCall.toolName}" requires approval but no approval callback is configured`,
1080
- };
1081
- }
1082
- try {
1083
- return await requestApproval({
1084
- sessionId:
1085
- this.config.sessionId?.trim() ||
1086
- this.config.conversationId?.trim() ||
1087
- this.state.runId ||
1088
- this.state.agentId,
1089
- agentId: this.state.agentId,
1090
- conversationId:
1091
- this.config.conversationId?.trim() ||
1092
- this.state.runId ||
1093
- this.state.agentId,
1094
- iteration: this.state.iteration,
1095
- toolCallId: toolCall.toolCallId,
1096
- toolName: toolCall.toolName,
1097
- input,
1098
- policy,
1099
- });
1100
- } catch (error) {
1101
- return {
1102
- approved: false,
1103
- reason: `Tool "${toolCall.toolName}" approval request failed: ${
1104
- error instanceof Error ? error.message : String(error)
1105
- }`,
1106
- };
1107
- }
1108
- }
1109
-
1110
- private async executePreparedTool(
1111
- prepared: PreparedToolExecution,
1112
- ): Promise<AgentMessage> {
1113
- const startedAt = new Date();
1114
- await this.emit({
1115
- type: "tool-started",
1116
- snapshot: this.snapshot(),
1117
- iteration: this.state.iteration,
1118
- toolCall: prepared.toolCall,
1119
- });
1120
-
1121
- let result: AgentToolResult;
1122
- if (prepared.skipReason) {
1123
- result = {
1124
- output: { error: prepared.skipReason },
1125
- isError: true,
1126
- };
1127
- } else if (!prepared.tool) {
1128
- result = {
1129
- output: { error: `Unknown tool: ${prepared.toolCall.toolName}` },
1130
- isError: true,
1131
- };
1132
- } else {
1133
- try {
1134
- const output = await prepared.tool.execute(prepared.input, {
1135
- sessionId: this.config.sessionId,
1136
- agentId: this.state.agentId,
1137
- conversationId: this.config.conversationId,
1138
- runId: this.state.runId ?? createUID("run"),
1139
- iteration: this.state.iteration,
1140
- toolCallId: prepared.toolCall.toolCallId,
1141
- signal: this.abortController?.signal,
1142
- metadata: this.config.toolContextMetadata,
1143
- snapshot: this.snapshot(),
1144
- emitUpdate: (update: unknown) => {
1145
- void this.emit({
1146
- type: "tool-updated",
1147
- snapshot: this.snapshot(),
1148
- iteration: this.state.iteration,
1149
- toolCall: prepared.toolCall,
1150
- update,
1151
- });
1152
- },
1153
- });
1154
- result = { output };
1155
- } catch (error) {
1156
- result = {
1157
- output: {
1158
- error: error instanceof Error ? error.message : String(error),
1159
- },
1160
- isError: true,
1161
- };
1162
- }
1163
- }
1164
-
1165
- const endedAt = new Date();
1166
- const durationMs = Math.max(0, endedAt.getTime() - startedAt.getTime());
1167
-
1168
- if (prepared.tool) {
1169
- for (const hook of this.hooks.afterTool) {
1170
- const after = (await hook({
1171
- snapshot: this.snapshot(),
1172
- tool: prepared.tool,
1173
- toolCall: prepared.toolCall,
1174
- input: prepared.input,
1175
- result,
1176
- startedAt,
1177
- endedAt,
1178
- durationMs,
1179
- })) as AgentAfterToolResult | undefined;
1180
- this.applyStopControl(after);
1181
- if (after?.result) {
1182
- result = after.result;
1183
- }
1184
- }
1185
- }
1186
-
1187
- const message = createMessage("tool", [
1188
- {
1189
- type: "tool-result",
1190
- toolCallId: prepared.toolCall.toolCallId,
1191
- toolName: prepared.toolCall.toolName,
1192
- output: result.output,
1193
- isError: result.isError,
1194
- },
1195
- ]);
1196
-
1197
- await this.emit({
1198
- type: "tool-finished",
1199
- snapshot: this.snapshot(),
1200
- iteration: this.state.iteration,
1201
- toolCall: prepared.toolCall,
1202
- message,
1203
- });
1204
-
1205
- return message;
1206
- }
1207
-
1208
- private finishRun(
1209
- status: AgentRunResult["status"],
1210
- assistantMessage?: AgentMessage,
1211
- outputText?: string,
1212
- ): AgentRunResult {
1213
- this.state.status = status;
1214
- return {
1215
- agentId: this.state.agentId,
1216
- agentRole: this.state.agentRole,
1217
- runId: this.state.runId ?? createUID("run"),
1218
- status,
1219
- iterations: this.state.iteration,
1220
- outputText:
1221
- outputText ??
1222
- textFromMessage(assistantMessage ?? this.findLastAssistantMessage()),
1223
- messages: cloneMessages(this.state.messages),
1224
- usage: cloneUsage(this.state.usage),
1225
- };
1226
- }
1227
-
1228
- private findLastAssistantMessage(): AgentMessage | undefined {
1229
- return [...this.state.messages]
1230
- .reverse()
1231
- .find((message) => message.role === "assistant");
1232
- }
1233
-
1234
- private throwIfAborted(): void {
1235
- if (this.abortController?.signal.aborted) {
1236
- throw this.normalizeAbortError();
1237
- }
1238
- }
1239
-
1240
- private normalizeAbortError(): Error {
1241
- const reason = this.abortController?.signal.reason;
1242
- if (reason instanceof Error) {
1243
- return reason;
1244
- }
1245
- if (typeof reason === "string") {
1246
- return new Error(reason);
1247
- }
1248
- return new Error(this.state.lastError ?? "Run aborted");
1249
- }
1250
-
1251
- private async emit(event: AgentRuntimeEvent): Promise<void> {
1252
- const metadata = buildEventMetadata(event);
1253
- switch (event.type) {
1254
- case "run-started":
1255
- // Verbatim clinee calls `logger?.info?.(...)`. sdk-re's
1256
- // `BasicLogger` does not declare `info` (it uses `log`), so
1257
- // we narrow to an optional-info shape at the call site to
1258
- // preserve the clinee runtime contract without mutating
1259
- // shared's `BasicLogger` interface.
1260
- (
1261
- this.config.logger as
1262
- | {
1263
- info?: (msg: string, md?: unknown) => void;
1264
- }
1265
- | undefined
1266
- )?.info?.("Agent run started", metadata);
1267
- break;
1268
- case "tool-finished":
1269
- (
1270
- this.config.logger as
1271
- | {
1272
- info?: (msg: string, md?: unknown) => void;
1273
- }
1274
- | undefined
1275
- )?.info?.("Agent tool finished", metadata);
1276
- break;
1277
- case "run-failed":
1278
- this.config.logger?.error?.("Agent run failed", {
1279
- ...metadata,
1280
- error: event.error,
1281
- });
1282
- break;
1283
- default:
1284
- this.config.logger?.debug?.("Agent event", metadata);
1285
- break;
1286
- }
1287
- void this.config.telemetry?.capture?.(`agent.${event.type}`, metadata);
1288
- for (const listener of this.listeners) {
1289
- listener(event);
1290
- }
1291
- for (const hook of this.hooks.onEvent) {
1292
- await hook(event);
1293
- }
1294
- }
1295
-
1296
- private applyStopControl(
1297
- control: AgentStopControl | undefined | undefined,
1298
- ): void {
1299
- if (!control?.stop) {
1300
- return;
1301
- }
1302
- if (control.reason) {
1303
- this.state.lastError = control.reason;
1304
- }
1305
- throw new ControlledStopError(control.reason);
1306
- }
1307
- }
1308
-
1309
- function buildEventMetadata(event: AgentRuntimeEvent): Record<string, unknown> {
1310
- return {
1311
- agentId: event.snapshot.agentId,
1312
- agentRole: event.snapshot.agentRole,
1313
- runId: event.snapshot.runId,
1314
- status: event.snapshot.status,
1315
- iteration: event.snapshot.iteration,
1316
- eventType: event.type,
1317
- };
1318
- }
1319
-
1320
- function mergeToolMetadata(current: unknown, patch: unknown): unknown {
1321
- if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
1322
- return patch;
1323
- }
1324
- if (!current || typeof current !== "object" || Array.isArray(current)) {
1325
- return patch;
1326
- }
1327
- return {
1328
- ...(current as Record<string, unknown>),
1329
- ...patch,
1330
- };
1331
- }
1332
-
1333
- function parseToolInput(assembly: PendingToolAssembly): {
1334
- input: unknown;
1335
- parseError?: string;
1336
- invalidInput: Record<string, unknown>;
1337
- reason?: InvalidToolCall["reason"];
1338
- } {
1339
- if (assembly.inputValue !== undefined) {
1340
- return {
1341
- input: assembly.inputValue,
1342
- invalidInput: buildInvalidToolInput(JSON.stringify(assembly.inputValue)),
1343
- };
1344
- }
1345
- if (!assembly.inputText.trim()) {
1346
- return {
1347
- input: {},
1348
- invalidInput: {},
1349
- };
1350
- }
1351
- const parsed = parseToolArguments(assembly.inputText);
1352
- if (parsed.ok) {
1353
- return {
1354
- input: parsed.value,
1355
- invalidInput: buildInvalidToolInput(assembly.inputText),
1356
- };
1357
- }
1358
- return {
1359
- input: {},
1360
- invalidInput: buildInvalidToolInput(assembly.inputText, parsed.error),
1361
- parseError: `Tool call ${assembly.toolName ?? assembly.toolCallId} emitted invalid JSON arguments: ${parsed.error}`,
1362
- reason: "invalid_arguments",
1363
- };
1364
- }
1365
-
1366
- function buildInvalidToolInput(
1367
- value: string,
1368
- parseError?: string,
1369
- ): Record<string, unknown> {
1370
- const trimmed = value.trim();
1371
- if (!trimmed) {
1372
- return {};
1373
- }
1374
- return parseError
1375
- ? { rawInputText: value, parseError }
1376
- : { rawInputText: value };
1377
- }
1378
-
1379
- function parseToolArguments(
1380
- value: string,
1381
- ): { ok: true; value: unknown } | { ok: false; error: string } {
1382
- const trimmed = value.trim();
1383
- if (!trimmed) {
1384
- return {
1385
- ok: false,
1386
- error: "Tool call arguments were empty.",
1387
- };
1388
- }
1389
-
1390
- try {
1391
- return { ok: true, value: JSON.parse(trimmed) };
1392
- } catch {
1393
- // Fall through to a normalized error below.
1394
- }
1395
-
1396
- if (!(trimmed.startsWith("{") || trimmed.startsWith("["))) {
1397
- return {
1398
- ok: false,
1399
- error: "Tool call arguments must be encoded as a JSON object or array.",
1400
- };
1401
- }
1402
-
1403
- return {
1404
- ok: false,
1405
- error:
1406
- "Tool call arguments could not be parsed as JSON. Ensure the outer tool payload is valid JSON and escape embedded quotes/newlines inside string fields.",
1407
- };
1408
- }
1409
-
1410
- function mergeToolInputText(current: string, incoming: string): string {
1411
- if (!current) {
1412
- return incoming;
1413
- }
1414
- const trimmed = incoming.trimStart();
1415
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
1416
- return incoming;
1417
- }
1418
- return current + incoming;
1419
- }
1420
-
1421
- export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
1422
- return new AgentRuntime(config);
1423
- }
1424
-
1425
- /**
1426
- * `Agent` is the user-friendly name for `AgentRuntime`. They are the same
1427
- * class; this alias exists so standalone callers can write:
1428
- *
1429
- * const agent = new Agent({ providerId, modelId, apiKey });
1430
- * await agent.run("hello");
1431
- *
1432
- * while `@cline/core` (which owns model construction) continues to use
1433
- * the `AgentRuntime` name with `{ model, ... }` configs.
1434
- */
1435
- export const Agent = AgentRuntime;
1436
- export type Agent = AgentRuntime;
1437
-
1438
- export function createAgent(config: AgentRuntimeConfig): AgentRuntime {
1439
- return new AgentRuntime(config);
1440
- }