/**
 * Crewly Agent Runner Service
 *
 * Core reasoning loop for the Crewly Agent runtime. Wraps Vercel AI SDK's
 * generateText with conversation history management, context compaction,
 * and structured result tracking.
 *
 * @module services/agent/crewly-agent/agent-runner.service
 */
import { ModelManager } from './model-manager.js';
import { CrewlyApiClient } from './api-client.js';
import { ApprovalQueueService } from './approval-queue.service.js';
import { type CrewlyAgentConfig, type ConversationState, type AgentRunResult, type ToolCallRecord, type CompactionResult, type ContextBudgetStatus, type AuditEntry, type SecurityPolicy, type StreamingEventCallbacks } from './types.js';
/**
 * Detects looping behavior in tool calls: consecutive identical calls or
 * consecutive error responses from the same tool.
 *
 * Usage: create per-run, call `recordToolCall()` in onStepFinish, check `loopDetected`.
 */
export declare class ToolCallLoopDetector {
    private readonly identicalThreshold;
    private readonly errorThreshold;
    /** Consecutive identical tool call fingerprints */
    private consecutiveIdentical;
    private lastFingerprint;
    /** Consecutive error results from the same tool */
    private consecutiveErrors;
    private lastErrorTool;
    /** Whether a loop was detected */
    loopDetected: boolean;
    /** Human-readable reason when loop is detected */
    loopReason: string;
    constructor(identicalThreshold?: number, errorThreshold?: number);
    /**
     * Record a tool call and check for loop patterns.
     *
     * @param toolName - Name of the tool called
     * @param args - Arguments passed to the tool
     * @param result - Result returned by the tool
     * @returns True if a loop was just detected on this call
     */
    recordToolCall(toolName: string, args: Record<string, unknown>, result: unknown): boolean;
    /**
     * Check if a tool result looks like an error (404, HTTP error codes, error strings).
     */
    private isErrorResult;
}
/**
 * Core agent runner that manages the AI SDK generateText loop.
 *
 * Responsibilities:
 * - Maintains conversation history (messages array)
 * - Calls generateText with tools and maxSteps for agentic behavior
 * - Tracks token usage across invocations
 * - Triggers context compaction when history grows too large
 * - Serializes concurrent message handling
 *
 * @example
 * ```typescript
 * const runner = new AgentRunnerService(config);
 * await runner.initialize();
 * const result = await runner.run('Check all team statuses');
 * ```
 */
