/** * Tests that successful compaction LLM calls land an `llm_request_logs` * row with `call_site = "compactionAgent"`. The compactor opts out of * automatic usage tracking (`usageTracking: "manual"`), so its calls * otherwise never reach `recordRequestLog` via the agent-loop * dispatcher. This test pins the explicit instrumentation in * `compactor.ts` so visibility doesn't silently regress. */ import { beforeEach, describe, expect, mock, test } from "bun:test"; mock.module("../persistence/conversation-crud.js", () => ({ setConversationProcessingStartedAt: () => {}, isConversationProcessing: () => false, getMessages: () => [], reserveMessage: mock(async () => ({ id: "msg-reserve" })), })); mock.module("../persistence/attachments-store.js", () => ({ getAttachmentMetadataForMessage: () => [], getAttachmentContent: () => null, })); const recordRequestLogCalls: Array<{ conversationId: string; requestPayload: string; responsePayload: string; messageId: string | undefined; provider: string | undefined; callSite: string | undefined; }> = []; mock.module("../persistence/llm-request-log-store.js", () => ({ recordRequestLog: ( conversationId: string, requestPayload: string, responsePayload: string, messageId?: string, provider?: string, callSite?: string, ): string => { recordRequestLogCalls.push({ conversationId, requestPayload, responsePayload, messageId, provider, callSite, }); return `mock-log-${recordRequestLogCalls.length}`; }, })); import { runAssistantDrivenCompaction } from "../context/compactor.js"; import type { Message, Provider } from "../providers/types.js"; const TAIL_TIMESTAMP = "2026-05-21 (Thursday) 10:00:00 -05:00 (America/Chicago)"; const compactionResponse = ` Earlier turns summarized here. - Nothing critical pending. `; const RAW_REQUEST = { model: "mock-model", messages: [] }; const RAW_RESPONSE = { id: "resp-1", content: compactionResponse }; function makeProvider(): Provider { return { name: "mock-provider", sendMessage: async () => ({ content: [{ type: "text", text: compactionResponse }], model: "mock-model", actualProvider: "actual-mock-provider", usage: { inputTokens: 100, outputTokens: 50 }, stopReason: "end_turn", rawRequest: RAW_REQUEST, rawResponse: RAW_RESPONSE, }), }; } function makeProviderWithoutRaw(): Provider { return { name: "mock-provider", sendMessage: async () => ({ content: [{ type: "text", text: compactionResponse }], model: "mock-model", usage: { inputTokens: 100, outputTokens: 50 }, stopReason: "end_turn", // rawRequest/rawResponse intentionally absent — best-effort skip path. }), }; } const userText = (text: string): Message => ({ role: "user", content: [{ type: "text", text }], }); const userTextWithTurnContext = (text: string, timestamp: string): Message => ({ role: "user", content: [ { type: "text", text: `\ncurrent_time: ${timestamp}\n\n${text}`, }, ], }); const assistantText = (text: string): Message => ({ role: "assistant", content: [{ type: "text", text }], }); function buildMessages(): Message[] { return [ userText("old user turn 1"), assistantText("old assistant reply 1"), userText("old user turn 2"), assistantText("old assistant reply 2"), userTextWithTurnContext("tail anchor message", TAIL_TIMESTAMP), assistantText("tail assistant reply"), ]; } const args = (provider: Provider) => ({ conversationId: "conv-compaction-log-1", messages: buildMessages(), provider, systemPrompt: "you are a test assistant", compaction: { enabled: true, autoThreshold: 0.7 }, maxInputTokens: 1000, // Above threshold so the auto-check fires. previousEstimatedInputTokens: 900, }); describe("compactor records llm_request_logs with call_site=compactionAgent", () => { beforeEach(() => { recordRequestLogCalls.length = 0; }); test("successful compaction call stamps call_site = compactionAgent", async () => { await runAssistantDrivenCompaction(args(makeProvider())); expect(recordRequestLogCalls.length).toBe(1); expect(recordRequestLogCalls[0]!.callSite).toBe("compactionAgent"); expect(recordRequestLogCalls[0]!.conversationId).toBe( "conv-compaction-log-1", ); // Provider name comes from actualProvider when present. expect(recordRequestLogCalls[0]!.provider).toBe("actual-mock-provider"); // Payloads should be JSON-stringified. expect(recordRequestLogCalls[0]!.requestPayload).toBe( JSON.stringify(RAW_REQUEST), ); expect(recordRequestLogCalls[0]!.responsePayload).toBe( JSON.stringify(RAW_RESPONSE), ); }); test("skips persistence when provider returns no rawRequest/rawResponse", async () => { await runAssistantDrivenCompaction(args(makeProviderWithoutRaw())); // Helper short-circuits when raw payloads are absent — non-fatal. expect(recordRequestLogCalls.length).toBe(0); }); test("uses provider.name when actualProvider is absent", async () => { const provider: Provider = { name: "fallback-provider-name", sendMessage: async () => ({ content: [{ type: "text", text: compactionResponse }], model: "mock-model", usage: { inputTokens: 100, outputTokens: 50 }, stopReason: "end_turn", rawRequest: RAW_REQUEST, rawResponse: RAW_RESPONSE, }), }; await runAssistantDrivenCompaction(args(provider)); expect(recordRequestLogCalls.length).toBe(1); expect(recordRequestLogCalls[0]!.provider).toBe("fallback-provider-name"); expect(recordRequestLogCalls[0]!.callSite).toBe("compactionAgent"); }); });