"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseOpencode = parseOpencode;
exports.parseOpencodeExit = parseOpencodeExit;
const shared_js_1 = require("./shared.js");
function parseOpencode(line) {
    const obj = (0, shared_js_1.tryJson)(line);
    if (!obj || obj.type !== 'text')
        return [];
    const part = obj.part;
    const text = part && typeof part.text === 'string' ? part.text : '';
    if (text.length === 0)
        return [];
    return [{ type: 'text_delta', text }];
}
/**
 * Walk every line of opencode JSON-Lines stdout, sum tokens + USD cost
 * from every `step_finish`, return undefined when no step_finish carried
 * any usable counts.
 *
 * Cost summing rationale: opencode's step_finish carries a per-step
 * `cost` (USD) computed against opencode-go's published per-token
 * pricing. Multi-step sessions (tool calls) accrue cost per step; summing
 * yields total session cost.
 */
function aggregateOpencodeUsage(fullStdout) {
    const acc = {};
    let any = false;
    for (const line of fullStdout.split('\n')) {
        const obj = (0, shared_js_1.tryJson)(line);
        if (!obj || obj.type !== 'step_finish')
            continue;
        const part = obj.part;
        const tokens = part?.tokens;
        if (tokens) {
            if (typeof tokens.input === 'number') {
                acc.inputTokens = (acc.inputTokens ?? 0) + tokens.input;
                any = true;
            }
            if (typeof tokens.output === 'number') {
                acc.outputTokens = (acc.outputTokens ?? 0) + tokens.output;
                any = true;
            }
            if (typeof tokens.cache?.read === 'number') {
                acc.cachedInputTokens = (acc.cachedInputTokens ?? 0) + tokens.cache.read;
                any = true;
            }
        }
        // Cost is present on every step_finish opencode emits, independent
        // of whether tokens.* fields were populated. A malformed-tokens-but-
        // known-cost step still represents real spend.
        if (typeof part?.cost === 'number') {
            acc.costUsd = (acc.costUsd ?? 0) + part.cost;
            any = true;
        }
    }
    return any ? acc : undefined;
}
/**
 * OpenCode on-exit handler. Two responsibilities:
 *
 * 1. JSON-Lines path (modern `opencode run --format json`):
 *    parseOpencode already emitted text_delta events; the runner
 *    accumulated them. Emit a single synthetic message_done with
 *    finalText="" (runner falls back to its accumulator) plus the SUM
 *    of step_finish token counts across the whole session.
 *
 * 2. Single-blob path (older opencode builds, fallback shape): parse
 *    the whole stdout as a JSON object, lift `message`/`result`/`output`
 *    as finalText. No usage available.
 *
 * Either way: ONE message_done — never multiple — so the runner's
 * participant_done lifecycle fires exactly once.
 *
 * Always emit message_done when JSON-Lines is detected, even when no
 * step_finish was found. Earlier code returned [] when usage was
 * undefined, which dropped the terminal event entirely and caused the
 * runner's `for await` loop to exit without firing participant_done —
 * the phase then sat in `working` until the watchdog timeout.
 */
function parseOpencodeExit(fullStdout) {
    if (fullStdout.trim().length === 0)
        return [];
    const firstLine = fullStdout.split('\n').find((l) => l.trim().length > 0);
    if (firstLine) {
        const probe = (0, shared_js_1.tryJson)(firstLine);
        if (probe && typeof probe.type === 'string') {
            const usage = aggregateOpencodeUsage(fullStdout);
            return [
                usage
                    ? { type: 'message_done', finalText: '', usage }
                    : { type: 'message_done', finalText: '' },
            ];
        }
    }
    const obj = (0, shared_js_1.tryJson)(fullStdout);
    if (!obj)
        return [{ type: 'message_done', finalText: fullStdout }];
    const text = (typeof obj.message === 'string' && obj.message) ||
        (typeof obj.result === 'string' && obj.result) ||
        (typeof obj.output === 'string' && obj.output) ||
        fullStdout;
    return [{ type: 'message_done', finalText: text }];
}
//# sourceMappingURL=opencode.js.map