/** Function type for generateText — used for dependency injection in tests */
type GenerateTextFn = (opts: Record<string, unknown>) => Promise<Record<string, unknown>>;
export declare class AgentRunnerService {
    private config;
    private modelManager;
    private apiClient;
    private model;
    /**
     * Per-conversation state map. Each Slack thread (or web chat
     * conversation) gets its own `ConversationState` so the LLM
     * context is isolated — messages from thread A never leak into
     * the prompt when responding to thread B. The conversation key
     * is the chat-v2 channel id (e.g. `slack-D0AC7-1777760999-956969`)
     * derived from the inbound message's `[CHAT:xxx]` marker, the
     * `[SLACK:channel:threadTs]` marker, or — if neither is present
     * — the literal `__default__` for runtime cases like REPL or
     * scheduled-check inputs that have no thread identity.
     *
     * 2026-05-15 fix per goal: "一个 Slack thread 代表一个 chat
     * thread, 不同 Slack thread 之间不会串联在一起."
     */
    private conversationStates;
    /**
     * Active conversation key for the message currently being
     * processed. `processQueue` sets this before each `executeRun`
     * so the getter `this.state` resolves to the right per-thread
     * state without every call site needing to know about the map.
     */
    private currentConversationKey;
    /**
     * Effective system prompt — captured at construction time and
     * applied to every fresh per-conversation state created on
     * demand. Held on the instance so `getOrCreateState` doesn't
     * need to recompute the eval-mode stripping logic.
     */
    private readonly effectiveSystemPrompt;
    /**
     * Soft cap on how many distinct conversation states we hold in
     * memory. When exceeded, the least-recently-active state is
     * evicted (its messages live on in chat-v2 SQLite so the next
     * access can re-hydrate). Prevents unbounded growth when a busy
     * agent participates in thousands of Slack threads over time.
     */
    private readonly MAX_LIVE_CONVERSATIONS;
    /**
     * Backward-compatible getter: every existing `this.state.X`
     * call site automatically routes to the active per-conversation
     * state. Lazy-creates a fresh state on first access for a new
     * conversation key.
     */
    private get state();
    /**
     * Look up or create the ConversationState for a given key.
     * Evicts the least-recently-active state when the live-set
     * size exceeds {@link MAX_LIVE_CONVERSATIONS}.
     *
     * @param key - Conversation key (chat-v2 channel id or `__default__`)
     * @returns The per-conversation state object
     */
    private getOrCreateConversationState;
    /**
     * Test / introspection helper — number of active conversation
     * states the runner currently holds. Surfaces in
     * `getConversationStatus` for observability.
     *
     * @returns Number of live per-conversation states
     */
    getConversationCount(): number;
    private processing;
    private messageQueue;
    private auditLog;
    private securityPolicy;
    /** Current conversationId extracted from [CHAT:xxx] prefix */
    private currentConversationId?;
    /** Last known conversationId — used as fallback when a message has no explicit conversationId */
    private lastKnownConversationId?;
    /** Current Slack context (channelId + threadTs) for routing NOTIFY responses */
    private currentSlackContext?;
    /** MCP client for external tool integration */
    private mcpClient;
    /** Cached MCP tool definitions loaded during initialization */
    private mcpToolDefs;
    /** Approval queue for tools requiring explicit approval (shared singleton) */
    private approvalQueue;
    private tracing;
    /** Guards against concurrent compaction — only one compaction at a time */
    private compacting;
    /** AbortController for the current run — allows external cancellation */
    private currentRunAbort;
    /** Streaming event callbacks — set per run by the runtime service */
    private streamingCallbacks;
    /** Output filter for redacting API keys from agent responses */
    private outputFilter;
    /** @internal Override for testing — replaces the AI SDK generateText call */
    _generateTextFn: GenerateTextFn | null;
    /**
     * Create a new AgentRunnerService.
     *
     * @param config - Agent configuration
     * @param modelManager - Optional model manager instance (for testing)
     * @param apiClient - Optional API client instance (for testing)
     */
    constructor(config: CrewlyAgentConfig, modelManager?: ModelManager, apiClient?: CrewlyApiClient);
    /**
     * Regex patterns that match TL delegation-first instructions in the system prompt.
     * These cause the agent to delegate instead of implementing in eval sandboxes.
     */
    private static readonly DELEGATION_PATTERNS;
    /**
     * Eval-mode override instruction injected after stripping delegation instructions.
     * Tells the agent to implement directly.
     */
    private static readonly EVAL_MODE_OVERRIDE;
    /**
     * Strip delegation-first instructions from a system prompt for eval mode.
     *
     * Removes TL delegation SOP sections, delegation-first protocol blocks,
     * and anti-pattern warnings that cause the agent to delegate instead of
     * implementing. Injects an eval-mode override instruction.
     *
     * @param prompt - Original system prompt
     * @returns Cleaned prompt with eval-mode overrides
     */
    static stripDelegationInstructions(prompt: string): string;
    /**
     * Patterns that indicate the task expects a file to be created.
     * Matches phrases like "create health.controller.ts", "write team-health.json",
     * "produce a report file", etc.
     */
    private static readonly FILE_CREATION_PATTERNS;
    /**
     * Extract expected output file names from the task prompt.
     *
     * Scans the message for file creation patterns and returns the
     * list of file names the task expects to be produced.
     *
     * @param taskPrompt - The original task prompt/message
     * @returns Array of expected file names (basename only)
     */
    static extractExpectedOutputFiles(taskPrompt: string): string[];
    /**
     * Check if the agent's tool calls produced the expected output files.
     *
     * Examines write_file and edit_file tool calls to see if the expected
     * files were written. Returns the list of missing files.
     *
     * @param expectedFiles - File names expected to be created
     * @param toolCalls     - Tool calls made during the run
     * @returns Array of file names that were NOT written
     */
    static checkMissingDeliverables(expectedFiles: string[], toolCalls: ToolCallRecord[]): string[];
    /**
     * Initialize the agent runner by loading the model.
     * Must be called before run().
     *
     * @throws Error if the model cannot be loaded
     */
    initialize(): Promise<void>;
    /**
     * Run the agent with a new user message.
     *
     * Messages are queued and processed serially to prevent concurrent
     * generateText calls which would corrupt conversation state.
     *
     * @param message - User/system message to process
     * @param conversationId - Optional conversation ID for routing
     * @param metadata - Optional metadata (Slack context, etc.)
     * @param options - Optional abort signal and streaming callbacks
     * @returns Result of the agent run including text, tool calls, and usage
     */
    run(message: string, conversationId?: string, metadata?: Record<string, string>, options?: {
        abortSignal?: AbortSignal;
        streaming?: StreamingEventCallbacks;
    }): Promise<AgentRunResult>;
    /**
     * Abort the current in-progress run.
     * Signals the active streamText/generateText call to cancel.
     *
     * @returns True if an active run was aborted, false if no run was in progress
     */
    abortCurrentRun(): boolean;
    /**
     * Check if the agent is currently processing a message.
     *
     * @returns True if processing is in progress
     */
    isProcessing(): boolean;
    /**
     * Get current conversation state (for inspection/debugging).
     *
     * @returns Current conversation state
     */
    getState(): ConversationState;
    /**
     * Shut down the agent runner, disconnecting MCP servers.
     *
     * Should be called when the agent session ends to clean up
     * child processes spawned by MCP server connections.
     */
    shutdown(): Promise<void>;
    /**
     * Get the names of connected MCP servers.
     *
     * @returns Array of server names, or empty if no MCP client is configured
     */
    getMcpServerNames(): string[];
    /**
     * Get the number of MCP tools currently loaded.
     *
     * @returns Number of MCP tool definitions
     */
    getMcpToolCount(): number;
    /**
     * Get the current Slack context (channelId + threadTs).
     * Used by the runtime service to inject Slack awareness into the agent.
     *
     * @returns Current Slack context or undefined
     */
    getSlackContext(): {
        channelId: string;
        threadTs?: string;
    } | undefined;
    /**
     * Get the number of messages in the conversation history.
     *
     * @returns Message count
     */
    getHistoryLength(): number;
    /**
     * Check if the agent runner has been initialized.
     *
     * @returns True if initialize() has been called successfully
     */
    isInitialized(): boolean;
    /**
     * Get current context budget status.
     *
     * Calculates token usage as a percentage of the model's context window
     * and determines the budget level (normal/warning/critical).
     *
     * @returns ContextBudgetStatus with usage stats and level
     */
    getContextBudget(): ContextBudgetStatus;
    /**
     * Process queued messages serially.
     */
    private processQueue;
    /**
     * Execute a single streamText run with the current conversation context.
     *
     * Uses streamText for real-time token emission and tool call feedback.
     * Falls back to generateText when _generateTextFn is set (testing).
     *
     * @param message - New message to add to the conversation
     * @param externalAbortSignal - Optional external abort signal for cancellation
     * @returns Agent run result
     */
    private executeRun;
    /**
     * Check if an error is recoverable and eligible for automatic retry.
     *
     * Recoverable errors include:
     * - HTTP 429 (rate limit)
     * - HTTP 5xx (server errors)
     * - Network timeouts and connection errors
     *
     * @param error - The error to classify
     * @returns True if the error is recoverable
     */
    private isRecoverableError;
    /**
     * Check if an error indicates the context length was exceeded.
     *
     * @param error - The error to classify
     * @returns True if the error is a context length exceeded error
     */
    private isContextLengthError;
    /**
     * Sleep for a specified duration.
     *
     * @param ms - Milliseconds to sleep
     * @returns Promise that resolves after the delay
     */
    private sleep;
    /**
     * Execute run using streamText for real-time streaming output.
     * This is the production path — emits events as tokens arrive.
     *
     * Includes automatic retry with exponential backoff for recoverable errors
     * (429, 5xx, network) and progressive context trimming for context length errors.
     */
    private executeRunWithStreamText;
    /**
     * Single attempt of streamText execution (no retry logic).
     */
    private executeStreamTextAttempt;
    /**
     * Execute run using generateText (batch mode).
     * Used when _generateTextFn is set for testing, or as fallback.
     *
     * Includes automatic retry with exponential backoff for recoverable errors
     * and progressive context trimming for context length errors.
     */
    private executeRunWithGenerateText;
    /**
     * Single attempt of generateText execution (no retry logic).
     */
    private executeGenerateTextAttempt;
    /**
     * Remove the oldest non-system messages to reduce context size.
     * Preserves the most recent messages and any system-role messages.
     */
    private trimOldestNonSystemMessages;
    /**
     * Handle a detected tool call loop by injecting a corrective system message
     * into conversation history and returning a structured result.
     *
     * @param detector - The loop detector with reason details
     * @param toolCalls - Tool calls collected so far
     * @param steps - Number of steps taken
     * @returns AgentRunResult with the loop warning as text
     */
    private handleLoopDetected;
    /**
     * Execute the Stop Hook: check if the agent produced all required deliverables.
     *
     * Scans the original task prompt (first user message) for expected output files,
     * then checks if write_file/edit_file tool calls created them. If files are
     * missing, injects a corrective prompt and runs one more generateText call
     * with tools so the agent can create the missing deliverables.
     *
     * Inspired by Claude Code's Stop hook which blocks the agent from finishing
     * until task requirements are met.
     *
     * @param toolCalls   - Tool calls made so far
     * @param tools       - Available tools for the follow-up run
     * @param abortSignal - Abort signal for cancellation
     * @returns Additional AgentRunResult from the follow-up, or null if no action needed
     */
    private executeStopHook;
    /**
     * Request a text summary from the model when the previous response had tool calls
     * but no text output. Injects a follow-up user message and makes a single
     * generateText call with no tools to force a text-only response.
     *
     * @returns The summary text, or empty string if the fallback also fails
     */
    private requestSummaryFallback;
    /**
     * Public method for agent-initiated context compaction.
     * Called by the compact_memory tool to intelligently summarize conversation state.
     *
     * Uses the model to generate a structured summary preserving:
     * - Active tasks and their status
     * - Key decisions made
     * - Important findings and blockers
     * - Current working context
     *
     * @returns CompactionResult with before/after stats
     */
    requestCompaction(): Promise<CompactionResult>;
    /**
     * Get the security audit log.
     *
     * @param limit - Maximum number of entries to return (most recent first)
     * @returns Array of audit entries
     */
    getAuditLog(limit?: number): AuditEntry[];
    /**
     * Get the current security policy.
     *
     * @returns Current security policy configuration
     */
    getSecurityPolicy(): SecurityPolicy;
    /**
     * Update the security policy.
     *
     * @param updates - Partial security policy to merge
     */
    updateSecurityPolicy(updates: Partial<SecurityPolicy>): void;
    /**
     * Get the approval queue service instance.
     * Used by the approvals controller to manage pending approvals.
     *
     * @returns The ApprovalQueueService instance
     */
    getApprovalQueue(): ApprovalQueueService;
    /**
     * Record an audit entry for a tool invocation.
     *
     * @param entry - Audit entry to record
     */
    private recordAudit;
    /**
     * Check if a tool is allowed to execute under the current security policy.
     *
     * Evaluates the tool against two checks:
     * 1. blockedTools — tools explicitly blocked by name (returns blocked=true)
     * 2. requireApproval — tools whose sensitivity requires approval (returns requiresApproval=true)
     *
     * @param toolName - Name of the tool being invoked
     * @param sensitivity - Sensitivity classification of the tool
     * @returns ApprovalCheckResult indicating if execution is allowed
     */
    private checkApproval;
    /**
     * Get filtered audit log entries.
     *
     * @param filters - Query filters for limit, sensitivity, and toolName
     * @returns Filtered audit entries (most recent first)
     */
    private getFilteredAuditLog;
    /**
     * Compact conversation history using AI-generated structured summary.
     *
     * Keeps the most recent messages and uses the model to generate an
     * intelligent summary of older messages that preserves critical state:
     * decisions, active tasks, findings, and working context.
     *
     * Falls back to truncation-based summary if AI summarization fails.
     *
     * @returns CompactionResult with before/after statistics
     */
    private compactHistory;
    /**
     * Generate an AI-powered structured summary of conversation messages.
     *
     * Asks the model to extract and preserve critical state from the
     * conversation history in a structured format. Pre-extracted critical
     * items from ContextFlushService are included in the prompt to ensure
     * they are preserved even if the AI would otherwise miss them.
     *
     * @param messages - Messages to summarize
     * @param extractedItems - Critical items extracted by ContextFlushService
     * @returns Structured summary string
     */
    private generateAISummary;
    /**
     * Generate a truncation-based fallback summary when AI summarization fails.
     * Includes pre-extracted critical items so they survive compaction.
     *
     * @param messages - Messages to summarize
     * @param extractedItems - Critical items extracted by ContextFlushService
     * @returns Simple concatenated summary string
     */
    private generateFallbackSummary;
}
export {};
//# sourceMappingURL=agent-runner.service.d.ts.map