import { z } from "zod";
import { isMigrationChecklistActive } from "../../../../migration/migration-checklist.js";
import { getLogger } from "../../../../util/logger.js";
import { runCodingSubagent, } from "../../subagents/coding/run-coding-subagent.js";
import { createToolFactory, PermissionLevel, ToolCategory } from "../types.js";
/** Hard cap on parallel sub-agents to keep token/memory cost bounded. */
const MAX_CONCURRENCY = 5;
/** Hard cap on total sub-agents per spawn call. Matches MAX_CONCURRENCY for now
 *  since we don't implement a queue — every sub-agent runs at once. */
const MAX_SUBAGENTS = MAX_CONCURRENCY;
/**
 * spawnCodingSubagents — main-agent-only tool that spawns a batch of
 * coding sub-agents in parallel and waits for all of them to finish.
 *
 * Currently gated structurally on an active migration checklist
 * (`scratch/migration-checklist.json` exists) — if no migration is in
 * progress, calling the tool errors. The sub-agent primitive itself is
 * general-purpose; we can loosen the gate once we have a second caller.
 */
export const spawnCodingSubagentsToolFactory = createToolFactory("spawnCodingSubagents", ({ clark, services, }) => {
    const logger = getLogger();
    return {
        description: `
Spawn a batch of coding sub-agents in parallel to complete a focused,
partitioned set of coding tasks. Useful when you have many independent
chunks of work (e.g. migrating 20 APIs) and want to fan them out.

**CRITICAL — call this tool ALONE:**
- This MUST be the ONLY tool call in your assistant turn. Do NOT emit
  any other tool calls alongside it (no file reads, no file writes, no
  checklist updates, nothing). If you do, the main agent and the
  sub-agents will race each other on the shared checklist and corrupt
  the migration state.
- This tool BLOCKS until every spawned sub-agent has finished. Do NOT
  start doing migration work yourself while waiting — there is no
  "waiting"; your next turn only happens after the tool returns.
- Only after the tool returns may you read the checklist and handle any
  \`failed\` items.

Each sub-agent:
- Runs in its own isolated context (does NOT see the main chat history)
- Gets only the \`instructions\` string you provide as its task
- Has access to file-edit, file-read, and \`build_manageChecklist\` tools
- CANNOT spawn further sub-agents, run tests, or run git operations
- Shares the migration checklist with every other sub-agent and the main
  agent — all writes are serialized automatically

You MUST partition the work yourself: each sub-agent's instructions
should name the exact items it owns. Do NOT spawn overlapping batches.

The return value summarizes per-sub-agent completion. Read the shared
checklist to see which items succeeded / failed.

**Only available during an active v2 → v3 migration** (the migration
checklist must be present). Outside of a migration, calling this tool
will error.
`,
        // ToolCategory.SUBAGENT is restricted to MAIN in access-control.ts's
        // subagentCategoryMapping — this is what prevents coding sub-agents
        // from recursively spawning more sub-agents.
        category: ToolCategory.SUBAGENT,
        defaultPermission: PermissionLevel.ALLOW,
        readOnly: false,
        inputSchema: z.object({
            subagents: z
                .array(z.object({
                id: z
                    .string()
                    .min(1)
                    .describe("Short, unique label for this sub-agent (e.g. 'worker-1'). Used for logging and the sub-agent's own identification."),
                instructions: z
                    .string()
                    .min(1)
                    .describe("The complete task for this sub-agent. MUST include the exact list of items (e.g. API names) this sub-agent owns, plus any procedural guidance the sub-agent needs. The sub-agent receives this and nothing else — it does not see the main chat history."),
            }))
                .min(1)
                .max(MAX_SUBAGENTS)
                .describe(`Array of sub-agents to spawn (max ${MAX_SUBAGENTS}). Each runs in parallel.`),
            maxStepsPerSubagent: z
                .number()
                .int()
                .positive()
                .max(200)
                .optional()
                .describe("Optional cap on streaming steps per sub-agent. Defaults to 80."),
        }),
        execute: async ({ subagents, maxStepsPerSubagent }) => {
            // Structural gate: only usable during an active migration.
            const inMigration = await isMigrationChecklistActive(services.appRootDirPath);
            if (!inMigration) {
                throw new Error("spawnCodingSubagents is only available during an active v2 → v3 migration (no migration checklist found).");
            }
            // Defensive: cap again at runtime in case schema max changes upstream.
            const batch = subagents.slice(0, MAX_CONCURRENCY);
            if (batch.length !== subagents.length) {
                logger.warn(`[spawnCodingSubagents] Received ${subagents.length} sub-agents, capping to ${MAX_CONCURRENCY}`);
            }
            // Reject duplicate IDs — partitioning is meaningless if two workers
            // share an ID, and logs become ambiguous.
            const ids = new Set();
            for (const sa of batch) {
                if (ids.has(sa.id)) {
                    throw new Error(`Duplicate sub-agent id "${sa.id}" in spawnCodingSubagents call`);
                }
                ids.add(sa.id);
            }
            logger.info(`[spawnCodingSubagents] Spawning ${batch.length} sub-agents: ${batch.map((s) => s.id).join(", ")}`);
            const results = await Promise.all(batch.map((sa) => runCodingSubagent({
                clark,
                services,
                workerId: sa.id,
                instructions: sa.instructions,
                maxSteps: maxStepsPerSubagent,
            })));
            const summary = summarize(results);
            logger.info(`[spawnCodingSubagents] All sub-agents finished: ${summary.completed} completed, ${summary.errored} errored, ${summary.maxSteps} hit max_steps`);
            return {
                success: true,
                subagents: results,
                ...summary,
            };
        },
        getActionName: () => ({
            future: "spawn coding sub-agents",
            present: "spawning coding sub-agents",
            past: "spawned coding sub-agents",
        }),
        formatInput: (input) => ({
            targets: input.subagents.map((s) => s.id),
            summary: `${input.subagents.length} sub-agent${input.subagents.length === 1 ? "" : "s"}`,
        }),
        getSummary: (input, output) => ({
            input: `${input.subagents.length} sub-agent${input.subagents.length === 1 ? "" : "s"}`,
            output: `${output.completed}/${input.subagents.length} completed${output.errored > 0 ? `, ${output.errored} errored` : ""}`,
        }),
    };
});
function summarize(results) {
    let completed = 0;
    let errored = 0;
    let maxSteps = 0;
    for (const r of results) {
        if (r.stopped === "completed")
            completed += 1;
        else if (r.stopped === "error")
            errored += 1;
        else if (r.stopped === "max_steps")
            maxSteps += 1;
    }
    return { completed, errored, maxSteps };
}
//# sourceMappingURL=spawn-coding-subagents.js.map