/**
 * Bootstrap a workflow run.
 *
 * Responsibilities (UI doc §3.4 / §7 landing #1):
 *   1. write the params blob to `runs/<runId>/blobs/<inputHash>`
 *   2. resolve every subagent's `bot` field through the supplied
 *      `botResolver` and freeze the result into `runCreated.botSnapshots`
 *      so future rename in bots.json doesn't drift the historical view
 *   3. append `runCreated` followed by `runStarted`
 *
 * The caller owns the EventLog (it already wrote the workflow.json
 * snapshot to `runs/<runId>/workflow.json` before calling) and drives
 * the orchestrator after `createRun` returns.
 */
import { createHash } from 'node:crypto';
import { join } from 'node:path';
import { atomicWriteFile } from '../utils/atomic-write.js';
import { loadBotConfigs } from '../bot-registry.js';
import { canonicalJsonStringify, computeRevisionId } from './definition.js';
import { snapshotWorkflowDefinition, writeRunChatBinding, } from './loader.js';
export async function createRun(log, input) {
    await snapshotWorkflowDefinition(log.runId, input.def, { runDir: log.runDir });
    if (input.chatBinding) {
        await writeRunChatBinding(log.runId, input.chatBinding, { runDir: log.runDir });
    }
    const inputRef = await writeRunInputBlob(log, input.params);
    const revisionId = input.revisionId ?? computeRevisionId(input.def);
    const botSnapshots = collectBotSnapshots(input.def, input.botResolver);
    const runCreatedEvent = (await log.append({
        runId: log.runId,
        type: 'runCreated',
        actor: 'system',
        payload: {
            workflowId: input.def.workflowId,
            revisionId,
            inputRef,
            initiator: input.initiator,
            ...(Object.keys(botSnapshots).length > 0 ? { botSnapshots } : {}),
        },
    }));
    const runStartedEvent = (await log.append({
        runId: log.runId,
        type: 'runStarted',
        actor: 'scheduler',
        payload: {},
    }));
    return { runCreatedEvent, runStartedEvent, inputRef };
}
async function writeRunInputBlob(log, params) {
    const canonical = canonicalJsonStringify(params);
    const buf = Buffer.from(canonical, 'utf-8');
    const hash = createHash('sha256').update(buf).digest('hex');
    const path = join(log.blobDir, hash);
    // Content-addressed: same input ⇒ same path; re-writes are harmless.
    // 原子写：路径即 sha256，裸写半截会留下「名字对、内容错」的 blob。
    await atomicWriteFile(path, buf);
    return {
        outputHash: `sha256:${hash}`,
        outputPath: path,
        outputBytes: buf.length,
        outputSchemaVersion: 1,
        contentType: 'application/json',
    };
}
function collectBotSnapshots(def, resolver) {
    const out = {};
    for (const node of Object.values(def.nodes)) {
        if (node.type !== 'subagent')
            continue;
        if (out[node.bot])
            continue;
        const snap = resolver(node.bot);
        if (!snap) {
            throw new Error(missingBotMessage(node.bot, def.workflowId, out));
        }
        out[node.bot] = snap;
    }
    return out;
}
function missingBotMessage(botRef, workflowId, resolvedSnapshots) {
    const availableIds = new Set();
    for (const snap of Object.values(resolvedSnapshots)) {
        if (snap.larkAppId)
            availableIds.add(snap.larkAppId);
    }
    try {
        for (const cfg of loadBotConfigs())
            availableIds.add(cfg.larkAppId);
    }
    catch {
        // Missing/invalid bots.json should not hide the original workflow error.
    }
    const idHint = availableIds.size > 0
        ? ` Available bot larkAppIds: ${Array.from(availableIds).join(', ')}.`
        : '';
    return (`Bot '${botRef}' referenced in workflow '${workflowId}' not found in registry. ` +
        `Use 'botmux bots list' and set subagent.bot to the bot's larkAppId, not its display name.` +
        idHint);
}
//# sourceMappingURL=run-init.js.map