/**
 * Clawx Agent — the core orchestrator.
 *
 * EXTRACTION NOTE:
 * OpenClaw's agent orchestration lives in pi-embedded-runner/run.ts (3000+ lines)
 * which handles auth rotation, sandbox environments, context engines, plugin hooks,
 * provider-specific quirks, compaction, failover, and channel integration.
 *
 * We DISCARD all of that and use the clean pi-agent-core agentLoop() directly.
 * The agentLoop (agent-loop.js, 308 lines) handles:
 *   user message → model call → tool calls → tool execution → next model call → end
 *
 * Our orchestrator is a thin wrapper that:
 * 1. Resolves the model from config
 * 2. Assembles tools (pi-coding-agent builtins + our custom tools)
 * 3. Builds the agent context (system prompt, messages, tools)
 * 4. Calls agentLoop() and streams events to the terminal
 * 5. Persists session state
 *
 * This replaces ~4000 lines of OpenClaw platform code with ~200 lines.
 */
import { agentLoop } from "./agent-loop.js";
import { createCodingTools, createGrepTool, createFindTool, createLsTool, convertToLlm, } from "./coding-tools.js";
import { resolveModel } from "./provider.js";
import { buildSystemPrompt, buildChatPrompt } from "../utils/system-prompt.js";
import { createSshRunTool } from "../tools/sshRun.js";
import { createGitStatusTool } from "../tools/gitStatus.js";
import { createGitDiffTool } from "../tools/gitDiff.js";
import { createSearchFilesTool } from "../tools/searchFiles.js";
import { createWebSearchTool } from "../tools/webSearch.js";
import { createWebReadTool } from "../tools/webRead.js";
import { createWebDownloadTool } from "../tools/webDownload.js";
// Forge tools loading removed for v1 - scaffolding creates files, doesn't load them
import { createToolParsingStreamFn } from "./text-tool-parser.js";
import { log } from "../utils/logger.js";
import { createMcpTools } from "../mcp/tools.js";
export class ToolsNotSupportedError extends Error {
    constructor(model) {
        super(`Model '${model}' does not support tools`);
        this.name = "ToolsNotSupportedError";
    }
}
/**
 * Create the full tool set for Clawx.
 *
 * EXTRACTION NOTE:
 * OpenClaw's createOpenClawCodingTools() (pi-tools.ts, 619 lines) handles:
 * - 6 layers of tool policies (global, agent, group, profile, provider, subagent)
 * - Sandbox vs gateway vs node execution routing
 * - Memory flush restrictions
 * - Channel capabilities filtering
 * - Tool schema sanitization per provider
 * - Image sanitization limits
 * - Abort signal wrapping
 * - Loop detection wrapping
 *
 * We discard ALL of that. Our tools are the pi-coding-agent defaults plus
 * our custom SSH/git/search tools. No policies, no sandboxing, no restrictions.
 */
async function createTools(config) {
    const cwd = config.workDir;
    // pi-coding-agent's built-in coding tools: read, write, edit, bash (exec)
    // These are KEPT AS-IS from the OpenClaw dependency chain.
    const builtinTools = createCodingTools(cwd);
    // Add grep, find, ls from pi-coding-agent
    builtinTools.push(createGrepTool(cwd));
    builtinTools.push(createFindTool(cwd));
    builtinTools.push(createLsTool(cwd));
    // Our custom tools (WRITTEN FRESH — not in OpenClaw)
    const customTools = [
        createSearchFilesTool(cwd),
        createGitStatusTool(cwd),
        createGitDiffTool(cwd),
        createWebSearchTool(),
        createWebReadTool(),
        createWebDownloadTool(),
    ];
    // SSH tool — only if targets are configured
    if (Object.keys(config.sshTargets).length > 0) {
        customTools.push(createSshRunTool(config.sshTargets));
    }
    // MCP tools — from configured MCP servers
    try {
        const mcpTools = await createMcpTools(config);
        customTools.push(...mcpTools);
    }
    catch (error) {
        log.warn(`Failed to load MCP tools: ${error instanceof Error ? error.message : String(error)}`);
    }
    // Forge v1: Scaffolding creates starter projects only
    // No runtime tool loading in this version
    return [...builtinTools, ...customTools];
}
/**
 * Run the Clawx agent loop.
 *
 * This is the main entry point. It:
 * 1. Resolves the model
 * 2. Builds tools
 * 3. Creates the agent context
 * 4. Runs agentLoop() from pi-agent-core
 * 5. Streams events via callback
 * 6. Returns the full message history
 */
