/**
 * Agent — unified runtime with strategy plug-ins.
 *
 * Replaces the legacy split between {@link OODAAgentImpl} (full ceremony,
 * OODA-only) and the inline preste loops (`standalone-react-loop.ts`,
 * `standalone-plan-loop.ts`). One class, four strategies :
 *
 *   - `ooda`     — Observe → Orient → Decide → Act → (Reflect?) → Feedback
 *   - `react`    — Thought → Action → Observe (text protocol + JSON fallback)
 *   - `plan`     — Plan → Execute → Synthesize
 *   - `one-shot` — Single LLM call, no tool loop
 *
 * Hooks (skillCapture, onStep, attestation) run uniformly across all
 * strategies. Strategies remain pure : they invoke `ctx.tools` + `ctx.llmFn`
 * and return an {@link AgentCycleResult}, nothing else.
 *
 * @public @experimental @since 2.0.0
 */
import { randomUUID } from "node:crypto";
import { createOneShotStrategy, createPlanStrategy, createReactStrategy, } from "../strategies/index.js";
import { createOODAStrategy, createUnconfiguredOODAInvoker, } from "../strategies/ooda.js";
import { captureSkillFromCycle } from "./ooda/skill-capture.js";
const NOOP_LOGGER = {
    info: () => undefined,
    warn: () => undefined,
    error: () => undefined,
    debug: () => undefined,
};
function resolveStrategy(cfg) {
    const s = cfg.strategy ?? "ooda";
    if (typeof s !== "string") {
        return { strategy: s, name: s.name };
    }
    switch (s) {
        case "ooda":
            return {
                strategy: createOODAStrategy(cfg.oodaInvoker ?? createUnconfiguredOODAInvoker()),
                name: "ooda",
            };
        case "react":
            return { strategy: createReactStrategy(), name: "react" };
        case "plan":
            return { strategy: createPlanStrategy(), name: "plan" };
        case "one-shot":
            return { strategy: createOneShotStrategy(), name: "one-shot" };
        default: {
            const _exhaustive = s;
            throw new Error(`Unknown strategy: ${String(_exhaustive)}`);
        }
    }
}
/**
 * The unified Agent — single runtime, four strategies, all hooks.
 *
 * @public @experimental @since 2.0.0
 */
export class Agent {
    cfg;
    strategy;
    strategyName;
    logger;
    constructor(cfg) {
        this.cfg = cfg;
        const resolved = resolveStrategy(cfg);
        this.strategy = resolved.strategy;
        this.strategyName = resolved.name;
        this.logger = cfg.logger ?? NOOP_LOGGER;
    }
    /**
     * Execute one full cycle of the configured strategy and return the result.
     *
     * Fires hooks uniformly across strategies :
     *   - `onStep` is invoked by each strategy on every meaningful phase.
     *   - skillCapture fires after a successful cycle when configured + quality met.
     */
    async run(task) {
        const runId = randomUUID();
        const startedAt = Date.now();
        const maxSteps = this.cfg.maxSteps ?? 10;
        // Wrap hooks to inject `strategy` into every event.
        const wrappedHooks = this.cfg.hooks
            ? {
                ...(this.cfg.hooks.onStep
                    ? {
                        onStep: async (event) => {
                            try {
                                await this.cfg.hooks?.onStep?.({
                                    ...event,
                                    strategy: this.strategyName,
                                });
                            }
                            catch (err) {
                                this.logger.warn?.({
                                    runId,
                                    err: err?.message ?? String(err),
                                }, "agent.onStep_failed");
                            }
                        },
                    }
                    : {}),
                ...(this.cfg.hooks.onPhaseComplete
                    ? {
                        onPhaseComplete: (phase, output) => {
                            try {
                                this.cfg.hooks?.onPhaseComplete?.(phase, output);
                            }
                            catch (err) {
                                this.logger.warn?.({
                                    runId,
                                    err: err?.message ?? String(err),
                                }, "agent.onPhaseComplete_failed");
                            }
                        },
                    }
                    : {}),
            }
            : undefined;
        const ctx = {
            task,
            systemPrompt: this.cfg.systemPrompt,
            tools: this.cfg.tools,
            ...(this.cfg.llm ? { llm: this.cfg.llm } : {}),
            ...(this.cfg.llmFn ? { llmFn: this.cfg.llmFn } : {}),
            maxSteps,
            logger: this.logger,
            ...(this.cfg.allowedTools ? { allowedTools: this.cfg.allowedTools } : {}),
            ...(wrappedHooks ? { hooks: wrappedHooks } : {}),
            ...(this.cfg.signal ? { signal: this.cfg.signal } : {}),
            runId,
            agentId: this.cfg.agentId,
        };
        let result;
        try {
            result = await this.strategy.run(ctx);
        }
        catch (err) {
            this.logger.error?.({
                runId,
                agentId: this.cfg.agentId,
                strategy: this.strategyName,
                err: err?.message ?? String(err),
            }, "agent.run_failed");
            return {
                runId,
                agentId: this.cfg.agentId,
                strategy: this.strategyName,
                finalMessage: "",
                stopReason: "error",
                stepCount: 0,
                tokensUsed: { in: 0, out: 0 },
                durationMs: Date.now() - startedAt,
            };
        }
        // Outcome mapping.
        let outcome = result.outcome ?? null;
        if (this.cfg.outcomeMapping) {
            try {
                outcome = this.cfg.outcomeMapping(result) ?? outcome;
            }
            catch (err) {
                this.logger.warn?.({
                    runId,
                    err: err?.message ?? String(err),
                }, "agent.outcomeMapping_failed");
            }
        }
        // Skill capture — fire after a successful cycle.
        let skillCaptured = false;
        if (this.cfg.skillCapture?.enabled && result.stopReason === "complete") {
            try {
                const trigger = {
                    toolCallCount: result.stepCount,
                    roi: outcome?.value_cents ? outcome.value_cents / 100 : 0,
                    durationMs: Date.now() - startedAt,
                    wasReplay: false,
                };
                const traceSummary = `Run ${runId} ${this.cfg.agentId} [${this.strategyName}]: ${result.stepCount} steps`;
                const captureResult = await captureSkillFromCycle(this.cfg.agentId, runId, trigger, outcome, traceSummary, this.cfg.skillCapture, this.logger);
                skillCaptured = captureResult.captured;
            }
            catch (err) {
                this.logger.warn?.({
                    runId,
                    err: err?.message ?? String(err),
                }, "agent.skill_capture_failed");
            }
        }
        return {
            runId,
            agentId: this.cfg.agentId,
            strategy: this.strategyName,
            finalMessage: result.finalMessage,
            stopReason: result.stopReason,
            stepCount: result.stepCount,
            tokensUsed: result.tokensUsed,
            durationMs: Date.now() - startedAt,
            ...(outcome ? { outcome } : {}),
            ...(result.stepsTrace ? { stepsTrace: result.stepsTrace } : {}),
            ...(result.model ? { model: result.model } : {}),
            ...(result.provider ? { provider: result.provider } : {}),
            skillCaptured,
        };
    }
    /** Read-only accessor — the strategy this Agent was constructed with. */
    get strategyKind() {
        return this.strategyName;
    }
}
/**
 * Factory for the unified Agent.
 *
 * @public @experimental @since 2.0.0
 */
export function createAgent(cfg) {
    return new Agent(cfg);
}
//# sourceMappingURL=agent.js.map