export const INTERACTIVE_ENGINE_PROTOCOL_VERSION = 1;
export const INTERACTIVE_ENGINE_MAX_FRAME_BYTES = 1_048_576;
const ACTIVITY_KINDS = [
    "extension.hook", "renderer", "tool.execute", "tool.prepare", "workflow.ctx_tool",
    "workflow.run", "workflow.stage_adapter",
];
export function isJsonValue(value) {
    if (value === null || typeof value !== "object")
        return true;
    if (Array.isArray(value))
        return value.every((item) => isJsonValue(item));
    return Object.values(value).every((item) => item !== undefined && isJsonValue(item));
}
function isJsonObject(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isActivityKind(value) {
    return typeof value === "string" && ACTIVITY_KINDS.includes(value);
}
function isCallbackActivity(value) {
    return isJsonObject(value) && typeof value.id === "string" && isActivityKind(value.kind) &&
        typeof value.name === "string" && typeof value.startedAt === "number";
}
function parseEngineTerminalControl(value) {
    if (value === undefined || !isJsonObject(value) || typeof value.enabled !== "boolean")
        return undefined;
    if (value.kind === "mouse-scroll-tracking" || value.kind === "autowrap") {
        return { kind: value.kind, enabled: value.enabled };
    }
    return undefined;
}
const SESSION_PICKER_MESSAGE_COLORS = ["success", "warning", "accent", "error"];
function parseSessionPickerRow(value) {
    if (!isJsonObject(value))
        return undefined;
    const { path, id, cwd, createdAt, modifiedAt, messageCount, firstMessage, allMessagesText, name, messageColor } = value;
    if (typeof path !== "string" || typeof id !== "string" || typeof cwd !== "string" ||
        typeof createdAt !== "number" || typeof modifiedAt !== "number" ||
        typeof messageCount !== "number" || typeof firstMessage !== "string")
        return undefined;
    if (allMessagesText !== undefined && typeof allMessagesText !== "string")
        return undefined;
    if (name !== undefined && typeof name !== "string")
        return undefined;
    if (messageColor !== undefined && !SESSION_PICKER_MESSAGE_COLORS.includes(messageColor))
        return undefined;
    return {
        path, id, cwd, createdAt, modifiedAt, messageCount, firstMessage,
        ...(allMessagesText !== undefined ? { allMessagesText } : {}),
        ...(name !== undefined ? { name } : {}),
        ...(messageColor !== undefined ? { messageColor: messageColor } : {}),
    };
}
function parseSessionPickerRows(value) {
    if (!Array.isArray(value))
        return undefined;
    const rows = [];
    for (const entry of value) {
        const row = parseSessionPickerRow(entry);
        if (!row)
            return undefined;
        rows.push(row);
    }
    return rows;
}
const INPUT_FORM_FIELD_TYPES = ["string", "text", "number", "integer", "boolean", "select"];
function parseInputFormFields(value) {
    if (!Array.isArray(value))
        return undefined;
    const fields = [];
    for (const entry of value) {
        if (!isJsonObject(entry))
            return undefined;
        const { name, type, description, required, choices, placeholder, initialValue } = entry;
        if (typeof name !== "string" || !INPUT_FORM_FIELD_TYPES.includes(type) || typeof initialValue !== "string")
            return undefined;
        if (description !== undefined && typeof description !== "string")
            return undefined;
        if (required !== undefined && typeof required !== "boolean")
            return undefined;
        if (choices !== undefined && (!Array.isArray(choices) || !choices.every((choice) => typeof choice === "string")))
            return undefined;
        if (placeholder !== undefined && typeof placeholder !== "string")
            return undefined;
        fields.push({ name, type: type, initialValue,
            ...(typeof description === "string" ? { description } : {}),
            ...(typeof required === "boolean" ? { required } : {}),
            ...(Array.isArray(choices) ? { choices: choices } : {}),
            ...(typeof placeholder === "string" ? { placeholder } : {}) });
    }
    return fields;
}
function parseStringRecord(value) {
    if (value === undefined || !isJsonObject(value))
        return undefined;
    const entries = Object.entries(value);
    if (entries.some(([, entry]) => typeof entry !== "string"))
        return undefined;
    return Object.fromEntries(entries);
}
function parseKeybindingsConfig(value) {
    if (value === undefined || !isJsonObject(value))
        return undefined;
    const config = {};
    for (const [key, binding] of Object.entries(value)) {
        if (typeof binding === "string")
            config[key] = binding;
        else if (Array.isArray(binding) && binding.every((entry) => typeof entry === "string")) {
            config[key] = binding;
        }
        else
            return undefined;
    }
    return config;
}
function parseKeybindingState(value) {
    if (value === undefined || !isJsonObject(value) || !Array.isArray(value.shortcuts))
        return undefined;
    const userBindings = parseKeybindingsConfig(value.userBindings);
    const effectiveBindings = parseKeybindingsConfig(value.effectiveBindings);
    if (!userBindings || !effectiveBindings)
        return undefined;
    const shortcuts = [];
    for (const shortcut of value.shortcuts) {
        if (!isJsonObject(shortcut) || typeof shortcut.key !== "string" ||
            (shortcut.description !== undefined && typeof shortcut.description !== "string"))
            return undefined;
        shortcuts.push({ key: shortcut.key, ...(typeof shortcut.description === "string" ? { description: shortcut.description } : {}) });
    }
    return { userBindings, effectiveBindings, shortcuts };
}
function parseJsonObject(line) {
    if (Buffer.byteLength(line, "utf8") > INTERACTIVE_ENGINE_MAX_FRAME_BYTES)
        return undefined;
    let value;
    try {
        value = JSON.parse(line);
    }
    catch {
        return undefined;
    }
    return isJsonObject(value) ? value : undefined;
}
export function parseInteractiveEngineMessage(line) {
    const value = parseJsonObject(line);
    if (!value || typeof value.type !== "string")
        return undefined;
    switch (value.type) {
        case "engine_ready":
            return value.protocolVersion === INTERACTIVE_ENGINE_PROTOCOL_VERSION && typeof value.pid === "number"
                ? { type: value.type, protocolVersion: INTERACTIVE_ENGINE_PROTOCOL_VERSION, pid: value.pid } : undefined;
        case "engine_bound": return { type: value.type };
        case "engine_keybindings_reloaded": {
            const state = parseKeybindingState(value.state);
            return state ? { type: value.type, state } : undefined;
        }
        case "engine_heartbeat": return typeof value.at === "number" ? { type: value.type, at: value.at } : undefined;
        case "engine_activity_started": return isCallbackActivity(value.activity) ? { type: value.type, activity: value.activity } : undefined;
        case "engine_activity_finished": return typeof value.activityId === "string" ? { type: value.type, activityId: value.activityId } : undefined;
        case "engine_custom_open":
            return typeof value.componentId === "string" && typeof value.overlay === "boolean"
                ? { type: value.type, componentId: value.componentId, overlay: value.overlay,
                    deferInlineCustomUiFocus: value.deferInlineCustomUiFocus === true,
                    overlayOptions: isJsonObject(value.overlayOptions) ? value.overlayOptions : undefined,
                    widgetKey: typeof value.widgetKey === "string" ? value.widgetKey : undefined,
                    widgetPlacement: value.widgetPlacement === "belowEditor" ? "belowEditor" : value.widgetPlacement === "aboveEditor" ? "aboveEditor" : undefined }
                : undefined;
        case "engine_custom_close": return typeof value.componentId === "string" ? { type: value.type, componentId: value.componentId } : undefined;
        case "engine_custom_frame":
            return typeof value.componentId === "string" && typeof value.requestId === "number" &&
                Array.isArray(value.lines) && value.lines.every((entry) => typeof entry === "string")
                ? { type: value.type, componentId: value.componentId, requestId: value.requestId, lines: value.lines } : undefined;
        case "engine_custom_invalidate": return typeof value.componentId === "string" ? { type: value.type, componentId: value.componentId } : undefined;
        case "engine_custom_done": return typeof value.componentId === "string" ? { type: value.type, componentId: value.componentId, result: value.result } : undefined;
        case "engine_custom_terminal": {
            const control = parseEngineTerminalControl(value.control);
            return typeof value.componentId === "string" && control
                ? { type: value.type, componentId: value.componentId, control } : undefined;
        }
        case "engine_custom_control":
            return typeof value.componentId === "string" && ["focus", "hide", "show", "unfocus"].includes(String(value.action))
                ? { type: value.type, componentId: value.componentId, action: value.action } : undefined;
        case "engine_session_picker_open": {
            const sessions = parseSessionPickerRows(value.sessions);
            return typeof value.componentId === "string" && sessions &&
                (value.showRenameHint === undefined || typeof value.showRenameHint === "boolean")
                ? { type: value.type, componentId: value.componentId, sessions,
                    ...(typeof value.showRenameHint === "boolean" ? { showRenameHint: value.showRenameHint } : {}) }
                : undefined;
        }
        case "engine_session_picker_update": {
            const sessions = parseSessionPickerRows(value.sessions);
            return typeof value.componentId === "string" && sessions
                ? { type: value.type, componentId: value.componentId, sessions } : undefined;
        }
        case "engine_session_picker_error":
            return typeof value.componentId === "string" && typeof value.message === "string"
                ? { type: value.type, componentId: value.componentId, message: value.message } : undefined;
        case "engine_session_picker_close":
            return typeof value.componentId === "string" ? { type: value.type, componentId: value.componentId } : undefined;
        case "engine_input_form_open": {
            const fields = parseInputFormFields(value.fields);
            return typeof value.componentId === "string" && typeof value.title === "string" && fields
                ? { type: value.type, componentId: value.componentId, title: value.title, fields } : undefined;
        }
        default: return undefined;
    }
}
export function parseInteractiveEngineCommand(line) {
    const value = parseJsonObject(line);
    if (!value || typeof value.type !== "string" || typeof value.componentId !== "string")
        return undefined;
    if (value.type === "engine_custom_render" && typeof value.requestId === "number" && typeof value.width === "number" && typeof value.rows === "number") {
        return { type: value.type, componentId: value.componentId, requestId: value.requestId, width: value.width, rows: value.rows };
    }
    if (value.type === "engine_custom_input" && typeof value.data === "string")
        return { type: value.type, componentId: value.componentId, data: value.data };
    if (value.type === "engine_custom_dispose" || value.type === "engine_render_dispose")
        return { type: value.type, componentId: value.componentId };
    if ((value.type === "engine_session_picker_select" || value.type === "engine_session_picker_delete") && typeof value.path === "string") {
        return { type: value.type, componentId: value.componentId, path: value.path };
    }
    if (value.type === "engine_session_picker_cancel")
        return { type: value.type, componentId: value.componentId };
    if (value.type === "engine_input_form_cancel")
        return { type: value.type, componentId: value.componentId };
    if (value.type === "engine_input_form_submit") {
        const values = parseStringRecord(value.values);
        return values ? { type: value.type, componentId: value.componentId, values } : undefined;
    }
    if (value.type === "engine_tool_render" && typeof value.requestId === "number" && typeof value.width === "number" &&
        typeof value.toolName === "string" && typeof value.toolCallId === "string" && typeof value.executionStarted === "boolean" &&
        typeof value.argsComplete === "boolean" && typeof value.isPartial === "boolean" && typeof value.expanded === "boolean" &&
        typeof value.showImages === "boolean" && typeof value.imageWidthCells === "number") {
        return { type: value.type, componentId: value.componentId, requestId: value.requestId, width: value.width,
            toolName: value.toolName, toolCallId: value.toolCallId, args: value.args, result: isJsonObject(value.result) ? value.result : undefined,
            executionStarted: value.executionStarted, argsComplete: value.argsComplete, isPartial: value.isPartial,
            expanded: value.expanded, showImages: value.showImages, imageWidthCells: value.imageWidthCells };
    }
    if (value.type === "engine_message_render" && typeof value.requestId === "number" && typeof value.width === "number" &&
        isJsonObject(value.message) && typeof value.expanded === "boolean") {
        return { type: value.type, componentId: value.componentId, requestId: value.requestId, width: value.width, message: value.message, expanded: value.expanded };
    }
    return undefined;
}
export function serializeInteractiveEngineFrame(message) {
    const line = JSON.stringify(message);
    if (Buffer.byteLength(line, "utf8") > INTERACTIVE_ENGINE_MAX_FRAME_BYTES) {
        throw new Error("Interactive engine frame exceeds 1 MiB");
    }
    return `${line}\n`;
}
export const serializeInteractiveEngineMessage = serializeInteractiveEngineFrame;
//# sourceMappingURL=protocol.js.map