@dreb/agent-core 2.23.0 → 2.24.0

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/README.md CHANGED
@@ -131,6 +131,7 @@ The last message in context must be `user` or `toolResult` (not `assistant`).
131
131
  | `message_update` | **Assistant only.** Includes `assistantMessageEvent` with delta |
132
132
  | `message_end` | Message completes |
133
133
  | `stream_retry` | Provider stream dropped; retrying after discarding partial response |
134
+ | `length_retry` | Response truncated at the token limit; retrying with a larger token budget |
134
135
  | `tool_execution_start` | Tool begins |
135
136
  | `tool_execution_update` | Tool streams progress |
136
137
  | `tool_execution_end` | Tool completes |
@@ -196,6 +197,15 @@ const agent = new Agent({
196
197
  // Base delay for exponential backoff between stream retries in ms (default: 1000)
197
198
  streamRetryBaseDelayMs: 1000,
198
199
 
200
+ // Length retry: number of retries when a turn ends with stopReason "length"
201
+ // (model exhausted its output token budget). Each retry escalates maxTokens.
202
+ // Separate from streamRetries (default: 2). Set to 0 to disable.
203
+ lengthRetries: 2,
204
+
205
+ // Factor by which to multiply maxTokens on each length retry, clamped to the
206
+ // model's output ceiling (default: 2)
207
+ lengthRetryBudgetMultiplier: 2,
208
+
199
209
  // Custom thinking budgets for token-based providers
200
210
  thinkingBudgets: {
201
211
  minimal: 128,
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGN,WAAW,EAIX,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EAIZ,QAAQ,EACR,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAiBzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAwBzC;AAED,wBAAsB,YAAY,CACjC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBzB","sourcesContent":["/**\n * Agent loop that works with AgentMessage throughout.\n * Transforms to Message[] only at the LLM call boundary.\n */\n\nimport {\n\ttype AssistantMessage,\n\ttype Context,\n\tEventStream,\n\tstreamSimple,\n\ttype ToolResultMessage,\n\tvalidateToolArguments,\n} from \"@dreb/ai\";\nimport type {\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentTool,\n\tAgentToolCall,\n\tAgentToolResult,\n\tStreamFn,\n} from \"./types.js\";\n\nexport type AgentEventSink = (event: AgentEvent) => Promise<void> | void;\n\n/**\n * Start an agent loop with a new prompt message.\n * The prompt is added to the context and events are emitted for it.\n */\nexport function agentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoop(\n\t\tprompts,\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\n/**\n * Continue an agent loop from the current context without adding a new message.\n * Used for retries - context already has user message or tool results.\n *\n * **Important:** The last message in context must convert to a `user` or `toolResult` message\n * via `convertToLlm`. If it doesn't, the LLM provider will reject the request.\n * This cannot be validated here since `convertToLlm` is only called once per turn.\n */\nexport function agentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoopContinue(\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\nexport async function runAgentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tconst newMessages: AgentMessage[] = [...prompts];\n\tconst currentContext: AgentContext = {\n\t\t...context,\n\t\tmessages: [...context.messages, ...prompts],\n\t};\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\tfor (const prompt of prompts) {\n\t\tawait emit({ type: \"message_start\", message: prompt });\n\t\tawait emit({ type: \"message_end\", message: prompt });\n\t}\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nexport async function runAgentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst newMessages: AgentMessage[] = [];\n\tconst currentContext: AgentContext = { ...context };\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nfunction createAgentStream(): EventStream<AgentEvent, AgentMessage[]> {\n\treturn new EventStream<AgentEvent, AgentMessage[]>(\n\t\t(event: AgentEvent) => event.type === \"agent_end\",\n\t\t(event: AgentEvent) => (event.type === \"agent_end\" ? event.messages : []),\n\t);\n}\n\n/**\n * Main loop logic shared by agentLoop and agentLoopContinue.\n */\nasync function runLoop(\n\tcurrentContext: AgentContext,\n\tnewMessages: AgentMessage[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<void> {\n\tlet firstTurn = true;\n\tlet endTurnRequested = false;\n\tlet llmCallCount = 0;\n\t// Check for steering messages at start (user may have typed while waiting)\n\tlet pendingMessages: AgentMessage[] = (await config.getSteeringMessages?.()) || [];\n\n\t// Outer loop: continues when queued follow-up messages arrive after agent would stop\n\twhile (true) {\n\t\tlet hasMoreToolCalls = true;\n\t\tendTurnRequested = false;\n\n\t\t// Inner loop: process tool calls and steering messages\n\t\twhile (hasMoreToolCalls || pendingMessages.length > 0) {\n\t\t\t// Process pending messages first (inject into context so they're never lost).\n\t\t\t// This happens before turn_start and shouldContinue so messages are always\n\t\t\t// preserved regardless of whether the turn proceeds.\n\t\t\tif (pendingMessages.length > 0) {\n\t\t\t\tfor (const message of pendingMessages) {\n\t\t\t\t\tawait emit({ type: \"message_start\", message });\n\t\t\t\t\tawait emit({ type: \"message_end\", message });\n\t\t\t\t\tcurrentContext.messages.push(message);\n\t\t\t\t\tnewMessages.push(message);\n\t\t\t\t}\n\t\t\t\tpendingMessages = [];\n\t\t\t}\n\n\t\t\t// Check shouldContinue before starting a new turn (not the very first).\n\t\t\t// This runs before turn_start so we never emit an orphaned turn_start.\n\t\t\tif (llmCallCount > 0 && config.shouldContinue && !config.shouldContinue()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!firstTurn) {\n\t\t\t\tawait emit({ type: \"turn_start\" });\n\t\t\t} else {\n\t\t\t\tfirstTurn = false;\n\t\t\t}\n\n\t\t\t// Stream assistant response\n\t\t\tconst message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn);\n\t\t\tnewMessages.push(message);\n\t\t\tllmCallCount++;\n\n\t\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") {\n\t\t\t\tawait emit({ type: \"turn_end\", message, toolResults: [] });\n\t\t\t\tawait emit({ type: \"agent_end\", messages: newMessages });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check for tool calls\n\t\t\tconst toolCalls = message.content.filter((c) => c.type === \"toolCall\");\n\t\t\thasMoreToolCalls = toolCalls.length > 0;\n\n\t\t\tconst toolResults: ToolResultMessage[] = [];\n\t\t\tif (hasMoreToolCalls) {\n\t\t\t\tconst executionResult = await executeToolCalls(currentContext, message, config, signal, emit);\n\t\t\t\ttoolResults.push(...executionResult.results);\n\n\t\t\t\tfor (const result of toolResults) {\n\t\t\t\t\tcurrentContext.messages.push(result);\n\t\t\t\t\tnewMessages.push(result);\n\t\t\t\t}\n\n\t\t\t\tif (executionResult.endTurn) {\n\t\t\t\t\tendTurnRequested = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait emit({ type: \"turn_end\", message, toolResults });\n\n\t\t\t// If a tool requested endTurn, stop the loop (skip further LLM calls)\n\t\t\tif (endTurnRequested) {\n\t\t\t\thasMoreToolCalls = false;\n\t\t\t\tpendingMessages = [];\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpendingMessages = (await config.getSteeringMessages?.()) || [];\n\t\t}\n\n\t\t// Agent would stop here. Drain any steering messages that arrived during\n\t\t// wind-down (e.g., background agent results delivered via steer() between\n\t\t// endTurn break and here). Then check for follow-up messages.\n\t\tconst leftoverSteering = (await config.getSteeringMessages?.()) || [];\n\t\tconst followUpMessages = (await config.getFollowUpMessages?.()) || [];\n\t\tconst allPending = [...leftoverSteering, ...followUpMessages];\n\t\tif (allPending.length > 0) {\n\t\t\t// Set as pending so inner loop processes them\n\t\t\tpendingMessages = allPending;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// No more messages, exit\n\t\tbreak;\n\t}\n\n\tawait emit({ type: \"agent_end\", messages: newMessages });\n}\n\n/**\n * Check if an error is a stream-drop error (connection dropped before terminal event).\n * Only these errors trigger automatic retry — all other errors propagate as-is.\n */\nfunction isStreamDropError(error: unknown): boolean {\n\tconst msg = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\treturn (\n\t\tmsg.includes(\"Stream ended without\") ||\n\t\tmsg.includes(\"connection likely dropped\") ||\n\t\tmsg.includes(\"WebSocket stream closed before\")\n\t);\n}\n\n/**\n * Sleep for a duration, aborting early if the signal fires.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tif (signal?.aborted) {\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\t\tconst timer = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Stream an assistant response from the LLM.\n * This is where AgentMessage[] gets transformed to Message[] for the LLM.\n */\nasync function streamAssistantResponse(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<AssistantMessage> {\n\t// Apply context transform if configured (AgentMessage[] → AgentMessage[])\n\tlet messages = context.messages;\n\tif (config.transformContext) {\n\t\tmessages = await config.transformContext(messages, signal);\n\t}\n\n\t// Convert to LLM-compatible messages (AgentMessage[] → Message[])\n\tconst llmMessages = await config.convertToLlm(messages);\n\n\t// Build LLM context\n\tconst llmContext: Context = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\tmessages: llmMessages,\n\t\ttools: context.tools,\n\t};\n\n\tconst streamFunction = streamFn || streamSimple;\n\n\t// Resolve API key (important for expiring tokens)\n\tconst resolvedApiKey =\n\t\t(config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;\n\n\tconst streamStart = performance.now();\n\tlet partialMessage: AssistantMessage | null = null;\n\tlet addedPartial = false;\n\n\tconst finalizeMessage = async (finalMessage: AssistantMessage): Promise<AssistantMessage> => {\n\t\tfinalMessage.durationMs = Math.max(0, performance.now() - streamStart);\n\t\tif (addedPartial) {\n\t\t\tcontext.messages[context.messages.length - 1] = finalMessage;\n\t\t} else {\n\t\t\tcontext.messages.push(finalMessage);\n\t\t}\n\t\tif (!addedPartial) {\n\t\t\tawait emit({ type: \"message_start\", message: { ...finalMessage } });\n\t\t}\n\t\tawait emit({ type: \"message_end\", message: finalMessage });\n\t\treturn finalMessage;\n\t};\n\n\tconst createErrorMessage = (error: unknown): AssistantMessage => ({\n\t\trole: \"assistant\",\n\t\tcontent: partialMessage?.content ?? [{ type: \"text\", text: \"\" }],\n\t\tapi: config.model.api,\n\t\tprovider: config.model.provider,\n\t\tmodel: config.model.id,\n\t\tusage: partialMessage?.usage ?? {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: signal?.aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: partialMessage?.timestamp ?? Date.now(),\n\t});\n\n\tconst maxRetries = config.streamRetries ?? 3;\n\tconst retryBaseDelay = config.streamRetryBaseDelayMs ?? 1000;\n\tconst clonePartialForDebug = (): AssistantMessage | undefined => {\n\t\tif (!partialMessage) return undefined;\n\t\treturn {\n\t\t\t...partialMessage,\n\t\t\tcontent: partialMessage.content.map((block) => ({ ...block })),\n\t\t\tusage: { ...partialMessage.usage, cost: { ...partialMessage.usage.cost } },\n\t\t};\n\t};\n\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\tlet response: Awaited<ReturnType<StreamFn>>;\n\t\ttry {\n\t\t\tresponse = await streamFunction(config.model, llmContext, {\n\t\t\t\t...config,\n\t\t\t\tapiKey: resolvedApiKey,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && attempt < maxRetries && !signal?.aborted) {\n\t\t\t\tawait emit({\n\t\t\t\t\ttype: \"stream_retry\",\n\t\t\t\t\tattempt: attempt + 1,\n\t\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t\t});\n\t\t\t\tawait sleep(retryBaseDelay * 2 ** attempt, signal);\n\t\t\t\tif (addedPartial) {\n\t\t\t\t\tcontext.messages.pop();\n\t\t\t\t\taddedPartial = false;\n\t\t\t\t}\n\t\t\t\tpartialMessage = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t: error instanceof Error\n\t\t\t\t\t? error\n\t\t\t\t\t: new Error(String(error));\n\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t}\n\n\t\tlet shouldRetry = false;\n\t\tlet retryError = \"\";\n\t\ttry {\n\t\t\tfor await (const event of response) {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\tcontext.messages.push(partialMessage);\n\t\t\t\t\t\taddedPartial = true;\n\t\t\t\t\t\tawait emit({ type: \"message_start\", message: { ...partialMessage } });\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\tcase \"text_delta\":\n\t\t\t\t\tcase \"text_end\":\n\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\tcase \"thinking_delta\":\n\t\t\t\t\tcase \"thinking_end\":\n\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\tcase \"toolcall_delta\":\n\t\t\t\t\tcase \"toolcall_end\":\n\t\t\t\t\t\tif (partialMessage) {\n\t\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\t\tcontext.messages[context.messages.length - 1] = partialMessage;\n\t\t\t\t\t\t\tawait emit({\n\t\t\t\t\t\t\t\ttype: \"message_update\",\n\t\t\t\t\t\t\t\tassistantMessageEvent: event,\n\t\t\t\t\t\t\t\tmessage: { ...partialMessage },\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"done\":\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst result = await response.result();\n\t\t\t\t\t\t// Check if this is a stream-drop error that should be retried\n\t\t\t\t\t\tconst isDrop = result.stopReason === \"error\" && isStreamDropError(result.errorMessage);\n\t\t\t\t\t\tif (isDrop && attempt < maxRetries && !signal?.aborted) {\n\t\t\t\t\t\t\tshouldRetry = true;\n\t\t\t\t\t\t\tretryError =\n\t\t\t\t\t\t\t\tresult.errorMessage ?? \"Stream ended without terminal event — connection likely dropped\";\n\t\t\t\t\t\t} else if (isDrop) {\n\t\t\t\t\t\t\t// Final attempt exhausted — surface friendly message\n\t\t\t\t\t\t\treturn finalizeMessage(\n\t\t\t\t\t\t\t\tcreateErrorMessage(new Error(\"Stream dropped repeatedly — connection likely unstable\")),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn finalizeMessage(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (shouldRetry) break;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && attempt < maxRetries && !signal?.aborted) {\n\t\t\t\tshouldRetry = true;\n\t\t\t\tretryError = error instanceof Error ? error.message : String(error);\n\t\t\t} else {\n\t\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t\t: error instanceof Error\n\t\t\t\t\t\t? error\n\t\t\t\t\t\t: new Error(String(error));\n\t\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t\t}\n\t\t}\n\n\t\tif (shouldRetry) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"stream_retry\",\n\t\t\t\tattempt: attempt + 1,\n\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\terror: retryError,\n\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t});\n\t\t\tawait sleep(retryBaseDelay * 2 ** attempt, signal);\n\t\t\tif (addedPartial) {\n\t\t\t\tcontext.messages.pop();\n\t\t\t\taddedPartial = false;\n\t\t\t}\n\t\t\tpartialMessage = null;\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn finalizeMessage(await response.result());\n\t}\n\n\treturn finalizeMessage(createErrorMessage(new Error(\"Stream dropped repeatedly — connection likely unstable\")));\n}\n\ninterface ToolExecutionResult {\n\tresults: ToolResultMessage[];\n\tendTurn: boolean;\n}\n\n/**\n * Execute tool calls from an assistant message.\n */\nasync function executeToolCalls(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\tif (config.toolExecution === \"sequential\") {\n\t\treturn executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit);\n\t}\n\treturn executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, signal, emit);\n}\n\nasync function executeToolCallsSequential(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\tconst executed = await executePreparedToolCall(preparation, signal, emit);\n\t\t\tif (executed.result.endTurn) endTurn = true;\n\t\t\tresults.push(\n\t\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\t\tcurrentContext,\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\tpreparation,\n\t\t\t\t\texecuted,\n\t\t\t\t\tconfig,\n\t\t\t\t\tsignal,\n\t\t\t\t\temit,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { results, endTurn };\n}\n\nasync function executeToolCallsParallel(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tconst runnableCalls: PreparedToolCall[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\trunnableCalls.push(preparation);\n\t\t}\n\t}\n\n\tconst runningCalls = runnableCalls.map((prepared) => ({\n\t\tprepared,\n\t\texecution: executePreparedToolCall(prepared, signal, emit),\n\t}));\n\n\tfor (const running of runningCalls) {\n\t\tconst executed = await running.execution;\n\t\tif (executed.result.endTurn) endTurn = true;\n\t\tresults.push(\n\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\tcurrentContext,\n\t\t\t\tassistantMessage,\n\t\t\t\trunning.prepared,\n\t\t\t\texecuted,\n\t\t\t\tconfig,\n\t\t\t\tsignal,\n\t\t\t\temit,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn { results, endTurn };\n}\n\ntype PreparedToolCall = {\n\tkind: \"prepared\";\n\ttoolCall: AgentToolCall;\n\ttool: AgentTool<any>;\n\targs: unknown;\n};\n\ntype ImmediateToolCallOutcome = {\n\tkind: \"immediate\";\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\ntype ExecutedToolCallOutcome = {\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\nasync function prepareToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCall: AgentToolCall,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n): Promise<PreparedToolCall | ImmediateToolCallOutcome> {\n\tconst tool = currentContext.tools?.find((t) => t.name === toolCall.name);\n\tif (!tool) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(`Tool ${toolCall.name} not found`),\n\t\t\tisError: true,\n\t\t};\n\t}\n\n\ttry {\n\t\tconst validatedArgs = validateToolArguments(tool, toolCall);\n\t\tif (config.beforeToolCall) {\n\t\t\tconst beforeResult = await config.beforeToolCall(\n\t\t\t\t{\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\ttoolCall,\n\t\t\t\t\targs: validatedArgs,\n\t\t\t\t\tcontext: currentContext,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (beforeResult?.block) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"immediate\",\n\t\t\t\t\tresult: createErrorToolResult(beforeResult.reason || \"Tool execution was blocked\"),\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tkind: \"prepared\",\n\t\t\ttoolCall,\n\t\t\ttool,\n\t\t\targs: validatedArgs,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function executePreparedToolCall(\n\tprepared: PreparedToolCall,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ExecutedToolCallOutcome> {\n\tconst updateEvents: Promise<void>[] = [];\n\n\ttry {\n\t\tconst result = await prepared.tool.execute(\n\t\t\tprepared.toolCall.id,\n\t\t\tprepared.args as never,\n\t\t\tsignal,\n\t\t\t(partialResult) => {\n\t\t\t\tupdateEvents.push(\n\t\t\t\t\tPromise.resolve(\n\t\t\t\t\t\temit({\n\t\t\t\t\t\t\ttype: \"tool_execution_update\",\n\t\t\t\t\t\t\ttoolCallId: prepared.toolCall.id,\n\t\t\t\t\t\t\ttoolName: prepared.toolCall.name,\n\t\t\t\t\t\t\targs: prepared.toolCall.arguments,\n\t\t\t\t\t\t\tpartialResult,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t\tawait Promise.all(updateEvents);\n\t\treturn { result, isError: false };\n\t} catch (error) {\n\t\tawait Promise.all(updateEvents);\n\t\treturn {\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function finalizeExecutedToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tprepared: PreparedToolCall,\n\texecuted: ExecutedToolCallOutcome,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\n\tif (config.afterToolCall) {\n\t\tconst afterResult = await config.afterToolCall(\n\t\t\t{\n\t\t\t\tassistantMessage,\n\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\targs: prepared.args,\n\t\t\t\tresult,\n\t\t\t\tisError,\n\t\t\t\tcontext: currentContext,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tif (afterResult) {\n\t\t\tresult = {\n\t\t\t\tcontent: afterResult.content ?? result.content,\n\t\t\t\tdetails: afterResult.details ?? result.details,\n\t\t\t};\n\t\t\tisError = afterResult.isError ?? isError;\n\t\t}\n\t}\n\n\treturn await emitToolCallOutcome(prepared.toolCall, result, isError, emit);\n}\n\nfunction createErrorToolResult(message: string): AgentToolResult<any> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text: message }],\n\t\tdetails: {},\n\t};\n}\n\nasync function emitToolCallOutcome(\n\ttoolCall: AgentToolCall,\n\tresult: AgentToolResult<any>,\n\tisError: boolean,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tawait emit({\n\t\ttype: \"tool_execution_end\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tresult,\n\t\tisError,\n\t});\n\n\tconst toolResultMessage: ToolResultMessage = {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tcontent: result.content,\n\t\tdetails: result.details,\n\t\tisError,\n\t\ttimestamp: Date.now(),\n\t};\n\n\tawait emit({ type: \"message_start\", message: toolResultMessage });\n\tawait emit({ type: \"message_end\", message: toolResultMessage });\n\treturn toolResultMessage;\n}\n"]}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAIN,WAAW,EAIX,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EACf,YAAY,EAIZ,QAAQ,EACR,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAiBzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAwBzC;AAED,wBAAsB,YAAY,CACjC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBzB","sourcesContent":["/**\n * Agent loop that works with AgentMessage throughout.\n * Transforms to Message[] only at the LLM call boundary.\n */\n\nimport {\n\ttype AssistantMessage,\n\ttype Context,\n\tDEFAULT_MAX_OUTPUT_TOKENS,\n\tEventStream,\n\tstreamSimple,\n\ttype ToolResultMessage,\n\tvalidateToolArguments,\n} from \"@dreb/ai\";\nimport type {\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentTool,\n\tAgentToolCall,\n\tAgentToolResult,\n\tStreamFn,\n} from \"./types.js\";\n\nexport type AgentEventSink = (event: AgentEvent) => Promise<void> | void;\n\n/**\n * Start an agent loop with a new prompt message.\n * The prompt is added to the context and events are emitted for it.\n */\nexport function agentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoop(\n\t\tprompts,\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\n/**\n * Continue an agent loop from the current context without adding a new message.\n * Used for retries - context already has user message or tool results.\n *\n * **Important:** The last message in context must convert to a `user` or `toolResult` message\n * via `convertToLlm`. If it doesn't, the LLM provider will reject the request.\n * This cannot be validated here since `convertToLlm` is only called once per turn.\n */\nexport function agentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoopContinue(\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\nexport async function runAgentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tconst newMessages: AgentMessage[] = [...prompts];\n\tconst currentContext: AgentContext = {\n\t\t...context,\n\t\tmessages: [...context.messages, ...prompts],\n\t};\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\tfor (const prompt of prompts) {\n\t\tawait emit({ type: \"message_start\", message: prompt });\n\t\tawait emit({ type: \"message_end\", message: prompt });\n\t}\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nexport async function runAgentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst newMessages: AgentMessage[] = [];\n\tconst currentContext: AgentContext = { ...context };\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nfunction createAgentStream(): EventStream<AgentEvent, AgentMessage[]> {\n\treturn new EventStream<AgentEvent, AgentMessage[]>(\n\t\t(event: AgentEvent) => event.type === \"agent_end\",\n\t\t(event: AgentEvent) => (event.type === \"agent_end\" ? event.messages : []),\n\t);\n}\n\n/**\n * Main loop logic shared by agentLoop and agentLoopContinue.\n */\nasync function runLoop(\n\tcurrentContext: AgentContext,\n\tnewMessages: AgentMessage[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<void> {\n\tlet firstTurn = true;\n\tlet endTurnRequested = false;\n\tlet llmCallCount = 0;\n\t// Check for steering messages at start (user may have typed while waiting)\n\tlet pendingMessages: AgentMessage[] = (await config.getSteeringMessages?.()) || [];\n\n\t// Outer loop: continues when queued follow-up messages arrive after agent would stop\n\twhile (true) {\n\t\tlet hasMoreToolCalls = true;\n\t\tendTurnRequested = false;\n\n\t\t// Inner loop: process tool calls and steering messages\n\t\twhile (hasMoreToolCalls || pendingMessages.length > 0) {\n\t\t\t// Process pending messages first (inject into context so they're never lost).\n\t\t\t// This happens before turn_start and shouldContinue so messages are always\n\t\t\t// preserved regardless of whether the turn proceeds.\n\t\t\tif (pendingMessages.length > 0) {\n\t\t\t\tfor (const message of pendingMessages) {\n\t\t\t\t\tawait emit({ type: \"message_start\", message });\n\t\t\t\t\tawait emit({ type: \"message_end\", message });\n\t\t\t\t\tcurrentContext.messages.push(message);\n\t\t\t\t\tnewMessages.push(message);\n\t\t\t\t}\n\t\t\t\tpendingMessages = [];\n\t\t\t}\n\n\t\t\t// Check shouldContinue before starting a new turn (not the very first).\n\t\t\t// This runs before turn_start so we never emit an orphaned turn_start.\n\t\t\tif (llmCallCount > 0 && config.shouldContinue && !config.shouldContinue()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!firstTurn) {\n\t\t\t\tawait emit({ type: \"turn_start\" });\n\t\t\t} else {\n\t\t\t\tfirstTurn = false;\n\t\t\t}\n\n\t\t\t// Stream assistant response\n\t\t\tconst message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn);\n\t\t\tnewMessages.push(message);\n\t\t\tllmCallCount++;\n\n\t\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") {\n\t\t\t\tawait emit({ type: \"turn_end\", message, toolResults: [] });\n\t\t\t\tawait emit({ type: \"agent_end\", messages: newMessages });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check for tool calls\n\t\t\tconst toolCalls = message.content.filter((c) => c.type === \"toolCall\");\n\t\t\thasMoreToolCalls = toolCalls.length > 0;\n\n\t\t\tconst toolResults: ToolResultMessage[] = [];\n\t\t\tif (hasMoreToolCalls) {\n\t\t\t\tconst executionResult = await executeToolCalls(currentContext, message, config, signal, emit);\n\t\t\t\ttoolResults.push(...executionResult.results);\n\n\t\t\t\tfor (const result of toolResults) {\n\t\t\t\t\tcurrentContext.messages.push(result);\n\t\t\t\t\tnewMessages.push(result);\n\t\t\t\t}\n\n\t\t\t\tif (executionResult.endTurn) {\n\t\t\t\t\tendTurnRequested = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait emit({ type: \"turn_end\", message, toolResults });\n\n\t\t\t// If a tool requested endTurn, stop the loop (skip further LLM calls)\n\t\t\tif (endTurnRequested) {\n\t\t\t\thasMoreToolCalls = false;\n\t\t\t\tpendingMessages = [];\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpendingMessages = (await config.getSteeringMessages?.()) || [];\n\t\t}\n\n\t\t// Agent would stop here. Drain any steering messages that arrived during\n\t\t// wind-down (e.g., background agent results delivered via steer() between\n\t\t// endTurn break and here). Then check for follow-up messages.\n\t\tconst leftoverSteering = (await config.getSteeringMessages?.()) || [];\n\t\tconst followUpMessages = (await config.getFollowUpMessages?.()) || [];\n\t\tconst allPending = [...leftoverSteering, ...followUpMessages];\n\t\tif (allPending.length > 0) {\n\t\t\t// Set as pending so inner loop processes them\n\t\t\tpendingMessages = allPending;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// No more messages, exit\n\t\tbreak;\n\t}\n\n\tawait emit({ type: \"agent_end\", messages: newMessages });\n}\n\n/**\n * Check if an error is a stream-drop error (connection dropped before terminal event).\n * Only these errors trigger automatic retry — all other errors propagate as-is.\n */\nfunction isStreamDropError(error: unknown): boolean {\n\tconst msg = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\treturn (\n\t\tmsg.includes(\"Stream ended without\") ||\n\t\tmsg.includes(\"connection likely dropped\") ||\n\t\tmsg.includes(\"WebSocket stream closed before\")\n\t);\n}\n\n/**\n * Sleep for a duration, aborting early if the signal fires.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tif (signal?.aborted) {\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\t\tconst timer = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Stream an assistant response from the LLM.\n * This is where AgentMessage[] gets transformed to Message[] for the LLM.\n */\nasync function streamAssistantResponse(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<AssistantMessage> {\n\t// Apply context transform if configured (AgentMessage[] → AgentMessage[])\n\tlet messages = context.messages;\n\tif (config.transformContext) {\n\t\tmessages = await config.transformContext(messages, signal);\n\t}\n\n\t// Convert to LLM-compatible messages (AgentMessage[] → Message[])\n\tconst llmMessages = await config.convertToLlm(messages);\n\n\t// Build LLM context\n\tconst llmContext: Context = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\tmessages: llmMessages,\n\t\ttools: context.tools,\n\t};\n\n\tconst streamFunction = streamFn || streamSimple;\n\n\t// Resolve API key (important for expiring tokens)\n\tconst resolvedApiKey =\n\t\t(config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;\n\n\tconst streamStart = performance.now();\n\tlet partialMessage: AssistantMessage | null = null;\n\tlet addedPartial = false;\n\n\tconst finalizeMessage = async (finalMessage: AssistantMessage): Promise<AssistantMessage> => {\n\t\tfinalMessage.durationMs = Math.max(0, performance.now() - streamStart);\n\t\tif (addedPartial) {\n\t\t\tcontext.messages[context.messages.length - 1] = finalMessage;\n\t\t} else {\n\t\t\tcontext.messages.push(finalMessage);\n\t\t}\n\t\tif (!addedPartial) {\n\t\t\tawait emit({ type: \"message_start\", message: { ...finalMessage } });\n\t\t}\n\t\tawait emit({ type: \"message_end\", message: finalMessage });\n\t\treturn finalMessage;\n\t};\n\n\tconst createErrorMessage = (error: unknown): AssistantMessage => ({\n\t\trole: \"assistant\",\n\t\tcontent: partialMessage?.content ?? [{ type: \"text\", text: \"\" }],\n\t\tapi: config.model.api,\n\t\tprovider: config.model.provider,\n\t\tmodel: config.model.id,\n\t\tusage: partialMessage?.usage ?? {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: signal?.aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: partialMessage?.timestamp ?? Date.now(),\n\t});\n\n\tconst maxRetries = config.streamRetries ?? 3;\n\tconst retryBaseDelay = config.streamRetryBaseDelayMs ?? 1000;\n\tconst lengthRetries = config.lengthRetries ?? 2;\n\tconst lengthMultiplier = config.lengthRetryBudgetMultiplier ?? 2;\n\t// The maxTokens budget requested for the current attempt. Starts at the\n\t// caller-configured budget (may be undefined → provider default) and is\n\t// escalated on each length retry. Kept separate from stream-drop retries.\n\tlet requestMaxTokens = config.maxTokens;\n\tlet lengthAttempts = 0;\n\tconst clonePartialForDebug = (): AssistantMessage | undefined => {\n\t\tif (!partialMessage) return undefined;\n\t\treturn {\n\t\t\t...partialMessage,\n\t\t\tcontent: partialMessage.content.map((block) => ({ ...block })),\n\t\t\tusage: { ...partialMessage.usage, cost: { ...partialMessage.usage.cost } },\n\t\t};\n\t};\n\n\t// Discard the truncated partial message (if any) before re-issuing a request.\n\tconst discardPartial = () => {\n\t\tif (addedPartial) {\n\t\t\tcontext.messages.pop();\n\t\t\taddedPartial = false;\n\t\t}\n\t\tpartialMessage = null;\n\t};\n\n\t// Compute the escalated token budget for a length retry, never exceeding the\n\t// model's ceiling. `current` is the *effective* budget that produced the\n\t// truncated response (resolved from the provider default when no explicit\n\t// maxTokens was set), so the escalation always requests strictly more.\n\tconst escalateLengthBudget = (current: number): number => {\n\t\tconst ceiling = config.model.maxTokens;\n\t\treturn Math.min(Math.ceil(current * lengthMultiplier), ceiling);\n\t};\n\n\t// Build the truncation failure message after length retries are exhausted.\n\tconst createLengthExhaustedMessage = (attempts: number): AssistantMessage => {\n\t\tconst errorMessage = createErrorMessage(\n\t\t\tnew Error(\n\t\t\t\t`Response truncated at token limit after ${attempts} attempt${attempts === 1 ? \"\" : \"s\"} — output exceeded the model's maximum token budget`,\n\t\t\t),\n\t\t);\n\t\t// Force \"error\" so the runLoop error guard terminates the turn loudly.\n\t\terrorMessage.stopReason = signal?.aborted ? \"aborted\" : \"error\";\n\t\treturn errorMessage;\n\t};\n\n\t// stream-drop retries are counted separately from length retries. A length retry\n\t// is a fresh request, so it resets the stream-drop attempt counter.\n\tlet streamDropAttempt = 0;\n\twhile (true) {\n\t\tlet response: Awaited<ReturnType<StreamFn>>;\n\t\ttry {\n\t\t\tresponse = await streamFunction(config.model, llmContext, {\n\t\t\t\t...config,\n\t\t\t\tmaxTokens: requestMaxTokens,\n\t\t\t\tapiKey: resolvedApiKey,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && streamDropAttempt < maxRetries && !signal?.aborted) {\n\t\t\t\tawait emit({\n\t\t\t\t\ttype: \"stream_retry\",\n\t\t\t\t\tattempt: streamDropAttempt + 1,\n\t\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t\t});\n\t\t\t\tawait sleep(retryBaseDelay * 2 ** streamDropAttempt, signal);\n\t\t\t\tdiscardPartial();\n\t\t\t\tstreamDropAttempt++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t: error instanceof Error\n\t\t\t\t\t? error\n\t\t\t\t\t: new Error(String(error));\n\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t}\n\n\t\tlet shouldRetry = false;\n\t\tlet retryError = \"\";\n\t\t// Set when the stream completes with a \"length\" result that warrants a budget escalation.\n\t\tlet lengthRetry: { previous: number; next: number } | null = null;\n\t\ttry {\n\t\t\tfor await (const event of response) {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\tcontext.messages.push(partialMessage);\n\t\t\t\t\t\taddedPartial = true;\n\t\t\t\t\t\tawait emit({ type: \"message_start\", message: { ...partialMessage } });\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\tcase \"text_delta\":\n\t\t\t\t\tcase \"text_end\":\n\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\tcase \"thinking_delta\":\n\t\t\t\t\tcase \"thinking_end\":\n\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\tcase \"toolcall_delta\":\n\t\t\t\t\tcase \"toolcall_end\":\n\t\t\t\t\t\tif (partialMessage) {\n\t\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\t\tcontext.messages[context.messages.length - 1] = partialMessage;\n\t\t\t\t\t\t\tawait emit({\n\t\t\t\t\t\t\t\ttype: \"message_update\",\n\t\t\t\t\t\t\t\tassistantMessageEvent: event,\n\t\t\t\t\t\t\t\tmessage: { ...partialMessage },\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"done\":\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst result = await response.result();\n\t\t\t\t\t\t// Check if this is a stream-drop error that should be retried\n\t\t\t\t\t\tconst isDrop = result.stopReason === \"error\" && isStreamDropError(result.errorMessage);\n\t\t\t\t\t\tif (isDrop && streamDropAttempt < maxRetries && !signal?.aborted) {\n\t\t\t\t\t\t\tshouldRetry = true;\n\t\t\t\t\t\t\tretryError =\n\t\t\t\t\t\t\t\tresult.errorMessage ?? \"Stream ended without terminal event — connection likely dropped\";\n\t\t\t\t\t\t} else if (isDrop) {\n\t\t\t\t\t\t\t// Final attempt exhausted — surface friendly message\n\t\t\t\t\t\t\treturn finalizeMessage(\n\t\t\t\t\t\t\t\tcreateErrorMessage(new Error(\"Stream dropped repeatedly — connection likely unstable\")),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (result.stopReason === \"length\") {\n\t\t\t\t\t\t\t// The model exhausted its output budget mid-response. Retry with a\n\t\t\t\t\t\t\t// larger budget if we have retries left, the budget can still grow,\n\t\t\t\t\t\t\t// and we have not been aborted. Otherwise, fail loudly below.\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// When no explicit maxTokens was set, the request used the provider\n\t\t\t\t\t\t\t// default budget — Math.min(model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS),\n\t\t\t\t\t\t\t// NOT the full model ceiling. Resolving the effective budget to the\n\t\t\t\t\t\t\t// real default lets escalation request strictly more, and lets the\n\t\t\t\t\t\t\t// ceiling guard correctly distinguish a default request (which can\n\t\t\t\t\t\t\t// still grow) from one already at the model ceiling.\n\t\t\t\t\t\t\tconst effectiveMaxTokens =\n\t\t\t\t\t\t\t\trequestMaxTokens ?? Math.min(config.model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS);\n\t\t\t\t\t\t\tconst atCeiling = effectiveMaxTokens >= config.model.maxTokens;\n\t\t\t\t\t\t\tif (lengthAttempts < lengthRetries && !atCeiling && !signal?.aborted) {\n\t\t\t\t\t\t\t\tlengthRetry = {\n\t\t\t\t\t\t\t\t\tprevious: effectiveMaxTokens,\n\t\t\t\t\t\t\t\t\tnext: escalateLengthBudget(effectiveMaxTokens),\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn finalizeMessage(createLengthExhaustedMessage(lengthAttempts + 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn finalizeMessage(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (shouldRetry || lengthRetry) break;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && streamDropAttempt < maxRetries && !signal?.aborted) {\n\t\t\t\tshouldRetry = true;\n\t\t\t\tretryError = error instanceof Error ? error.message : String(error);\n\t\t\t} else {\n\t\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t\t: error instanceof Error\n\t\t\t\t\t\t? error\n\t\t\t\t\t\t: new Error(String(error));\n\t\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t\t}\n\t\t}\n\n\t\tif (lengthRetry) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"length_retry\",\n\t\t\t\tattempt: lengthAttempts + 1,\n\t\t\t\tmaxAttempts: lengthRetries,\n\t\t\t\tpreviousMaxTokens: lengthRetry.previous,\n\t\t\t\tnextMaxTokens: lengthRetry.next,\n\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t});\n\t\t\tdiscardPartial();\n\t\t\trequestMaxTokens = lengthRetry.next;\n\t\t\tlengthAttempts++;\n\t\t\t// A length retry is a fresh request; reset the stream-drop counter.\n\t\t\tstreamDropAttempt = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (shouldRetry) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"stream_retry\",\n\t\t\t\tattempt: streamDropAttempt + 1,\n\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\terror: retryError,\n\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t});\n\t\t\tawait sleep(retryBaseDelay * 2 ** streamDropAttempt, signal);\n\t\t\tdiscardPartial();\n\t\t\tstreamDropAttempt++;\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn finalizeMessage(await response.result());\n\t}\n}\n\ninterface ToolExecutionResult {\n\tresults: ToolResultMessage[];\n\tendTurn: boolean;\n}\n\n/**\n * Execute tool calls from an assistant message.\n */\nasync function executeToolCalls(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\tif (config.toolExecution === \"sequential\") {\n\t\treturn executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit);\n\t}\n\treturn executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, signal, emit);\n}\n\nasync function executeToolCallsSequential(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\tconst executed = await executePreparedToolCall(preparation, signal, emit);\n\t\t\tif (executed.result.endTurn) endTurn = true;\n\t\t\tresults.push(\n\t\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\t\tcurrentContext,\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\tpreparation,\n\t\t\t\t\texecuted,\n\t\t\t\t\tconfig,\n\t\t\t\t\tsignal,\n\t\t\t\t\temit,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { results, endTurn };\n}\n\nasync function executeToolCallsParallel(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tconst runnableCalls: PreparedToolCall[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\trunnableCalls.push(preparation);\n\t\t}\n\t}\n\n\tconst runningCalls = runnableCalls.map((prepared) => ({\n\t\tprepared,\n\t\texecution: executePreparedToolCall(prepared, signal, emit),\n\t}));\n\n\tfor (const running of runningCalls) {\n\t\tconst executed = await running.execution;\n\t\tif (executed.result.endTurn) endTurn = true;\n\t\tresults.push(\n\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\tcurrentContext,\n\t\t\t\tassistantMessage,\n\t\t\t\trunning.prepared,\n\t\t\t\texecuted,\n\t\t\t\tconfig,\n\t\t\t\tsignal,\n\t\t\t\temit,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn { results, endTurn };\n}\n\ntype PreparedToolCall = {\n\tkind: \"prepared\";\n\ttoolCall: AgentToolCall;\n\ttool: AgentTool<any>;\n\targs: unknown;\n};\n\ntype ImmediateToolCallOutcome = {\n\tkind: \"immediate\";\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\ntype ExecutedToolCallOutcome = {\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\nasync function prepareToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCall: AgentToolCall,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n): Promise<PreparedToolCall | ImmediateToolCallOutcome> {\n\tconst tool = currentContext.tools?.find((t) => t.name === toolCall.name);\n\tif (!tool) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(`Tool ${toolCall.name} not found`),\n\t\t\tisError: true,\n\t\t};\n\t}\n\n\ttry {\n\t\tconst validatedArgs = validateToolArguments(tool, toolCall);\n\t\tif (config.beforeToolCall) {\n\t\t\tconst beforeResult = await config.beforeToolCall(\n\t\t\t\t{\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\ttoolCall,\n\t\t\t\t\targs: validatedArgs,\n\t\t\t\t\tcontext: currentContext,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (beforeResult?.block) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"immediate\",\n\t\t\t\t\tresult: createErrorToolResult(beforeResult.reason || \"Tool execution was blocked\"),\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tkind: \"prepared\",\n\t\t\ttoolCall,\n\t\t\ttool,\n\t\t\targs: validatedArgs,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function executePreparedToolCall(\n\tprepared: PreparedToolCall,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ExecutedToolCallOutcome> {\n\tconst updateEvents: Promise<void>[] = [];\n\n\ttry {\n\t\tconst result = await prepared.tool.execute(\n\t\t\tprepared.toolCall.id,\n\t\t\tprepared.args as never,\n\t\t\tsignal,\n\t\t\t(partialResult) => {\n\t\t\t\tupdateEvents.push(\n\t\t\t\t\tPromise.resolve(\n\t\t\t\t\t\temit({\n\t\t\t\t\t\t\ttype: \"tool_execution_update\",\n\t\t\t\t\t\t\ttoolCallId: prepared.toolCall.id,\n\t\t\t\t\t\t\ttoolName: prepared.toolCall.name,\n\t\t\t\t\t\t\targs: prepared.toolCall.arguments,\n\t\t\t\t\t\t\tpartialResult,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t\tawait Promise.all(updateEvents);\n\t\treturn { result, isError: false };\n\t} catch (error) {\n\t\tawait Promise.all(updateEvents);\n\t\treturn {\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function finalizeExecutedToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tprepared: PreparedToolCall,\n\texecuted: ExecutedToolCallOutcome,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\n\tif (config.afterToolCall) {\n\t\tconst afterResult = await config.afterToolCall(\n\t\t\t{\n\t\t\t\tassistantMessage,\n\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\targs: prepared.args,\n\t\t\t\tresult,\n\t\t\t\tisError,\n\t\t\t\tcontext: currentContext,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tif (afterResult) {\n\t\t\tresult = {\n\t\t\t\tcontent: afterResult.content ?? result.content,\n\t\t\t\tdetails: afterResult.details ?? result.details,\n\t\t\t};\n\t\t\tisError = afterResult.isError ?? isError;\n\t\t}\n\t}\n\n\treturn await emitToolCallOutcome(prepared.toolCall, result, isError, emit);\n}\n\nfunction createErrorToolResult(message: string): AgentToolResult<any> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text: message }],\n\t\tdetails: {},\n\t};\n}\n\nasync function emitToolCallOutcome(\n\ttoolCall: AgentToolCall,\n\tresult: AgentToolResult<any>,\n\tisError: boolean,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tawait emit({\n\t\ttype: \"tool_execution_end\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tresult,\n\t\tisError,\n\t});\n\n\tconst toolResultMessage: ToolResultMessage = {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tcontent: result.content,\n\t\tdetails: result.details,\n\t\tisError,\n\t\ttimestamp: Date.now(),\n\t};\n\n\tawait emit({ type: \"message_start\", message: toolResultMessage });\n\tawait emit({ type: \"message_end\", message: toolResultMessage });\n\treturn toolResultMessage;\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Agent loop that works with AgentMessage throughout.
3
3
  * Transforms to Message[] only at the LLM call boundary.
4
4
  */
5
- import { EventStream, streamSimple, validateToolArguments, } from "@dreb/ai";
5
+ import { DEFAULT_MAX_OUTPUT_TOKENS, EventStream, streamSimple, validateToolArguments, } from "@dreb/ai";
6
6
  /**
7
7
  * Start an agent loop with a new prompt message.
8
8
  * The prompt is added to the context and events are emitted for it.
@@ -242,6 +242,13 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
242
242
  });
243
243
  const maxRetries = config.streamRetries ?? 3;
244
244
  const retryBaseDelay = config.streamRetryBaseDelayMs ?? 1000;
245
+ const lengthRetries = config.lengthRetries ?? 2;
246
+ const lengthMultiplier = config.lengthRetryBudgetMultiplier ?? 2;
247
+ // The maxTokens budget requested for the current attempt. Starts at the
248
+ // caller-configured budget (may be undefined → provider default) and is
249
+ // escalated on each length retry. Kept separate from stream-drop retries.
250
+ let requestMaxTokens = config.maxTokens;
251
+ let lengthAttempts = 0;
245
252
  const clonePartialForDebug = () => {
246
253
  if (!partialMessage)
247
254
  return undefined;
@@ -251,30 +258,54 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
251
258
  usage: { ...partialMessage.usage, cost: { ...partialMessage.usage.cost } },
252
259
  };
253
260
  };
254
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
261
+ // Discard the truncated partial message (if any) before re-issuing a request.
262
+ const discardPartial = () => {
263
+ if (addedPartial) {
264
+ context.messages.pop();
265
+ addedPartial = false;
266
+ }
267
+ partialMessage = null;
268
+ };
269
+ // Compute the escalated token budget for a length retry, never exceeding the
270
+ // model's ceiling. `current` is the *effective* budget that produced the
271
+ // truncated response (resolved from the provider default when no explicit
272
+ // maxTokens was set), so the escalation always requests strictly more.
273
+ const escalateLengthBudget = (current) => {
274
+ const ceiling = config.model.maxTokens;
275
+ return Math.min(Math.ceil(current * lengthMultiplier), ceiling);
276
+ };
277
+ // Build the truncation failure message after length retries are exhausted.
278
+ const createLengthExhaustedMessage = (attempts) => {
279
+ const errorMessage = createErrorMessage(new Error(`Response truncated at token limit after ${attempts} attempt${attempts === 1 ? "" : "s"} — output exceeded the model's maximum token budget`));
280
+ // Force "error" so the runLoop error guard terminates the turn loudly.
281
+ errorMessage.stopReason = signal?.aborted ? "aborted" : "error";
282
+ return errorMessage;
283
+ };
284
+ // stream-drop retries are counted separately from length retries. A length retry
285
+ // is a fresh request, so it resets the stream-drop attempt counter.
286
+ let streamDropAttempt = 0;
287
+ while (true) {
255
288
  let response;
256
289
  try {
257
290
  response = await streamFunction(config.model, llmContext, {
258
291
  ...config,
292
+ maxTokens: requestMaxTokens,
259
293
  apiKey: resolvedApiKey,
260
294
  signal,
261
295
  });
262
296
  }
263
297
  catch (error) {
264
- if (isStreamDropError(error) && attempt < maxRetries && !signal?.aborted) {
298
+ if (isStreamDropError(error) && streamDropAttempt < maxRetries && !signal?.aborted) {
265
299
  await emit({
266
300
  type: "stream_retry",
267
- attempt: attempt + 1,
301
+ attempt: streamDropAttempt + 1,
268
302
  maxAttempts: maxRetries,
269
303
  error: error instanceof Error ? error.message : String(error),
270
304
  discardedPartial: clonePartialForDebug(),
271
305
  });
272
- await sleep(retryBaseDelay * 2 ** attempt, signal);
273
- if (addedPartial) {
274
- context.messages.pop();
275
- addedPartial = false;
276
- }
277
- partialMessage = null;
306
+ await sleep(retryBaseDelay * 2 ** streamDropAttempt, signal);
307
+ discardPartial();
308
+ streamDropAttempt++;
278
309
  continue;
279
310
  }
280
311
  const surfacedError = isStreamDropError(error)
@@ -286,6 +317,8 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
286
317
  }
287
318
  let shouldRetry = false;
288
319
  let retryError = "";
320
+ // Set when the stream completes with a "length" result that warrants a budget escalation.
321
+ let lengthRetry = null;
289
322
  try {
290
323
  for await (const event of response) {
291
324
  switch (event.type) {
@@ -319,7 +352,7 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
319
352
  const result = await response.result();
320
353
  // Check if this is a stream-drop error that should be retried
321
354
  const isDrop = result.stopReason === "error" && isStreamDropError(result.errorMessage);
322
- if (isDrop && attempt < maxRetries && !signal?.aborted) {
355
+ if (isDrop && streamDropAttempt < maxRetries && !signal?.aborted) {
323
356
  shouldRetry = true;
324
357
  retryError =
325
358
  result.errorMessage ?? "Stream ended without terminal event — connection likely dropped";
@@ -328,18 +361,41 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
328
361
  // Final attempt exhausted — surface friendly message
329
362
  return finalizeMessage(createErrorMessage(new Error("Stream dropped repeatedly — connection likely unstable")));
330
363
  }
364
+ else if (result.stopReason === "length") {
365
+ // The model exhausted its output budget mid-response. Retry with a
366
+ // larger budget if we have retries left, the budget can still grow,
367
+ // and we have not been aborted. Otherwise, fail loudly below.
368
+ //
369
+ // When no explicit maxTokens was set, the request used the provider
370
+ // default budget — Math.min(model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS),
371
+ // NOT the full model ceiling. Resolving the effective budget to the
372
+ // real default lets escalation request strictly more, and lets the
373
+ // ceiling guard correctly distinguish a default request (which can
374
+ // still grow) from one already at the model ceiling.
375
+ const effectiveMaxTokens = requestMaxTokens ?? Math.min(config.model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS);
376
+ const atCeiling = effectiveMaxTokens >= config.model.maxTokens;
377
+ if (lengthAttempts < lengthRetries && !atCeiling && !signal?.aborted) {
378
+ lengthRetry = {
379
+ previous: effectiveMaxTokens,
380
+ next: escalateLengthBudget(effectiveMaxTokens),
381
+ };
382
+ }
383
+ else {
384
+ return finalizeMessage(createLengthExhaustedMessage(lengthAttempts + 1));
385
+ }
386
+ }
331
387
  else {
332
388
  return finalizeMessage(result);
333
389
  }
334
390
  break;
335
391
  }
336
392
  }
337
- if (shouldRetry)
393
+ if (shouldRetry || lengthRetry)
338
394
  break;
339
395
  }
340
396
  }
341
397
  catch (error) {
342
- if (isStreamDropError(error) && attempt < maxRetries && !signal?.aborted) {
398
+ if (isStreamDropError(error) && streamDropAttempt < maxRetries && !signal?.aborted) {
343
399
  shouldRetry = true;
344
400
  retryError = error instanceof Error ? error.message : String(error);
345
401
  }
@@ -352,25 +408,37 @@ async function streamAssistantResponse(context, config, signal, emit, streamFn)
352
408
  return finalizeMessage(createErrorMessage(surfacedError));
353
409
  }
354
410
  }
411
+ if (lengthRetry) {
412
+ await emit({
413
+ type: "length_retry",
414
+ attempt: lengthAttempts + 1,
415
+ maxAttempts: lengthRetries,
416
+ previousMaxTokens: lengthRetry.previous,
417
+ nextMaxTokens: lengthRetry.next,
418
+ discardedPartial: clonePartialForDebug(),
419
+ });
420
+ discardPartial();
421
+ requestMaxTokens = lengthRetry.next;
422
+ lengthAttempts++;
423
+ // A length retry is a fresh request; reset the stream-drop counter.
424
+ streamDropAttempt = 0;
425
+ continue;
426
+ }
355
427
  if (shouldRetry) {
356
428
  await emit({
357
429
  type: "stream_retry",
358
- attempt: attempt + 1,
430
+ attempt: streamDropAttempt + 1,
359
431
  maxAttempts: maxRetries,
360
432
  error: retryError,
361
433
  discardedPartial: clonePartialForDebug(),
362
434
  });
363
- await sleep(retryBaseDelay * 2 ** attempt, signal);
364
- if (addedPartial) {
365
- context.messages.pop();
366
- addedPartial = false;
367
- }
368
- partialMessage = null;
435
+ await sleep(retryBaseDelay * 2 ** streamDropAttempt, signal);
436
+ discardPartial();
437
+ streamDropAttempt++;
369
438
  continue;
370
439
  }
371
440
  return finalizeMessage(await response.result());
372
441
  }
373
- return finalizeMessage(createErrorMessage(new Error("Stream dropped repeatedly — connection likely unstable")));
374
442
  }
375
443
  /**
376
444
  * Execute tool calls from an assistant message.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.js","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent loop that works with AgentMessage throughout.\n * Transforms to Message[] only at the LLM call boundary.\n */\n\nimport {\n\ttype AssistantMessage,\n\ttype Context,\n\tEventStream,\n\tstreamSimple,\n\ttype ToolResultMessage,\n\tvalidateToolArguments,\n} from \"@dreb/ai\";\nimport type {\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentTool,\n\tAgentToolCall,\n\tAgentToolResult,\n\tStreamFn,\n} from \"./types.js\";\n\nexport type AgentEventSink = (event: AgentEvent) => Promise<void> | void;\n\n/**\n * Start an agent loop with a new prompt message.\n * The prompt is added to the context and events are emitted for it.\n */\nexport function agentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoop(\n\t\tprompts,\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\n/**\n * Continue an agent loop from the current context without adding a new message.\n * Used for retries - context already has user message or tool results.\n *\n * **Important:** The last message in context must convert to a `user` or `toolResult` message\n * via `convertToLlm`. If it doesn't, the LLM provider will reject the request.\n * This cannot be validated here since `convertToLlm` is only called once per turn.\n */\nexport function agentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoopContinue(\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\nexport async function runAgentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tconst newMessages: AgentMessage[] = [...prompts];\n\tconst currentContext: AgentContext = {\n\t\t...context,\n\t\tmessages: [...context.messages, ...prompts],\n\t};\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\tfor (const prompt of prompts) {\n\t\tawait emit({ type: \"message_start\", message: prompt });\n\t\tawait emit({ type: \"message_end\", message: prompt });\n\t}\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nexport async function runAgentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst newMessages: AgentMessage[] = [];\n\tconst currentContext: AgentContext = { ...context };\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nfunction createAgentStream(): EventStream<AgentEvent, AgentMessage[]> {\n\treturn new EventStream<AgentEvent, AgentMessage[]>(\n\t\t(event: AgentEvent) => event.type === \"agent_end\",\n\t\t(event: AgentEvent) => (event.type === \"agent_end\" ? event.messages : []),\n\t);\n}\n\n/**\n * Main loop logic shared by agentLoop and agentLoopContinue.\n */\nasync function runLoop(\n\tcurrentContext: AgentContext,\n\tnewMessages: AgentMessage[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<void> {\n\tlet firstTurn = true;\n\tlet endTurnRequested = false;\n\tlet llmCallCount = 0;\n\t// Check for steering messages at start (user may have typed while waiting)\n\tlet pendingMessages: AgentMessage[] = (await config.getSteeringMessages?.()) || [];\n\n\t// Outer loop: continues when queued follow-up messages arrive after agent would stop\n\twhile (true) {\n\t\tlet hasMoreToolCalls = true;\n\t\tendTurnRequested = false;\n\n\t\t// Inner loop: process tool calls and steering messages\n\t\twhile (hasMoreToolCalls || pendingMessages.length > 0) {\n\t\t\t// Process pending messages first (inject into context so they're never lost).\n\t\t\t// This happens before turn_start and shouldContinue so messages are always\n\t\t\t// preserved regardless of whether the turn proceeds.\n\t\t\tif (pendingMessages.length > 0) {\n\t\t\t\tfor (const message of pendingMessages) {\n\t\t\t\t\tawait emit({ type: \"message_start\", message });\n\t\t\t\t\tawait emit({ type: \"message_end\", message });\n\t\t\t\t\tcurrentContext.messages.push(message);\n\t\t\t\t\tnewMessages.push(message);\n\t\t\t\t}\n\t\t\t\tpendingMessages = [];\n\t\t\t}\n\n\t\t\t// Check shouldContinue before starting a new turn (not the very first).\n\t\t\t// This runs before turn_start so we never emit an orphaned turn_start.\n\t\t\tif (llmCallCount > 0 && config.shouldContinue && !config.shouldContinue()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!firstTurn) {\n\t\t\t\tawait emit({ type: \"turn_start\" });\n\t\t\t} else {\n\t\t\t\tfirstTurn = false;\n\t\t\t}\n\n\t\t\t// Stream assistant response\n\t\t\tconst message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn);\n\t\t\tnewMessages.push(message);\n\t\t\tllmCallCount++;\n\n\t\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") {\n\t\t\t\tawait emit({ type: \"turn_end\", message, toolResults: [] });\n\t\t\t\tawait emit({ type: \"agent_end\", messages: newMessages });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check for tool calls\n\t\t\tconst toolCalls = message.content.filter((c) => c.type === \"toolCall\");\n\t\t\thasMoreToolCalls = toolCalls.length > 0;\n\n\t\t\tconst toolResults: ToolResultMessage[] = [];\n\t\t\tif (hasMoreToolCalls) {\n\t\t\t\tconst executionResult = await executeToolCalls(currentContext, message, config, signal, emit);\n\t\t\t\ttoolResults.push(...executionResult.results);\n\n\t\t\t\tfor (const result of toolResults) {\n\t\t\t\t\tcurrentContext.messages.push(result);\n\t\t\t\t\tnewMessages.push(result);\n\t\t\t\t}\n\n\t\t\t\tif (executionResult.endTurn) {\n\t\t\t\t\tendTurnRequested = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait emit({ type: \"turn_end\", message, toolResults });\n\n\t\t\t// If a tool requested endTurn, stop the loop (skip further LLM calls)\n\t\t\tif (endTurnRequested) {\n\t\t\t\thasMoreToolCalls = false;\n\t\t\t\tpendingMessages = [];\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpendingMessages = (await config.getSteeringMessages?.()) || [];\n\t\t}\n\n\t\t// Agent would stop here. Drain any steering messages that arrived during\n\t\t// wind-down (e.g., background agent results delivered via steer() between\n\t\t// endTurn break and here). Then check for follow-up messages.\n\t\tconst leftoverSteering = (await config.getSteeringMessages?.()) || [];\n\t\tconst followUpMessages = (await config.getFollowUpMessages?.()) || [];\n\t\tconst allPending = [...leftoverSteering, ...followUpMessages];\n\t\tif (allPending.length > 0) {\n\t\t\t// Set as pending so inner loop processes them\n\t\t\tpendingMessages = allPending;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// No more messages, exit\n\t\tbreak;\n\t}\n\n\tawait emit({ type: \"agent_end\", messages: newMessages });\n}\n\n/**\n * Check if an error is a stream-drop error (connection dropped before terminal event).\n * Only these errors trigger automatic retry — all other errors propagate as-is.\n */\nfunction isStreamDropError(error: unknown): boolean {\n\tconst msg = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\treturn (\n\t\tmsg.includes(\"Stream ended without\") ||\n\t\tmsg.includes(\"connection likely dropped\") ||\n\t\tmsg.includes(\"WebSocket stream closed before\")\n\t);\n}\n\n/**\n * Sleep for a duration, aborting early if the signal fires.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tif (signal?.aborted) {\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\t\tconst timer = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Stream an assistant response from the LLM.\n * This is where AgentMessage[] gets transformed to Message[] for the LLM.\n */\nasync function streamAssistantResponse(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<AssistantMessage> {\n\t// Apply context transform if configured (AgentMessage[] → AgentMessage[])\n\tlet messages = context.messages;\n\tif (config.transformContext) {\n\t\tmessages = await config.transformContext(messages, signal);\n\t}\n\n\t// Convert to LLM-compatible messages (AgentMessage[] → Message[])\n\tconst llmMessages = await config.convertToLlm(messages);\n\n\t// Build LLM context\n\tconst llmContext: Context = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\tmessages: llmMessages,\n\t\ttools: context.tools,\n\t};\n\n\tconst streamFunction = streamFn || streamSimple;\n\n\t// Resolve API key (important for expiring tokens)\n\tconst resolvedApiKey =\n\t\t(config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;\n\n\tconst streamStart = performance.now();\n\tlet partialMessage: AssistantMessage | null = null;\n\tlet addedPartial = false;\n\n\tconst finalizeMessage = async (finalMessage: AssistantMessage): Promise<AssistantMessage> => {\n\t\tfinalMessage.durationMs = Math.max(0, performance.now() - streamStart);\n\t\tif (addedPartial) {\n\t\t\tcontext.messages[context.messages.length - 1] = finalMessage;\n\t\t} else {\n\t\t\tcontext.messages.push(finalMessage);\n\t\t}\n\t\tif (!addedPartial) {\n\t\t\tawait emit({ type: \"message_start\", message: { ...finalMessage } });\n\t\t}\n\t\tawait emit({ type: \"message_end\", message: finalMessage });\n\t\treturn finalMessage;\n\t};\n\n\tconst createErrorMessage = (error: unknown): AssistantMessage => ({\n\t\trole: \"assistant\",\n\t\tcontent: partialMessage?.content ?? [{ type: \"text\", text: \"\" }],\n\t\tapi: config.model.api,\n\t\tprovider: config.model.provider,\n\t\tmodel: config.model.id,\n\t\tusage: partialMessage?.usage ?? {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: signal?.aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: partialMessage?.timestamp ?? Date.now(),\n\t});\n\n\tconst maxRetries = config.streamRetries ?? 3;\n\tconst retryBaseDelay = config.streamRetryBaseDelayMs ?? 1000;\n\tconst clonePartialForDebug = (): AssistantMessage | undefined => {\n\t\tif (!partialMessage) return undefined;\n\t\treturn {\n\t\t\t...partialMessage,\n\t\t\tcontent: partialMessage.content.map((block) => ({ ...block })),\n\t\t\tusage: { ...partialMessage.usage, cost: { ...partialMessage.usage.cost } },\n\t\t};\n\t};\n\n\tfor (let attempt = 0; attempt <= maxRetries; attempt++) {\n\t\tlet response: Awaited<ReturnType<StreamFn>>;\n\t\ttry {\n\t\t\tresponse = await streamFunction(config.model, llmContext, {\n\t\t\t\t...config,\n\t\t\t\tapiKey: resolvedApiKey,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && attempt < maxRetries && !signal?.aborted) {\n\t\t\t\tawait emit({\n\t\t\t\t\ttype: \"stream_retry\",\n\t\t\t\t\tattempt: attempt + 1,\n\t\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t\t});\n\t\t\t\tawait sleep(retryBaseDelay * 2 ** attempt, signal);\n\t\t\t\tif (addedPartial) {\n\t\t\t\t\tcontext.messages.pop();\n\t\t\t\t\taddedPartial = false;\n\t\t\t\t}\n\t\t\t\tpartialMessage = null;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t: error instanceof Error\n\t\t\t\t\t? error\n\t\t\t\t\t: new Error(String(error));\n\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t}\n\n\t\tlet shouldRetry = false;\n\t\tlet retryError = \"\";\n\t\ttry {\n\t\t\tfor await (const event of response) {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\tcontext.messages.push(partialMessage);\n\t\t\t\t\t\taddedPartial = true;\n\t\t\t\t\t\tawait emit({ type: \"message_start\", message: { ...partialMessage } });\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\tcase \"text_delta\":\n\t\t\t\t\tcase \"text_end\":\n\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\tcase \"thinking_delta\":\n\t\t\t\t\tcase \"thinking_end\":\n\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\tcase \"toolcall_delta\":\n\t\t\t\t\tcase \"toolcall_end\":\n\t\t\t\t\t\tif (partialMessage) {\n\t\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\t\tcontext.messages[context.messages.length - 1] = partialMessage;\n\t\t\t\t\t\t\tawait emit({\n\t\t\t\t\t\t\t\ttype: \"message_update\",\n\t\t\t\t\t\t\t\tassistantMessageEvent: event,\n\t\t\t\t\t\t\t\tmessage: { ...partialMessage },\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"done\":\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst result = await response.result();\n\t\t\t\t\t\t// Check if this is a stream-drop error that should be retried\n\t\t\t\t\t\tconst isDrop = result.stopReason === \"error\" && isStreamDropError(result.errorMessage);\n\t\t\t\t\t\tif (isDrop && attempt < maxRetries && !signal?.aborted) {\n\t\t\t\t\t\t\tshouldRetry = true;\n\t\t\t\t\t\t\tretryError =\n\t\t\t\t\t\t\t\tresult.errorMessage ?? \"Stream ended without terminal event — connection likely dropped\";\n\t\t\t\t\t\t} else if (isDrop) {\n\t\t\t\t\t\t\t// Final attempt exhausted — surface friendly message\n\t\t\t\t\t\t\treturn finalizeMessage(\n\t\t\t\t\t\t\t\tcreateErrorMessage(new Error(\"Stream dropped repeatedly — connection likely unstable\")),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn finalizeMessage(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (shouldRetry) break;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && attempt < maxRetries && !signal?.aborted) {\n\t\t\t\tshouldRetry = true;\n\t\t\t\tretryError = error instanceof Error ? error.message : String(error);\n\t\t\t} else {\n\t\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t\t: error instanceof Error\n\t\t\t\t\t\t? error\n\t\t\t\t\t\t: new Error(String(error));\n\t\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t\t}\n\t\t}\n\n\t\tif (shouldRetry) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"stream_retry\",\n\t\t\t\tattempt: attempt + 1,\n\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\terror: retryError,\n\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t});\n\t\t\tawait sleep(retryBaseDelay * 2 ** attempt, signal);\n\t\t\tif (addedPartial) {\n\t\t\t\tcontext.messages.pop();\n\t\t\t\taddedPartial = false;\n\t\t\t}\n\t\t\tpartialMessage = null;\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn finalizeMessage(await response.result());\n\t}\n\n\treturn finalizeMessage(createErrorMessage(new Error(\"Stream dropped repeatedly — connection likely unstable\")));\n}\n\ninterface ToolExecutionResult {\n\tresults: ToolResultMessage[];\n\tendTurn: boolean;\n}\n\n/**\n * Execute tool calls from an assistant message.\n */\nasync function executeToolCalls(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\tif (config.toolExecution === \"sequential\") {\n\t\treturn executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit);\n\t}\n\treturn executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, signal, emit);\n}\n\nasync function executeToolCallsSequential(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\tconst executed = await executePreparedToolCall(preparation, signal, emit);\n\t\t\tif (executed.result.endTurn) endTurn = true;\n\t\t\tresults.push(\n\t\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\t\tcurrentContext,\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\tpreparation,\n\t\t\t\t\texecuted,\n\t\t\t\t\tconfig,\n\t\t\t\t\tsignal,\n\t\t\t\t\temit,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { results, endTurn };\n}\n\nasync function executeToolCallsParallel(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tconst runnableCalls: PreparedToolCall[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\trunnableCalls.push(preparation);\n\t\t}\n\t}\n\n\tconst runningCalls = runnableCalls.map((prepared) => ({\n\t\tprepared,\n\t\texecution: executePreparedToolCall(prepared, signal, emit),\n\t}));\n\n\tfor (const running of runningCalls) {\n\t\tconst executed = await running.execution;\n\t\tif (executed.result.endTurn) endTurn = true;\n\t\tresults.push(\n\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\tcurrentContext,\n\t\t\t\tassistantMessage,\n\t\t\t\trunning.prepared,\n\t\t\t\texecuted,\n\t\t\t\tconfig,\n\t\t\t\tsignal,\n\t\t\t\temit,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn { results, endTurn };\n}\n\ntype PreparedToolCall = {\n\tkind: \"prepared\";\n\ttoolCall: AgentToolCall;\n\ttool: AgentTool<any>;\n\targs: unknown;\n};\n\ntype ImmediateToolCallOutcome = {\n\tkind: \"immediate\";\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\ntype ExecutedToolCallOutcome = {\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\nasync function prepareToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCall: AgentToolCall,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n): Promise<PreparedToolCall | ImmediateToolCallOutcome> {\n\tconst tool = currentContext.tools?.find((t) => t.name === toolCall.name);\n\tif (!tool) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(`Tool ${toolCall.name} not found`),\n\t\t\tisError: true,\n\t\t};\n\t}\n\n\ttry {\n\t\tconst validatedArgs = validateToolArguments(tool, toolCall);\n\t\tif (config.beforeToolCall) {\n\t\t\tconst beforeResult = await config.beforeToolCall(\n\t\t\t\t{\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\ttoolCall,\n\t\t\t\t\targs: validatedArgs,\n\t\t\t\t\tcontext: currentContext,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (beforeResult?.block) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"immediate\",\n\t\t\t\t\tresult: createErrorToolResult(beforeResult.reason || \"Tool execution was blocked\"),\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tkind: \"prepared\",\n\t\t\ttoolCall,\n\t\t\ttool,\n\t\t\targs: validatedArgs,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function executePreparedToolCall(\n\tprepared: PreparedToolCall,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ExecutedToolCallOutcome> {\n\tconst updateEvents: Promise<void>[] = [];\n\n\ttry {\n\t\tconst result = await prepared.tool.execute(\n\t\t\tprepared.toolCall.id,\n\t\t\tprepared.args as never,\n\t\t\tsignal,\n\t\t\t(partialResult) => {\n\t\t\t\tupdateEvents.push(\n\t\t\t\t\tPromise.resolve(\n\t\t\t\t\t\temit({\n\t\t\t\t\t\t\ttype: \"tool_execution_update\",\n\t\t\t\t\t\t\ttoolCallId: prepared.toolCall.id,\n\t\t\t\t\t\t\ttoolName: prepared.toolCall.name,\n\t\t\t\t\t\t\targs: prepared.toolCall.arguments,\n\t\t\t\t\t\t\tpartialResult,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t\tawait Promise.all(updateEvents);\n\t\treturn { result, isError: false };\n\t} catch (error) {\n\t\tawait Promise.all(updateEvents);\n\t\treturn {\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function finalizeExecutedToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tprepared: PreparedToolCall,\n\texecuted: ExecutedToolCallOutcome,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\n\tif (config.afterToolCall) {\n\t\tconst afterResult = await config.afterToolCall(\n\t\t\t{\n\t\t\t\tassistantMessage,\n\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\targs: prepared.args,\n\t\t\t\tresult,\n\t\t\t\tisError,\n\t\t\t\tcontext: currentContext,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tif (afterResult) {\n\t\t\tresult = {\n\t\t\t\tcontent: afterResult.content ?? result.content,\n\t\t\t\tdetails: afterResult.details ?? result.details,\n\t\t\t};\n\t\t\tisError = afterResult.isError ?? isError;\n\t\t}\n\t}\n\n\treturn await emitToolCallOutcome(prepared.toolCall, result, isError, emit);\n}\n\nfunction createErrorToolResult(message: string): AgentToolResult<any> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text: message }],\n\t\tdetails: {},\n\t};\n}\n\nasync function emitToolCallOutcome(\n\ttoolCall: AgentToolCall,\n\tresult: AgentToolResult<any>,\n\tisError: boolean,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tawait emit({\n\t\ttype: \"tool_execution_end\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tresult,\n\t\tisError,\n\t});\n\n\tconst toolResultMessage: ToolResultMessage = {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tcontent: result.content,\n\t\tdetails: result.details,\n\t\tisError,\n\t\ttimestamp: Date.now(),\n\t};\n\n\tawait emit({ type: \"message_start\", message: toolResultMessage });\n\tawait emit({ type: \"message_end\", message: toolResultMessage });\n\treturn toolResultMessage;\n}\n"]}
1
+ {"version":3,"file":"agent-loop.js","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent loop that works with AgentMessage throughout.\n * Transforms to Message[] only at the LLM call boundary.\n */\n\nimport {\n\ttype AssistantMessage,\n\ttype Context,\n\tDEFAULT_MAX_OUTPUT_TOKENS,\n\tEventStream,\n\tstreamSimple,\n\ttype ToolResultMessage,\n\tvalidateToolArguments,\n} from \"@dreb/ai\";\nimport type {\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentTool,\n\tAgentToolCall,\n\tAgentToolResult,\n\tStreamFn,\n} from \"./types.js\";\n\nexport type AgentEventSink = (event: AgentEvent) => Promise<void> | void;\n\n/**\n * Start an agent loop with a new prompt message.\n * The prompt is added to the context and events are emitted for it.\n */\nexport function agentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoop(\n\t\tprompts,\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\n/**\n * Continue an agent loop from the current context without adding a new message.\n * Used for retries - context already has user message or tool results.\n *\n * **Important:** The last message in context must convert to a `user` or `toolResult` message\n * via `convertToLlm`. If it doesn't, the LLM provider will reject the request.\n * This cannot be validated here since `convertToLlm` is only called once per turn.\n */\nexport function agentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): EventStream<AgentEvent, AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst stream = createAgentStream();\n\n\tvoid runAgentLoopContinue(\n\t\tcontext,\n\t\tconfig,\n\t\tasync (event) => {\n\t\t\tstream.push(event);\n\t\t},\n\t\tsignal,\n\t\tstreamFn,\n\t).then((messages) => {\n\t\tstream.end(messages);\n\t});\n\n\treturn stream;\n}\n\nexport async function runAgentLoop(\n\tprompts: AgentMessage[],\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tconst newMessages: AgentMessage[] = [...prompts];\n\tconst currentContext: AgentContext = {\n\t\t...context,\n\t\tmessages: [...context.messages, ...prompts],\n\t};\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\tfor (const prompt of prompts) {\n\t\tawait emit({ type: \"message_start\", message: prompt });\n\t\tawait emit({ type: \"message_end\", message: prompt });\n\t}\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nexport async function runAgentLoopContinue(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\temit: AgentEventSink,\n\tsignal?: AbortSignal,\n\tstreamFn?: StreamFn,\n): Promise<AgentMessage[]> {\n\tif (context.messages.length === 0) {\n\t\tthrow new Error(\"Cannot continue: no messages in context\");\n\t}\n\n\tif (context.messages[context.messages.length - 1].role === \"assistant\") {\n\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t}\n\n\tconst newMessages: AgentMessage[] = [];\n\tconst currentContext: AgentContext = { ...context };\n\n\tawait emit({ type: \"agent_start\", model: { provider: config.model.provider, id: config.model.id } });\n\tawait emit({ type: \"turn_start\" });\n\n\tawait runLoop(currentContext, newMessages, config, signal, emit, streamFn);\n\treturn newMessages;\n}\n\nfunction createAgentStream(): EventStream<AgentEvent, AgentMessage[]> {\n\treturn new EventStream<AgentEvent, AgentMessage[]>(\n\t\t(event: AgentEvent) => event.type === \"agent_end\",\n\t\t(event: AgentEvent) => (event.type === \"agent_end\" ? event.messages : []),\n\t);\n}\n\n/**\n * Main loop logic shared by agentLoop and agentLoopContinue.\n */\nasync function runLoop(\n\tcurrentContext: AgentContext,\n\tnewMessages: AgentMessage[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<void> {\n\tlet firstTurn = true;\n\tlet endTurnRequested = false;\n\tlet llmCallCount = 0;\n\t// Check for steering messages at start (user may have typed while waiting)\n\tlet pendingMessages: AgentMessage[] = (await config.getSteeringMessages?.()) || [];\n\n\t// Outer loop: continues when queued follow-up messages arrive after agent would stop\n\twhile (true) {\n\t\tlet hasMoreToolCalls = true;\n\t\tendTurnRequested = false;\n\n\t\t// Inner loop: process tool calls and steering messages\n\t\twhile (hasMoreToolCalls || pendingMessages.length > 0) {\n\t\t\t// Process pending messages first (inject into context so they're never lost).\n\t\t\t// This happens before turn_start and shouldContinue so messages are always\n\t\t\t// preserved regardless of whether the turn proceeds.\n\t\t\tif (pendingMessages.length > 0) {\n\t\t\t\tfor (const message of pendingMessages) {\n\t\t\t\t\tawait emit({ type: \"message_start\", message });\n\t\t\t\t\tawait emit({ type: \"message_end\", message });\n\t\t\t\t\tcurrentContext.messages.push(message);\n\t\t\t\t\tnewMessages.push(message);\n\t\t\t\t}\n\t\t\t\tpendingMessages = [];\n\t\t\t}\n\n\t\t\t// Check shouldContinue before starting a new turn (not the very first).\n\t\t\t// This runs before turn_start so we never emit an orphaned turn_start.\n\t\t\tif (llmCallCount > 0 && config.shouldContinue && !config.shouldContinue()) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!firstTurn) {\n\t\t\t\tawait emit({ type: \"turn_start\" });\n\t\t\t} else {\n\t\t\t\tfirstTurn = false;\n\t\t\t}\n\n\t\t\t// Stream assistant response\n\t\t\tconst message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn);\n\t\t\tnewMessages.push(message);\n\t\t\tllmCallCount++;\n\n\t\t\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") {\n\t\t\t\tawait emit({ type: \"turn_end\", message, toolResults: [] });\n\t\t\t\tawait emit({ type: \"agent_end\", messages: newMessages });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Check for tool calls\n\t\t\tconst toolCalls = message.content.filter((c) => c.type === \"toolCall\");\n\t\t\thasMoreToolCalls = toolCalls.length > 0;\n\n\t\t\tconst toolResults: ToolResultMessage[] = [];\n\t\t\tif (hasMoreToolCalls) {\n\t\t\t\tconst executionResult = await executeToolCalls(currentContext, message, config, signal, emit);\n\t\t\t\ttoolResults.push(...executionResult.results);\n\n\t\t\t\tfor (const result of toolResults) {\n\t\t\t\t\tcurrentContext.messages.push(result);\n\t\t\t\t\tnewMessages.push(result);\n\t\t\t\t}\n\n\t\t\t\tif (executionResult.endTurn) {\n\t\t\t\t\tendTurnRequested = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait emit({ type: \"turn_end\", message, toolResults });\n\n\t\t\t// If a tool requested endTurn, stop the loop (skip further LLM calls)\n\t\t\tif (endTurnRequested) {\n\t\t\t\thasMoreToolCalls = false;\n\t\t\t\tpendingMessages = [];\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tpendingMessages = (await config.getSteeringMessages?.()) || [];\n\t\t}\n\n\t\t// Agent would stop here. Drain any steering messages that arrived during\n\t\t// wind-down (e.g., background agent results delivered via steer() between\n\t\t// endTurn break and here). Then check for follow-up messages.\n\t\tconst leftoverSteering = (await config.getSteeringMessages?.()) || [];\n\t\tconst followUpMessages = (await config.getFollowUpMessages?.()) || [];\n\t\tconst allPending = [...leftoverSteering, ...followUpMessages];\n\t\tif (allPending.length > 0) {\n\t\t\t// Set as pending so inner loop processes them\n\t\t\tpendingMessages = allPending;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// No more messages, exit\n\t\tbreak;\n\t}\n\n\tawait emit({ type: \"agent_end\", messages: newMessages });\n}\n\n/**\n * Check if an error is a stream-drop error (connection dropped before terminal event).\n * Only these errors trigger automatic retry — all other errors propagate as-is.\n */\nfunction isStreamDropError(error: unknown): boolean {\n\tconst msg = error instanceof Error ? error.message : typeof error === \"string\" ? error : \"\";\n\treturn (\n\t\tmsg.includes(\"Stream ended without\") ||\n\t\tmsg.includes(\"connection likely dropped\") ||\n\t\tmsg.includes(\"WebSocket stream closed before\")\n\t);\n}\n\n/**\n * Sleep for a duration, aborting early if the signal fires.\n */\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tif (signal?.aborted) {\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\t\tconst timer = setTimeout(resolve, ms);\n\t\tsignal?.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\n/**\n * Stream an assistant response from the LLM.\n * This is where AgentMessage[] gets transformed to Message[] for the LLM.\n */\nasync function streamAssistantResponse(\n\tcontext: AgentContext,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n\tstreamFn?: StreamFn,\n): Promise<AssistantMessage> {\n\t// Apply context transform if configured (AgentMessage[] → AgentMessage[])\n\tlet messages = context.messages;\n\tif (config.transformContext) {\n\t\tmessages = await config.transformContext(messages, signal);\n\t}\n\n\t// Convert to LLM-compatible messages (AgentMessage[] → Message[])\n\tconst llmMessages = await config.convertToLlm(messages);\n\n\t// Build LLM context\n\tconst llmContext: Context = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\tmessages: llmMessages,\n\t\ttools: context.tools,\n\t};\n\n\tconst streamFunction = streamFn || streamSimple;\n\n\t// Resolve API key (important for expiring tokens)\n\tconst resolvedApiKey =\n\t\t(config.getApiKey ? await config.getApiKey(config.model.provider) : undefined) || config.apiKey;\n\n\tconst streamStart = performance.now();\n\tlet partialMessage: AssistantMessage | null = null;\n\tlet addedPartial = false;\n\n\tconst finalizeMessage = async (finalMessage: AssistantMessage): Promise<AssistantMessage> => {\n\t\tfinalMessage.durationMs = Math.max(0, performance.now() - streamStart);\n\t\tif (addedPartial) {\n\t\t\tcontext.messages[context.messages.length - 1] = finalMessage;\n\t\t} else {\n\t\t\tcontext.messages.push(finalMessage);\n\t\t}\n\t\tif (!addedPartial) {\n\t\t\tawait emit({ type: \"message_start\", message: { ...finalMessage } });\n\t\t}\n\t\tawait emit({ type: \"message_end\", message: finalMessage });\n\t\treturn finalMessage;\n\t};\n\n\tconst createErrorMessage = (error: unknown): AssistantMessage => ({\n\t\trole: \"assistant\",\n\t\tcontent: partialMessage?.content ?? [{ type: \"text\", text: \"\" }],\n\t\tapi: config.model.api,\n\t\tprovider: config.model.provider,\n\t\tmodel: config.model.id,\n\t\tusage: partialMessage?.usage ?? {\n\t\t\tinput: 0,\n\t\t\toutput: 0,\n\t\t\tcacheRead: 0,\n\t\t\tcacheWrite: 0,\n\t\t\ttotalTokens: 0,\n\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t},\n\t\tstopReason: signal?.aborted ? \"aborted\" : \"error\",\n\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\ttimestamp: partialMessage?.timestamp ?? Date.now(),\n\t});\n\n\tconst maxRetries = config.streamRetries ?? 3;\n\tconst retryBaseDelay = config.streamRetryBaseDelayMs ?? 1000;\n\tconst lengthRetries = config.lengthRetries ?? 2;\n\tconst lengthMultiplier = config.lengthRetryBudgetMultiplier ?? 2;\n\t// The maxTokens budget requested for the current attempt. Starts at the\n\t// caller-configured budget (may be undefined → provider default) and is\n\t// escalated on each length retry. Kept separate from stream-drop retries.\n\tlet requestMaxTokens = config.maxTokens;\n\tlet lengthAttempts = 0;\n\tconst clonePartialForDebug = (): AssistantMessage | undefined => {\n\t\tif (!partialMessage) return undefined;\n\t\treturn {\n\t\t\t...partialMessage,\n\t\t\tcontent: partialMessage.content.map((block) => ({ ...block })),\n\t\t\tusage: { ...partialMessage.usage, cost: { ...partialMessage.usage.cost } },\n\t\t};\n\t};\n\n\t// Discard the truncated partial message (if any) before re-issuing a request.\n\tconst discardPartial = () => {\n\t\tif (addedPartial) {\n\t\t\tcontext.messages.pop();\n\t\t\taddedPartial = false;\n\t\t}\n\t\tpartialMessage = null;\n\t};\n\n\t// Compute the escalated token budget for a length retry, never exceeding the\n\t// model's ceiling. `current` is the *effective* budget that produced the\n\t// truncated response (resolved from the provider default when no explicit\n\t// maxTokens was set), so the escalation always requests strictly more.\n\tconst escalateLengthBudget = (current: number): number => {\n\t\tconst ceiling = config.model.maxTokens;\n\t\treturn Math.min(Math.ceil(current * lengthMultiplier), ceiling);\n\t};\n\n\t// Build the truncation failure message after length retries are exhausted.\n\tconst createLengthExhaustedMessage = (attempts: number): AssistantMessage => {\n\t\tconst errorMessage = createErrorMessage(\n\t\t\tnew Error(\n\t\t\t\t`Response truncated at token limit after ${attempts} attempt${attempts === 1 ? \"\" : \"s\"} — output exceeded the model's maximum token budget`,\n\t\t\t),\n\t\t);\n\t\t// Force \"error\" so the runLoop error guard terminates the turn loudly.\n\t\terrorMessage.stopReason = signal?.aborted ? \"aborted\" : \"error\";\n\t\treturn errorMessage;\n\t};\n\n\t// stream-drop retries are counted separately from length retries. A length retry\n\t// is a fresh request, so it resets the stream-drop attempt counter.\n\tlet streamDropAttempt = 0;\n\twhile (true) {\n\t\tlet response: Awaited<ReturnType<StreamFn>>;\n\t\ttry {\n\t\t\tresponse = await streamFunction(config.model, llmContext, {\n\t\t\t\t...config,\n\t\t\t\tmaxTokens: requestMaxTokens,\n\t\t\t\tapiKey: resolvedApiKey,\n\t\t\t\tsignal,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && streamDropAttempt < maxRetries && !signal?.aborted) {\n\t\t\t\tawait emit({\n\t\t\t\t\ttype: \"stream_retry\",\n\t\t\t\t\tattempt: streamDropAttempt + 1,\n\t\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t\t});\n\t\t\t\tawait sleep(retryBaseDelay * 2 ** streamDropAttempt, signal);\n\t\t\t\tdiscardPartial();\n\t\t\t\tstreamDropAttempt++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t: error instanceof Error\n\t\t\t\t\t? error\n\t\t\t\t\t: new Error(String(error));\n\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t}\n\n\t\tlet shouldRetry = false;\n\t\tlet retryError = \"\";\n\t\t// Set when the stream completes with a \"length\" result that warrants a budget escalation.\n\t\tlet lengthRetry: { previous: number; next: number } | null = null;\n\t\ttry {\n\t\t\tfor await (const event of response) {\n\t\t\t\tswitch (event.type) {\n\t\t\t\t\tcase \"start\":\n\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\tcontext.messages.push(partialMessage);\n\t\t\t\t\t\taddedPartial = true;\n\t\t\t\t\t\tawait emit({ type: \"message_start\", message: { ...partialMessage } });\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"text_start\":\n\t\t\t\t\tcase \"text_delta\":\n\t\t\t\t\tcase \"text_end\":\n\t\t\t\t\tcase \"thinking_start\":\n\t\t\t\t\tcase \"thinking_delta\":\n\t\t\t\t\tcase \"thinking_end\":\n\t\t\t\t\tcase \"toolcall_start\":\n\t\t\t\t\tcase \"toolcall_delta\":\n\t\t\t\t\tcase \"toolcall_end\":\n\t\t\t\t\t\tif (partialMessage) {\n\t\t\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\t\t\tcontext.messages[context.messages.length - 1] = partialMessage;\n\t\t\t\t\t\t\tawait emit({\n\t\t\t\t\t\t\t\ttype: \"message_update\",\n\t\t\t\t\t\t\t\tassistantMessageEvent: event,\n\t\t\t\t\t\t\t\tmessage: { ...partialMessage },\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"done\":\n\t\t\t\t\tcase \"error\": {\n\t\t\t\t\t\tconst result = await response.result();\n\t\t\t\t\t\t// Check if this is a stream-drop error that should be retried\n\t\t\t\t\t\tconst isDrop = result.stopReason === \"error\" && isStreamDropError(result.errorMessage);\n\t\t\t\t\t\tif (isDrop && streamDropAttempt < maxRetries && !signal?.aborted) {\n\t\t\t\t\t\t\tshouldRetry = true;\n\t\t\t\t\t\t\tretryError =\n\t\t\t\t\t\t\t\tresult.errorMessage ?? \"Stream ended without terminal event — connection likely dropped\";\n\t\t\t\t\t\t} else if (isDrop) {\n\t\t\t\t\t\t\t// Final attempt exhausted — surface friendly message\n\t\t\t\t\t\t\treturn finalizeMessage(\n\t\t\t\t\t\t\t\tcreateErrorMessage(new Error(\"Stream dropped repeatedly — connection likely unstable\")),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (result.stopReason === \"length\") {\n\t\t\t\t\t\t\t// The model exhausted its output budget mid-response. Retry with a\n\t\t\t\t\t\t\t// larger budget if we have retries left, the budget can still grow,\n\t\t\t\t\t\t\t// and we have not been aborted. Otherwise, fail loudly below.\n\t\t\t\t\t\t\t//\n\t\t\t\t\t\t\t// When no explicit maxTokens was set, the request used the provider\n\t\t\t\t\t\t\t// default budget — Math.min(model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS),\n\t\t\t\t\t\t\t// NOT the full model ceiling. Resolving the effective budget to the\n\t\t\t\t\t\t\t// real default lets escalation request strictly more, and lets the\n\t\t\t\t\t\t\t// ceiling guard correctly distinguish a default request (which can\n\t\t\t\t\t\t\t// still grow) from one already at the model ceiling.\n\t\t\t\t\t\t\tconst effectiveMaxTokens =\n\t\t\t\t\t\t\t\trequestMaxTokens ?? Math.min(config.model.maxTokens, DEFAULT_MAX_OUTPUT_TOKENS);\n\t\t\t\t\t\t\tconst atCeiling = effectiveMaxTokens >= config.model.maxTokens;\n\t\t\t\t\t\t\tif (lengthAttempts < lengthRetries && !atCeiling && !signal?.aborted) {\n\t\t\t\t\t\t\t\tlengthRetry = {\n\t\t\t\t\t\t\t\t\tprevious: effectiveMaxTokens,\n\t\t\t\t\t\t\t\t\tnext: escalateLengthBudget(effectiveMaxTokens),\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn finalizeMessage(createLengthExhaustedMessage(lengthAttempts + 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn finalizeMessage(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (shouldRetry || lengthRetry) break;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (isStreamDropError(error) && streamDropAttempt < maxRetries && !signal?.aborted) {\n\t\t\t\tshouldRetry = true;\n\t\t\t\tretryError = error instanceof Error ? error.message : String(error);\n\t\t\t} else {\n\t\t\t\tconst surfacedError = isStreamDropError(error)\n\t\t\t\t\t? new Error(\"Stream dropped repeatedly — connection likely unstable\")\n\t\t\t\t\t: error instanceof Error\n\t\t\t\t\t\t? error\n\t\t\t\t\t\t: new Error(String(error));\n\t\t\t\treturn finalizeMessage(createErrorMessage(surfacedError));\n\t\t\t}\n\t\t}\n\n\t\tif (lengthRetry) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"length_retry\",\n\t\t\t\tattempt: lengthAttempts + 1,\n\t\t\t\tmaxAttempts: lengthRetries,\n\t\t\t\tpreviousMaxTokens: lengthRetry.previous,\n\t\t\t\tnextMaxTokens: lengthRetry.next,\n\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t});\n\t\t\tdiscardPartial();\n\t\t\trequestMaxTokens = lengthRetry.next;\n\t\t\tlengthAttempts++;\n\t\t\t// A length retry is a fresh request; reset the stream-drop counter.\n\t\t\tstreamDropAttempt = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (shouldRetry) {\n\t\t\tawait emit({\n\t\t\t\ttype: \"stream_retry\",\n\t\t\t\tattempt: streamDropAttempt + 1,\n\t\t\t\tmaxAttempts: maxRetries,\n\t\t\t\terror: retryError,\n\t\t\t\tdiscardedPartial: clonePartialForDebug(),\n\t\t\t});\n\t\t\tawait sleep(retryBaseDelay * 2 ** streamDropAttempt, signal);\n\t\t\tdiscardPartial();\n\t\t\tstreamDropAttempt++;\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn finalizeMessage(await response.result());\n\t}\n}\n\ninterface ToolExecutionResult {\n\tresults: ToolResultMessage[];\n\tendTurn: boolean;\n}\n\n/**\n * Execute tool calls from an assistant message.\n */\nasync function executeToolCalls(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\tif (config.toolExecution === \"sequential\") {\n\t\treturn executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit);\n\t}\n\treturn executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, signal, emit);\n}\n\nasync function executeToolCallsSequential(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\tconst executed = await executePreparedToolCall(preparation, signal, emit);\n\t\t\tif (executed.result.endTurn) endTurn = true;\n\t\t\tresults.push(\n\t\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\t\tcurrentContext,\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\tpreparation,\n\t\t\t\t\texecuted,\n\t\t\t\t\tconfig,\n\t\t\t\t\tsignal,\n\t\t\t\t\temit,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { results, endTurn };\n}\n\nasync function executeToolCallsParallel(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCalls: AgentToolCall[],\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolExecutionResult> {\n\tconst results: ToolResultMessage[] = [];\n\tconst runnableCalls: PreparedToolCall[] = [];\n\tlet endTurn = false;\n\n\tfor (const toolCall of toolCalls) {\n\t\tawait emit({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tconst preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);\n\t\tif (preparation.kind === \"immediate\") {\n\t\t\tif (preparation.result.endTurn) endTurn = true;\n\t\t\tresults.push(await emitToolCallOutcome(toolCall, preparation.result, preparation.isError, emit));\n\t\t} else {\n\t\t\trunnableCalls.push(preparation);\n\t\t}\n\t}\n\n\tconst runningCalls = runnableCalls.map((prepared) => ({\n\t\tprepared,\n\t\texecution: executePreparedToolCall(prepared, signal, emit),\n\t}));\n\n\tfor (const running of runningCalls) {\n\t\tconst executed = await running.execution;\n\t\tif (executed.result.endTurn) endTurn = true;\n\t\tresults.push(\n\t\t\tawait finalizeExecutedToolCall(\n\t\t\t\tcurrentContext,\n\t\t\t\tassistantMessage,\n\t\t\t\trunning.prepared,\n\t\t\t\texecuted,\n\t\t\t\tconfig,\n\t\t\t\tsignal,\n\t\t\t\temit,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn { results, endTurn };\n}\n\ntype PreparedToolCall = {\n\tkind: \"prepared\";\n\ttoolCall: AgentToolCall;\n\ttool: AgentTool<any>;\n\targs: unknown;\n};\n\ntype ImmediateToolCallOutcome = {\n\tkind: \"immediate\";\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\ntype ExecutedToolCallOutcome = {\n\tresult: AgentToolResult<any>;\n\tisError: boolean;\n};\n\nasync function prepareToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\ttoolCall: AgentToolCall,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n): Promise<PreparedToolCall | ImmediateToolCallOutcome> {\n\tconst tool = currentContext.tools?.find((t) => t.name === toolCall.name);\n\tif (!tool) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(`Tool ${toolCall.name} not found`),\n\t\t\tisError: true,\n\t\t};\n\t}\n\n\ttry {\n\t\tconst validatedArgs = validateToolArguments(tool, toolCall);\n\t\tif (config.beforeToolCall) {\n\t\t\tconst beforeResult = await config.beforeToolCall(\n\t\t\t\t{\n\t\t\t\t\tassistantMessage,\n\t\t\t\t\ttoolCall,\n\t\t\t\t\targs: validatedArgs,\n\t\t\t\t\tcontext: currentContext,\n\t\t\t\t},\n\t\t\t\tsignal,\n\t\t\t);\n\t\t\tif (beforeResult?.block) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"immediate\",\n\t\t\t\t\tresult: createErrorToolResult(beforeResult.reason || \"Tool execution was blocked\"),\n\t\t\t\t\tisError: true,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tkind: \"prepared\",\n\t\t\ttoolCall,\n\t\t\ttool,\n\t\t\targs: validatedArgs,\n\t\t};\n\t} catch (error) {\n\t\treturn {\n\t\t\tkind: \"immediate\",\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function executePreparedToolCall(\n\tprepared: PreparedToolCall,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ExecutedToolCallOutcome> {\n\tconst updateEvents: Promise<void>[] = [];\n\n\ttry {\n\t\tconst result = await prepared.tool.execute(\n\t\t\tprepared.toolCall.id,\n\t\t\tprepared.args as never,\n\t\t\tsignal,\n\t\t\t(partialResult) => {\n\t\t\t\tupdateEvents.push(\n\t\t\t\t\tPromise.resolve(\n\t\t\t\t\t\temit({\n\t\t\t\t\t\t\ttype: \"tool_execution_update\",\n\t\t\t\t\t\t\ttoolCallId: prepared.toolCall.id,\n\t\t\t\t\t\t\ttoolName: prepared.toolCall.name,\n\t\t\t\t\t\t\targs: prepared.toolCall.arguments,\n\t\t\t\t\t\t\tpartialResult,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t\tawait Promise.all(updateEvents);\n\t\treturn { result, isError: false };\n\t} catch (error) {\n\t\tawait Promise.all(updateEvents);\n\t\treturn {\n\t\t\tresult: createErrorToolResult(error instanceof Error ? error.message : String(error)),\n\t\t\tisError: true,\n\t\t};\n\t}\n}\n\nasync function finalizeExecutedToolCall(\n\tcurrentContext: AgentContext,\n\tassistantMessage: AssistantMessage,\n\tprepared: PreparedToolCall,\n\texecuted: ExecutedToolCallOutcome,\n\tconfig: AgentLoopConfig,\n\tsignal: AbortSignal | undefined,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tlet result = executed.result;\n\tlet isError = executed.isError;\n\n\tif (config.afterToolCall) {\n\t\tconst afterResult = await config.afterToolCall(\n\t\t\t{\n\t\t\t\tassistantMessage,\n\t\t\t\ttoolCall: prepared.toolCall,\n\t\t\t\targs: prepared.args,\n\t\t\t\tresult,\n\t\t\t\tisError,\n\t\t\t\tcontext: currentContext,\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tif (afterResult) {\n\t\t\tresult = {\n\t\t\t\tcontent: afterResult.content ?? result.content,\n\t\t\t\tdetails: afterResult.details ?? result.details,\n\t\t\t};\n\t\t\tisError = afterResult.isError ?? isError;\n\t\t}\n\t}\n\n\treturn await emitToolCallOutcome(prepared.toolCall, result, isError, emit);\n}\n\nfunction createErrorToolResult(message: string): AgentToolResult<any> {\n\treturn {\n\t\tcontent: [{ type: \"text\", text: message }],\n\t\tdetails: {},\n\t};\n}\n\nasync function emitToolCallOutcome(\n\ttoolCall: AgentToolCall,\n\tresult: AgentToolResult<any>,\n\tisError: boolean,\n\temit: AgentEventSink,\n): Promise<ToolResultMessage> {\n\tawait emit({\n\t\ttype: \"tool_execution_end\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tresult,\n\t\tisError,\n\t});\n\n\tconst toolResultMessage: ToolResultMessage = {\n\t\trole: \"toolResult\",\n\t\ttoolCallId: toolCall.id,\n\t\ttoolName: toolCall.name,\n\t\tcontent: result.content,\n\t\tdetails: result.details,\n\t\tisError,\n\t\ttimestamp: Date.now(),\n\t};\n\n\tawait emit({ type: \"message_start\", message: toolResultMessage });\n\tawait emit({ type: \"message_end\", message: toolResultMessage });\n\treturn toolResultMessage;\n}\n"]}
package/dist/agent.d.ts CHANGED
@@ -69,6 +69,17 @@ export interface AgentOptions {
69
69
  * Default: 1000
70
70
  */
71
71
  streamRetryBaseDelayMs?: number;
72
+ /**
73
+ * Maximum number of times to retry when a turn ends with stopReason "length".
74
+ * Each retry escalates the token budget. Separate from streamRetries.
75
+ * Default: 2
76
+ */
77
+ lengthRetries?: number;
78
+ /**
79
+ * Factor by which to multiply maxTokens on each length retry, clamped to the
80
+ * model's output ceiling. Default: 2
81
+ */
82
+ lengthRetryBudgetMultiplier?: number;
72
83
  /** Called before a tool is executed, after arguments have been validated. */
73
84
  beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
74
85
  /** Called after a tool finishes executing, before final tool events are emitted. */
@@ -105,6 +116,8 @@ export declare class Agent {
105
116
  private _maxRetryDelayMs?;
106
117
  private _streamRetries?;
107
118
  private _streamRetryBaseDelayMs?;
119
+ private _lengthRetries?;
120
+ private _lengthRetryBudgetMultiplier?;
108
121
  private _toolExecution;
109
122
  private _beforeToolCall?;
110
123
  private _afterToolCall?;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent class that uses the agent-loop directly.\n * No transport abstraction - calls streamSimple via the loop.\n */\n\nimport {\n\tgetModel,\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@dreb/ai\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.js\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tStreamFn,\n\tThinkingLevel,\n\tToolExecutionMode,\n} from \"./types.js\";\n\n/**\n * Default convertToLlm: Keep only LLM-compatible messages, convert attachments.\n */\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter((m) => m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\");\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t * Default filters to user/assistant/toolResult and converts attachments.\n\t */\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to context before convertToLlm.\n\t * Use for context pruning, injecting external context, etc.\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Steering mode: \"all\" = send all steering messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Follow-up mode: \"all\" = send all follow-up messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Custom stream function (for proxy backends, etc.). Default uses streamSimple.\n\t */\n\tstreamFn?: StreamFn;\n\n\t/**\n\t * Optional session identifier forwarded to LLM providers.\n\t * Used by providers that support session-based caching (e.g., OpenAI Codex).\n\t */\n\tsessionId?: string;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t * Useful for expiring tokens (e.g., GitHub Copilot OAuth).\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Inspect or replace provider payloads before they are sent.\n\t */\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\n\t/**\n\t * Custom token budgets for thinking levels (token-based providers only).\n\t */\n\tthinkingBudgets?: ThinkingBudgets;\n\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t */\n\ttransport?: Transport;\n\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately,\n\t * allowing higher-level retry logic to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\n\t/** Tool execution mode. Default: \"parallel\" */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/** Called before a tool is executed, after arguments have been validated. */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/** Called after a tool finishes executing, before final tool events are emitted. */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\n\t/**\n\t * Called before each subsequent LLM call in the agent loop.\n\t * If it returns false, the loop exits cleanly.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Optional callback for non-fatal warnings during streaming.\n\t * Forwarded to providers via StreamOptions.onWarning.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tprivate transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tprivate steeringQueue: AgentMessage[] = [];\n\tprivate followUpQueue: AgentMessage[] = [];\n\tprivate steeringMode: \"all\" | \"one-at-a-time\";\n\tprivate followUpMode: \"all\" | \"one-at-a-time\";\n\tpublic streamFn: StreamFn;\n\tprivate _sessionId?: string;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tprivate _onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\tprivate _thinkingBudgets?: ThinkingBudgets;\n\tprivate _transport: Transport;\n\tprivate _maxRetryDelayMs?: number;\n\tprivate _streamRetries?: number;\n\tprivate _streamRetryBaseDelayMs?: number;\n\tprivate _toolExecution: ToolExecutionMode;\n\tprivate _beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tprivate _afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tprivate _shouldContinue?: () => boolean;\n\tprivate _onWarning?: (code: string, message: string) => void;\n\n\tconstructor(opts: AgentOptions = {}) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.convertToLlm = opts.convertToLlm || defaultConvertToLlm;\n\t\tthis.transformContext = opts.transformContext;\n\t\tthis.steeringMode = opts.steeringMode || \"one-at-a-time\";\n\t\tthis.followUpMode = opts.followUpMode || \"one-at-a-time\";\n\t\tthis.streamFn = opts.streamFn || streamSimple;\n\t\tthis._sessionId = opts.sessionId;\n\t\tthis.getApiKey = opts.getApiKey;\n\t\tthis._onPayload = opts.onPayload;\n\t\tthis._thinkingBudgets = opts.thinkingBudgets;\n\t\tthis._transport = opts.transport ?? \"sse\";\n\t\tthis._maxRetryDelayMs = opts.maxRetryDelayMs;\n\t\tthis._streamRetries = opts.streamRetries;\n\t\tthis._streamRetryBaseDelayMs = opts.streamRetryBaseDelayMs;\n\t\tthis._toolExecution = opts.toolExecution ?? \"parallel\";\n\t\tthis._beforeToolCall = opts.beforeToolCall;\n\t\tthis._shouldContinue = opts.shouldContinue;\n\t\tthis._afterToolCall = opts.afterToolCall;\n\t\tthis._onWarning = opts.onWarning;\n\t}\n\n\t/**\n\t * Get the current session ID used for provider caching.\n\t */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Set the session ID for provider caching.\n\t * Call this when switching sessions (new session, branch, resume).\n\t */\n\tset sessionId(value: string | undefined) {\n\t\tthis._sessionId = value;\n\t}\n\n\t/**\n\t * Get the current thinking budgets.\n\t */\n\tget thinkingBudgets(): ThinkingBudgets | undefined {\n\t\treturn this._thinkingBudgets;\n\t}\n\n\t/**\n\t * Set custom thinking budgets for token-based providers.\n\t */\n\tset thinkingBudgets(value: ThinkingBudgets | undefined) {\n\t\tthis._thinkingBudgets = value;\n\t}\n\n\t/**\n\t * Get the current preferred transport.\n\t */\n\tget transport(): Transport {\n\t\treturn this._transport;\n\t}\n\n\t/**\n\t * Set the preferred transport.\n\t */\n\tsetTransport(value: Transport) {\n\t\tthis._transport = value;\n\t}\n\n\t/**\n\t * Get the current max retry delay in milliseconds.\n\t */\n\tget maxRetryDelayMs(): number | undefined {\n\t\treturn this._maxRetryDelayMs;\n\t}\n\n\t/**\n\t * Set the maximum delay to wait for server-requested retries.\n\t * Set to 0 to disable the cap.\n\t */\n\tset maxRetryDelayMs(value: number | undefined) {\n\t\tthis._maxRetryDelayMs = value;\n\t}\n\n\tget toolExecution(): ToolExecutionMode {\n\t\treturn this._toolExecution;\n\t}\n\n\tsetToolExecution(value: ToolExecutionMode) {\n\t\tthis._toolExecution = value;\n\t}\n\n\tsetBeforeToolCall(\n\t\tvalue:\n\t\t\t| ((context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._beforeToolCall = value;\n\t}\n\n\tsetAfterToolCall(\n\t\tvalue:\n\t\t\t| ((context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._afterToolCall = value;\n\t}\n\n\tsetShouldContinue(value: (() => boolean) | undefined) {\n\t\tthis._shouldContinue = value;\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: Model<any>) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.steeringMode = mode;\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.steeringMode;\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.followUpMode = mode;\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.followUpMode;\n\t}\n\n\tsetTools(t: AgentTool<any>[]) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AgentMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AgentMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\t/**\n\t * Queue a steering message while the agent is running.\n\t * Delivered after the current assistant turn finishes executing its tool calls,\n\t * before the next LLM call.\n\t */\n\tsteer(m: AgentMessage) {\n\t\tthis.steeringQueue.push(m);\n\t}\n\n\t/**\n\t * Queue a follow-up message to be processed after the agent finishes.\n\t * Delivered only when agent has no more tool calls or steering messages.\n\t */\n\tfollowUp(m: AgentMessage) {\n\t\tthis.followUpQueue.push(m);\n\t}\n\n\tclearSteeringQueue() {\n\t\tthis.steeringQueue = [];\n\t}\n\n\tclearFollowUpQueue() {\n\t\tthis.followUpQueue = [];\n\t}\n\n\tclearAllQueues() {\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.length > 0 || this.followUpQueue.length > 0;\n\t}\n\n\tprivate dequeueSteeringMessages(): AgentMessage[] {\n\t\tif (this.steeringMode === \"one-at-a-time\") {\n\t\t\tif (this.steeringQueue.length > 0) {\n\t\t\t\tconst first = this.steeringQueue[0];\n\t\t\t\tthis.steeringQueue = this.steeringQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst steering = this.steeringQueue.slice();\n\t\tthis.steeringQueue = [];\n\t\treturn steering;\n\t}\n\n\tprivate dequeueFollowUpMessages(): AgentMessage[] {\n\t\tif (this.followUpMode === \"one-at-a-time\") {\n\t\t\tif (this.followUpQueue.length > 0) {\n\t\t\t\tconst first = this.followUpQueue[0];\n\t\t\t\tthis.followUpQueue = this.followUpQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst followUp = this.followUpQueue.slice();\n\t\tthis.followUpQueue = [];\n\t\treturn followUp;\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\t/** Send a prompt with an AgentMessage */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]) {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tlet msgs: AgentMessage[];\n\n\t\tif (Array.isArray(input)) {\n\t\t\tmsgs = input;\n\t\t} else if (typeof input === \"string\") {\n\t\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\t\tif (images && images.length > 0) {\n\t\t\t\tcontent.push(...images);\n\t\t\t}\n\t\t\tmsgs = [\n\t\t\t\t{\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t];\n\t\t} else {\n\t\t\tmsgs = [input];\n\t\t}\n\n\t\tawait this._runLoop(msgs);\n\t}\n\n\t/**\n\t * Continue from current context (used for retries and resuming queued messages).\n\t */\n\tasync continue() {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst messages = this._state.messages;\n\t\tif (messages.length === 0) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\t\tif (messages[messages.length - 1].role === \"assistant\") {\n\t\t\tconst queuedSteering = this.dequeueSteeringMessages();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this._runLoop(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUp = this.dequeueFollowUpMessages();\n\t\t\tif (queuedFollowUp.length > 0) {\n\t\t\t\tawait this._runLoop(queuedFollowUp);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this._runLoop(undefined);\n\t}\n\n\tprivate _processLoopEvent(event: AgentEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tthis.appendMessage(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && (event.message as any).errorMessage) {\n\t\t\t\t\tthis._state.error = (event.message as any).errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.isStreaming = false;\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tthis.emit(event);\n\t}\n\n\t/**\n\t * Run the agent loop.\n\t * If messages are provided, starts a new conversation turn with those messages.\n\t * Otherwise, continues from existing context.\n\t */\n\tprivate async _runLoop(messages?: AgentMessage[], options?: { skipInitialSteeringPoll?: boolean }) {\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning = this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel;\n\n\t\tconst context: AgentContext = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools,\n\t\t};\n\n\t\tlet skipInitialSteeringPoll = options?.skipInitialSteeringPoll === true;\n\n\t\tconst config: AgentLoopConfig = {\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tsessionId: this._sessionId,\n\t\t\tonPayload: this._onPayload,\n\t\t\ttransport: this._transport,\n\t\t\tthinkingBudgets: this._thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this._maxRetryDelayMs,\n\t\t\tstreamRetries: this._streamRetries,\n\t\t\tstreamRetryBaseDelayMs: this._streamRetryBaseDelayMs,\n\t\t\tonWarning: this._onWarning,\n\t\t\ttoolExecution: this._toolExecution,\n\t\t\tbeforeToolCall: this._beforeToolCall,\n\t\t\tafterToolCall: this._afterToolCall,\n\t\t\tshouldContinue: this._shouldContinue,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.dequeueSteeringMessages();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.dequeueFollowUpMessages(),\n\t\t};\n\n\t\ttry {\n\t\t\tif (messages) {\n\t\t\t\tawait runAgentLoop(\n\t\t\t\t\tmessages,\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait runAgentLoopContinue(\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst errorMsg: AgentMessage = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t} as AgentMessage;\n\n\t\t\tthis.appendMessage(errorMsg);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t\tthis.emit({ type: \"agent_end\", messages: [errorMsg] });\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent class that uses the agent-loop directly.\n * No transport abstraction - calls streamSimple via the loop.\n */\n\nimport {\n\tgetModel,\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@dreb/ai\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.js\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tStreamFn,\n\tThinkingLevel,\n\tToolExecutionMode,\n} from \"./types.js\";\n\n/**\n * Default convertToLlm: Keep only LLM-compatible messages, convert attachments.\n */\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter((m) => m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\");\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t * Default filters to user/assistant/toolResult and converts attachments.\n\t */\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to context before convertToLlm.\n\t * Use for context pruning, injecting external context, etc.\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Steering mode: \"all\" = send all steering messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Follow-up mode: \"all\" = send all follow-up messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Custom stream function (for proxy backends, etc.). Default uses streamSimple.\n\t */\n\tstreamFn?: StreamFn;\n\n\t/**\n\t * Optional session identifier forwarded to LLM providers.\n\t * Used by providers that support session-based caching (e.g., OpenAI Codex).\n\t */\n\tsessionId?: string;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t * Useful for expiring tokens (e.g., GitHub Copilot OAuth).\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Inspect or replace provider payloads before they are sent.\n\t */\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\n\t/**\n\t * Custom token budgets for thinking levels (token-based providers only).\n\t */\n\tthinkingBudgets?: ThinkingBudgets;\n\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t */\n\ttransport?: Transport;\n\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately,\n\t * allowing higher-level retry logic to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\n\t/** Tool execution mode. Default: \"parallel\" */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/**\n\t * Maximum number of times to retry when a turn ends with stopReason \"length\".\n\t * Each retry escalates the token budget. Separate from streamRetries.\n\t * Default: 2\n\t */\n\tlengthRetries?: number;\n\n\t/**\n\t * Factor by which to multiply maxTokens on each length retry, clamped to the\n\t * model's output ceiling. Default: 2\n\t */\n\tlengthRetryBudgetMultiplier?: number;\n\n\t/** Called before a tool is executed, after arguments have been validated. */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/** Called after a tool finishes executing, before final tool events are emitted. */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\n\t/**\n\t * Called before each subsequent LLM call in the agent loop.\n\t * If it returns false, the loop exits cleanly.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Optional callback for non-fatal warnings during streaming.\n\t * Forwarded to providers via StreamOptions.onWarning.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tprivate transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tprivate steeringQueue: AgentMessage[] = [];\n\tprivate followUpQueue: AgentMessage[] = [];\n\tprivate steeringMode: \"all\" | \"one-at-a-time\";\n\tprivate followUpMode: \"all\" | \"one-at-a-time\";\n\tpublic streamFn: StreamFn;\n\tprivate _sessionId?: string;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tprivate _onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\tprivate _thinkingBudgets?: ThinkingBudgets;\n\tprivate _transport: Transport;\n\tprivate _maxRetryDelayMs?: number;\n\tprivate _streamRetries?: number;\n\tprivate _streamRetryBaseDelayMs?: number;\n\tprivate _lengthRetries?: number;\n\tprivate _lengthRetryBudgetMultiplier?: number;\n\tprivate _toolExecution: ToolExecutionMode;\n\tprivate _beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tprivate _afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tprivate _shouldContinue?: () => boolean;\n\tprivate _onWarning?: (code: string, message: string) => void;\n\n\tconstructor(opts: AgentOptions = {}) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.convertToLlm = opts.convertToLlm || defaultConvertToLlm;\n\t\tthis.transformContext = opts.transformContext;\n\t\tthis.steeringMode = opts.steeringMode || \"one-at-a-time\";\n\t\tthis.followUpMode = opts.followUpMode || \"one-at-a-time\";\n\t\tthis.streamFn = opts.streamFn || streamSimple;\n\t\tthis._sessionId = opts.sessionId;\n\t\tthis.getApiKey = opts.getApiKey;\n\t\tthis._onPayload = opts.onPayload;\n\t\tthis._thinkingBudgets = opts.thinkingBudgets;\n\t\tthis._transport = opts.transport ?? \"sse\";\n\t\tthis._maxRetryDelayMs = opts.maxRetryDelayMs;\n\t\tthis._streamRetries = opts.streamRetries;\n\t\tthis._streamRetryBaseDelayMs = opts.streamRetryBaseDelayMs;\n\t\tthis._lengthRetries = opts.lengthRetries;\n\t\tthis._lengthRetryBudgetMultiplier = opts.lengthRetryBudgetMultiplier;\n\t\tthis._toolExecution = opts.toolExecution ?? \"parallel\";\n\t\tthis._beforeToolCall = opts.beforeToolCall;\n\t\tthis._shouldContinue = opts.shouldContinue;\n\t\tthis._afterToolCall = opts.afterToolCall;\n\t\tthis._onWarning = opts.onWarning;\n\t}\n\n\t/**\n\t * Get the current session ID used for provider caching.\n\t */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Set the session ID for provider caching.\n\t * Call this when switching sessions (new session, branch, resume).\n\t */\n\tset sessionId(value: string | undefined) {\n\t\tthis._sessionId = value;\n\t}\n\n\t/**\n\t * Get the current thinking budgets.\n\t */\n\tget thinkingBudgets(): ThinkingBudgets | undefined {\n\t\treturn this._thinkingBudgets;\n\t}\n\n\t/**\n\t * Set custom thinking budgets for token-based providers.\n\t */\n\tset thinkingBudgets(value: ThinkingBudgets | undefined) {\n\t\tthis._thinkingBudgets = value;\n\t}\n\n\t/**\n\t * Get the current preferred transport.\n\t */\n\tget transport(): Transport {\n\t\treturn this._transport;\n\t}\n\n\t/**\n\t * Set the preferred transport.\n\t */\n\tsetTransport(value: Transport) {\n\t\tthis._transport = value;\n\t}\n\n\t/**\n\t * Get the current max retry delay in milliseconds.\n\t */\n\tget maxRetryDelayMs(): number | undefined {\n\t\treturn this._maxRetryDelayMs;\n\t}\n\n\t/**\n\t * Set the maximum delay to wait for server-requested retries.\n\t * Set to 0 to disable the cap.\n\t */\n\tset maxRetryDelayMs(value: number | undefined) {\n\t\tthis._maxRetryDelayMs = value;\n\t}\n\n\tget toolExecution(): ToolExecutionMode {\n\t\treturn this._toolExecution;\n\t}\n\n\tsetToolExecution(value: ToolExecutionMode) {\n\t\tthis._toolExecution = value;\n\t}\n\n\tsetBeforeToolCall(\n\t\tvalue:\n\t\t\t| ((context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._beforeToolCall = value;\n\t}\n\n\tsetAfterToolCall(\n\t\tvalue:\n\t\t\t| ((context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._afterToolCall = value;\n\t}\n\n\tsetShouldContinue(value: (() => boolean) | undefined) {\n\t\tthis._shouldContinue = value;\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: Model<any>) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.steeringMode = mode;\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.steeringMode;\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.followUpMode = mode;\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.followUpMode;\n\t}\n\n\tsetTools(t: AgentTool<any>[]) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AgentMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AgentMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\t/**\n\t * Queue a steering message while the agent is running.\n\t * Delivered after the current assistant turn finishes executing its tool calls,\n\t * before the next LLM call.\n\t */\n\tsteer(m: AgentMessage) {\n\t\tthis.steeringQueue.push(m);\n\t}\n\n\t/**\n\t * Queue a follow-up message to be processed after the agent finishes.\n\t * Delivered only when agent has no more tool calls or steering messages.\n\t */\n\tfollowUp(m: AgentMessage) {\n\t\tthis.followUpQueue.push(m);\n\t}\n\n\tclearSteeringQueue() {\n\t\tthis.steeringQueue = [];\n\t}\n\n\tclearFollowUpQueue() {\n\t\tthis.followUpQueue = [];\n\t}\n\n\tclearAllQueues() {\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.length > 0 || this.followUpQueue.length > 0;\n\t}\n\n\tprivate dequeueSteeringMessages(): AgentMessage[] {\n\t\tif (this.steeringMode === \"one-at-a-time\") {\n\t\t\tif (this.steeringQueue.length > 0) {\n\t\t\t\tconst first = this.steeringQueue[0];\n\t\t\t\tthis.steeringQueue = this.steeringQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst steering = this.steeringQueue.slice();\n\t\tthis.steeringQueue = [];\n\t\treturn steering;\n\t}\n\n\tprivate dequeueFollowUpMessages(): AgentMessage[] {\n\t\tif (this.followUpMode === \"one-at-a-time\") {\n\t\t\tif (this.followUpQueue.length > 0) {\n\t\t\t\tconst first = this.followUpQueue[0];\n\t\t\t\tthis.followUpQueue = this.followUpQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst followUp = this.followUpQueue.slice();\n\t\tthis.followUpQueue = [];\n\t\treturn followUp;\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\t/** Send a prompt with an AgentMessage */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]) {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tlet msgs: AgentMessage[];\n\n\t\tif (Array.isArray(input)) {\n\t\t\tmsgs = input;\n\t\t} else if (typeof input === \"string\") {\n\t\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\t\tif (images && images.length > 0) {\n\t\t\t\tcontent.push(...images);\n\t\t\t}\n\t\t\tmsgs = [\n\t\t\t\t{\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t];\n\t\t} else {\n\t\t\tmsgs = [input];\n\t\t}\n\n\t\tawait this._runLoop(msgs);\n\t}\n\n\t/**\n\t * Continue from current context (used for retries and resuming queued messages).\n\t */\n\tasync continue() {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst messages = this._state.messages;\n\t\tif (messages.length === 0) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\t\tif (messages[messages.length - 1].role === \"assistant\") {\n\t\t\tconst queuedSteering = this.dequeueSteeringMessages();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this._runLoop(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUp = this.dequeueFollowUpMessages();\n\t\t\tif (queuedFollowUp.length > 0) {\n\t\t\t\tawait this._runLoop(queuedFollowUp);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this._runLoop(undefined);\n\t}\n\n\tprivate _processLoopEvent(event: AgentEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tthis.appendMessage(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && (event.message as any).errorMessage) {\n\t\t\t\t\tthis._state.error = (event.message as any).errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.isStreaming = false;\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tthis.emit(event);\n\t}\n\n\t/**\n\t * Run the agent loop.\n\t * If messages are provided, starts a new conversation turn with those messages.\n\t * Otherwise, continues from existing context.\n\t */\n\tprivate async _runLoop(messages?: AgentMessage[], options?: { skipInitialSteeringPoll?: boolean }) {\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning = this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel;\n\n\t\tconst context: AgentContext = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools,\n\t\t};\n\n\t\tlet skipInitialSteeringPoll = options?.skipInitialSteeringPoll === true;\n\n\t\tconst config: AgentLoopConfig = {\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tsessionId: this._sessionId,\n\t\t\tonPayload: this._onPayload,\n\t\t\ttransport: this._transport,\n\t\t\tthinkingBudgets: this._thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this._maxRetryDelayMs,\n\t\t\tstreamRetries: this._streamRetries,\n\t\t\tstreamRetryBaseDelayMs: this._streamRetryBaseDelayMs,\n\t\t\tlengthRetries: this._lengthRetries,\n\t\t\tlengthRetryBudgetMultiplier: this._lengthRetryBudgetMultiplier,\n\t\t\tonWarning: this._onWarning,\n\t\t\ttoolExecution: this._toolExecution,\n\t\t\tbeforeToolCall: this._beforeToolCall,\n\t\t\tafterToolCall: this._afterToolCall,\n\t\t\tshouldContinue: this._shouldContinue,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.dequeueSteeringMessages();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.dequeueFollowUpMessages(),\n\t\t};\n\n\t\ttry {\n\t\t\tif (messages) {\n\t\t\t\tawait runAgentLoop(\n\t\t\t\t\tmessages,\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait runAgentLoopContinue(\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst errorMsg: AgentMessage = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t} as AgentMessage;\n\n\t\t\tthis.appendMessage(errorMsg);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t\tthis.emit({ type: \"agent_end\", messages: [errorMsg] });\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
package/dist/agent.js CHANGED
@@ -41,6 +41,8 @@ export class Agent {
41
41
  _maxRetryDelayMs;
42
42
  _streamRetries;
43
43
  _streamRetryBaseDelayMs;
44
+ _lengthRetries;
45
+ _lengthRetryBudgetMultiplier;
44
46
  _toolExecution;
45
47
  _beforeToolCall;
46
48
  _afterToolCall;
@@ -61,6 +63,8 @@ export class Agent {
61
63
  this._maxRetryDelayMs = opts.maxRetryDelayMs;
62
64
  this._streamRetries = opts.streamRetries;
63
65
  this._streamRetryBaseDelayMs = opts.streamRetryBaseDelayMs;
66
+ this._lengthRetries = opts.lengthRetries;
67
+ this._lengthRetryBudgetMultiplier = opts.lengthRetryBudgetMultiplier;
64
68
  this._toolExecution = opts.toolExecution ?? "parallel";
65
69
  this._beforeToolCall = opts.beforeToolCall;
66
70
  this._shouldContinue = opts.shouldContinue;
@@ -366,6 +370,8 @@ export class Agent {
366
370
  maxRetryDelayMs: this._maxRetryDelayMs,
367
371
  streamRetries: this._streamRetries,
368
372
  streamRetryBaseDelayMs: this._streamRetryBaseDelayMs,
373
+ lengthRetries: this._lengthRetries,
374
+ lengthRetryBudgetMultiplier: this._lengthRetryBudgetMultiplier,
369
375
  onWarning: this._onWarning,
370
376
  toolExecution: this._toolExecution,
371
377
  beforeToolCall: this._beforeToolCall,
package/dist/agent.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,QAAQ,EAKR,YAAY,GAIZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAiBrE;;GAEG;AACH,SAAS,mBAAmB,CAAC,QAAwB,EAAa;IACjE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AAAA,CACtG;AAqGD,MAAM,OAAO,KAAK;IACT,MAAM,GAAe;QAC5B,YAAY,EAAE,EAAE;QAChB,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;QAClD,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE,IAAI;QACnB,gBAAgB,EAAE,IAAI,GAAG,EAAU;QACnC,KAAK,EAAE,SAAS;KAChB,CAAC;IAEM,SAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;IAC/C,eAAe,CAAmB;IAClC,YAAY,CAA+D;IAC3E,gBAAgB,CAA+E;IAC/F,aAAa,GAAmB,EAAE,CAAC;IACnC,aAAa,GAAmB,EAAE,CAAC;IACnC,YAAY,CAA0B;IACtC,YAAY,CAA0B;IACvC,QAAQ,CAAW;IAClB,UAAU,CAAU;IACrB,SAAS,CAA0E;IAClF,UAAU,CAAoC;IAC9C,aAAa,CAAiB;IAC9B,oBAAoB,CAAc;IAClC,gBAAgB,CAAmB;IACnC,UAAU,CAAY;IACtB,gBAAgB,CAAU;IAC1B,cAAc,CAAU;IACxB,uBAAuB,CAAU;IACjC,cAAc,CAAoB;IAClC,eAAe,CAGwB;IACvC,cAAc,CAGwB;IACtC,eAAe,CAAiB;IAChC,UAAU,CAA2C;IAE7D,YAAY,IAAI,GAAiB,EAAE,EAAE;QACpC,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACvD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,mBAAmB,CAAC;QAC7D,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC9C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,eAAe,CAAC;QACzD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,eAAe,CAAC;QACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC;QAC9C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,eAAe,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;QAC1C,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,eAAe,CAAC;QAC7C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC;QACzC,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,sBAAsB,CAAC;QAC3D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,IAAI,UAAU,CAAC;QACvD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC;QAC3C,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC;QACzC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;IAAA,CACjC;IAED;;OAEG;IACH,IAAI,SAAS,GAAuB;QACnC,OAAO,IAAI,CAAC,UAAU,CAAC;IAAA,CACvB;IAED;;;OAGG;IACH,IAAI,SAAS,CAAC,KAAyB,EAAE;QACxC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAAA,CACxB;IAED;;OAEG;IACH,IAAI,eAAe,GAAgC;QAClD,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAAA,CAC7B;IAED;;OAEG;IACH,IAAI,eAAe,CAAC,KAAkC,EAAE;QACvD,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAAA,CAC9B;IAED;;OAEG;IACH,IAAI,SAAS,GAAc;QAC1B,OAAO,IAAI,CAAC,UAAU,CAAC;IAAA,CACvB;IAED;;OAEG;IACH,YAAY,CAAC,KAAgB,EAAE;QAC9B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAAA,CACxB;IAED;;OAEG;IACH,IAAI,eAAe,GAAuB;QACzC,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAAA,CAC7B;IAED;;;OAGG;IACH,IAAI,eAAe,CAAC,KAAyB,EAAE;QAC9C,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;IAAA,CAC9B;IAED,IAAI,aAAa,GAAsB;QACtC,OAAO,IAAI,CAAC,cAAc,CAAC;IAAA,CAC3B;IAED,gBAAgB,CAAC,KAAwB,EAAE;QAC1C,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;IAAA,CAC5B;IAED,iBAAiB,CAChB,KAEY,EACX;QACD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;IAAA,CAC7B;IAED,gBAAgB,CACf,KAEY,EACX;QACD,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;IAAA,CAC5B;IAED,iBAAiB,CAAC,KAAkC,EAAE;QACrD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;IAAA,CAC7B;IAED,IAAI,KAAK,GAAe;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC;IAAA,CACnB;IAED,SAAS,CAAC,EAA2B,EAAc;QAClD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAAA,CACvC;IAED,iBAAiB;IACjB,eAAe,CAAC,CAAS,EAAE;QAC1B,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;IAAA,CAC7B;IAED,QAAQ,CAAC,CAAa,EAAE;QACvB,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;IAAA,CACtB;IAED,gBAAgB,CAAC,CAAgB,EAAE;QAClC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC;IAAA,CAC9B;IAED,eAAe,CAAC,IAA6B,EAAE;QAC9C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAAA,CACzB;IAED,eAAe,GAA4B;QAC1C,OAAO,IAAI,CAAC,YAAY,CAAC;IAAA,CACzB;IAED,eAAe,CAAC,IAA6B,EAAE;QAC9C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAAA,CACzB;IAED,eAAe,GAA4B;QAC1C,OAAO,IAAI,CAAC,YAAY,CAAC;IAAA,CACzB;IAED,QAAQ,CAAC,CAAmB,EAAE;QAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;IAAA,CACtB;IAED,eAAe,CAAC,EAAkB,EAAE;QACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;IAAA,CAClC;IAED,aAAa,CAAC,CAAe,EAAE;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAAA,CACpD;IAED;;;;OAIG;IACH,KAAK,CAAC,CAAe,EAAE;QACtB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAAA,CAC3B;IAED;;;OAGG;IACH,QAAQ,CAAC,CAAe,EAAE;QACzB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAAA,CAC3B;IAED,kBAAkB,GAAG;QACpB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IAAA,CACxB;IAED,kBAAkB,GAAG;QACpB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IAAA,CACxB;IAED,cAAc,GAAG;QAChB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IAAA,CACxB;IAED,iBAAiB,GAAY;QAC5B,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IAAA,CACtE;IAEO,uBAAuB,GAAmB;QACjD,IAAI,IAAI,CAAC,YAAY,KAAK,eAAe,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;gBACpC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACjD,OAAO,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;YACD,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC5C,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAAA,CAChB;IAEO,uBAAuB,GAAmB;QACjD,IAAI,IAAI,CAAC,YAAY,KAAK,eAAe,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;gBACpC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACjD,OAAO,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;YACD,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC5C,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAAA,CAChB;IAED,aAAa,GAAG;QACf,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;IAAA,CAC1B;IAED,KAAK,GAAG;QACP,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,CAAC;IAAA,CAC9B;IAED,WAAW,GAAkB;QAC5B,OAAO,IAAI,CAAC,aAAa,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAAA,CAC/C;IAED,KAAK,GAAG;QACP,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;QAC9B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IAAA,CACxB;IAKD,KAAK,CAAC,MAAM,CAAC,KAA6C,EAAE,MAAuB,EAAE;QACpF,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACd,4GAA4G,CAC5G,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAEnD,IAAI,IAAoB,CAAC;QAEzB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,GAAG,KAAK,CAAC;QACd,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,OAAO,GAAsC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACnF,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;YACzB,CAAC;YACD,IAAI,GAAG;gBACN;oBACC,IAAI,EAAE,MAAM;oBACZ,OAAO;oBACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;iBACrB;aACD,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAAA,CAC1B;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,GAAG;QAChB,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACjD,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACxD,MAAM,cAAc,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACtD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvE,OAAO;YACR,CAAC;YAED,MAAM,cAAc,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACtD,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;gBACpC,OAAO;YACR,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAAA,CAC/B;IAEO,iBAAiB,CAAC,KAAiB,EAAQ;QAClD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,eAAe;gBACnB,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC1C,MAAM;YAEP,KAAK,gBAAgB;gBACpB,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC1C,MAAM;YAEP,KAAK,aAAa;gBACjB,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;gBACjC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAClC,MAAM;YAEP,KAAK,sBAAsB,EAAE,CAAC;gBAC7B,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC/D,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;gBAChD,MAAM;YACP,CAAC;YAED,KAAK,oBAAoB,EAAE,CAAC;gBAC3B,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC/D,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;gBAChD,MAAM;YACP,CAAC;YAED,KAAK,UAAU;gBACd,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,IAAK,KAAK,CAAC,OAAe,CAAC,YAAY,EAAE,CAAC;oBAC/E,IAAI,CAAC,MAAM,CAAC,KAAK,GAAI,KAAK,CAAC,OAAe,CAAC,YAAY,CAAC;gBACzD,CAAC;gBACD,MAAM;YAEP,KAAK,WAAW;gBACf,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;gBACjC,MAAM;QACR,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAAA,CACjB;IAED;;;;OAIG;IACK,KAAK,CAAC,QAAQ,CAAC,QAAyB,EAAE,OAA+C,EAAE;QAClG,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAEnD,IAAI,CAAC,aAAa,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACnD,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC;QAAA,CACpC,CAAC,CAAC;QAEH,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,SAAS,CAAC;QAE9B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QAE9F,MAAM,OAAO,GAAiB;YAC7B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;YACtC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE;YACtC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK;SACxB,CAAC;QAEF,IAAI,uBAAuB,GAAG,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAC;QAExE,MAAM,MAAM,GAAoB;YAC/B,KAAK;YACL,SAAS;YACT,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,eAAe,EAAE,IAAI,CAAC,gBAAgB;YACtC,eAAe,EAAE,IAAI,CAAC,gBAAgB;YACtC,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,sBAAsB,EAAE,IAAI,CAAC,uBAAuB;YACpD,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,cAAc,EAAE,IAAI,CAAC,eAAe;YACpC,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,cAAc,EAAE,IAAI,CAAC,eAAe;YACpC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC;gBAChC,IAAI,uBAAuB,EAAE,CAAC;oBAC7B,uBAAuB,GAAG,KAAK,CAAC;oBAChC,OAAO,EAAE,CAAC;gBACX,CAAC;gBACD,OAAO,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAAA,CACtC;YACD,mBAAmB,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE;SAC/D,CAAC;QAEF,IAAI,CAAC;YACJ,IAAI,QAAQ,EAAE,CAAC;gBACd,MAAM,YAAY,CACjB,QAAQ,EACR,OAAO,EACP,MAAM,EACN,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC9C,IAAI,CAAC,eAAe,CAAC,MAAM,EAC3B,IAAI,CAAC,QAAQ,CACb,CAAC;YACH,CAAC;iBAAM,CAAC;gBACP,MAAM,oBAAoB,CACzB,OAAO,EACP,MAAM,EACN,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAC9C,IAAI,CAAC,eAAe,CAAC,MAAM,EAC3B,IAAI,CAAC,QAAQ,CACb,CAAC;YACH,CAAC;QACF,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YACnB,MAAM,QAAQ,GAAiB;gBAC9B,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;gBACrC,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,KAAK,EAAE,KAAK,CAAC,EAAE;gBACf,KAAK,EAAE;oBACN,KAAK,EAAE,CAAC;oBACR,MAAM,EAAE,CAAC;oBACT,SAAS,EAAE,CAAC;oBACZ,UAAU,EAAE,CAAC;oBACb,WAAW,EAAE,CAAC;oBACd,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;iBACpE;gBACD,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;gBACtE,YAAY,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC;gBACzC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACL,CAAC;YAElB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;YAChD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxD,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,KAAK,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;YACjD,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;QACvC,CAAC;IAAA,CACD;IAEO,IAAI,CAAC,CAAa,EAAE;QAC3B,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACvC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACb,CAAC;IAAA,CACD;CACD","sourcesContent":["/**\n * Agent class that uses the agent-loop directly.\n * No transport abstraction - calls streamSimple via the loop.\n */\n\nimport {\n\tgetModel,\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@dreb/ai\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.js\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tStreamFn,\n\tThinkingLevel,\n\tToolExecutionMode,\n} from \"./types.js\";\n\n/**\n * Default convertToLlm: Keep only LLM-compatible messages, convert attachments.\n */\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter((m) => m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\");\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t * Default filters to user/assistant/toolResult and converts attachments.\n\t */\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to context before convertToLlm.\n\t * Use for context pruning, injecting external context, etc.\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Steering mode: \"all\" = send all steering messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Follow-up mode: \"all\" = send all follow-up messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Custom stream function (for proxy backends, etc.). Default uses streamSimple.\n\t */\n\tstreamFn?: StreamFn;\n\n\t/**\n\t * Optional session identifier forwarded to LLM providers.\n\t * Used by providers that support session-based caching (e.g., OpenAI Codex).\n\t */\n\tsessionId?: string;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t * Useful for expiring tokens (e.g., GitHub Copilot OAuth).\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Inspect or replace provider payloads before they are sent.\n\t */\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\n\t/**\n\t * Custom token budgets for thinking levels (token-based providers only).\n\t */\n\tthinkingBudgets?: ThinkingBudgets;\n\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t */\n\ttransport?: Transport;\n\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately,\n\t * allowing higher-level retry logic to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\n\t/** Tool execution mode. Default: \"parallel\" */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/** Called before a tool is executed, after arguments have been validated. */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/** Called after a tool finishes executing, before final tool events are emitted. */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\n\t/**\n\t * Called before each subsequent LLM call in the agent loop.\n\t * If it returns false, the loop exits cleanly.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Optional callback for non-fatal warnings during streaming.\n\t * Forwarded to providers via StreamOptions.onWarning.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tprivate transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tprivate steeringQueue: AgentMessage[] = [];\n\tprivate followUpQueue: AgentMessage[] = [];\n\tprivate steeringMode: \"all\" | \"one-at-a-time\";\n\tprivate followUpMode: \"all\" | \"one-at-a-time\";\n\tpublic streamFn: StreamFn;\n\tprivate _sessionId?: string;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tprivate _onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\tprivate _thinkingBudgets?: ThinkingBudgets;\n\tprivate _transport: Transport;\n\tprivate _maxRetryDelayMs?: number;\n\tprivate _streamRetries?: number;\n\tprivate _streamRetryBaseDelayMs?: number;\n\tprivate _toolExecution: ToolExecutionMode;\n\tprivate _beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tprivate _afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tprivate _shouldContinue?: () => boolean;\n\tprivate _onWarning?: (code: string, message: string) => void;\n\n\tconstructor(opts: AgentOptions = {}) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.convertToLlm = opts.convertToLlm || defaultConvertToLlm;\n\t\tthis.transformContext = opts.transformContext;\n\t\tthis.steeringMode = opts.steeringMode || \"one-at-a-time\";\n\t\tthis.followUpMode = opts.followUpMode || \"one-at-a-time\";\n\t\tthis.streamFn = opts.streamFn || streamSimple;\n\t\tthis._sessionId = opts.sessionId;\n\t\tthis.getApiKey = opts.getApiKey;\n\t\tthis._onPayload = opts.onPayload;\n\t\tthis._thinkingBudgets = opts.thinkingBudgets;\n\t\tthis._transport = opts.transport ?? \"sse\";\n\t\tthis._maxRetryDelayMs = opts.maxRetryDelayMs;\n\t\tthis._streamRetries = opts.streamRetries;\n\t\tthis._streamRetryBaseDelayMs = opts.streamRetryBaseDelayMs;\n\t\tthis._toolExecution = opts.toolExecution ?? \"parallel\";\n\t\tthis._beforeToolCall = opts.beforeToolCall;\n\t\tthis._shouldContinue = opts.shouldContinue;\n\t\tthis._afterToolCall = opts.afterToolCall;\n\t\tthis._onWarning = opts.onWarning;\n\t}\n\n\t/**\n\t * Get the current session ID used for provider caching.\n\t */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Set the session ID for provider caching.\n\t * Call this when switching sessions (new session, branch, resume).\n\t */\n\tset sessionId(value: string | undefined) {\n\t\tthis._sessionId = value;\n\t}\n\n\t/**\n\t * Get the current thinking budgets.\n\t */\n\tget thinkingBudgets(): ThinkingBudgets | undefined {\n\t\treturn this._thinkingBudgets;\n\t}\n\n\t/**\n\t * Set custom thinking budgets for token-based providers.\n\t */\n\tset thinkingBudgets(value: ThinkingBudgets | undefined) {\n\t\tthis._thinkingBudgets = value;\n\t}\n\n\t/**\n\t * Get the current preferred transport.\n\t */\n\tget transport(): Transport {\n\t\treturn this._transport;\n\t}\n\n\t/**\n\t * Set the preferred transport.\n\t */\n\tsetTransport(value: Transport) {\n\t\tthis._transport = value;\n\t}\n\n\t/**\n\t * Get the current max retry delay in milliseconds.\n\t */\n\tget maxRetryDelayMs(): number | undefined {\n\t\treturn this._maxRetryDelayMs;\n\t}\n\n\t/**\n\t * Set the maximum delay to wait for server-requested retries.\n\t * Set to 0 to disable the cap.\n\t */\n\tset maxRetryDelayMs(value: number | undefined) {\n\t\tthis._maxRetryDelayMs = value;\n\t}\n\n\tget toolExecution(): ToolExecutionMode {\n\t\treturn this._toolExecution;\n\t}\n\n\tsetToolExecution(value: ToolExecutionMode) {\n\t\tthis._toolExecution = value;\n\t}\n\n\tsetBeforeToolCall(\n\t\tvalue:\n\t\t\t| ((context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._beforeToolCall = value;\n\t}\n\n\tsetAfterToolCall(\n\t\tvalue:\n\t\t\t| ((context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._afterToolCall = value;\n\t}\n\n\tsetShouldContinue(value: (() => boolean) | undefined) {\n\t\tthis._shouldContinue = value;\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: Model<any>) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.steeringMode = mode;\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.steeringMode;\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.followUpMode = mode;\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.followUpMode;\n\t}\n\n\tsetTools(t: AgentTool<any>[]) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AgentMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AgentMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\t/**\n\t * Queue a steering message while the agent is running.\n\t * Delivered after the current assistant turn finishes executing its tool calls,\n\t * before the next LLM call.\n\t */\n\tsteer(m: AgentMessage) {\n\t\tthis.steeringQueue.push(m);\n\t}\n\n\t/**\n\t * Queue a follow-up message to be processed after the agent finishes.\n\t * Delivered only when agent has no more tool calls or steering messages.\n\t */\n\tfollowUp(m: AgentMessage) {\n\t\tthis.followUpQueue.push(m);\n\t}\n\n\tclearSteeringQueue() {\n\t\tthis.steeringQueue = [];\n\t}\n\n\tclearFollowUpQueue() {\n\t\tthis.followUpQueue = [];\n\t}\n\n\tclearAllQueues() {\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.length > 0 || this.followUpQueue.length > 0;\n\t}\n\n\tprivate dequeueSteeringMessages(): AgentMessage[] {\n\t\tif (this.steeringMode === \"one-at-a-time\") {\n\t\t\tif (this.steeringQueue.length > 0) {\n\t\t\t\tconst first = this.steeringQueue[0];\n\t\t\t\tthis.steeringQueue = this.steeringQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst steering = this.steeringQueue.slice();\n\t\tthis.steeringQueue = [];\n\t\treturn steering;\n\t}\n\n\tprivate dequeueFollowUpMessages(): AgentMessage[] {\n\t\tif (this.followUpMode === \"one-at-a-time\") {\n\t\t\tif (this.followUpQueue.length > 0) {\n\t\t\t\tconst first = this.followUpQueue[0];\n\t\t\t\tthis.followUpQueue = this.followUpQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst followUp = this.followUpQueue.slice();\n\t\tthis.followUpQueue = [];\n\t\treturn followUp;\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\t/** Send a prompt with an AgentMessage */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]) {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tlet msgs: AgentMessage[];\n\n\t\tif (Array.isArray(input)) {\n\t\t\tmsgs = input;\n\t\t} else if (typeof input === \"string\") {\n\t\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\t\tif (images && images.length > 0) {\n\t\t\t\tcontent.push(...images);\n\t\t\t}\n\t\t\tmsgs = [\n\t\t\t\t{\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t];\n\t\t} else {\n\t\t\tmsgs = [input];\n\t\t}\n\n\t\tawait this._runLoop(msgs);\n\t}\n\n\t/**\n\t * Continue from current context (used for retries and resuming queued messages).\n\t */\n\tasync continue() {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst messages = this._state.messages;\n\t\tif (messages.length === 0) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\t\tif (messages[messages.length - 1].role === \"assistant\") {\n\t\t\tconst queuedSteering = this.dequeueSteeringMessages();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this._runLoop(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUp = this.dequeueFollowUpMessages();\n\t\t\tif (queuedFollowUp.length > 0) {\n\t\t\t\tawait this._runLoop(queuedFollowUp);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this._runLoop(undefined);\n\t}\n\n\tprivate _processLoopEvent(event: AgentEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tthis.appendMessage(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && (event.message as any).errorMessage) {\n\t\t\t\t\tthis._state.error = (event.message as any).errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.isStreaming = false;\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tthis.emit(event);\n\t}\n\n\t/**\n\t * Run the agent loop.\n\t * If messages are provided, starts a new conversation turn with those messages.\n\t * Otherwise, continues from existing context.\n\t */\n\tprivate async _runLoop(messages?: AgentMessage[], options?: { skipInitialSteeringPoll?: boolean }) {\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning = this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel;\n\n\t\tconst context: AgentContext = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools,\n\t\t};\n\n\t\tlet skipInitialSteeringPoll = options?.skipInitialSteeringPoll === true;\n\n\t\tconst config: AgentLoopConfig = {\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tsessionId: this._sessionId,\n\t\t\tonPayload: this._onPayload,\n\t\t\ttransport: this._transport,\n\t\t\tthinkingBudgets: this._thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this._maxRetryDelayMs,\n\t\t\tstreamRetries: this._streamRetries,\n\t\t\tstreamRetryBaseDelayMs: this._streamRetryBaseDelayMs,\n\t\t\tonWarning: this._onWarning,\n\t\t\ttoolExecution: this._toolExecution,\n\t\t\tbeforeToolCall: this._beforeToolCall,\n\t\t\tafterToolCall: this._afterToolCall,\n\t\t\tshouldContinue: this._shouldContinue,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.dequeueSteeringMessages();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.dequeueFollowUpMessages(),\n\t\t};\n\n\t\ttry {\n\t\t\tif (messages) {\n\t\t\t\tawait runAgentLoop(\n\t\t\t\t\tmessages,\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait runAgentLoopContinue(\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst errorMsg: AgentMessage = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t} as AgentMessage;\n\n\t\t\tthis.appendMessage(errorMsg);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t\tthis.emit({ type: \"agent_end\", messages: [errorMsg] });\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Agent class that uses the agent-loop directly.\n * No transport abstraction - calls streamSimple via the loop.\n */\n\nimport {\n\tgetModel,\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@dreb/ai\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.js\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tStreamFn,\n\tThinkingLevel,\n\tToolExecutionMode,\n} from \"./types.js\";\n\n/**\n * Default convertToLlm: Keep only LLM-compatible messages, convert attachments.\n */\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter((m) => m.role === \"user\" || m.role === \"assistant\" || m.role === \"toolResult\");\n}\n\nexport interface AgentOptions {\n\tinitialState?: Partial<AgentState>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t * Default filters to user/assistant/toolResult and converts attachments.\n\t */\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to context before convertToLlm.\n\t * Use for context pruning, injecting external context, etc.\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Steering mode: \"all\" = send all steering messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Follow-up mode: \"all\" = send all follow-up messages at once, \"one-at-a-time\" = one per turn\n\t */\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\n\t/**\n\t * Custom stream function (for proxy backends, etc.). Default uses streamSimple.\n\t */\n\tstreamFn?: StreamFn;\n\n\t/**\n\t * Optional session identifier forwarded to LLM providers.\n\t * Used by providers that support session-based caching (e.g., OpenAI Codex).\n\t */\n\tsessionId?: string;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t * Useful for expiring tokens (e.g., GitHub Copilot OAuth).\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Inspect or replace provider payloads before they are sent.\n\t */\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\n\t/**\n\t * Custom token budgets for thinking levels (token-based providers only).\n\t */\n\tthinkingBudgets?: ThinkingBudgets;\n\n\t/**\n\t * Preferred transport for providers that support multiple transports.\n\t */\n\ttransport?: Transport;\n\n\t/**\n\t * Maximum delay in milliseconds to wait for a retry when the server requests a long wait.\n\t * If the server's requested delay exceeds this value, the request fails immediately,\n\t * allowing higher-level retry logic to handle it with user visibility.\n\t * Default: 60000 (60 seconds). Set to 0 to disable the cap.\n\t */\n\tmaxRetryDelayMs?: number;\n\n\t/** Tool execution mode. Default: \"parallel\" */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/**\n\t * Maximum number of times to retry when a turn ends with stopReason \"length\".\n\t * Each retry escalates the token budget. Separate from streamRetries.\n\t * Default: 2\n\t */\n\tlengthRetries?: number;\n\n\t/**\n\t * Factor by which to multiply maxTokens on each length retry, clamped to the\n\t * model's output ceiling. Default: 2\n\t */\n\tlengthRetryBudgetMultiplier?: number;\n\n\t/** Called before a tool is executed, after arguments have been validated. */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/** Called after a tool finishes executing, before final tool events are emitted. */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\n\t/**\n\t * Called before each subsequent LLM call in the agent loop.\n\t * If it returns false, the loop exits cleanly.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Optional callback for non-fatal warnings during streaming.\n\t * Forwarded to providers via StreamOptions.onWarning.\n\t */\n\tonWarning?: (code: string, message: string) => void;\n}\n\nexport class Agent {\n\tprivate _state: AgentState = {\n\t\tsystemPrompt: \"\",\n\t\tmodel: getModel(\"google\", \"gemini-2.5-flash-lite\"),\n\t\tthinkingLevel: \"off\",\n\t\ttools: [],\n\t\tmessages: [],\n\t\tisStreaming: false,\n\t\tstreamMessage: null,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terror: undefined,\n\t};\n\n\tprivate listeners = new Set<(e: AgentEvent) => void>();\n\tprivate abortController?: AbortController;\n\tprivate convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tprivate transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tprivate steeringQueue: AgentMessage[] = [];\n\tprivate followUpQueue: AgentMessage[] = [];\n\tprivate steeringMode: \"all\" | \"one-at-a-time\";\n\tprivate followUpMode: \"all\" | \"one-at-a-time\";\n\tpublic streamFn: StreamFn;\n\tprivate _sessionId?: string;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tprivate _onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tprivate runningPrompt?: Promise<void>;\n\tprivate resolveRunningPrompt?: () => void;\n\tprivate _thinkingBudgets?: ThinkingBudgets;\n\tprivate _transport: Transport;\n\tprivate _maxRetryDelayMs?: number;\n\tprivate _streamRetries?: number;\n\tprivate _streamRetryBaseDelayMs?: number;\n\tprivate _lengthRetries?: number;\n\tprivate _lengthRetryBudgetMultiplier?: number;\n\tprivate _toolExecution: ToolExecutionMode;\n\tprivate _beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tprivate _afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tprivate _shouldContinue?: () => boolean;\n\tprivate _onWarning?: (code: string, message: string) => void;\n\n\tconstructor(opts: AgentOptions = {}) {\n\t\tthis._state = { ...this._state, ...opts.initialState };\n\t\tthis.convertToLlm = opts.convertToLlm || defaultConvertToLlm;\n\t\tthis.transformContext = opts.transformContext;\n\t\tthis.steeringMode = opts.steeringMode || \"one-at-a-time\";\n\t\tthis.followUpMode = opts.followUpMode || \"one-at-a-time\";\n\t\tthis.streamFn = opts.streamFn || streamSimple;\n\t\tthis._sessionId = opts.sessionId;\n\t\tthis.getApiKey = opts.getApiKey;\n\t\tthis._onPayload = opts.onPayload;\n\t\tthis._thinkingBudgets = opts.thinkingBudgets;\n\t\tthis._transport = opts.transport ?? \"sse\";\n\t\tthis._maxRetryDelayMs = opts.maxRetryDelayMs;\n\t\tthis._streamRetries = opts.streamRetries;\n\t\tthis._streamRetryBaseDelayMs = opts.streamRetryBaseDelayMs;\n\t\tthis._lengthRetries = opts.lengthRetries;\n\t\tthis._lengthRetryBudgetMultiplier = opts.lengthRetryBudgetMultiplier;\n\t\tthis._toolExecution = opts.toolExecution ?? \"parallel\";\n\t\tthis._beforeToolCall = opts.beforeToolCall;\n\t\tthis._shouldContinue = opts.shouldContinue;\n\t\tthis._afterToolCall = opts.afterToolCall;\n\t\tthis._onWarning = opts.onWarning;\n\t}\n\n\t/**\n\t * Get the current session ID used for provider caching.\n\t */\n\tget sessionId(): string | undefined {\n\t\treturn this._sessionId;\n\t}\n\n\t/**\n\t * Set the session ID for provider caching.\n\t * Call this when switching sessions (new session, branch, resume).\n\t */\n\tset sessionId(value: string | undefined) {\n\t\tthis._sessionId = value;\n\t}\n\n\t/**\n\t * Get the current thinking budgets.\n\t */\n\tget thinkingBudgets(): ThinkingBudgets | undefined {\n\t\treturn this._thinkingBudgets;\n\t}\n\n\t/**\n\t * Set custom thinking budgets for token-based providers.\n\t */\n\tset thinkingBudgets(value: ThinkingBudgets | undefined) {\n\t\tthis._thinkingBudgets = value;\n\t}\n\n\t/**\n\t * Get the current preferred transport.\n\t */\n\tget transport(): Transport {\n\t\treturn this._transport;\n\t}\n\n\t/**\n\t * Set the preferred transport.\n\t */\n\tsetTransport(value: Transport) {\n\t\tthis._transport = value;\n\t}\n\n\t/**\n\t * Get the current max retry delay in milliseconds.\n\t */\n\tget maxRetryDelayMs(): number | undefined {\n\t\treturn this._maxRetryDelayMs;\n\t}\n\n\t/**\n\t * Set the maximum delay to wait for server-requested retries.\n\t * Set to 0 to disable the cap.\n\t */\n\tset maxRetryDelayMs(value: number | undefined) {\n\t\tthis._maxRetryDelayMs = value;\n\t}\n\n\tget toolExecution(): ToolExecutionMode {\n\t\treturn this._toolExecution;\n\t}\n\n\tsetToolExecution(value: ToolExecutionMode) {\n\t\tthis._toolExecution = value;\n\t}\n\n\tsetBeforeToolCall(\n\t\tvalue:\n\t\t\t| ((context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._beforeToolCall = value;\n\t}\n\n\tsetAfterToolCall(\n\t\tvalue:\n\t\t\t| ((context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>)\n\t\t\t| undefined,\n\t) {\n\t\tthis._afterToolCall = value;\n\t}\n\n\tsetShouldContinue(value: (() => boolean) | undefined) {\n\t\tthis._shouldContinue = value;\n\t}\n\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\tsubscribe(fn: (e: AgentEvent) => void): () => void {\n\t\tthis.listeners.add(fn);\n\t\treturn () => this.listeners.delete(fn);\n\t}\n\n\t// State mutators\n\tsetSystemPrompt(v: string) {\n\t\tthis._state.systemPrompt = v;\n\t}\n\n\tsetModel(m: Model<any>) {\n\t\tthis._state.model = m;\n\t}\n\n\tsetThinkingLevel(l: ThinkingLevel) {\n\t\tthis._state.thinkingLevel = l;\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.steeringMode = mode;\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.steeringMode;\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\") {\n\t\tthis.followUpMode = mode;\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.followUpMode;\n\t}\n\n\tsetTools(t: AgentTool<any>[]) {\n\t\tthis._state.tools = t;\n\t}\n\n\treplaceMessages(ms: AgentMessage[]) {\n\t\tthis._state.messages = ms.slice();\n\t}\n\n\tappendMessage(m: AgentMessage) {\n\t\tthis._state.messages = [...this._state.messages, m];\n\t}\n\n\t/**\n\t * Queue a steering message while the agent is running.\n\t * Delivered after the current assistant turn finishes executing its tool calls,\n\t * before the next LLM call.\n\t */\n\tsteer(m: AgentMessage) {\n\t\tthis.steeringQueue.push(m);\n\t}\n\n\t/**\n\t * Queue a follow-up message to be processed after the agent finishes.\n\t * Delivered only when agent has no more tool calls or steering messages.\n\t */\n\tfollowUp(m: AgentMessage) {\n\t\tthis.followUpQueue.push(m);\n\t}\n\n\tclearSteeringQueue() {\n\t\tthis.steeringQueue = [];\n\t}\n\n\tclearFollowUpQueue() {\n\t\tthis.followUpQueue = [];\n\t}\n\n\tclearAllQueues() {\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.length > 0 || this.followUpQueue.length > 0;\n\t}\n\n\tprivate dequeueSteeringMessages(): AgentMessage[] {\n\t\tif (this.steeringMode === \"one-at-a-time\") {\n\t\t\tif (this.steeringQueue.length > 0) {\n\t\t\t\tconst first = this.steeringQueue[0];\n\t\t\t\tthis.steeringQueue = this.steeringQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst steering = this.steeringQueue.slice();\n\t\tthis.steeringQueue = [];\n\t\treturn steering;\n\t}\n\n\tprivate dequeueFollowUpMessages(): AgentMessage[] {\n\t\tif (this.followUpMode === \"one-at-a-time\") {\n\t\t\tif (this.followUpQueue.length > 0) {\n\t\t\t\tconst first = this.followUpQueue[0];\n\t\t\t\tthis.followUpQueue = this.followUpQueue.slice(1);\n\t\t\t\treturn [first];\n\t\t\t}\n\t\t\treturn [];\n\t\t}\n\n\t\tconst followUp = this.followUpQueue.slice();\n\t\tthis.followUpQueue = [];\n\t\treturn followUp;\n\t}\n\n\tclearMessages() {\n\t\tthis._state.messages = [];\n\t}\n\n\tabort() {\n\t\tthis.abortController?.abort();\n\t}\n\n\twaitForIdle(): Promise<void> {\n\t\treturn this.runningPrompt ?? Promise.resolve();\n\t}\n\n\treset() {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.error = undefined;\n\t\tthis.steeringQueue = [];\n\t\tthis.followUpQueue = [];\n\t}\n\n\t/** Send a prompt with an AgentMessage */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]) {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tlet msgs: AgentMessage[];\n\n\t\tif (Array.isArray(input)) {\n\t\t\tmsgs = input;\n\t\t} else if (typeof input === \"string\") {\n\t\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\t\tif (images && images.length > 0) {\n\t\t\t\tcontent.push(...images);\n\t\t\t}\n\t\t\tmsgs = [\n\t\t\t\t{\n\t\t\t\t\trole: \"user\",\n\t\t\t\t\tcontent,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t},\n\t\t\t];\n\t\t} else {\n\t\t\tmsgs = [input];\n\t\t}\n\n\t\tawait this._runLoop(msgs);\n\t}\n\n\t/**\n\t * Continue from current context (used for retries and resuming queued messages).\n\t */\n\tasync continue() {\n\t\tif (this._state.isStreaming) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst messages = this._state.messages;\n\t\tif (messages.length === 0) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\t\tif (messages[messages.length - 1].role === \"assistant\") {\n\t\t\tconst queuedSteering = this.dequeueSteeringMessages();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this._runLoop(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUp = this.dequeueFollowUpMessages();\n\t\t\tif (queuedFollowUp.length > 0) {\n\t\t\t\tawait this._runLoop(queuedFollowUp);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this._runLoop(undefined);\n\t}\n\n\tprivate _processLoopEvent(event: AgentEvent): void {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tthis.appendMessage(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && (event.message as any).errorMessage) {\n\t\t\t\t\tthis._state.error = (event.message as any).errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.isStreaming = false;\n\t\t\t\tthis._state.streamMessage = null;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tthis.emit(event);\n\t}\n\n\t/**\n\t * Run the agent loop.\n\t * If messages are provided, starts a new conversation turn with those messages.\n\t * Otherwise, continues from existing context.\n\t */\n\tprivate async _runLoop(messages?: AgentMessage[], options?: { skipInitialSteeringPoll?: boolean }) {\n\t\tconst model = this._state.model;\n\t\tif (!model) throw new Error(\"No model configured\");\n\n\t\tthis.runningPrompt = new Promise<void>((resolve) => {\n\t\t\tthis.resolveRunningPrompt = resolve;\n\t\t});\n\n\t\tthis.abortController = new AbortController();\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamMessage = null;\n\t\tthis._state.error = undefined;\n\n\t\tconst reasoning = this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel;\n\n\t\tconst context: AgentContext = {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools,\n\t\t};\n\n\t\tlet skipInitialSteeringPoll = options?.skipInitialSteeringPoll === true;\n\n\t\tconst config: AgentLoopConfig = {\n\t\t\tmodel,\n\t\t\treasoning,\n\t\t\tsessionId: this._sessionId,\n\t\t\tonPayload: this._onPayload,\n\t\t\ttransport: this._transport,\n\t\t\tthinkingBudgets: this._thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this._maxRetryDelayMs,\n\t\t\tstreamRetries: this._streamRetries,\n\t\t\tstreamRetryBaseDelayMs: this._streamRetryBaseDelayMs,\n\t\t\tlengthRetries: this._lengthRetries,\n\t\t\tlengthRetryBudgetMultiplier: this._lengthRetryBudgetMultiplier,\n\t\t\tonWarning: this._onWarning,\n\t\t\ttoolExecution: this._toolExecution,\n\t\t\tbeforeToolCall: this._beforeToolCall,\n\t\t\tafterToolCall: this._afterToolCall,\n\t\t\tshouldContinue: this._shouldContinue,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.dequeueSteeringMessages();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.dequeueFollowUpMessages(),\n\t\t};\n\n\t\ttry {\n\t\t\tif (messages) {\n\t\t\t\tawait runAgentLoop(\n\t\t\t\t\tmessages,\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait runAgentLoopContinue(\n\t\t\t\t\tcontext,\n\t\t\t\t\tconfig,\n\t\t\t\t\tasync (event) => this._processLoopEvent(event),\n\t\t\t\t\tthis.abortController.signal,\n\t\t\t\t\tthis.streamFn,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (err: any) {\n\t\t\tconst errorMsg: AgentMessage = {\n\t\t\t\trole: \"assistant\",\n\t\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\t\tapi: model.api,\n\t\t\t\tprovider: model.provider,\n\t\t\t\tmodel: model.id,\n\t\t\t\tusage: {\n\t\t\t\t\tinput: 0,\n\t\t\t\t\toutput: 0,\n\t\t\t\t\tcacheRead: 0,\n\t\t\t\t\tcacheWrite: 0,\n\t\t\t\t\ttotalTokens: 0,\n\t\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t\t},\n\t\t\t\tstopReason: this.abortController?.signal.aborted ? \"aborted\" : \"error\",\n\t\t\t\terrorMessage: err?.message || String(err),\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t} as AgentMessage;\n\n\t\t\tthis.appendMessage(errorMsg);\n\t\t\tthis._state.error = err?.message || String(err);\n\t\t\tthis.emit({ type: \"agent_end\", messages: [errorMsg] });\n\t\t} finally {\n\t\t\tthis._state.isStreaming = false;\n\t\t\tthis._state.streamMessage = null;\n\t\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\t\tthis.abortController = undefined;\n\t\t\tthis.resolveRunningPrompt?.();\n\t\t\tthis.runningPrompt = undefined;\n\t\t\tthis.resolveRunningPrompt = undefined;\n\t\t}\n\t}\n\n\tprivate emit(e: AgentEvent) {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener(e);\n\t\t}\n\t}\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -189,6 +189,23 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
189
189
  * Default: 1000
190
190
  */
191
191
  streamRetryBaseDelayMs?: number;
192
+ /**
193
+ * Maximum number of times to retry when a turn ends with stopReason "length"
194
+ * (the model exhausted its output token budget mid-response). Each retry
195
+ * discards the truncated partial and re-issues the request with an escalated
196
+ * `maxTokens` budget. This is SEPARATE from `streamRetries`.
197
+ *
198
+ * Default: 2
199
+ */
200
+ lengthRetries?: number;
201
+ /**
202
+ * Factor by which to multiply `maxTokens` on each length retry, clamped to the
203
+ * model's output ceiling (`model.maxTokens`). Each retry requests a strictly
204
+ * larger budget than the previous attempt.
205
+ *
206
+ * Default: 2
207
+ */
208
+ lengthRetryBudgetMultiplier?: number;
192
209
  /**
193
210
  * Called before a tool is executed, after arguments have been validated.
194
211
  *
@@ -325,5 +342,15 @@ export type AgentEvent = {
325
342
  error: string;
326
343
  /** Partial assistant message discarded before retry, for debugging/instrumentation only. */
327
344
  discardedPartial?: AssistantMessage;
345
+ } | {
346
+ type: "length_retry";
347
+ attempt: number;
348
+ maxAttempts: number;
349
+ /** The maxTokens budget used for the truncated attempt. */
350
+ previousMaxTokens: number;
351
+ /** The escalated maxTokens budget the retry will request. */
352
+ nextMaxTokens: number;
353
+ /** Partial assistant message discarded before retry, for debugging/instrumentation only. */
354
+ discardedPartial?: AssistantMessage;
328
355
  };
329
356
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tImageContent,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tstreamSimple,\n\tTextContent,\n\tTool,\n\tToolResultMessage,\n} from \"@dreb/ai\";\nimport type { Static, TSchema } from \"@sinclair/typebox\";\n\n/**\n * Stream function used by the agent loop.\n *\n * Contract:\n * - Must not throw or return a rejected promise for request/model/runtime failures.\n * - Must return an AssistantMessageEventStream.\n * - Failures must be encoded in the returned stream via protocol events and a\n * final AssistantMessage with stopReason \"error\" or \"aborted\" and errorMessage.\n */\nexport type StreamFn = (\n\t...args: Parameters<typeof streamSimple>\n) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;\n\n/**\n * Configuration for how tool calls from a single assistant message are executed.\n *\n * - \"sequential\": each tool call is prepared, executed, and finalized before the next one starts.\n * - \"parallel\": tool calls are prepared sequentially, then allowed tools execute concurrently.\n * Final tool results are still emitted in assistant source order.\n */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/** A single tool call content block emitted by an assistant message. */\nexport type AgentToolCall = Extract<AssistantMessage[\"content\"][number], { type: \"toolCall\" }>;\n\n/**\n * Result returned from `beforeToolCall`.\n *\n * Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.\n * `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.\n */\nexport interface BeforeToolCallResult {\n\tblock?: boolean;\n\treason?: string;\n}\n\n/**\n * Partial override returned from `afterToolCall`.\n *\n * Merge semantics are field-by-field:\n * - `content`: if provided, replaces the tool result content array in full\n * - `details`: if provided, replaces the tool result details value in full\n * - `isError`: if provided, replaces the tool result error flag\n *\n * Omitted fields keep the original executed tool result values.\n * There is no deep merge for `content` or `details`.\n */\nexport interface AfterToolCallResult {\n\tcontent?: (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tisError?: boolean;\n}\n\n/** Context passed to `beforeToolCall`. */\nexport interface BeforeToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** Current agent context at the time the tool call is prepared. */\n\tcontext: AgentContext;\n}\n\n/** Context passed to `afterToolCall`. */\nexport interface AfterToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** The executed tool result before any `afterToolCall` overrides are applied. */\n\tresult: AgentToolResult<any>;\n\t/** Whether the executed tool result is currently treated as an error. */\n\tisError: boolean;\n\t/** Current agent context at the time the tool call is finalized. */\n\tcontext: AgentContext;\n}\n\nexport interface AgentLoopConfig extends SimpleStreamOptions {\n\tmodel: Model<any>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t *\n\t * Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage\n\t * that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,\n\t * status messages) should be filtered out.\n\t *\n\t * Contract: must not throw or reject. Return a safe fallback value instead.\n\t * Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t *\n\t * @example\n\t * ```typescript\n\t * convertToLlm: (messages) => messages.flatMap(m => {\n\t * if (m.role === \"custom\") {\n\t * // Convert custom message to user message\n\t * return [{ role: \"user\", content: m.content, timestamp: m.timestamp }];\n\t * }\n\t * if (m.role === \"notification\") {\n\t * // Filter out UI-only messages\n\t * return [];\n\t * }\n\t * // Pass through standard LLM messages\n\t * return [m];\n\t * })\n\t * ```\n\t */\n\tconvertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to the context before `convertToLlm`.\n\t *\n\t * Use this for operations that work at the AgentMessage level:\n\t * - Context window management (pruning old messages)\n\t * - Injecting context from external sources\n\t *\n\t * Contract: must not throw or reject. Return the original messages or another\n\t * safe fallback value instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * transformContext: async (messages) => {\n\t * if (estimateTokens(messages) > MAX_TOKENS) {\n\t * return pruneOldMessages(messages);\n\t * }\n\t * return messages;\n\t * }\n\t * ```\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t *\n\t * Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire\n\t * during long-running tool execution phases.\n\t *\n\t * Contract: must not throw or reject. Return undefined when no key is available.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Returns steering messages to inject into the conversation mid-run.\n\t *\n\t * Called after the current assistant turn finishes executing its tool calls.\n\t * If messages are returned, they are added to the context before the next LLM call.\n\t * Tool calls from the current assistant message are not skipped.\n\t *\n\t * Use this for \"steering\" the agent while it's working.\n\t *\n\t * Contract: must not throw or reject. Return [] when no steering messages are available.\n\t */\n\tgetSteeringMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Returns follow-up messages to process after the agent would otherwise stop.\n\t *\n\t * Called when the agent has no more tool calls and no steering messages.\n\t * If messages are returned, they're added to the context and the agent\n\t * continues with another turn.\n\t *\n\t * Use this for follow-up messages that should wait until the agent finishes.\n\t *\n\t * Contract: must not throw or reject. Return [] when no follow-up messages are available.\n\t */\n\tgetFollowUpMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Tool execution mode.\n\t * - \"sequential\": execute tool calls one by one\n\t * - \"parallel\": preflight tool calls sequentially, then execute allowed tools concurrently\n\t *\n\t * Default: \"parallel\"\n\t */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Called before each LLM call in the inner loop. If it returns false,\n\t * the loop exits cleanly as if no more tool calls were pending.\n\t *\n\t * Use this to enforce turn limits or other dynamic stopping conditions.\n\t * The callback is NOT called before the very first LLM call of a run —\n\t * only before subsequent calls triggered by tool results or steering.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Retries only trigger on detected stream-drop errors (connection dropped before\n\t * the provider sent its terminal completion event). Other errors are not retried.\n\t *\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Actual delay is `baseDelay * 2^attempt` (e.g., 1000, 2000, 4000).\n\t *\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/**\n\t * Called before a tool is executed, after arguments have been validated.\n\t *\n\t * Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/**\n\t * Called after a tool finishes executing, before final tool events are emitted.\n\t *\n\t * Return an `AfterToolCallResult` to override parts of the executed tool result:\n\t * - `content` replaces the full content array\n\t * - `details` replaces the full details payload\n\t * - `isError` replaces the error flag\n\t *\n\t * Any omitted fields keep their original values. No deep merge is performed.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n}\n\n/**\n * Thinking/reasoning level for models that support it.\n * Note: \"xhigh\" is only supported by OpenAI reasoning models that explicitly advertise extended reasoning (for example GPT-5.2+ and Codex GPT-5.3+).\n */\nexport type ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/**\n * Extensible interface for custom app messages.\n * Apps can extend via declaration merging:\n *\n * @example\n * ```typescript\n * declare module \"@dreb/agent-core\" {\n * interface CustomAgentMessages {\n * artifact: ArtifactMessage;\n * notification: NotificationMessage;\n * }\n * }\n * ```\n */\nexport interface CustomAgentMessages {\n\t// Empty by default - apps extend via declaration merging\n}\n\n/**\n * AgentMessage: Union of LLM messages + custom messages.\n * This abstraction allows apps to add custom message types while maintaining\n * type safety and compatibility with the base LLM messages.\n */\nexport type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];\n\n/**\n * Agent state containing all configuration and conversation data.\n */\nexport interface AgentState {\n\tsystemPrompt: string;\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\ttools: AgentTool<any>[];\n\tmessages: AgentMessage[]; // Can include attachments + custom message types\n\tisStreaming: boolean;\n\tstreamMessage: AgentMessage | null;\n\tpendingToolCalls: Set<string>;\n\terror?: string;\n}\n\nexport interface AgentToolResult<T> {\n\t// Content blocks supporting text and images\n\tcontent: (TextContent | ImageContent)[];\n\t// Details to be displayed in a UI or logged\n\tdetails: T;\n\t/**\n\t * When true, the agent loop will stop after processing all tool results\n\t * from the current assistant message. No further LLM call is made.\n\t *\n\t * Use this when a tool's result means the agent should yield control\n\t * (e.g., after launching background agents that will deliver results later).\n\t */\n\tendTurn?: boolean;\n}\n\n// Callback for streaming tool execution updates\nexport type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;\n\n// AgentTool extends Tool but adds the execute function\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {\n\t// A human-readable label for the tool to be displayed in UI\n\tlabel: string;\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n}\n\n// AgentContext is like Context but uses AgentTool\nexport interface AgentContext {\n\tsystemPrompt: string;\n\tmessages: AgentMessage[];\n\ttools?: AgentTool<any>[];\n}\n\n/**\n * Events emitted by the Agent for UI updates.\n * These events provide fine-grained lifecycle information for messages, turns, and tool executions.\n */\nexport type AgentEvent =\n\t// Agent lifecycle\n\t| { type: \"agent_start\"; model?: { provider: string; id: string } }\n\t| { type: \"agent_end\"; messages: AgentMessage[] }\n\t// Turn lifecycle - a turn is one assistant response + any tool calls/results\n\t| { type: \"turn_start\" }\n\t| { type: \"turn_end\"; message: AgentMessage; toolResults: ToolResultMessage[] }\n\t// Message lifecycle - emitted for user, assistant, and toolResult messages\n\t| { type: \"message_start\"; message: AgentMessage }\n\t// Only emitted for assistant messages during streaming\n\t| { type: \"message_update\"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }\n\t| { type: \"message_end\"; message: AgentMessage }\n\t// Tool execution lifecycle\n\t| { type: \"tool_execution_start\"; toolCallId: string; toolName: string; args: any }\n\t| { type: \"tool_execution_update\"; toolCallId: string; toolName: string; args: any; partialResult: any }\n\t| { type: \"tool_execution_end\"; toolCallId: string; toolName: string; result: any; isError: boolean }\n\t// Stream reliability\n\t| {\n\t\t\ttype: \"stream_retry\";\n\t\t\tattempt: number;\n\t\t\tmaxAttempts: number;\n\t\t\terror: string;\n\t\t\t/** Partial assistant message discarded before retry, for debugging/instrumentation only. */\n\t\t\tdiscardedPartial?: AssistantMessage;\n\t };\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tImageContent,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tstreamSimple,\n\tTextContent,\n\tTool,\n\tToolResultMessage,\n} from \"@dreb/ai\";\nimport type { Static, TSchema } from \"@sinclair/typebox\";\n\n/**\n * Stream function used by the agent loop.\n *\n * Contract:\n * - Must not throw or return a rejected promise for request/model/runtime failures.\n * - Must return an AssistantMessageEventStream.\n * - Failures must be encoded in the returned stream via protocol events and a\n * final AssistantMessage with stopReason \"error\" or \"aborted\" and errorMessage.\n */\nexport type StreamFn = (\n\t...args: Parameters<typeof streamSimple>\n) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;\n\n/**\n * Configuration for how tool calls from a single assistant message are executed.\n *\n * - \"sequential\": each tool call is prepared, executed, and finalized before the next one starts.\n * - \"parallel\": tool calls are prepared sequentially, then allowed tools execute concurrently.\n * Final tool results are still emitted in assistant source order.\n */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/** A single tool call content block emitted by an assistant message. */\nexport type AgentToolCall = Extract<AssistantMessage[\"content\"][number], { type: \"toolCall\" }>;\n\n/**\n * Result returned from `beforeToolCall`.\n *\n * Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.\n * `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.\n */\nexport interface BeforeToolCallResult {\n\tblock?: boolean;\n\treason?: string;\n}\n\n/**\n * Partial override returned from `afterToolCall`.\n *\n * Merge semantics are field-by-field:\n * - `content`: if provided, replaces the tool result content array in full\n * - `details`: if provided, replaces the tool result details value in full\n * - `isError`: if provided, replaces the tool result error flag\n *\n * Omitted fields keep the original executed tool result values.\n * There is no deep merge for `content` or `details`.\n */\nexport interface AfterToolCallResult {\n\tcontent?: (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tisError?: boolean;\n}\n\n/** Context passed to `beforeToolCall`. */\nexport interface BeforeToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** Current agent context at the time the tool call is prepared. */\n\tcontext: AgentContext;\n}\n\n/** Context passed to `afterToolCall`. */\nexport interface AfterToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** The executed tool result before any `afterToolCall` overrides are applied. */\n\tresult: AgentToolResult<any>;\n\t/** Whether the executed tool result is currently treated as an error. */\n\tisError: boolean;\n\t/** Current agent context at the time the tool call is finalized. */\n\tcontext: AgentContext;\n}\n\nexport interface AgentLoopConfig extends SimpleStreamOptions {\n\tmodel: Model<any>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t *\n\t * Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage\n\t * that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,\n\t * status messages) should be filtered out.\n\t *\n\t * Contract: must not throw or reject. Return a safe fallback value instead.\n\t * Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t *\n\t * @example\n\t * ```typescript\n\t * convertToLlm: (messages) => messages.flatMap(m => {\n\t * if (m.role === \"custom\") {\n\t * // Convert custom message to user message\n\t * return [{ role: \"user\", content: m.content, timestamp: m.timestamp }];\n\t * }\n\t * if (m.role === \"notification\") {\n\t * // Filter out UI-only messages\n\t * return [];\n\t * }\n\t * // Pass through standard LLM messages\n\t * return [m];\n\t * })\n\t * ```\n\t */\n\tconvertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to the context before `convertToLlm`.\n\t *\n\t * Use this for operations that work at the AgentMessage level:\n\t * - Context window management (pruning old messages)\n\t * - Injecting context from external sources\n\t *\n\t * Contract: must not throw or reject. Return the original messages or another\n\t * safe fallback value instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * transformContext: async (messages) => {\n\t * if (estimateTokens(messages) > MAX_TOKENS) {\n\t * return pruneOldMessages(messages);\n\t * }\n\t * return messages;\n\t * }\n\t * ```\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t *\n\t * Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire\n\t * during long-running tool execution phases.\n\t *\n\t * Contract: must not throw or reject. Return undefined when no key is available.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Returns steering messages to inject into the conversation mid-run.\n\t *\n\t * Called after the current assistant turn finishes executing its tool calls.\n\t * If messages are returned, they are added to the context before the next LLM call.\n\t * Tool calls from the current assistant message are not skipped.\n\t *\n\t * Use this for \"steering\" the agent while it's working.\n\t *\n\t * Contract: must not throw or reject. Return [] when no steering messages are available.\n\t */\n\tgetSteeringMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Returns follow-up messages to process after the agent would otherwise stop.\n\t *\n\t * Called when the agent has no more tool calls and no steering messages.\n\t * If messages are returned, they're added to the context and the agent\n\t * continues with another turn.\n\t *\n\t * Use this for follow-up messages that should wait until the agent finishes.\n\t *\n\t * Contract: must not throw or reject. Return [] when no follow-up messages are available.\n\t */\n\tgetFollowUpMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Tool execution mode.\n\t * - \"sequential\": execute tool calls one by one\n\t * - \"parallel\": preflight tool calls sequentially, then execute allowed tools concurrently\n\t *\n\t * Default: \"parallel\"\n\t */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Called before each LLM call in the inner loop. If it returns false,\n\t * the loop exits cleanly as if no more tool calls were pending.\n\t *\n\t * Use this to enforce turn limits or other dynamic stopping conditions.\n\t * The callback is NOT called before the very first LLM call of a run —\n\t * only before subsequent calls triggered by tool results or steering.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Retries only trigger on detected stream-drop errors (connection dropped before\n\t * the provider sent its terminal completion event). Other errors are not retried.\n\t *\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Actual delay is `baseDelay * 2^attempt` (e.g., 1000, 2000, 4000).\n\t *\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/**\n\t * Maximum number of times to retry when a turn ends with stopReason \"length\"\n\t * (the model exhausted its output token budget mid-response). Each retry\n\t * discards the truncated partial and re-issues the request with an escalated\n\t * `maxTokens` budget. This is SEPARATE from `streamRetries`.\n\t *\n\t * Default: 2\n\t */\n\tlengthRetries?: number;\n\n\t/**\n\t * Factor by which to multiply `maxTokens` on each length retry, clamped to the\n\t * model's output ceiling (`model.maxTokens`). Each retry requests a strictly\n\t * larger budget than the previous attempt.\n\t *\n\t * Default: 2\n\t */\n\tlengthRetryBudgetMultiplier?: number;\n\n\t/**\n\t * Called before a tool is executed, after arguments have been validated.\n\t *\n\t * Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/**\n\t * Called after a tool finishes executing, before final tool events are emitted.\n\t *\n\t * Return an `AfterToolCallResult` to override parts of the executed tool result:\n\t * - `content` replaces the full content array\n\t * - `details` replaces the full details payload\n\t * - `isError` replaces the error flag\n\t *\n\t * Any omitted fields keep their original values. No deep merge is performed.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n}\n\n/**\n * Thinking/reasoning level for models that support it.\n * Note: \"xhigh\" is only supported by OpenAI reasoning models that explicitly advertise extended reasoning (for example GPT-5.2+ and Codex GPT-5.3+).\n */\nexport type ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/**\n * Extensible interface for custom app messages.\n * Apps can extend via declaration merging:\n *\n * @example\n * ```typescript\n * declare module \"@dreb/agent-core\" {\n * interface CustomAgentMessages {\n * artifact: ArtifactMessage;\n * notification: NotificationMessage;\n * }\n * }\n * ```\n */\nexport interface CustomAgentMessages {\n\t// Empty by default - apps extend via declaration merging\n}\n\n/**\n * AgentMessage: Union of LLM messages + custom messages.\n * This abstraction allows apps to add custom message types while maintaining\n * type safety and compatibility with the base LLM messages.\n */\nexport type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];\n\n/**\n * Agent state containing all configuration and conversation data.\n */\nexport interface AgentState {\n\tsystemPrompt: string;\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\ttools: AgentTool<any>[];\n\tmessages: AgentMessage[]; // Can include attachments + custom message types\n\tisStreaming: boolean;\n\tstreamMessage: AgentMessage | null;\n\tpendingToolCalls: Set<string>;\n\terror?: string;\n}\n\nexport interface AgentToolResult<T> {\n\t// Content blocks supporting text and images\n\tcontent: (TextContent | ImageContent)[];\n\t// Details to be displayed in a UI or logged\n\tdetails: T;\n\t/**\n\t * When true, the agent loop will stop after processing all tool results\n\t * from the current assistant message. No further LLM call is made.\n\t *\n\t * Use this when a tool's result means the agent should yield control\n\t * (e.g., after launching background agents that will deliver results later).\n\t */\n\tendTurn?: boolean;\n}\n\n// Callback for streaming tool execution updates\nexport type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;\n\n// AgentTool extends Tool but adds the execute function\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {\n\t// A human-readable label for the tool to be displayed in UI\n\tlabel: string;\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n}\n\n// AgentContext is like Context but uses AgentTool\nexport interface AgentContext {\n\tsystemPrompt: string;\n\tmessages: AgentMessage[];\n\ttools?: AgentTool<any>[];\n}\n\n/**\n * Events emitted by the Agent for UI updates.\n * These events provide fine-grained lifecycle information for messages, turns, and tool executions.\n */\nexport type AgentEvent =\n\t// Agent lifecycle\n\t| { type: \"agent_start\"; model?: { provider: string; id: string } }\n\t| { type: \"agent_end\"; messages: AgentMessage[] }\n\t// Turn lifecycle - a turn is one assistant response + any tool calls/results\n\t| { type: \"turn_start\" }\n\t| { type: \"turn_end\"; message: AgentMessage; toolResults: ToolResultMessage[] }\n\t// Message lifecycle - emitted for user, assistant, and toolResult messages\n\t| { type: \"message_start\"; message: AgentMessage }\n\t// Only emitted for assistant messages during streaming\n\t| { type: \"message_update\"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }\n\t| { type: \"message_end\"; message: AgentMessage }\n\t// Tool execution lifecycle\n\t| { type: \"tool_execution_start\"; toolCallId: string; toolName: string; args: any }\n\t| { type: \"tool_execution_update\"; toolCallId: string; toolName: string; args: any; partialResult: any }\n\t| { type: \"tool_execution_end\"; toolCallId: string; toolName: string; result: any; isError: boolean }\n\t// Stream reliability\n\t| {\n\t\t\ttype: \"stream_retry\";\n\t\t\tattempt: number;\n\t\t\tmaxAttempts: number;\n\t\t\terror: string;\n\t\t\t/** Partial assistant message discarded before retry, for debugging/instrumentation only. */\n\t\t\tdiscardedPartial?: AssistantMessage;\n\t }\n\t// Emitted when a turn ends with stopReason \"length\" and is retried with a larger token budget.\n\t| {\n\t\t\ttype: \"length_retry\";\n\t\t\tattempt: number;\n\t\t\tmaxAttempts: number;\n\t\t\t/** The maxTokens budget used for the truncated attempt. */\n\t\t\tpreviousMaxTokens: number;\n\t\t\t/** The escalated maxTokens budget the retry will request. */\n\t\t\tnextMaxTokens: number;\n\t\t\t/** Partial assistant message discarded before retry, for debugging/instrumentation only. */\n\t\t\tdiscardedPartial?: AssistantMessage;\n\t };\n"]}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tImageContent,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tstreamSimple,\n\tTextContent,\n\tTool,\n\tToolResultMessage,\n} from \"@dreb/ai\";\nimport type { Static, TSchema } from \"@sinclair/typebox\";\n\n/**\n * Stream function used by the agent loop.\n *\n * Contract:\n * - Must not throw or return a rejected promise for request/model/runtime failures.\n * - Must return an AssistantMessageEventStream.\n * - Failures must be encoded in the returned stream via protocol events and a\n * final AssistantMessage with stopReason \"error\" or \"aborted\" and errorMessage.\n */\nexport type StreamFn = (\n\t...args: Parameters<typeof streamSimple>\n) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;\n\n/**\n * Configuration for how tool calls from a single assistant message are executed.\n *\n * - \"sequential\": each tool call is prepared, executed, and finalized before the next one starts.\n * - \"parallel\": tool calls are prepared sequentially, then allowed tools execute concurrently.\n * Final tool results are still emitted in assistant source order.\n */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/** A single tool call content block emitted by an assistant message. */\nexport type AgentToolCall = Extract<AssistantMessage[\"content\"][number], { type: \"toolCall\" }>;\n\n/**\n * Result returned from `beforeToolCall`.\n *\n * Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.\n * `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.\n */\nexport interface BeforeToolCallResult {\n\tblock?: boolean;\n\treason?: string;\n}\n\n/**\n * Partial override returned from `afterToolCall`.\n *\n * Merge semantics are field-by-field:\n * - `content`: if provided, replaces the tool result content array in full\n * - `details`: if provided, replaces the tool result details value in full\n * - `isError`: if provided, replaces the tool result error flag\n *\n * Omitted fields keep the original executed tool result values.\n * There is no deep merge for `content` or `details`.\n */\nexport interface AfterToolCallResult {\n\tcontent?: (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tisError?: boolean;\n}\n\n/** Context passed to `beforeToolCall`. */\nexport interface BeforeToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** Current agent context at the time the tool call is prepared. */\n\tcontext: AgentContext;\n}\n\n/** Context passed to `afterToolCall`. */\nexport interface AfterToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** The executed tool result before any `afterToolCall` overrides are applied. */\n\tresult: AgentToolResult<any>;\n\t/** Whether the executed tool result is currently treated as an error. */\n\tisError: boolean;\n\t/** Current agent context at the time the tool call is finalized. */\n\tcontext: AgentContext;\n}\n\nexport interface AgentLoopConfig extends SimpleStreamOptions {\n\tmodel: Model<any>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t *\n\t * Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage\n\t * that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,\n\t * status messages) should be filtered out.\n\t *\n\t * Contract: must not throw or reject. Return a safe fallback value instead.\n\t * Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t *\n\t * @example\n\t * ```typescript\n\t * convertToLlm: (messages) => messages.flatMap(m => {\n\t * if (m.role === \"custom\") {\n\t * // Convert custom message to user message\n\t * return [{ role: \"user\", content: m.content, timestamp: m.timestamp }];\n\t * }\n\t * if (m.role === \"notification\") {\n\t * // Filter out UI-only messages\n\t * return [];\n\t * }\n\t * // Pass through standard LLM messages\n\t * return [m];\n\t * })\n\t * ```\n\t */\n\tconvertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to the context before `convertToLlm`.\n\t *\n\t * Use this for operations that work at the AgentMessage level:\n\t * - Context window management (pruning old messages)\n\t * - Injecting context from external sources\n\t *\n\t * Contract: must not throw or reject. Return the original messages or another\n\t * safe fallback value instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * transformContext: async (messages) => {\n\t * if (estimateTokens(messages) > MAX_TOKENS) {\n\t * return pruneOldMessages(messages);\n\t * }\n\t * return messages;\n\t * }\n\t * ```\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t *\n\t * Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire\n\t * during long-running tool execution phases.\n\t *\n\t * Contract: must not throw or reject. Return undefined when no key is available.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Returns steering messages to inject into the conversation mid-run.\n\t *\n\t * Called after the current assistant turn finishes executing its tool calls.\n\t * If messages are returned, they are added to the context before the next LLM call.\n\t * Tool calls from the current assistant message are not skipped.\n\t *\n\t * Use this for \"steering\" the agent while it's working.\n\t *\n\t * Contract: must not throw or reject. Return [] when no steering messages are available.\n\t */\n\tgetSteeringMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Returns follow-up messages to process after the agent would otherwise stop.\n\t *\n\t * Called when the agent has no more tool calls and no steering messages.\n\t * If messages are returned, they're added to the context and the agent\n\t * continues with another turn.\n\t *\n\t * Use this for follow-up messages that should wait until the agent finishes.\n\t *\n\t * Contract: must not throw or reject. Return [] when no follow-up messages are available.\n\t */\n\tgetFollowUpMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Tool execution mode.\n\t * - \"sequential\": execute tool calls one by one\n\t * - \"parallel\": preflight tool calls sequentially, then execute allowed tools concurrently\n\t *\n\t * Default: \"parallel\"\n\t */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Called before each LLM call in the inner loop. If it returns false,\n\t * the loop exits cleanly as if no more tool calls were pending.\n\t *\n\t * Use this to enforce turn limits or other dynamic stopping conditions.\n\t * The callback is NOT called before the very first LLM call of a run —\n\t * only before subsequent calls triggered by tool results or steering.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Retries only trigger on detected stream-drop errors (connection dropped before\n\t * the provider sent its terminal completion event). Other errors are not retried.\n\t *\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Actual delay is `baseDelay * 2^attempt` (e.g., 1000, 2000, 4000).\n\t *\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/**\n\t * Called before a tool is executed, after arguments have been validated.\n\t *\n\t * Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/**\n\t * Called after a tool finishes executing, before final tool events are emitted.\n\t *\n\t * Return an `AfterToolCallResult` to override parts of the executed tool result:\n\t * - `content` replaces the full content array\n\t * - `details` replaces the full details payload\n\t * - `isError` replaces the error flag\n\t *\n\t * Any omitted fields keep their original values. No deep merge is performed.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n}\n\n/**\n * Thinking/reasoning level for models that support it.\n * Note: \"xhigh\" is only supported by OpenAI reasoning models that explicitly advertise extended reasoning (for example GPT-5.2+ and Codex GPT-5.3+).\n */\nexport type ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/**\n * Extensible interface for custom app messages.\n * Apps can extend via declaration merging:\n *\n * @example\n * ```typescript\n * declare module \"@dreb/agent-core\" {\n * interface CustomAgentMessages {\n * artifact: ArtifactMessage;\n * notification: NotificationMessage;\n * }\n * }\n * ```\n */\nexport interface CustomAgentMessages {\n\t// Empty by default - apps extend via declaration merging\n}\n\n/**\n * AgentMessage: Union of LLM messages + custom messages.\n * This abstraction allows apps to add custom message types while maintaining\n * type safety and compatibility with the base LLM messages.\n */\nexport type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];\n\n/**\n * Agent state containing all configuration and conversation data.\n */\nexport interface AgentState {\n\tsystemPrompt: string;\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\ttools: AgentTool<any>[];\n\tmessages: AgentMessage[]; // Can include attachments + custom message types\n\tisStreaming: boolean;\n\tstreamMessage: AgentMessage | null;\n\tpendingToolCalls: Set<string>;\n\terror?: string;\n}\n\nexport interface AgentToolResult<T> {\n\t// Content blocks supporting text and images\n\tcontent: (TextContent | ImageContent)[];\n\t// Details to be displayed in a UI or logged\n\tdetails: T;\n\t/**\n\t * When true, the agent loop will stop after processing all tool results\n\t * from the current assistant message. No further LLM call is made.\n\t *\n\t * Use this when a tool's result means the agent should yield control\n\t * (e.g., after launching background agents that will deliver results later).\n\t */\n\tendTurn?: boolean;\n}\n\n// Callback for streaming tool execution updates\nexport type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;\n\n// AgentTool extends Tool but adds the execute function\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {\n\t// A human-readable label for the tool to be displayed in UI\n\tlabel: string;\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n}\n\n// AgentContext is like Context but uses AgentTool\nexport interface AgentContext {\n\tsystemPrompt: string;\n\tmessages: AgentMessage[];\n\ttools?: AgentTool<any>[];\n}\n\n/**\n * Events emitted by the Agent for UI updates.\n * These events provide fine-grained lifecycle information for messages, turns, and tool executions.\n */\nexport type AgentEvent =\n\t// Agent lifecycle\n\t| { type: \"agent_start\"; model?: { provider: string; id: string } }\n\t| { type: \"agent_end\"; messages: AgentMessage[] }\n\t// Turn lifecycle - a turn is one assistant response + any tool calls/results\n\t| { type: \"turn_start\" }\n\t| { type: \"turn_end\"; message: AgentMessage; toolResults: ToolResultMessage[] }\n\t// Message lifecycle - emitted for user, assistant, and toolResult messages\n\t| { type: \"message_start\"; message: AgentMessage }\n\t// Only emitted for assistant messages during streaming\n\t| { type: \"message_update\"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }\n\t| { type: \"message_end\"; message: AgentMessage }\n\t// Tool execution lifecycle\n\t| { type: \"tool_execution_start\"; toolCallId: string; toolName: string; args: any }\n\t| { type: \"tool_execution_update\"; toolCallId: string; toolName: string; args: any; partialResult: any }\n\t| { type: \"tool_execution_end\"; toolCallId: string; toolName: string; result: any; isError: boolean }\n\t// Stream reliability\n\t| {\n\t\t\ttype: \"stream_retry\";\n\t\t\tattempt: number;\n\t\t\tmaxAttempts: number;\n\t\t\terror: string;\n\t\t\t/** Partial assistant message discarded before retry, for debugging/instrumentation only. */\n\t\t\tdiscardedPartial?: AssistantMessage;\n\t };\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tAssistantMessage,\n\tAssistantMessageEvent,\n\tImageContent,\n\tMessage,\n\tModel,\n\tSimpleStreamOptions,\n\tstreamSimple,\n\tTextContent,\n\tTool,\n\tToolResultMessage,\n} from \"@dreb/ai\";\nimport type { Static, TSchema } from \"@sinclair/typebox\";\n\n/**\n * Stream function used by the agent loop.\n *\n * Contract:\n * - Must not throw or return a rejected promise for request/model/runtime failures.\n * - Must return an AssistantMessageEventStream.\n * - Failures must be encoded in the returned stream via protocol events and a\n * final AssistantMessage with stopReason \"error\" or \"aborted\" and errorMessage.\n */\nexport type StreamFn = (\n\t...args: Parameters<typeof streamSimple>\n) => ReturnType<typeof streamSimple> | Promise<ReturnType<typeof streamSimple>>;\n\n/**\n * Configuration for how tool calls from a single assistant message are executed.\n *\n * - \"sequential\": each tool call is prepared, executed, and finalized before the next one starts.\n * - \"parallel\": tool calls are prepared sequentially, then allowed tools execute concurrently.\n * Final tool results are still emitted in assistant source order.\n */\nexport type ToolExecutionMode = \"sequential\" | \"parallel\";\n\n/** A single tool call content block emitted by an assistant message. */\nexport type AgentToolCall = Extract<AssistantMessage[\"content\"][number], { type: \"toolCall\" }>;\n\n/**\n * Result returned from `beforeToolCall`.\n *\n * Returning `{ block: true }` prevents the tool from executing. The loop emits an error tool result instead.\n * `reason` becomes the text shown in that error result. If omitted, a default blocked message is used.\n */\nexport interface BeforeToolCallResult {\n\tblock?: boolean;\n\treason?: string;\n}\n\n/**\n * Partial override returned from `afterToolCall`.\n *\n * Merge semantics are field-by-field:\n * - `content`: if provided, replaces the tool result content array in full\n * - `details`: if provided, replaces the tool result details value in full\n * - `isError`: if provided, replaces the tool result error flag\n *\n * Omitted fields keep the original executed tool result values.\n * There is no deep merge for `content` or `details`.\n */\nexport interface AfterToolCallResult {\n\tcontent?: (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tisError?: boolean;\n}\n\n/** Context passed to `beforeToolCall`. */\nexport interface BeforeToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** Current agent context at the time the tool call is prepared. */\n\tcontext: AgentContext;\n}\n\n/** Context passed to `afterToolCall`. */\nexport interface AfterToolCallContext {\n\t/** The assistant message that requested the tool call. */\n\tassistantMessage: AssistantMessage;\n\t/** The raw tool call block from `assistantMessage.content`. */\n\ttoolCall: AgentToolCall;\n\t/** Validated tool arguments for the target tool schema. */\n\targs: unknown;\n\t/** The executed tool result before any `afterToolCall` overrides are applied. */\n\tresult: AgentToolResult<any>;\n\t/** Whether the executed tool result is currently treated as an error. */\n\tisError: boolean;\n\t/** Current agent context at the time the tool call is finalized. */\n\tcontext: AgentContext;\n}\n\nexport interface AgentLoopConfig extends SimpleStreamOptions {\n\tmodel: Model<any>;\n\n\t/**\n\t * Converts AgentMessage[] to LLM-compatible Message[] before each LLM call.\n\t *\n\t * Each AgentMessage must be converted to a UserMessage, AssistantMessage, or ToolResultMessage\n\t * that the LLM can understand. AgentMessages that cannot be converted (e.g., UI-only notifications,\n\t * status messages) should be filtered out.\n\t *\n\t * Contract: must not throw or reject. Return a safe fallback value instead.\n\t * Throwing interrupts the low-level agent loop without producing a normal event sequence.\n\t *\n\t * @example\n\t * ```typescript\n\t * convertToLlm: (messages) => messages.flatMap(m => {\n\t * if (m.role === \"custom\") {\n\t * // Convert custom message to user message\n\t * return [{ role: \"user\", content: m.content, timestamp: m.timestamp }];\n\t * }\n\t * if (m.role === \"notification\") {\n\t * // Filter out UI-only messages\n\t * return [];\n\t * }\n\t * // Pass through standard LLM messages\n\t * return [m];\n\t * })\n\t * ```\n\t */\n\tconvertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\n\t/**\n\t * Optional transform applied to the context before `convertToLlm`.\n\t *\n\t * Use this for operations that work at the AgentMessage level:\n\t * - Context window management (pruning old messages)\n\t * - Injecting context from external sources\n\t *\n\t * Contract: must not throw or reject. Return the original messages or another\n\t * safe fallback value instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * transformContext: async (messages) => {\n\t * if (estimateTokens(messages) > MAX_TOKENS) {\n\t * return pruneOldMessages(messages);\n\t * }\n\t * return messages;\n\t * }\n\t * ```\n\t */\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\n\t/**\n\t * Resolves an API key dynamically for each LLM call.\n\t *\n\t * Useful for short-lived OAuth tokens (e.g., GitHub Copilot) that may expire\n\t * during long-running tool execution phases.\n\t *\n\t * Contract: must not throw or reject. Return undefined when no key is available.\n\t */\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\n\t/**\n\t * Returns steering messages to inject into the conversation mid-run.\n\t *\n\t * Called after the current assistant turn finishes executing its tool calls.\n\t * If messages are returned, they are added to the context before the next LLM call.\n\t * Tool calls from the current assistant message are not skipped.\n\t *\n\t * Use this for \"steering\" the agent while it's working.\n\t *\n\t * Contract: must not throw or reject. Return [] when no steering messages are available.\n\t */\n\tgetSteeringMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Returns follow-up messages to process after the agent would otherwise stop.\n\t *\n\t * Called when the agent has no more tool calls and no steering messages.\n\t * If messages are returned, they're added to the context and the agent\n\t * continues with another turn.\n\t *\n\t * Use this for follow-up messages that should wait until the agent finishes.\n\t *\n\t * Contract: must not throw or reject. Return [] when no follow-up messages are available.\n\t */\n\tgetFollowUpMessages?: () => Promise<AgentMessage[]>;\n\n\t/**\n\t * Tool execution mode.\n\t * - \"sequential\": execute tool calls one by one\n\t * - \"parallel\": preflight tool calls sequentially, then execute allowed tools concurrently\n\t *\n\t * Default: \"parallel\"\n\t */\n\ttoolExecution?: ToolExecutionMode;\n\n\t/**\n\t * Called before each LLM call in the inner loop. If it returns false,\n\t * the loop exits cleanly as if no more tool calls were pending.\n\t *\n\t * Use this to enforce turn limits or other dynamic stopping conditions.\n\t * The callback is NOT called before the very first LLM call of a run —\n\t * only before subsequent calls triggered by tool results or steering.\n\t */\n\tshouldContinue?: () => boolean;\n\n\t/**\n\t * Maximum number of times to retry when a stream drops mid-response.\n\t * Retries only trigger on detected stream-drop errors (connection dropped before\n\t * the provider sent its terminal completion event). Other errors are not retried.\n\t *\n\t * Default: 3\n\t */\n\tstreamRetries?: number;\n\n\t/**\n\t * Base delay in milliseconds for exponential backoff between stream retries.\n\t * Actual delay is `baseDelay * 2^attempt` (e.g., 1000, 2000, 4000).\n\t *\n\t * Default: 1000\n\t */\n\tstreamRetryBaseDelayMs?: number;\n\n\t/**\n\t * Maximum number of times to retry when a turn ends with stopReason \"length\"\n\t * (the model exhausted its output token budget mid-response). Each retry\n\t * discards the truncated partial and re-issues the request with an escalated\n\t * `maxTokens` budget. This is SEPARATE from `streamRetries`.\n\t *\n\t * Default: 2\n\t */\n\tlengthRetries?: number;\n\n\t/**\n\t * Factor by which to multiply `maxTokens` on each length retry, clamped to the\n\t * model's output ceiling (`model.maxTokens`). Each retry requests a strictly\n\t * larger budget than the previous attempt.\n\t *\n\t * Default: 2\n\t */\n\tlengthRetryBudgetMultiplier?: number;\n\n\t/**\n\t * Called before a tool is executed, after arguments have been validated.\n\t *\n\t * Return `{ block: true }` to prevent execution. The loop emits an error tool result instead.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\n\t/**\n\t * Called after a tool finishes executing, before final tool events are emitted.\n\t *\n\t * Return an `AfterToolCallResult` to override parts of the executed tool result:\n\t * - `content` replaces the full content array\n\t * - `details` replaces the full details payload\n\t * - `isError` replaces the error flag\n\t *\n\t * Any omitted fields keep their original values. No deep merge is performed.\n\t * The hook receives the agent abort signal and is responsible for honoring it.\n\t */\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n}\n\n/**\n * Thinking/reasoning level for models that support it.\n * Note: \"xhigh\" is only supported by OpenAI reasoning models that explicitly advertise extended reasoning (for example GPT-5.2+ and Codex GPT-5.3+).\n */\nexport type ThinkingLevel = \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\n/**\n * Extensible interface for custom app messages.\n * Apps can extend via declaration merging:\n *\n * @example\n * ```typescript\n * declare module \"@dreb/agent-core\" {\n * interface CustomAgentMessages {\n * artifact: ArtifactMessage;\n * notification: NotificationMessage;\n * }\n * }\n * ```\n */\nexport interface CustomAgentMessages {\n\t// Empty by default - apps extend via declaration merging\n}\n\n/**\n * AgentMessage: Union of LLM messages + custom messages.\n * This abstraction allows apps to add custom message types while maintaining\n * type safety and compatibility with the base LLM messages.\n */\nexport type AgentMessage = Message | CustomAgentMessages[keyof CustomAgentMessages];\n\n/**\n * Agent state containing all configuration and conversation data.\n */\nexport interface AgentState {\n\tsystemPrompt: string;\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\ttools: AgentTool<any>[];\n\tmessages: AgentMessage[]; // Can include attachments + custom message types\n\tisStreaming: boolean;\n\tstreamMessage: AgentMessage | null;\n\tpendingToolCalls: Set<string>;\n\terror?: string;\n}\n\nexport interface AgentToolResult<T> {\n\t// Content blocks supporting text and images\n\tcontent: (TextContent | ImageContent)[];\n\t// Details to be displayed in a UI or logged\n\tdetails: T;\n\t/**\n\t * When true, the agent loop will stop after processing all tool results\n\t * from the current assistant message. No further LLM call is made.\n\t *\n\t * Use this when a tool's result means the agent should yield control\n\t * (e.g., after launching background agents that will deliver results later).\n\t */\n\tendTurn?: boolean;\n}\n\n// Callback for streaming tool execution updates\nexport type AgentToolUpdateCallback<T = any> = (partialResult: AgentToolResult<T>) => void;\n\n// AgentTool extends Tool but adds the execute function\nexport interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {\n\t// A human-readable label for the tool to be displayed in UI\n\tlabel: string;\n\texecute: (\n\t\ttoolCallId: string,\n\t\tparams: Static<TParameters>,\n\t\tsignal?: AbortSignal,\n\t\tonUpdate?: AgentToolUpdateCallback<TDetails>,\n\t) => Promise<AgentToolResult<TDetails>>;\n}\n\n// AgentContext is like Context but uses AgentTool\nexport interface AgentContext {\n\tsystemPrompt: string;\n\tmessages: AgentMessage[];\n\ttools?: AgentTool<any>[];\n}\n\n/**\n * Events emitted by the Agent for UI updates.\n * These events provide fine-grained lifecycle information for messages, turns, and tool executions.\n */\nexport type AgentEvent =\n\t// Agent lifecycle\n\t| { type: \"agent_start\"; model?: { provider: string; id: string } }\n\t| { type: \"agent_end\"; messages: AgentMessage[] }\n\t// Turn lifecycle - a turn is one assistant response + any tool calls/results\n\t| { type: \"turn_start\" }\n\t| { type: \"turn_end\"; message: AgentMessage; toolResults: ToolResultMessage[] }\n\t// Message lifecycle - emitted for user, assistant, and toolResult messages\n\t| { type: \"message_start\"; message: AgentMessage }\n\t// Only emitted for assistant messages during streaming\n\t| { type: \"message_update\"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }\n\t| { type: \"message_end\"; message: AgentMessage }\n\t// Tool execution lifecycle\n\t| { type: \"tool_execution_start\"; toolCallId: string; toolName: string; args: any }\n\t| { type: \"tool_execution_update\"; toolCallId: string; toolName: string; args: any; partialResult: any }\n\t| { type: \"tool_execution_end\"; toolCallId: string; toolName: string; result: any; isError: boolean }\n\t// Stream reliability\n\t| {\n\t\t\ttype: \"stream_retry\";\n\t\t\tattempt: number;\n\t\t\tmaxAttempts: number;\n\t\t\terror: string;\n\t\t\t/** Partial assistant message discarded before retry, for debugging/instrumentation only. */\n\t\t\tdiscardedPartial?: AssistantMessage;\n\t }\n\t// Emitted when a turn ends with stopReason \"length\" and is retried with a larger token budget.\n\t| {\n\t\t\ttype: \"length_retry\";\n\t\t\tattempt: number;\n\t\t\tmaxAttempts: number;\n\t\t\t/** The maxTokens budget used for the truncated attempt. */\n\t\t\tpreviousMaxTokens: number;\n\t\t\t/** The escalated maxTokens budget the retry will request. */\n\t\t\tnextMaxTokens: number;\n\t\t\t/** Partial assistant message discarded before retry, for debugging/instrumentation only. */\n\t\t\tdiscardedPartial?: AssistantMessage;\n\t };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreb/agent-core",
3
- "version": "2.23.0",
3
+ "version": "2.24.0",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",