import { type AgentTool, type AgentTurnTiming, type TransformContextOptions } from "@kenkaiiii/gg-agent"; import type { Message, Provider, ThinkingLevel, TextContent, ImageContent, VideoContent } from "@kenkaiiii/gg-ai"; import { type IdealReviewStats, type ReviewCoverageTracker } from "../../core/ideal-review.js"; import type { LspManager } from "../../core/lsp/manager.js"; import { type LoopBreakStats } from "../../core/loop-breaker.js"; export declare function shouldRetainThinkingDelta(): boolean; export interface ActiveToolCall { toolCallId: string; name: string; args: Record; startTime: number; updates: unknown[]; } export interface AgentLoopOptions { provider: Provider; model: string; tools: AgentTool[]; webSearch?: boolean; maxTokens: number; /** Whether the active model supports native image input. */ supportsImages?: boolean; /** Whether the active model supports native video input. */ supportsVideo?: boolean; thinking?: ThinkingLevel; apiKey?: string; baseUrl?: string; accountId?: string; projectId?: string; /** Resolve fresh credentials before each run (e.g. OAuth token refresh). * When `forceRefresh` is true, bypass cache and fetch a new token (used on 401 retry). */ resolveCredentials?: (opts?: { forceRefresh?: boolean; /** Access token the provider just rejected, so the refresh can adopt a * newer token another process already wrote instead of minting one. */ rejectedToken?: string; }) => Promise<{ apiKey: string; accountId?: string; projectId?: string; }>; transformContext?: (messages: Message[], options: TransformContextOptions) => Message[] | Promise; getIdealReviewMessage?: (stats: IdealReviewStats, touchedFiles: string[]) => Message | null; /** Harness-owned successful read/mutation evidence for fail-closed Ideal review. */ reviewCoverageTracker?: ReviewCoverageTracker; /** Detailed diagnostics evidence shown only to the internal review turn. */ lspManager?: LspManager; /** Polled mid-loop when the agent appears stuck (repeated failures / calls / * edits, or degenerate output). Return a user message to break the loop. */ getLoopBreakMessage?: (stats: LoopBreakStats, stage: 1 | 2) => Message | null; /** Polled mid-loop after a compaction reduced the context. Return a user * message that re-pins the original request. */ getRegroundingMessage?: (originalRequest: string) => Message | null; } export type ActivityPhase = "waiting" | "thinking" | "generating" | "tools" | "retrying" | "idle"; export interface RetryInfo { reason: "overloaded" | "rate_limit" | "provider_error" | "empty_response" | "stream_stall" | "overflow_compact" | "tool_argument_glitch" | "runaway_toolcall"; attempt: number; maxAttempts: number; delayMs: number; } export type UserContent = string | (TextContent | ImageContent | VideoContent)[]; export interface StreamSnapshot { text: string; thinking: string; thinkingMs: number; } export interface UseAgentLoopReturn { run: (userContent: UserContent) => Promise; abort: () => void; reset: () => void; /** Queue a message to be processed after the current run completes. * `text` is the original typed text, retained so it can be restored to the * composer if the run is interrupted before the queue drains. */ queueMessage: (content: UserContent, text?: string) => void; /** Number of messages currently waiting in the queue. */ queuedCount: number; /** Clear all queued messages. */ clearQueue: () => void; /** Pop every queued message, clear the queue, and return the combined * original text (joined with blank lines). Empty string when nothing was * queued. Used to restore unsent input to the composer on interrupt. */ drainQueuedText: () => string; isRunning: boolean; streamingText: string; streamingThinking: string; activeToolCalls: ActiveToolCall[]; currentTurn: number; totalTokens: { input: number; output: number; }; /** Latest turn's input tokens — reflects current context window usage */ contextUsed: number; activityPhase: ActivityPhase; retryInfo: RetryInfo | null; /** Non-null when the agent stopped due to an unrecoverable stream error (e.g. stall retries exhausted). */ stallError: string | null; elapsedMs: number; thinkingMs: number; isThinking: boolean; streamedTokenEstimate: number; /** Raw character count ref — read directly by ActivityIndicator for smooth animation */ charCountRef: React.RefObject; /** Accumulated real tokens from completed turns */ realTokensAccumRef: React.RefObject; /** Run start timestamp ref — for smooth elapsed time computation */ runStartRef: React.RefObject; linesChanged: { added: number; removed: number; }; } export declare function useAgentLoop(messages: React.MutableRefObject, options: AgentLoopOptions, callbacks?: { onComplete?: (newMessages: Message[]) => void; onTurnText?: (text: string, thinking: string, thinkingMs: number) => void; onToolStart?: (toolCallId: string, name: string, args: Record, stream: StreamSnapshot) => void; onToolUpdate?: (toolCallId: string, update: unknown) => void; onToolEnd?: (toolCallId: string, name: string, result: string, isError: boolean, durationMs: number, details?: unknown, args?: Record) => void; onServerToolCall?: (id: string, name: string, input: unknown, stream: StreamSnapshot) => void; onServerToolResult?: (toolUseId: string, resultType: string, data: unknown) => void; onTurnEnd?: (turn: number, stopReason: string, usage: { inputTokens: number; outputTokens: number; cacheRead?: number; cacheWrite?: number; }, timing: AgentTurnTiming) => void; onDone?: (durationMs: number, toolsUsed: string[], runStats?: { counts: Record; tokens: number; }) => void; onAborted?: () => void; /** Called when a queued message starts processing (after the previous run completes). */ onQueuedStart?: (content: UserContent) => void; /** Called when the agent restarts a turn after a stall/overload retry. * The UI should roll back any pending progressive flushes from the * aborted attempt so the retry's regenerated text doesn't duplicate. */ onRetry?: () => void; /** Called when a turn ended on a non-clean stop (max_tokens/refusal/error) * so the UI can warn instead of presenting truncated output as done. */ onTruncated?: (reason: "max_tokens" | "refusal" | "provider_error" | "empty_response", continued: boolean) => void; /** Polled when the agent would otherwise stop. Return a user message to * inject and continue the loop (e.g. "continue with the next plan step"). */ getFollowUpMessages?: () => Message[] | null; }): UseAgentLoopReturn; //# sourceMappingURL=useAgentLoop.d.ts.map