import { describe, it, expect, beforeEach } from "bun:test" import { ContextCollector } from "./collector" import { createContextInjectorMessagesTransformHook, } from "./injector" describe("createContextInjectorMessagesTransformHook", () => { let collector: ContextCollector beforeEach(() => { collector = new ContextCollector() }) const createMockMessage = ( role: "user" | "assistant", text: string, sessionID: string ) => ({ info: { id: `msg_${Date.now()}_${Math.random()}`, sessionID, role, time: { created: Date.now() }, agent: "sisyphus", model: { providerID: "test", modelID: "test" }, path: { cwd: "/", root: "/" }, }, parts: [ { id: `part_${Date.now()}`, sessionID, messageID: `msg_${Date.now()}`, type: "text" as const, text, }, ], }) it("inserts synthetic part before text part in last user message", async () => { // given const hook = createContextInjectorMessagesTransformHook(collector) const sessionID = "ses_transform1" collector.register(sessionID, { id: "ulw", source: "keyword-detector", content: "Ultrawork context", }) const messages = [ createMockMessage("user", "First message", sessionID), createMockMessage("assistant", "Response", sessionID), createMockMessage("user", "Second message", sessionID), ] // eslint-disable-next-line @typescript-eslint/no-explicit-any const output = { messages } as any // when await hook["experimental.chat.messages.transform"]!({}, output) // then - synthetic part inserted before original text part expect(output.messages.length).toBe(3) expect(output.messages[2].parts.length).toBe(2) expect(output.messages[2].parts[0].text).toBe("Ultrawork context") expect(output.messages[2].parts[0].synthetic).toBe(true) expect(output.messages[2].parts[1].text).toBe("Second message") }) it("does nothing when no pending context", async () => { // given const hook = createContextInjectorMessagesTransformHook(collector) const sessionID = "ses_transform2" const messages = [createMockMessage("user", "Hello world", sessionID)] // eslint-disable-next-line @typescript-eslint/no-explicit-any const output = { messages } as any // when await hook["experimental.chat.messages.transform"]!({}, output) // then expect(output.messages.length).toBe(1) }) it("does nothing when no user messages", async () => { // given const hook = createContextInjectorMessagesTransformHook(collector) const sessionID = "ses_transform3" collector.register(sessionID, { id: "ctx", source: "keyword-detector", content: "Context", }) const messages = [createMockMessage("assistant", "Response", sessionID)] // eslint-disable-next-line @typescript-eslint/no-explicit-any const output = { messages } as any // when await hook["experimental.chat.messages.transform"]!({}, output) // then expect(output.messages.length).toBe(1) expect(collector.hasPending(sessionID)).toBe(true) }) it("consumes context after injection", async () => { // given const hook = createContextInjectorMessagesTransformHook(collector) const sessionID = "ses_transform4" collector.register(sessionID, { id: "ctx", source: "keyword-detector", content: "Context", }) const messages = [createMockMessage("user", "Message", sessionID)] // eslint-disable-next-line @typescript-eslint/no-explicit-any const output = { messages } as any // when await hook["experimental.chat.messages.transform"]!({}, output) // then expect(collector.hasPending(sessionID)).toBe(false) }) })