/**
 * Pushed notifications must reach the model on the steering path — a finished
 * child or an exited background build should arrive on its own, without the
 * agent spending a turn on wait_agent/task_output, and without displacing
 * anything the user queued.
 */
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { useFakeHome } from "../test-support/fake-home.js";
import { NOTIFICATION_DRAIN_MAX_CHARS } from "./agent-notifications.js";
import { NOTIFICATION_PREFIX, STEERING_PREFIX } from "./steering.js";
const agentLoopMock = vi.hoisted(() => vi.fn());
vi.mock("@kenkaiiii/gg-agent", async () => {
    const actual = await vi.importActual("@kenkaiiii/gg-agent");
    return { ...actual, agentLoop: agentLoopMock };
});
vi.mock("./mcp/index.js", async () => {
    const actual = await vi.importActual("./mcp/index.js");
    return {
        ...actual,
        MCPClientManager: vi.fn(function MCPClientManagerMock() {
            return { connectAll: vi.fn(async () => []), dispose: vi.fn(async () => { }) };
        }),
    };
});
let restoreHome;
let tmpHome;
let tmpProject;
beforeEach(async () => {
    tmpHome = await fs.mkdtemp(path.join(os.tmpdir(), "agent-session-notify-home-"));
    tmpProject = await fs.mkdtemp(path.join(os.tmpdir(), "agent-session-notify-project-"));
    restoreHome = useFakeHome(tmpHome);
    agentLoopMock.mockReset();
    await fs.mkdir(path.join(tmpHome, ".gg"), { recursive: true });
    await fs.writeFile(path.join(tmpHome, ".gg", "auth.json"), JSON.stringify({
        anthropic: {
            accessToken: "test",
            refreshToken: "test",
            expiresAt: Date.now() + 3_600_000,
        },
    }), "utf-8");
});
afterEach(async () => {
    restoreHome?.();
    await fs.rm(tmpHome, { recursive: true, force: true });
    await fs.rm(tmpProject, { recursive: true, force: true });
    vi.clearAllMocks();
});
async function makeSession() {
    const { AgentSession } = await import("./agent-session.js");
    const session = new AgentSession({
        provider: "anthropic",
        model: "claude-test",
        cwd: tmpProject,
        systemPrompt: "test system prompt",
        transient: true,
    });
    await session.initialize();
    const internals = session;
    return { session, internals };
}
describe("AgentSession pushed notifications", () => {
    it("injects queued notifications as one framed steering message", async () => {
        const { session, internals } = await makeSession();
        try {
            internals.notifications.enqueue("subagent", "a1", 'Child agent "one" is completed.', {
                terminal: true,
            });
            internals.notifications.enqueue("process", "p1", "Build exited 0.", { terminal: true });
            const messages = internals.getHookSteeringMessages();
            expect(messages).toHaveLength(1);
            const content = messages[0].content;
            expect(content.startsWith(NOTIFICATION_PREFIX)).toBe(true);
            expect(content).toContain('Child agent "one" is completed.');
            expect(content).toContain("Build exited 0.");
            expect(content.length).toBeLessThanOrEqual(NOTIFICATION_PREFIX.length + NOTIFICATION_DRAIN_MAX_CHARS + 8);
            // Consumed exactly once — a second poll must not re-inject them.
            expect(internals.getHookSteeringMessages()).toBeNull();
        }
        finally {
            await session.dispose();
        }
    }, 15_000);
    it("never displaces user steering — both ride out in the same batch", async () => {
        const { session, internals } = await makeSession();
        try {
            session.queueMessage("also check the tests");
            internals.notifications.enqueue("subagent", "a1", 'Child agent "one" is completed.', {
                terminal: true,
            });
            const messages = internals.getHookSteeringMessages();
            expect(messages).toHaveLength(2);
            // User steering first: it is an instruction, the notification is a fact.
            expect(messages[0].content).toBe(`${STEERING_PREFIX}also check the tests`);
            expect(messages[1].content).toContain(NOTIFICATION_PREFIX);
            expect(session.getQueuedCount()).toBe(0);
        }
        finally {
            await session.dispose();
        }
    }, 15_000);
    it("returns null when nothing is pending", async () => {
        const { session, internals } = await makeSession();
        try {
            expect(internals.getHookSteeringMessages()).toBeNull();
        }
        finally {
            await session.dispose();
        }
    }, 15_000);
});
//# sourceMappingURL=agent-session-notifications.test.js.map