export async function runAgent(config, options) {
    const model = resolveModel(config);
    const tools = options.noTools ? [] : await createTools(config);
    const systemPrompt = options.noTools ? buildChatPrompt(config) : buildSystemPrompt(config);
    // MCP manager is initialized automatically when createMcpTools is called
    // No need for separate initialization
    log.info(`Tools: ${tools.length > 0 ? tools.map((t) => t.name).join(", ") : "(none — chat mode)"}`);
    // Build context with existing messages or empty
    const existingMessages = options.messages || [];
    const context = {
        systemPrompt,
        messages: existingMessages,
        tools,
    };
    // Build the user message
    const userMessage = {
        role: "user",
        content: options.prompt,
        timestamp: Date.now(),
    };
    // Agent loop config
    const loopConfig = {
        model: model,
        apiKey: config.apiKey,
        maxTokens: config.maxTokens,
        reasoning: config.thinkingLevel === "off" ? undefined : config.thinkingLevel,
        convertToLlm: async (messages) => {
            // Use pi-coding-agent's convertToLlm which handles the standard message types
            try {
                return convertToLlm(messages);
            }
            catch {
                // Fallback: filter to standard LLM messages
                return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult");
            }
        },
        getSteeringMessages: async () => {
            // Support mid-run message injection (e.g., user typing while agent works)
            if (options.steeringQueue && options.steeringQueue.length > 0) {
                const queued = [...options.steeringQueue];
                options.steeringQueue.length = 0;
                return queued;
            }
            return [];
        },
    };
    // Create text tool call parser if enabled
    const streamFn = options.parseTextToolCalls && tools.length > 0
        ? createToolParsingStreamFn(tools.map((t) => t.name))
        : undefined;
    // Run the loop
    const eventStream = agentLoop([userMessage], context, loopConfig, options.signal, streamFn);
    let aborted = false;
    try {
        // Process events
        for await (const event of eventStream) {
            // Detect "does not support tools" error from provider
            if (event.type === "turn_end" && event.message?.role === "assistant" && "stopReason" in event.message && event.message.stopReason === "error") {
                const errorMsg = ("errorMessage" in event.message ? event.message.errorMessage : "") || "";
                if (errorMsg.includes("does not support tools") || errorMsg.includes("does not support tool")) {
                    throw new ToolsNotSupportedError(config.model);
                }
            }
            if (options.onEvent) {
                options.onEvent(event);
            }
            if (event.type === "agent_end" ||
                (event.type === "turn_end" &&
                    event.message.role === "assistant" &&
                    "stopReason" in event.message &&
                    (event.message.stopReason === "error" || event.message.stopReason === "aborted"))) {
                if ("message" in event &&
                    event.message.role === "assistant" &&
                    "stopReason" in event.message &&
                    event.message.stopReason === "aborted") {
                    aborted = true;
                }
            }
        }
    }
    catch (loopError) {
        const msg = loopError instanceof Error ? loopError.message : String(loopError);
        if (msg.includes("does not support tools") || msg.includes("does not support tool")) {
            throw new ToolsNotSupportedError(config.model);
        }
        throw loopError;
    }
    // Get the final messages from the event stream result
    const result = await eventStream.result();
    return {
        messages: [...existingMessages, ...result],
        aborted,
    };
}
//# sourceMappingURL=agent.js.map