"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.AgentTool = void 0;
exports.createSubagentTool = createSubagentTool;
exports.runSubagent = runSubagent;
exports.resolveSubagentConfig = resolveSubagentConfig;
exports.appendSubagentRoutingMetric = appendSubagentRoutingMetric;
exports.listSubagentRoutingMetrics = listSubagentRoutingMetrics;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const ollamaClient_1 = require("../core/ollamaClient");
const queryLoop_1 = require("../core/queryLoop");
const atomicFile_1 = require("../persistence/atomicFile");
const modelRouting_1 = require("./modelRouting");
const agentLoader_1 = require("./agentLoader");
const subagentRegistry_1 = require("../services/subagentRegistry");
const subagentRuns_1 = require("../services/subagentRuns");
const silentFailureSink_1 = require("../observability/silentFailureSink");
exports.AgentTool = {
    name: 'agent',
    description: 'Delegate a task to a subagent that runs in an isolated context. Returns only the summary, not the full conversation.',
    parameters: {
        type: 'object',
        properties: {
            prompt: { type: 'string', description: 'The task prompt for the subagent' },
            type: { type: 'string', description: 'Subagent type: explore (read-only), plan (creates plan), general (full capabilities)' },
        },
        required: ['prompt'],
    },
    isReadOnly: false,
    async execute(input) {
        return {
            success: false,
            output: 'AgentTool.execute should not be called directly — use createSubagentTool() to bind a client and tool list.',
            error: 'Direct execution not supported',
        };
    },
};
function createSubagentTool(deps) {
    const runner = deps.runner ?? runSubagent;
    return {
        name: 'agent',
        description: 'Delegate a task to a sub-agent that runs in an isolated context. Pass agent_id (e.g. researcher, developer, qa, writer, architect, security, or any custom agent under .harness/agents/) to choose a role. Returns only the summary, not the full conversation.',
        parameters: {
            type: 'object',
            properties: {
                prompt: { type: 'string', description: 'The task prompt for the sub-agent' },
                agent_id: { type: 'string', description: 'Built-in role id or custom agent id from .harness/agents/' },
                type: { type: 'string', description: 'Legacy subagent type alias: explore, plan, general' },
            },
            required: ['prompt'],
        },
        isReadOnly: false,
        async execute(input) {
            const prompt = typeof input.prompt === 'string' ? input.prompt.trim() : '';
            if (!prompt) {
                return { success: false, output: 'prompt is required', error: 'prompt is required' };
            }
            const agentId = typeof input.agent_id === 'string' && input.agent_id.trim() ? input.agent_id.trim() : undefined;
            const legacyType = typeof input.type === 'string' && input.type.trim() ? input.type.trim() : undefined;
            const customAgents = deps.getCustomAgents ? deps.getCustomAgents() : [];
            // When agent_id resolves to nothing and legacy type is provided, fall
            // back to the original behaviour where `name` selected the tool filter.
            // Generate a runId so the run is visible in /api/subagents and the
            // active sub-agents UI bar.
            const runId = `agent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
            const config = {
                name: agentId ?? legacyType ?? 'general',
                systemPrompt: '',
                agentId,
                customAgents,
                runId,
            };
            try {
                let effectivePrompt = prompt;
                if (deps.getRecallContext) {
                    try {
                        const recall = await deps.getRecallContext(prompt);
                        if (recall && recall.trim().length > 0) {
                            effectivePrompt = `${recall}\n\n${prompt}`;
                        }
                    }
                    catch (err) {
                        (0, silentFailureSink_1.recordSwallowed)('subagent.getRecallContext', err);
                    }
                }
                const summary = await runner(config, effectivePrompt, deps.getParentClient(), deps.getAvailableTools());
                return { success: true, output: summary };
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                return { success: false, output: `Subagent failed: ${message}`, error: message };
            }
        },
    };
}
async function runSubagent(config, prompt, parentClient, availableTools) {
    const effectiveConfig = resolveSubagentConfig(config, prompt);
    const started = Date.now();
    let success = false;
    // Create isolated client (may use different model)
    const client = effectiveConfig.model
        ? new ollamaClient_1.OllamaClient({ model: effectiveConfig.model })
        : parentClient;
    // Determine tool set for the subagent
    let subagentTools = effectiveConfig.tools ?? filterToolsForSubagent(availableTools, effectiveConfig.name);
    if (effectiveConfig.allowedTools && effectiveConfig.allowedTools.length > 0) {
        const allow = new Set(effectiveConfig.allowedTools);
        subagentTools = subagentTools.filter((tool) => allow.has(tool.name));
    }
    const deps = {
        client,
        tools: subagentTools,
    };
    // Build the abort controller used by the cancel endpoint. If the caller
    // supplied a signal, link them so either source can abort the run.
    const internalController = new AbortController();
    const externalSignal = effectiveConfig.abortSignal;
    const onExternalAbort = () => internalController.abort();
    if (externalSignal) {
        if (externalSignal.aborted)
            internalController.abort();
        else
            externalSignal.addEventListener('abort', onExternalAbort, { once: true });
    }
    const loopConfig = {
        model: client.getModel(),
        systemPrompt: effectiveConfig.systemPrompt,
        maxTurns: effectiveConfig.maxTurns ?? 10,
        abortSignal: internalController.signal,
    };
    const runId = effectiveConfig.runId;
    if (runId) {
        (0, subagentRegistry_1.registerSubagent)({ id: runId, name: effectiveConfig.name || 'subagent', prompt, controller: internalController, startedAtMs: started });
    }
    // Run the subagent loop in an isolated context
    const messages = [{ role: 'user', content: prompt }];
    let lastText = '';
    let cancelled = false;
    let runError = null;
    try {
        for await (const event of (0, queryLoop_1.queryLoop)(loopConfig, deps, messages)) {
            if (effectiveConfig.onEvent) {
                try {
                    effectiveConfig.onEvent(event);
                }
                catch { /* listener errors are non-fatal */ }
            }
            // Surface live activity to the sub-agents bar. Best-effort — no-op
            // when the run has no runId (registry never registered it).
            if (runId) {
                const ev = event;
                if (ev.type === 'tool_call' && ev.call && ev.call.name) {
                    (0, subagentRegistry_1.updateSubagentActivity)(runId, '\uD83D\uDD27 ' + String(ev.call.name));
                }
                else if (ev.type === 'synthesis_fired') {
                    (0, subagentRegistry_1.updateSubagentActivity)(runId, 'finalising\u2026');
                }
                else if (ev.type === 'text' && typeof ev.content === 'string' && ev.content.trim()) {
                    (0, subagentRegistry_1.updateSubagentActivity)(runId, '\u270D writing reply');
                }
            }
            if (event.type === 'text') {
                lastText = event.content;
            }
        }
        success = lastText.length > 0;
    }
    catch (error) {
        if (error?.name === 'AbortError' || internalController.signal.aborted) {
            cancelled = true;
        }
        else {
            runError = error instanceof Error ? error : new Error(String(error));
            throw error;
        }
    }
    finally {
        if (externalSignal) {
            try {
                externalSignal.removeEventListener('abort', onExternalAbort);
            }
            catch { /* best-effort */ }
        }
        // Snapshot the tool history from the registry BEFORE we unregister so
        // it can be persisted into the run record. Without this, the "what did
        // this agent actually do?" answer is lost the moment the run ends.
        const activeRecord = runId ? (0, subagentRegistry_1.getActiveSubagent)(runId) : undefined;
        const toolHistory = activeRecord?.activityHistory?.map((entry) => entry.label) ?? [];
        if (runId)
            (0, subagentRegistry_1.unregisterSubagent)(runId);
        await appendSubagentRoutingMetric(effectiveConfig.metricsProjectDir ?? process.cwd(), {
            timestamp: new Date().toISOString(),
            name: effectiveConfig.name,
            preset: effectiveConfig.preset,
            model: effectiveConfig.model ?? client.getModel(),
            tier: effectiveConfig.routingDecision?.tier,
            escalated: effectiveConfig.routingDecision?.escalated,
            reasons: effectiveConfig.routingDecision?.reasons ?? [],
            success,
            durationMs: Date.now() - started,
            outputChars: lastText.length,
        }).catch(() => { });
        // Persistent run record so the Agents tab can show a history view
        // and the user can answer "what has this agent done lately?" without
        // grepping session events.
        const projectDir = effectiveConfig.metricsProjectDir
            ?? process.env.HARNESS_PROJECT_DIR
            ?? process.cwd();
        const now = Date.now();
        await (0, subagentRuns_1.appendSubagentRun)(projectDir, {
            runId: runId ?? `subagent-${now}-${Math.random().toString(36).slice(2, 8)}`,
            name: effectiveConfig.name || 'subagent',
            startedAt: new Date(started).toISOString(),
            endedAt: new Date(now).toISOString(),
            durationMs: now - started,
            status: cancelled ? 'cancelled' : runError ? 'failed' : 'completed',
            prompt: prompt.slice(0, 500),
            output: (lastText || '').slice(0, 2000),
            toolHistory: toolHistory.slice(-50),
            model: effectiveConfig.model ?? client.getModel(),
            outputDir: process.env.HARNESS_AGENT_OUTPUT_DIR || undefined,
            error: runError ? runError.message : undefined,
        }).catch(() => { });
    }
    if (cancelled && !lastText) {
        return '(subagent cancelled)';
    }
    // Return only the summary text — never the full conversation history
    return lastText || '(subagent produced no output)';
}
function resolveSubagentConfig(config, prompt) {
    // Step 1: apply built-in or custom agent definition (when agentId is set).
    let working = config;
    if (config.agentId) {
        const definition = (0, agentLoader_1.resolveAgentDefinition)(config.agentId, config.customAgents ?? []);
        if (definition) {
            working = {
                ...config,
                name: config.name || definition.name || definition.id,
                systemPrompt: config.systemPrompt || definition.systemPrompt,
                model: config.model ?? definition.model,
                maxTurns: config.maxTurns ?? definition.maxTurns,
                preset: config.preset ?? definition.preset,
                allowedTools: config.allowedTools ?? definition.allowedTools,
            };
        }
    }
    // Step 2: apply preset routing if a preset is set.
    if (!working.preset) {
        return working;
    }
    const helper = (0, modelRouting_1.createHelperAgentConfig)({
        taskType: working.preset,
        prompt,
        ...working.routingInput,
    }, working.routingPolicy);
    return {
        ...working,
        name: working.name || helper.name,
        systemPrompt: working.systemPrompt || helper.systemPrompt,
        model: working.model ?? helper.model,
        maxTurns: working.maxTurns ?? helper.maxTurns,
        routingDecision: helper.routing,
    };
}
async function appendSubagentRoutingMetric(projectDir, metric) {
    const filePath = path.join(projectDir, '.harness', 'learning', 'subagent-routing.jsonl');
    await fs.mkdir(path.dirname(filePath), { recursive: true });
    await (0, atomicFile_1.withFileLock)(filePath, () => fs.appendFile(filePath, JSON.stringify(metric) + '\n', 'utf-8'));
    return filePath;
}
async function listSubagentRoutingMetrics(projectDir, limit = 20) {
    const filePath = path.join(projectDir, '.harness', 'learning', 'subagent-routing.jsonl');
    try {
        const raw = await fs.readFile(filePath, 'utf-8');
        return raw.trim().split('\n')
            .filter(Boolean)
            .map((line) => JSON.parse(line))
            .slice(-limit);
    }
    catch {
        return [];
    }
}
function filterToolsForSubagent(tools, subagentType) {
    switch (subagentType) {
        case 'explore':
            // Read-only tools only
            return tools.filter((t) => t.isReadOnly);
        case 'plan':
            // Read-only + file write for plan output
            return tools.filter((t) => t.isReadOnly || t.name === 'file_write');
        default:
            // General: all tools except the agent tool itself (prevent recursion bombs)
            return tools.filter((t) => t.name !== 'agent');
    }
}
//# sourceMappingURL=subagent.js.map