import { afterEach, describe, expect, it, vi } from "bun:test"; import { Agent, type AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { Message, SimpleStreamOptions } from "@oh-my-pi/pi-ai"; import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings"; import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session"; import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager"; function createAgent(): Agent { return new Agent({ initialState: { systemPrompt: "system prompt", messages: [], tools: [], }, }); } describe("AgentSession message pipeline", () => { const sessions: AgentSession[] = []; afterEach(async () => { vi.restoreAllMocks(); for (const session of sessions.splice(0)) { await session.dispose(); } }); it("applies transformContext before convertToLlm", async () => { const inputMessages: AgentMessage[] = [{ role: "user", content: "hello", timestamp: Date.now() }]; const transformedMessages: AgentMessage[] = [ ...inputMessages, { role: "user", content: "injected context", timestamp: Date.now() }, ]; const convertedMessages: Message[] = [ { role: "user", content: [{ type: "text", text: "converted" }], attribution: "user", timestamp: Date.now(), }, ]; const transformContext = vi.fn(async (messages: AgentMessage[], signal?: AbortSignal) => { expect(signal).toBe(abortController.signal); return [...messages, ...transformedMessages.slice(messages.length)]; }); const convertToLlm = vi.fn(async (_messages: AgentMessage[]) => { return convertedMessages; }); const abortController = new AbortController(); const session = new AgentSession({ agent: createAgent(), sessionManager: SessionManager.inMemory(), settings: Settings.isolated({ "compaction.enabled": false }), modelRegistry: {} as never, transformContext, convertToLlm, }); sessions.push(session); const result = await session.convertMessagesToLlm(inputMessages, abortController.signal); expect(transformContext).toHaveBeenCalledWith(inputMessages, abortController.signal); expect(convertToLlm).toHaveBeenCalledWith(transformedMessages); expect(result).toEqual(convertedMessages); }); it("composes session payload hooks into direct side-request options", async () => { const sessionOnPayload = vi.fn(async (payload: unknown) => ({ ...(payload as Record), session: true, })); const requestOnPayload = vi.fn(async () => undefined); const session = new AgentSession({ agent: createAgent(), sessionManager: SessionManager.inMemory(), settings: Settings.isolated({ "compaction.enabled": false }), modelRegistry: {} as never, onPayload: sessionOnPayload, }); sessions.push(session); const options: SimpleStreamOptions = { apiKey: "key", onPayload: requestOnPayload, }; const prepared = session.prepareSimpleStreamOptions(options); const result = await prepared.onPayload?.({ original: true }); expect(sessionOnPayload).toHaveBeenCalledWith({ original: true }, undefined); expect(requestOnPayload).toHaveBeenCalledWith({ original: true, session: true }, undefined); expect(result).toEqual({ original: true, session: true }); }); });