/**
 * Sub-agent orchestration for skills.
 *
 * Spawns independent agent sessions with isolated context for skill execution.
 * The sub-agent receives ONLY:
 *   - Skill instructions (from skill definition)
 *   - Context passed explicitly (e.g. branch names, repo path)
 *   - AGENTS.md from the working directory (for project knowledge)
 *
 * It does NOT receive the parent session's conversation history (for full isolation).
 *
 * Simplified from workflows/sub-agent.ts — no phase tracking, no workflow tool.
 */
import { randomUUID } from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { bobbitStateDir } from "../bobbit-dir.js";
import { RpcBridge } from "../agent/rpc-bridge.js";
import { assembleSystemPrompt } from "../agent/system-prompt.js";
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
/**
 * Build a system prompt for a skill sub-agent.
 * Contains skill instructions + explicit context — no parent conversation.
 */
function buildSkillPrompt(skill, context) {
    const sections = [];
    sections.push(`# Skill: ${skill.name}`);
    sections.push("");
    sections.push(skill.instructions);
    if (context && Object.keys(context).length > 0) {
        sections.push("");
        sections.push("## Context");
        for (const [key, value] of Object.entries(context)) {
            sections.push(`- **${key}**: ${value}`);
        }
    }
    sections.push("");
    sections.push("## Expected Output");
    sections.push(skill.expectedOutput);
    sections.push("");
    sections.push("## Important");
    sections.push("- Complete this skill and then stop.");
    sections.push("- Produce the expected output format described above.");
    return sections.join("\n");
}
/**
 * Run a skill in an independent sub-agent process.
 *
 * Spawns a new agent, sends a kickoff prompt, waits for completion,
 * and collects the conversation output.
 */
export async function runSkillAgent(request) {
    const startTime = Date.now();
    const subSessionId = `skill-${request.parentSessionId.slice(0, 8)}-${request.skill.id}-${randomUUID().slice(0, 8)}`;
    const timeoutMs = request.skill.timeoutMs ?? DEFAULT_TIMEOUT_MS;
    const skillPrompt = buildSkillPrompt(request.skill, request.context);
    // Assemble system prompt: AGENTS.md for project context + skill instructions
    const systemPromptPath = assembleSystemPrompt(subSessionId, {
        cwd: request.cwd,
        goalSpec: skillPrompt,
        goalTitle: `Skill: ${request.skill.name}`,
        goalState: "active",
    });
    const bridgeOptions = {
        cwd: request.cwd,
    };
    if (request.agentCliPath)
        bridgeOptions.cliPath = request.agentCliPath;
    if (systemPromptPath)
        bridgeOptions.systemPromptPath = systemPromptPath;
    const rpc = new RpcBridge(bridgeOptions);
    let output = "";
    try {
        await rpc.start();
        // Wait for the agent to finish
        const completionPromise = new Promise((resolve, reject) => {
            const timer = setTimeout(() => {
                reject(new Error(`Skill agent timed out after ${timeoutMs}ms`));
            }, timeoutMs);
            const eventUnsub = rpc.onEvent((event) => {
                if (event.type === "agent_end") {
                    clearTimeout(timer);
                    eventUnsub();
                    resolve();
                }
            });
        });
        // Send kickoff prompt
        await rpc.prompt(`Execute the skill "${request.skill.name}". Follow the instructions in your system prompt carefully.`);
        await completionPromise;
        // Collect conversation output
        try {
            const msgsResp = await rpc.getMessages();
            if (msgsResp.success && Array.isArray(msgsResp.data)) {
                output = extractLastAssistantOutput(msgsResp.data);
            }
        }
        catch {
            // Non-fatal
        }
        return {
            id: request.id,
            skillId: request.skill.id,
            status: "completed",
            output,
            durationMs: Date.now() - startTime,
        };
    }
    catch (err) {
        const isTimeout = err.message?.includes("timed out");
        return {
            id: request.id,
            skillId: request.skill.id,
            status: isTimeout ? "timeout" : "failed",
            output,
            durationMs: Date.now() - startTime,
            error: err.message,
        };
    }
    finally {
        await rpc.stop().catch(() => { });
        // Clean up temporary system prompt
        try {
            const promptDir = path.join(bobbitStateDir(), "session-prompts");
            const promptFile = path.join(promptDir, `${subSessionId}.md`);
            if (fs.existsSync(promptFile))
                fs.unlinkSync(promptFile);
        }
        catch { /* ignore */ }
    }
}
/**
 * Run multiple skills in parallel as independent sub-agents.
 */
export async function runSkillAgentsParallel(requests) {
    return Promise.all(requests.map((req) => runSkillAgent(req)));
}
/**
 * Build a skill invocation request.
 */
export function createSkillRequest(skill, parentSessionId, cwd, context, agentCliPath) {
    return {
        id: randomUUID(),
        skill,
        cwd,
        context,
        parentSessionId,
        agentCliPath,
    };
}
// --- Helpers ---
/**
 * Extract the last assistant message text from agent messages.
 * This is the skill's final output.
 */
function extractLastAssistantOutput(messages) {
    for (let i = messages.length - 1; i >= 0; i--) {
        const msg = messages[i];
        if (!msg || msg.role !== "assistant")
            continue;
        const text = Array.isArray(msg.content)
            ? msg.content.filter((c) => c.type === "text").map((c) => c.text).join("\n")
            : typeof msg.content === "string" ? msg.content : "";
        if (text)
            return text;
    }
    return "";
}
//# sourceMappingURL=sub-agent.js.map