import { describe, expect, it } from "vitest"; import { agentLoop, agentLoopContinue } from "./agent-loop.js"; import type { Message, Model } from "./llm.js"; import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types.js"; const model: Model = { id: "test-model", name: "Test Model", api: "test-api", provider: "test-provider", baseUrl: "https://example.test", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1000, maxTokens: 1000, }; const config: AgentLoopConfig = { model, convertToLlm: (messages) => messages as Message[], }; const failingStreamFn: StreamFn = async () => { throw new Error("provider exploded"); }; async function collectEvents(stream: AsyncIterable): Promise { const events: AgentEvent[] = []; for await (const event of stream) { events.push(event); } return events; } function expectTerminalFailure(events: AgentEvent[], result: AgentMessage[]): void { expect(events.map((event) => event.type)).toContain("agent_end"); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ role: "assistant", stopReason: "error", errorMessage: "provider exploded", }); } describe("agentLoop EventStream failures", () => { it("ends the public stream when a new prompt run rejects", async () => { const stream = agentLoop( [{ role: "user", content: "hello", timestamp: 1 }], { systemPrompt: "", messages: [] }, config, undefined, failingStreamFn, ); const events = await collectEvents(stream); const result = await stream.result(); expectTerminalFailure(events, result); }); it("ends the public stream when a continue run rejects", async () => { const context: AgentContext = { systemPrompt: "", messages: [{ role: "user", content: "hello", timestamp: 1 }], }; const stream = agentLoopContinue(context, config, undefined, failingStreamFn); const events = await collectEvents(stream); const result = await stream.result(); expectTerminalFailure(events, result); }); });