"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.dispatchLlmCall = dispatchLlmCall;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const train_json_sync_1 = require("./train-json-sync");
const run_state_file_1 = require("./run-state-file");
/**
 * v-j 起的完成判定：
 *   1. 写 cwd train.json snapshot（业务变量）
 *   2. inject prompt — 系统指令告诉 LLM 完成后改 `.ccweb/tracks/<basename>.run-state.json`
 *      中 nodes[nodeId].done=true，或失败时 nodes[nodeId].failed=true + reason='...'
 *   3. polling run-state.json mtime；每次变化读 nodes[nodeId]：
 *        - failed=true → 立即 kind:'failed' + LLM 给的 reason
 *        - done=true → kind:'success'，仍读 train.json 拿 varsDiff；outputs 检查降级
 *          为 missingOutputs warning（不阻进下一节点，由 runtime 选择处理）
 *   4. timeout / signal aborted / PTY ref 变化 走原有 fail-fast 路径
 *
 * **不再**依赖 train.json mtime 作为完成信号 —— LLM 多步调用过程中会反复改 train.json，
 * mtime 触发完成会让 LLM 第一次 Edit 就被强制结束节点。
 */
async function dispatchLlmCall(opts) {
    const cwd = opts.projectFolder;
    const timeoutMs = opts.timeoutMs ?? 600000;
    // 1. 写 snapshot（业务变量初值）
    (0, train_json_sync_1.copyToProjectCwd)(cwd, (0, train_json_sync_1.filterByWhitelist)(opts.beforeSnapshot, opts.whitelist));
    // 记录 run-state 初始 mtime（runtime 刚写完 active 状态）
    const initialMtimeMs = (0, run_state_file_1.getRunStateMtime)(opts.projectFolder, opts.basename);
    try {
        // 2. inject prompt
        const ptyRefAtInject = opts.getTerminalRef?.() ?? null;
        if (opts.getTerminalRef && ptyRefAtInject === null) {
            (0, train_json_sync_1.cleanupProjectCwd)(cwd);
            return { kind: 'failed', reason: 'PTY 未启动或已退出，无法注入 prompt。请确保 CLI 已运行后重新运行工作轨' };
        }
        await opts.injector(opts.prompt);
        // 3. polling run-state.json，读 nodes[nodeId].done / .failed
        const startedAt = Date.now();
        const pollInterval = 500;
        while (true) {
            if (opts.signal.aborted) {
                (0, train_json_sync_1.cleanupProjectCwd)(cwd);
                return { kind: 'cancelled' };
            }
            if (Date.now() - startedAt > timeoutMs) {
                (0, train_json_sync_1.cleanupProjectCwd)(cwd);
                return { kind: 'failed', reason: `LLM 调用超时（${timeoutMs}ms 内未标 done/failed）` };
            }
            if (ptyRefAtInject && opts.getTerminalRef) {
                const cur = opts.getTerminalRef();
                if (cur !== ptyRefAtInject) {
                    (0, train_json_sync_1.cleanupProjectCwd)(cwd);
                    return { kind: 'failed', reason: 'PTY 在 LLM 调用中重启，prompt 落空。请重新运行' };
                }
            }
            const mtimeMs = (0, run_state_file_1.getRunStateMtime)(opts.projectFolder, opts.basename);
            if (mtimeMs > initialMtimeMs + 1) {
                // run-state.json 被 LLM 改了。读出来看 nodes[nodeId]
                const cur = (0, run_state_file_1.readRunState)(opts.projectFolder, opts.basename);
                if (cur === null) {
                    // codex P3：文件存在但 readRunState 返 null = schema 不识别（版本不匹配
                    // / 文件损坏）。fail-fast 不傻等超时。
                    const filePath = path.join(opts.projectFolder, '.ccweb', 'tracks', `${opts.basename}.run-state.json`);
                    if (fs.existsSync(filePath)) {
                        (0, train_json_sync_1.cleanupProjectCwd)(cwd);
                        return { kind: 'failed', reason: 'run-state.json 解析失败或版本不支持（请检查文件）' };
                    }
                    // 文件不存在：runtime 还没写 active 状态？等一轮
                    await sleep(pollInterval);
                    continue;
                }
                const nodeState = cur.nodes?.[opts.nodeId];
                // codex P0：iter 校验。LLM 上一轮可能写过 done=true，runtime 进新一轮时
                // 清了 done=false，但 LLM 进程仍活着可能再写。只 accept 当前 iter 的标记。
                if (nodeState && nodeState.iter === opts.expectedIter) {
                    if (nodeState.failed === true) {
                        (0, train_json_sync_1.cleanupProjectCwd)(cwd);
                        // codex P2 #5：reason 必须是字符串才采纳
                        const reason = typeof nodeState.reason === 'string' && nodeState.reason.length > 0
                            ? `LLM 自报失败：${nodeState.reason}`
                            : 'LLM 标记 failed=true 但未给有效 reason';
                        return { kind: 'failed', reason };
                    }
                    if (nodeState.done === true) {
                        break; // 完成
                    }
                }
                // 否则只是 LLM 改了别的字段 / iter 不匹配的 stale 标记，继续等
            }
            await sleep(pollInterval);
        }
        // 4. reload train.json 拿变量更新 diff
        const reload = await (0, train_json_sync_1.reloadFromProjectCwd)(cwd);
        if (!reload.ok || !reload.data) {
            (0, train_json_sync_1.cleanupProjectCwd)(cwd);
            return { kind: 'failed', reason: `.ccweb-flow-train.json reload 失败：${reload.error ?? 'unknown'}` };
        }
        const newSnapshotFiltered = (0, train_json_sync_1.filterByWhitelist)(reload.data, opts.whitelist);
        const varsDiff = [];
        for (const k of Object.keys(newSnapshotFiltered)) {
            const oldV = opts.beforeSnapshot[k] ?? null;
            const newV = newSnapshotFiltered[k];
            if (!sameValue(oldV, newV)) {
                varsDiff.push({ key: k, old: oldV, new: newV });
            }
        }
        // outputs 校验降级为 warning：done=true 但某些 outputs 未改 → 仍 success，
        // 但返回 missingOutputs 让 runtime 记 warning（spec §6.2 调整：相信 LLM 自报）
        const changedKeys = new Set(varsDiff.map((d) => d.key));
        const missingOutputs = opts.outputs.filter((k) => !changedKeys.has(k));
        (0, train_json_sync_1.cleanupProjectCwd)(cwd);
        return { kind: 'success', newSnapshot: newSnapshotFiltered, varsDiff, missingOutputs: missingOutputs.length > 0 ? missingOutputs : undefined };
    }
    catch (e) {
        (0, train_json_sync_1.cleanupProjectCwd)(cwd);
        if (opts.signal.aborted)
            return { kind: 'cancelled' };
        return { kind: 'failed', reason: `dispatch 异常：${e.message}` };
    }
}
function sameValue(a, b) {
    if (a === b)
        return true;
    if (typeof a !== typeof b)
        return false;
    if (typeof a === 'object') {
        try {
            return JSON.stringify(a) === JSON.stringify(b);
        }
        catch {
            return false;
        }
    }
    return false;
}
function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}
//# sourceMappingURL=llm-dispatcher.js.map