import { type Message, type Provider, type ThinkingLevel } from "@kenkaiiii/gg-ai";
import { EventBus } from "./event-bus.js";
import { SlashCommandRegistry } from "./slash-commands.js";
import { type BranchInfo } from "./session-manager.js";
import type { BackgroundProcess } from "./process-manager.js";
import { type EnhanceResult } from "../utils/prompt-enhancer.js";
/** A chat attachment (image / video / other file) prepared for the model. The
 *  raw base64 `data` rides native blocks; `path` (when persisted to disk) lets
 *  the agent's tools open the file directly. */
export interface SessionAttachment {
    kind: "image" | "video" | "file";
    mediaType: string;
    data: string;
    name: string;
    path?: string;
}
export interface AgentSessionOptions {
    provider: Provider;
    model: string;
    cwd: string;
    baseUrl?: string;
    systemPrompt?: string;
    sessionId?: string;
    continueRecent?: boolean;
    maxTokens?: number;
    thinkingLevel?: ThinkingLevel;
    signal?: AbortSignal;
    /** Prefix used for provider prompt-cache routing keys. */
    promptCacheKeyPrefix?: string;
    /**
     * Explicit prompt-cache routing key. When set, overrides the
     * `${promptCacheKeyPrefix}:${sessionId}` default so spawned sub-agents can
     * inherit a stable parent-scoped key — without this, each sub-agent process
     * generates a fresh sessionId and starts with a cold cache.
     */
    promptCacheKey?: string;
    /**
     * If true, this session does NOT create a `.jsonl` session file or persist
     * any messages. Used by subagent spawns (`--json` mode) so their transcripts
     * don't leak into `ggcoder continue` for the parent project. Subagent runs
     * are one-shot, NDJSON-streamed to the parent over stdout, and have no
     * resumable identity.
     */
    transient?: boolean;
    /**
     * If true, `initialize()` returns WITHOUT waiting for MCP servers to connect —
     * the connection runs in the background and tools are appended when ready.
     * Hosts whose readiness is gated on `initialize()` (the gg-app sidecar, which
     * can't emit its listening handshake until init resolves) set this so a slow
     * or hanging stdio MCP server (e.g. a first-run `npx -y …` download) can't
     * delay the session from becoming usable. Default (false) keeps the CLI's
     * connect-before-ready behavior so MCP tools are present on the first turn.
     */
    backgroundMcpConnect?: boolean;
    /**
     * Plan-mode callbacks. When provided, the `enter_plan`/`exit_plan` tools are
     * registered and the session manages plan-mode restrictions + system-prompt
     * rebuilds. Hosts (e.g. the gg-app sidecar) use these to surface plan-mode
     * UI. Omitted by callers that don't want plan mode (CLI wires its own).
     */
    onEnterPlan?: (reason?: string) => void | Promise<void>;
    onExitPlan?: (planPath: string) => Promise<string>;
}
export interface AgentSessionState {
    provider: Provider;
    model: string;
    cwd: string;
    sessionId: string;
    sessionPath: string;
    messageCount: number;
    planMode: boolean;
}
export declare class AgentSession {
    readonly eventBus: EventBus;
    readonly slashCommands: SlashCommandRegistry;
    private settingsManager;
    private authStorage;
    private sessionManager;
    private extensionLoader;
    private messages;
    private tools;
    /** Rebuilds the read tool for a new model (video byte cap is baked in at
     *  creation). Called from switchModel so video-capable models get the
     *  read-tool's native-video path after a mid-session model change. */
    private rebuildReadTool;
    private skills;
    private cacheKeyLogged;
    private hookStats;
    private hookText;
    private hookConsecutiveFailures;
    private hookMaxSignatureRepeats;
    private hookMaxSameFileEdits;
    private hookSignatureCounts;
    private hookFileEditCounts;
    private hookToolCalls;
    private idealReviewInjected;
    private loopBreakInjected;
    private regroundingInjected;
    private compactionOccurred;
    private originalRequest;
    /** True after the cache has been pre-warmed for this session. Ensures we only
     *  fire the warm-up call once (before the first real turn). */
    private cachePrewarmed;
    private userQueue;
    private processManager?;
    private lspManager?;
    private mcpManager?;
    private provider;
    private model;
    private cwd;
    private baseUrl?;
    private maxTokens;
    private thinkingLevel?;
    private customSystemPrompt?;
    /** Shared with the tool layer so plan-mode restrictions read live state. */
    private planModeRef;
    /** Path of the approved plan currently being implemented, or undefined. When
     *  set, the system prompt carries the `[DONE:n]` progress contract so the
     *  model emits step-completion markers the UI's plan-progress widget reads. */
    private approvedPlanPath?;
    private sessionId;
    private sessionPath;
    private lastPersistedIndex;
    /** Current leaf entry ID in the session DAG — used to chain parentIds for branching. */
    private currentLeafId;
    private opts;
    constructor(options: AgentSessionOptions);
    /**
     * Derive the output-token cap for a model. Follows the active model's
     * `maxOutputTokens` so a session booted on a large-output model (e.g. Kimi's
     * 256K) doesn't carry that cap to a smaller one (e.g. Opus's 128K) after a
     * model switch — that mismatch surfaces from the provider as
     * `max_tokens: 262144 > 128000, which is the maximum allowed …`. An explicit
     * `maxTokens` override is honored but clamped to the model's ceiling.
     */
    private resolveMaxTokens;
    initialize(): Promise<void>;
    /**
     * Connect all configured MCP servers and append their tools to `this.tools`.
     * Resolves the GLM api key first (Z.AI's bundled servers need it). Never
     * throws — a failed connect is logged and skipped — so it is safe to either
     * `await` (CLI: tools ready before the first turn) or fire-and-forget
     * (sidecar: `backgroundMcpConnect`, so a slow stdio server can't stall
     * startup). Tools are pushed onto the live array the agent loop reads each
     * turn, so background-connected servers become available on the next prompt.
     */
    private connectMcpServers;
    /**
     * Process user input. Handles slash commands or runs agent loop.
     */
    prompt(content: string): Promise<void>;
    /**
     * Prompt with multimodal attachments (images / videos) alongside optional
     * text. Images and videos become native content blocks the model can see;
     * non-media files are surfaced as a text note with their saved path so the
     * agent can open them with its tools. Slash-command parsing is skipped —
     * attachments are always a direct conversational turn.
     */
    promptWithAttachments(text: string, attachments: SessionAttachment[]): Promise<void>;
    /**
     * Build the native content blocks (text + image/video notes + file notes) for
     * a user message with attachments. Shared by {@link promptWithAttachments} and
     * the mid-run steering drain so queued media is delivered identically.
     */
    private buildAttachmentParts;
    /**
     * Reset per-run self-correction hook state. Mirrors the TUI's run_start
     * resets so each run evaluates the hooks from a clean slate. `originalRequest`
     * is the verbatim user ask, pinned for post-compaction re-grounding.
     */
    private resetHookState;
    /**
     * Fold one agent event into the hook stat accumulators. Pure bookkeeping —
     * the same signals the TUI's useAgentLoop collects, so the loop-break and
     * ideal-review decisions match across the CLI and the app.
     */
    private trackHookEvent;
    /**
     * Mid-loop steering hook: fires the loop-breaker when the agent looks stuck,
     * then post-compaction re-grounding. At most one of each per run. Mirrors the
     * TUI's getSteeringMessages ordering (minus user steering, which the app
     * delivers as normal prompts).
     */
    private getHookSteeringMessages;
    /**
     * Pre-stop follow-up hook: runs the ideal review once, when the agent would
     * otherwise finish and the change set is substantial enough to warrant it.
     */
    private getHookFollowUpMessages;
    /** Auto-compact if needed, run agent loop with auth retry, and persist messages. */
    private runLoop;
    switchModel(provider: string, model: string): Promise<void>;
    compact(existingCredentials?: {
        accessToken: string;
        accountId?: string;
        projectId?: string;
        baseUrl?: string;
    }): Promise<void>;
    newSession(): Promise<void>;
    loadSession(sessionPath: string): Promise<void>;
    /**
     * Create a branch at a specific point in the conversation.
     * Rewinds the message history to the given entry and sets the leaf
     * so new messages fork from that point.
     *
     * @param stepsBack Number of messages to rewind (default: 2 — backs up past last assistant + tool)
     */
    branch(stepsBack?: number): Promise<{
        branchedFrom: number;
        messagesKept: number;
    }>;
    /**
     * List all branches in the current session.
     */
    listBranches(): Promise<BranchInfo[]>;
    getState(): AgentSessionState;
    getPlanMode(): boolean;
    /** Queue a user message (optionally with attachments) to be injected mid-run
     *  as steering. Returns the new queue length. No-op semantics are the caller's
     *  concern. */
    queueMessage(text: string, attachments?: SessionAttachment[]): number;
    /** Number of messages currently queued. */
    getQueuedCount(): number;
    /** Clear the queue, returning the combined text (to restore to the composer).
     *  Queued attachments are dropped on cancel — the composer only restores text. */
    drainQueue(): string;
    /** Snapshot of background processes (bash run_in_background), newest-state. */
    listBackgroundProcesses(): BackgroundProcess[];
    /** Stop a background process by id. Returns a human-readable status string. */
    killBackgroundProcess(id: string): Promise<string>;
    /**
     * Toggle plan mode: flips the shared ref (so tools enforce read-only
     * restrictions) and rebuilds the system prompt in place so the model is told
     * about the mode change on its next turn. No-op when a custom system prompt
     * is in force (the host owns the prompt then).
     */
    setPlanMode(active: boolean): Promise<void>;
    /**
     * Bake an approved plan into the system prompt so the model is told to emit
     * `[DONE:n]` markers as it completes each step (the contract the UI's
     * plan-progress widget reads). Pass `undefined` to clear it. No-op when a
     * custom system prompt is in force (the host owns the prompt then).
     */
    setApprovedPlan(approvedPlanPath: string | undefined): Promise<void>;
    /** Rebuild messages[0] from current plan-mode + approved-plan state. */
    private rebuildSystemPromptInPlace;
    getMessages(): Message[];
    /**
     * Generate a short LLM session title from the conversation so far (first user
     * message + first assistant reply). Best-effort; returns null on failure or
     * when there's no user message yet. Uses the cheapest model for the provider.
     */
    generateTitle(): Promise<string | null>;
    /**
     * Rewrite a draft prompt into a tighter, terminology-correct version using
     * the ACTIVE provider/model. A stateless one-off LLM call (no agent loop, no
     * tools, no session mutation) — safe to run even mid-run. Returns the plain
     * enhanced text plus typed segments marking each corrected term. Errors throw
     * so the caller can surface them (unlike best-effort title generation).
     */
    enhancePrompt(text: string): Promise<EnhanceResult>;
    /** Current reasoning/thinking level, or undefined when thinking is off. */
    getThinkingLevel(): ThinkingLevel | undefined;
    /** Set the reasoning/thinking level (undefined turns thinking off). Takes
     * effect on the next prompt, since the in-flight loop reads it at start. */
    setThinkingLevel(level: ThinkingLevel | undefined): void;
    /** Replace the abort signal (e.g. after cancellation). */
    setSignal(signal: AbortSignal): void;
    /** True when speedProfile is "optimized" (1-h cache TTL + pre-warm). */
    private isSpeedOptimized;
    /** Fire a cache pre-warm request for Anthropic so the first real turn is a
     *  cache read instead of a cold write. No-op for other providers and when
     *  speedProfile is not "optimized". Entirely best-effort — any failure is
     *  swallowed so prewarm never blocks or aborts the real prompt. */
    private maybePrewarmCache;
    private getPromptCacheKey;
    /** Stable cache-routing key for downstream sub-agent processes. */
    getCurrentCacheKey(): string | undefined;
    dispose(): Promise<void>;
    private createNewSession;
    private loadExistingSession;
    private prepareDynamicContext;
    private persistMessage;
    private createSlashCommandContext;
}
//# sourceMappingURL=agent-session.d.ts.map