// Regression test for queued-message interrupt behavior in the OpenCode plugin.
// Reproduces the retry-status bug where queued user messages are not interrupted.
import { afterEach, expect, test } from 'vitest';
import { interruptOpencodeSessionOnUserMessage } from './opencode-plugin.js';
function createSessionStatusEvent({ sessionID, status, }) {
    if (status === 'retry') {
        return {
            type: 'session.status',
            properties: {
                sessionID,
                status: {
                    type: 'retry',
                    attempt: 1,
                    message: 'rate limited',
                    next: Date.now() + 1000,
                },
            },
        };
    }
    return {
        type: 'session.status',
        properties: {
            sessionID,
            status: {
                type: status,
            },
        },
    };
}
function createPluginInput({ client }) {
    return {
        client: client,
        project: {
            id: 'project-id',
            worktree: '/Users/morse/Documents/GitHub/kimakivoice',
            time: {
                created: Date.now(),
            },
        },
        directory: '/Users/morse/Documents/GitHub/kimakivoice',
        worktree: '/Users/morse/Documents/GitHub/kimakivoice',
        serverUrl: new URL('http://127.0.0.1:4096'),
        $: {},
    };
}
function createQueuedUserMessage({ sessionID, messageID, }) {
    return {
        id: messageID,
        sessionID,
        role: 'user',
        time: { created: Date.now() },
        agent: 'build',
        model: {
            providerID: 'deterministic-provider',
            modelID: 'deterministic-v2',
        },
    };
}
function delay({ ms }) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
}
afterEach(() => {
    delete process.env['KIMAKI_INTERRUPT_STEP_TIMEOUT_MS'];
});
test('queued user message should still interrupt when session enters retry status', async () => {
    process.env['KIMAKI_INTERRUPT_STEP_TIMEOUT_MS'] = '20';
    const abortCalls = [];
    const promptAsyncCalls = [];
    const client = {
        session: {
            abort: async (input) => {
                abortCalls.push(input);
            },
            promptAsync: async (input) => {
                promptAsyncCalls.push(input);
            },
        },
    };
    const hooks = await interruptOpencodeSessionOnUserMessage(createPluginInput({ client }));
    const onEvent = hooks.event;
    const onChatMessage = hooks['chat.message'];
    if (!onEvent) {
        throw new Error('Expected event hook');
    }
    if (!onChatMessage) {
        throw new Error('Expected chat.message hook');
    }
    const sessionID = 'ses_retry_case';
    const queuedMessageID = 'msg_retry_case';
    const queuedMessage = createQueuedUserMessage({
        sessionID,
        messageID: queuedMessageID,
    });
    await onEvent({
        event: createSessionStatusEvent({ sessionID, status: 'busy' }),
    });
    await onChatMessage({
        sessionID,
        messageID: queuedMessageID,
    }, {
        message: queuedMessage,
        parts: [],
    });
    await onEvent({
        event: createSessionStatusEvent({ sessionID, status: 'retry' }),
    });
    await delay({ ms: 60 });
    await onEvent({
        event: {
            type: 'session.error',
            properties: {
                sessionID,
                error: {
                    name: 'MessageAbortedError',
                    data: { message: 'The operation was aborted.' },
                },
            },
        },
    });
    await onEvent({
        event: createSessionStatusEvent({ sessionID, status: 'idle' }),
    });
    await onEvent({
        event: {
            type: 'session.idle',
            properties: {
                sessionID,
            },
        },
    });
    await delay({ ms: 20 });
    expect(abortCalls.length).toBe(1);
    expect(promptAsyncCalls.length).toBe(1);
});
