import { Readable, Writable } from 'node:stream';
import { createServer } from 'node:http';

type TraceEventType = "session_start" | "user_message" | "llm_request" | "llm_response" | "tool_call" | "permission_decision" | "tool_result" | "code_mode_call" | "compaction" | "guardrail" | "completion_check" | "subagent" | "turn_end" | "outcome" | "session_end";
/** Which surface created the session. */
type TraceSessionMode = "tui" | "print" | "sdk" | "server" | "eval" | "worker";
interface SessionStartPayload {
    autopilot_version: string;
    /** git HEAD of the running Autopilot checkout, or null when not a checkout. */
    harness_commit: string | null;
    cwd: string;
    repo_remote?: string | null;
    repo_head?: string | null;
    model: string;
    mode: TraceSessionMode;
    /** KimiConfig with credential-bearing keys replaced by "<redacted>". */
    config: Record<string, unknown>;
    tool_profile: string;
    /** sha256 of the JSON array of ToolDef sent to the model. */
    tool_schemas_sha: string;
    /** Blob sha of that same JSON array. */
    tools_blob: string;
    system_prompt_sha: string;
}
type UserMessageSource = "typed" | "deny_feedback" | "continuation" | "eval" | "sdk" | "print" | "server";
interface UserMessagePayload {
    content_blob: string;
    source: UserMessageSource;
    /** Number of attachments (images, files) on the message. */
    attachments: number;
}
interface LlmRequestPayload {
    request_id: string;
    attempt: number;
    model: string;
    /** Request body minus `messages` and `tools`. */
    params: Record<string, unknown>;
    /** Blob sha of the exact serialized body sent over the wire. */
    request_blob: string;
    messages_sha: string;
    tools_sha: string;
    estimated_tokens: number;
}
interface TraceToolCall {
    id: string;
    name: string;
    arguments: string;
}
interface TraceUsage {
    prompt_tokens: number;
    completion_tokens: number;
    cached_tokens?: number;
    cost?: number;
}
interface LlmResponsePayload {
    request_id: string;
    text_blob: string;
    reasoning_blob?: string;
    reasoning_details?: unknown[];
    tool_calls: TraceToolCall[];
    finish_reason: string | null;
    usage?: TraceUsage;
    generation_id?: string;
    latency_ms: number;
    aborted: boolean;
    error?: string;
}
interface ToolCallPayload {
    tool_call_id: string;
    name: string;
    args_blob: string;
    /** The parsed arguments, inline when their JSON is ≤ 2 KB. */
    args_inline?: Record<string, unknown>;
    needs_permission: boolean;
    code_mode_redirected: boolean;
}
type PermissionDecision$1 = "allow" | "allow_session" | "deny" | "auto";
interface PermissionDecisionPayload {
    tool_call_id: string;
    decision: PermissionDecision$1;
    feedback_text?: string;
    latency_ms: number;
}
interface ToolResultPayload {
    tool_call_id: string;
    name: string;
    ok: boolean;
    error_code?: string;
    raw_blob: string;
    raw_bytes: number;
    reduced_blob: string;
    reduced_bytes: number;
    artifact_id?: string;
    exit_code?: number;
    timed_out?: boolean;
    duration_ms: number;
}
interface CodeModeCallPayload {
    parent_tool_call_id: string;
    name: string;
    args_blob: string;
    raw_blob: string;
    ok: boolean;
    duration_ms: number;
}
type CompactionMethod = "compiled" | "llm" | "preflight";
interface CompactionPayload {
    method: CompactionMethod;
    tokens_before: number;
    tokens_after: number;
    turns_removed: number;
    artifacts_archived?: number;
    summary_blob?: string;
}
type GuardrailKind = "loop_recovery" | "loop_stopped" | "limit_ceiling" | "code_mode_redirect_cap"
/** `maxToolIterations` reached and the counter reset automatically. */
 | "limit_reset"
/** `maxToolIterations` reached with `toolLimitBehavior: "stop"`. */
 | "limit_stopped";
interface GuardrailPayload {
    kind: GuardrailKind;
    message: string;
}
interface CompletionCheckPayload {
    probability: number;
    nudged: boolean;
}
interface SubagentPayload {
    worker_id: string;
    mode: string;
    child_session_id?: string;
    status: string;
    cost_usd?: number;
    duration_ms?: number;
}
type TurnEndReason = "complete" | "aborted" | "error" | "guardrail" | "needs_input";
interface TurnEndPayload {
    reason: TurnEndReason;
    /** Tool names in call order. */
    tools_used: string[];
    cost_usd?: number;
    duration_ms: number;
    final_text_blob?: string;
}
type VerificationCategory = "test" | "lint" | "typecheck" | "build";
type NextMessageClassification = "continue" | "correction" | "new_task" | "question" | "other";
type RevertHow = "clear" | "restore_checkpoint" | "git_checkout";
/** Outcome signals a dataset builder can filter or weight on (plan §3.1). */
type OutcomePayload = {
    kind: "git_delta";
    turn_id: string;
    files_changed: number;
    insertions: number;
    deletions: number;
    /** Blob of the raw `git diff --numstat` output after the turn. */
    numstat_blob: string;
    /** Blob of the patch, when it is at most 512 KB. */
    patch_blob?: string;
} | {
    kind: "verification";
    turn_id: string;
    tool_call_id: string;
    command: string;
    category: VerificationCategory;
    exit_code: number;
} | {
    kind: "rating";
    turn_id: string;
    value: "good" | "bad";
    reason?: string;
} | {
    kind: "next_message";
    turn_id: string;
    classification: NextMessageClassification;
    classifier: "regex";
} | {
    kind: "reverted";
    turn_id: string;
    how: RevertHow;
} | {
    kind: "test_verdict";
    task_id: string;
    passed: boolean;
    command: string;
    exit_code: number;
    duration_ms: number;
    log_blob: string;
};
type SessionEndReason = "exit" | "clear" | "restore_checkpoint" | "crash" | "eval_done";
interface SessionEndPayload {
    reason: SessionEndReason;
    total_cost_usd?: number;
}
interface TracePayloads {
    session_start: SessionStartPayload;
    user_message: UserMessagePayload;
    llm_request: LlmRequestPayload;
    llm_response: LlmResponsePayload;
    tool_call: ToolCallPayload;
    permission_decision: PermissionDecisionPayload;
    tool_result: ToolResultPayload;
    code_mode_call: CodeModeCallPayload;
    compaction: CompactionPayload;
    guardrail: GuardrailPayload;
    completion_check: CompletionCheckPayload;
    subagent: SubagentPayload;
    turn_end: TurnEndPayload;
    outcome: OutcomePayload;
    session_end: SessionEndPayload;
}
type PayloadFor<T extends TraceEventType> = TracePayloads[T];

/**
 * Append-only session trace recorder.
 *
 * `createRecorder({ enabled, sessionId, root })` returns a recorder that is
 * a no-op unless `enabled` is true. When enabled it appends one JSON line
 * per event to `<sessionTraceDir>/trace.jsonl` with `appendFileSync` and
 * stores large bodies in a content-addressed blob store (blobs.ts).
 *
 * Guarantees:
 *   - Never throws. The first disk error is logged once via
 *     `logger.warn("trace:write_failed")` and the recorder disables itself
 *     for the rest of the session.
 *   - Nothing is created on disk until the first event or blob, so a
 *     disabled recorder (or an enabled one that never emits) leaves no trace
 *     directory behind.
 *   - Every string that hits disk goes through `redactLight` first.
 *   - A session stops recording (with one warning) once it has written
 *     `RETENTION.traceMaxSessionBytes`.
 *   - Reopening a session that already has a trace.jsonl (resume,
 *     `--continue`, checkpoint restore) appends: `seq` continues from the
 *     number of lines already on disk.
 */

interface TraceRecorder {
    readonly enabled: boolean;
    /** Session id this recorder writes under. */
    readonly sessionId: string;
    /** Absolute session trace directory, or null when disabled. */
    readonly dir: string | null;
    /**
     * Append one event. `turnId` defaults to the ambient turn id from the
     * structured logger (`getLogTurnId()`), or null when there is none.
     */
    emit<T extends TraceEventType>(type: T, payload: PayloadFor<T>, turnId?: string | null): void;
    /** Store `content` (redacted) in the blob store. Returns its sha, or null when disabled. */
    blob(content: string): string | null;
    /** Resolve once every pending write is on disk. Writes are synchronous, so this is immediate. */
    flush(): Promise<void>;
}

type Mode = "edit" | "plan" | "auto";

type Role = "system" | "user" | "assistant" | "tool";
interface ToolCall {
    id: string;
    type: "function";
    function: {
        name: string;
        arguments: string;
    };
}
interface TextContentPart {
    type: "text";
    text: string;
}
interface ImageContentPart {
    type: "image_url";
    image_url: {
        url: string;
    };
}
type ContentPart = TextContentPart | ImageContentPart;
/**
 * One structured reasoning block as OpenRouter reports it
 * (`reasoning.text` / `reasoning.summary` / `reasoning.encrypted` / …).
 * Opaque to us: stored as received and echoed back unchanged.
 */
interface ReasoningDetail {
    type: string;
    index?: number;
    id?: string | null;
    format?: string;
    text?: string | null;
    summary?: string;
    data?: string;
    signature?: string | null;
    [key: string]: unknown;
}
interface ChatMessage {
    role: Role;
    content: string | ContentPart[] | null;
    reasoning_content?: string | null;
    /** Structured reasoning to echo back to OpenRouter (Claude, GPT-5, Gemini). */
    reasoning_details?: ReasoningDetail[];
    tool_calls?: ToolCall[];
    tool_call_id?: string;
    name?: string;
}

/**
 * Custom OpenAI-compatible endpoint routing.
 *
 * When `KIMIFLARE_BASE_URL` is set (or `baseUrl` in config), every model call
 * — chat and embeddings — is sent to `<baseUrl>/chat/completions` (or
 * `/embeddings`) with `Authorization: Bearer <KIMIFLARE_API_KEY>` instead of
 * OpenRouter, and no OpenRouter key is needed.
 *
 * This is how a host application (e.g. an agents platform running kimiflare
 * inside a container) points the CLI at its own gateway/broker instead of
 * handing the process a raw provider key. The broker terminates the bearer it
 * issued and forwards upstream with credentials that never enter this process.
 *
 * Precedence (per field): env var > config field. Model ids pass through to
 * the body unchanged — no OpenRouter id-shape validation.
 */
interface CustomEndpoint {
    /** OpenAI-compatible base URL. `/chat/completions` is appended unless the URL already ends with it. */
    baseUrl: string;
    /** Bearer sent as `Authorization` to `baseUrl`. Omitted entirely when unset (unauthenticated local gateways). */
    apiKey?: string;
}

/**
 * OpenRouter `provider` routing object (subset we pass through). See
 * https://openrouter.ai/docs/guides/routing/provider-selection. Setting
 * `order` or `sort` disables sticky routing (prompt-cache locality) and
 * load balancing, so they're opt-in only.
 */
interface OpenRouterProviderPrefs {
    order?: string[];
    only?: string[];
    ignore?: string[];
    allow_fallbacks?: boolean;
    require_parameters?: boolean;
    data_collection?: "allow" | "deny";
    zdr?: boolean;
    quantizations?: string[];
    sort?: "price" | "throughput" | "latency";
    max_price?: Record<string, number>;
}

type SubagentPolicy = "off" | "suggest" | "auto";

type ReasoningEffort = "low" | "medium" | "high";
interface McpServerConfig {
    type: "local" | "remote";
    command?: string[];
    url?: string;
    env?: Record<string, string>;
    headers?: Record<string, string>;
    enabled?: boolean;
    /** Per-call timeout in milliseconds for tool invocations on this server. Default: 60000. */
    timeoutMs?: number;
}
interface LspServerConfig {
    command: string[];
    env?: Record<string, string>;
    enabled?: boolean;
    rootPatterns?: string[];
    /** Per-request timeout in milliseconds for LSP calls. Default: 10000. */
    timeoutMs?: number;
    /** Max auto-restart attempts after a crash. Default: 3. Set 0 to disable. */
    maxRestartAttempts?: number;
}
/** Permission rule: allow, deny, or ask (default). */
type PermissionRule = "allow" | "deny" | "ask";
/** Per-tool permission rules keyed by glob pattern. */
type PermissionRules = Record<string, PermissionRule>;
interface TraceConfig {
    enabled: boolean;
    root?: string;
}
interface KimiConfig {
    /**
     * The user's own OpenRouter API key (`sk-or-…`; env: OPENROUTER_API_KEY or
     * KIMIFLARE_OPENROUTER_KEY, which win over the file). Every model call —
     * chat, memory embeddings, plumbing side-calls — is billed to this key.
     * Bring-your-own only: kimiflare never pays for or proxies model calls.
     */
    openrouterApiKey?: string;
    /**
     * Optional Requesty API key (env: REQUESTY_API_KEY, which wins over the
     * file). Used only when no OpenRouter key and no custom endpoint are
     * configured: model calls then go to Requesty (https://requesty.ai). Set
     * REQUESTY_BASE_URL to pick a region, e.g. https://router.eu.requesty.ai/v1.
     */
    requestyApiKey?: string;
    /** OpenRouter model id, e.g. "moonshotai/kimi-k2.6". */
    model: string;
    /**
     * Optional OpenRouter provider-routing preferences, merged into every
     * request's `provider` object (kimiflare always sends
     * `require_parameters: true`). E.g. `{ "ignore": ["SomeProvider"] }` or
     * `{ "quantizations": ["fp8", "bf16"] }`. Avoid `order`/`sort` unless you
     * need them: they disable OpenRouter's sticky routing, which is what keeps
     * the prompt cache warm. See https://openrouter.ai/docs/guides/routing/provider-selection
     */
    openrouterProvider?: OpenRouterProviderPrefs;
    /**
     * Custom OpenAI-compatible endpoint base URL (env: KIMIFLARE_BASE_URL).
     * For a host application that embeds kimiflare and owns its own broker:
     * when set, ALL model calls go to `<baseUrl>/chat/completions` instead of
     * OpenRouter and no OpenRouter key is needed. See src/agent/custom-endpoint.ts.
     */
    baseUrl?: string;
    /**
     * Bearer sent as `Authorization` to `baseUrl` (env: KIMIFLARE_API_KEY).
     * Only used when `baseUrl` is set; omitted from the request entirely when
     * unset (for unauthenticated local gateways).
     */
    apiKey?: string;
    /**
     * Cloudflare account id + API token (env: CLOUDFLARE_ACCOUNT_ID /
     * CLOUDFLARE_API_TOKEN). Not used for model calls. Remote session
     * deployment uses these credentials to provision infrastructure.
     */
    accountId?: string;
    apiToken?: string;
    reasoningEffort?: ReasoningEffort;
    /** Harness subagent routing: off, auto (default; coordinator assesses substantial tasks), or suggest. Worker calls remain permission-gated and budget-limited. */
    subagentPolicy?: SubagentPolicy;
    /** Set only when the user explicitly chose a policy; used to migrate the former implicit suggest default. */
    subagentPolicyExplicit?: boolean;
    /**
     * Feature flag for the plan / edit / auto permission modes. Off by default:
     * every session runs in auto (tools run without per-call prompts) and the
     * mode UI (Shift+Tab, /mode, /plan, /edit, /auto, the mode badge) is hidden.
     * Turn on with `/settings modes on` to get the mode system back, starting
     * in edit.
     */
    modesEnabled?: boolean;
    coauthor?: boolean;
    coauthorName?: string;
    coauthorEmail?: string;
    mcpServers?: Record<string, McpServerConfig>;
    cacheStablePrompts?: boolean;
    /** Enable compiled context (token-optimized state packet + artifact store). */
    compiledContext?: boolean;
    /** Number of recent user turns to retain image content; older images are dropped. */
    imageHistoryTurns?: number;
    /** Hard safety ceiling on tool iterations per turn, across automatic
     *  200-call counter resets. Defaults to 1000. */
    maxTotalToolIterations?: number;
    /** Enable local structured memory (SQLite + embeddings). */
    memoryEnabled?: boolean;
    /** Path to memory database. Defaults to .kimiflare/memory.db in repo root, or ~/.local/share/kimiflare/memory.db. */
    memoryDbPath?: string;
    /** Max age of memories in days before cleanup. Default: 90. */
    memoryMaxAgeDays?: number;
    /** Max memories per repo. Default: 1000. */
    memoryMaxEntries?: number;
    /** Embedding model for memory vectors (OpenRouter id). Default: baai/bge-base-en-v1.5. */
    memoryEmbeddingModel?: string;
    /** Model for internal plumbing tasks (memory verification, hypothetical queries). Default: DEFAULT_PLUMBING_MODEL. */
    plumbingModel?: string;
    /** Model for auto-extracting high-signal edit events. Default: DEFAULT_PLUMBING_MODEL. */
    memoryExtractionModel?: string;
    /** Enable Code Mode: present tools as a TypeScript API and execute generated code in a sandbox. */
    codeMode?: boolean;
    /** End-of-turn "did the agent finish?" check via Jev (OpenRouter only). Default true. */
    completionCheck?: boolean;
    /** Enable LSP integration. Default: false. */
    lspEnabled?: boolean;
    /** LSP server configurations. */
    lspServers?: Record<string, LspServerConfig>;
    /** Enable cost attribution by task type. Default: false. Once stable for 2 releases, consider defaulting to true. */
    costAttribution?: boolean;
    /** Enable @ file mention picker in chat input. Default: false. */
    filePicker?: boolean;
    /** UI theme name. Default: everforest-dark. */
    theme?: string;
    /** URL of the remote orchestrator Worker. */
    remoteWorkerUrl?: string;
    /** Shared secret for authenticating with the remote Worker. */
    remoteAuthSecret?: string;
    /** Configurable TTL for remote sessions in minutes (default: 30). */
    remoteTtlMinutes?: number;
    /** Max input token budget per remote job (default: 5_000_000). */
    remoteMaxInputTokens?: number;
    /** GitHub OAuth token for remote PR creation. */
    githubOAuthToken?: string;
    /** GitHub refresh token (if available). */
    githubRefreshToken?: string;
    /** GitHub token expiry timestamp. */
    githubTokenExpiry?: number;
    /** Default GitHub repo for remote sessions (owner/repo). */
    githubRepo?: string;
    /** Shell override for the bash tool. "auto" (default) detects the platform, or specify "bash", "cmd", "powershell", or an absolute path. */
    shell?: string;
    /**
     * Deprecated/ignored: Camouflage is always used (the hidden `--ui ink`
     * picks Ink for one run). Kept so existing configs still load.
     */
    uiEngine?: "ink" | "camouflage";
    /** Max cost per Hotcell worker in USD (default: 1.0). */
    workerBudgetUsd?: number;
    /** Hard ceiling for workerBudgetUsd. Any configured or programmatic value above this is silently capped. Default: 5.0. */
    workerBudgetMaxUsd?: number;
    /** Max workers to spawn in parallel (default: 3). */
    workerMaxParallel?: number;
    /** Timeout per worker in milliseconds (default: 300000 = 5 min). */
    workerTimeoutMs?: number;
    /** Turn count at which KimiFlare suggests /fresh in auto/edit mode. 0 = disabled. Default: 30. */
    autoFreshSuggestionTurns?: number;
    /** If true, automatically execute /fresh when the threshold is hit instead of just suggesting it. Default: false. */
    autoFreshEnabled?: boolean;
    /** Estimated in-memory token threshold to trigger aggressive auto-compaction between turns. Default: 500_000. */
    autoCompactTokenThreshold?: number;
    /** Estimated in-memory token threshold to trigger auto-fresh when compaction cannot reduce below this. Default: 2_000_000. */
    autoFreshTokenThreshold?: number;
    /** Model used for LLM-based task decomposition in multi-agent mode.
     *  Default: DEFAULT_PLUMBING_MODEL (fast and cheap). */
    decompositionModel?: string;
    /** Strategy for decomposing heavy prompts into parallel research tasks.
     *  - "llm": use a lightweight LLM call (default)
     *  - "regex": pure regex heuristic (no LLM, fastest)
     *  - "hybrid": regex for explicit lists, LLM for prose */
    decompositionStrategy?: "llm" | "regex" | "hybrid";
    /** Model for synthesizing multi-agent findings.
     *  Default: DEFAULT_PLUMBING_MODEL (fast and cheap). */
    synthesisModel?: string;
    /** Strategy for synthesizing worker findings.
     *  - "llm": use a lightweight LLM call (default)
     *  - "heuristic": pure heuristic (no LLM, fastest)
     *  - "hybrid": try LLM, fall back to heuristic on failure */
    synthesisStrategy?: "llm" | "heuristic" | "hybrid";
    /** Explicit opt-out for LLM-based synthesis. When true, always uses heuristic. */
    disableLlmSynthesis?: boolean;
    /** Files to pre-read on the coordinator and inject into every worker's
     *  context. Saves redundant `read` tool calls across workers. Paths are
     *  relative to the repo root. */
    workerPreReadFiles?: string[];
    /** Max characters of pre-read content to inject per worker batch.
     *  Default: 50_000. */
    workerPreReadMaxChars?: number;
    /** Permission rules for headless/CI mode. Keys are tool names (e.g. "bash", "write").
     *  Values are glob-pattern → rule mappings. Patterns are matched against the
     *  target path (for file tools) or command string (for bash). */
    permissions?: Record<string, PermissionRules>;
    /** Prefer creating pull requests over pushing directly to the default branch.
     *  Injected into the system prompt. Default: true. */
    preferPullRequests?: boolean;
    /** Allow the bash tool to run `git push` directly to the repository's default
     *  branch. When false (default), such pushes are blocked and the model is
     *  directed to open a PR instead. */
    allowDirectPush?: boolean;
    /**
     * Local session tracing (docs/plans/trace-and-dataset-dev-plan.md). Off by
     * default; writes `trace.jsonl` + blobs per session under `root`
     * (default `${XDG_DATA_HOME:-~/.local/share}/kimiflare/traces`).
     * Env: KIMIFLARE_TRACE=1|0, KIMIFLARE_TRACE_DIR=<path>.
     */
    trace?: TraceConfig;
}
declare const DEFAULT_MODEL = "moonshotai/kimi-k2.6";
declare const DEFAULT_REASONING_EFFORT: ReasoningEffort;
declare function loadConfig(): Promise<KimiConfig | null>;
declare function saveConfig(cfg: KimiConfig): Promise<string>;

/**
 * The one place that turns config into model-call credentials.
 *
 * Every `runKimi` / `fetchEmbeddings` call site spreads `llmAuthFromConfig(cfg)`
 * into its options instead of picking individual credential fields, so the
 * main loop, side-calls (summaries, memory, decomposition, …), embeddings,
 * print/emit/serve/SDK entry points all authenticate the same way — and a
 * custom endpoint configured in the file (not just the env) applies to all
 * of them.
 */

interface LlmAuth {
    /** The user's OpenRouter key. */
    openrouterApiKey?: string;
    /** The user's Requesty key; used only when there is no OpenRouter key or custom endpoint. */
    requestyApiKey?: string;
    /** Host-app broker endpoint; when set, used instead of OpenRouter. */
    customEndpoint?: CustomEndpoint;
    /** Extra OpenRouter provider-routing preferences. */
    provider?: OpenRouterProviderPrefs;
}

type MemoryCategory = "fact" | "event" | "instruction" | "task" | "preference";
interface Memory {
    id: string;
    content: string;
    embedding: Float32Array;
    category: MemoryCategory;
    sourceSessionId: string;
    repoPath: string;
    createdAt: number;
    accessedAt: number;
    importance: number;
    relatedFiles: string[];
    topicKey: string | null;
    supersededBy: string | null;
    forgotten: boolean;
    vectorized: boolean;
    /** Agent role that created this memory (plan, build, general). */
    agentRole: string | null;
}
interface MemoryQuery {
    text: string;
    embedding?: Float32Array;
    repoPath?: string;
    category?: MemoryCategory;
    limit?: number;
    maxAgeDays?: number;
    /** Filter recalled memories by the agent role that created them. */
    agentRole?: string;
}
interface HybridResult {
    memory: Memory;
    ftsScore: number;
    vectorScore: number;
    exactScore: number;
    topicKeyScore: number;
    combinedScore: number;
}
interface MemoryStats {
    totalCount: number;
    dbSizeBytes: number;
    lastCleanupAt: number | null;
    byCategory: Record<MemoryCategory, number>;
}

interface MemoryManagerOpts {
    dbPath: string;
    openrouterApiKey?: string;
    requestyApiKey?: string;
    customEndpoint?: CustomEndpoint;
    model?: string;
    plumbingModel?: string;
    extractionModel?: string;
    embeddingModel?: string;
    provider?: OpenRouterProviderPrefs;
    maxAgeDays?: number;
    maxEntries?: number;
    redactSecrets?: boolean;
}
interface LlmOpts {
    openrouterApiKey?: string;
    requestyApiKey?: string;
    customEndpoint?: CustomEndpoint;
    model: string;
    provider?: OpenRouterProviderPrefs;
    signal?: AbortSignal;
}
declare class MemoryManager {
    private db;
    private opts;
    constructor(opts: MemoryManagerOpts);
    open(): void;
    close(): void;
    isOpen(): boolean;
    private get llmOpts();
    private get plumbingLlmOpts();
    private get extractionLlmOpts();
    /** Expose extraction LLM opts so the agent loop can pass them to extractors. */
    getExtractionLlmOpts(): LlmOpts;
    private shouldRedact;
    /**
     * Store a memory with verification, topic-key normalization, hypothetical queries,
     * secret redaction, and supersession.
     */
    remember(content: string, category: MemoryCategory, importance: number, repoPath: string, sessionId: string, signal?: AbortSignal, agentRole?: string, topicKey?: string): Promise<{
        id: string;
        superseded?: string[];
    }>;
    /**
     * Store a plan directly under a deterministic topic key.
     * Skips embedding, verification, and hypothetical queries so it is fast and
     * deterministic. Supersedes any previous plan stored under the same key.
     */
    rememberPlan(plan: string, repoPath: string, sessionId: string, topicKey?: string): Promise<{
        id: string;
        superseded?: string[];
    }>;
    /**
     * Recall the latest memory for an exact topic key.
     * Does not use embeddings; returns null if no matching memory exists.
     */
    getByTopicKey(repoPath: string, topicKey: string): Memory | null;
    /**
     * Count high-signal memories created since the given timestamp.
     * Used for KIMI.md drift detection (Trigger A: session-start check).
     */
    countHighSignalMemoriesSince(repoPath: string, since: number): number;
    /**
     * Get the timestamp of the most recent KIMI.md refresh memory.
     * Returns 0 if none exists.
     */
    getLastKimiMdRefreshTime(repoPath: string): number;
    /**
     * Record that KIMI.md was refreshed. Creates a lightweight memory
     * so drift detection knows when the snapshot was last updated.
     */
    recordKimiMdRefresh(repoPath: string, sessionId: string): Promise<void>;
    /**
     * Recall memories using the full hybrid retrieval pipeline.
     */
    recall(query: MemoryQuery): Promise<HybridResult[]>;
    /**
     * Recall memories created by a specific agent role.
     */
    recallByRole(query: MemoryQuery, agentRole: string): Promise<HybridResult[]>;
    /**
     * Format recalled memories as a compact context block for injection into messages.
     */
    static formatRecalled(results: HybridResult[]): string;
    /**
     * Synthesize recalled memories into a dense prose paragraph.
     * Uses the lightweight plumbing model (Scout) to keep costs low.
     */
    synthesizeRecalled(results: HybridResult[], signal?: AbortSignal): Promise<string>;
    /**
     * Soft-delete a memory by ID.
     */
    forget(id: string): Promise<boolean>;
    /**
     * Backfill un-vectorized memories at startup.
     */
    backfill(repoPath: string): Promise<number>;
    cleanup(repoPath: string): Promise<{
        oldDeleted: number;
        excessDeleted: number;
        duplicatesMerged: number;
    }>;
    getStats(): MemoryStats | null;
    clearRepo(repoPath: string): number;
    private verifyMemory;
    private normalizeTopicKey;
    private generateHypotheticalQueries;
}

interface PlanOption {
    label: string;
    plan: string;
}
interface ToolContext {
    cwd: string;
    signal?: AbortSignal;
    onTasks?: (tasks: Task[]) => void;
    onPlanOptions?: (options: PlanOption[]) => void;
    coauthor?: {
        name: string;
        email: string;
    };
    memoryManager?: MemoryManager | null;
    sessionId?: string;
    githubToken?: string;
    /** Shell override for the bash tool. If omitted, the tool auto-detects based on platform. */
    shell?: string;
    /**
     * Intent tier classified for this turn, when known. Carried into
     * hook payloads (PreToolUse / PostToolUse) so user hooks can branch
     * on tier — e.g. skip auto-format for light-tier turns, or audit
     * every heavy-tier action. Optional because code-mode sub-calls
     * and SDK consumers may not have a tier.
     */
    intentTier?: "light" | "medium" | "heavy";
    /** Credentials for tools that need to call an LLM (see llm-auth.ts). */
    llmAuth?: LlmAuth;
    /** Model id for tools that need to call an LLM. */
    model?: string;
    /** Optional override for the durable managed-jobs database (primarily for embedding/tests). */
    jobsDbPath?: string;
    /** Run identifier for durable tool-boundary journaling in unattended runs. */
    runId?: string;
    /** Notifies the host that a durable run budget paused execution. */
    onRunBudgetExceeded?: (reason: string) => void;
    /** Optional override for the durable run/event/timer database. */
    runsDbPath?: string;
    /** When false (default), the bash tool blocks `git push` to the repository's
     *  default branch and directs the model to open a PR instead. */
    allowDirectPush?: boolean;
    /** Session trace recorder for this call (set by the agent loop). Tools and
     *  the executor emit on it; absent or disabled means no-op. */
    trace?: TraceRecorder;
    /** Turn id stamped on trace events for this call. */
    turnId?: string | null;
    /** Set when the call is an `api.*` invocation from inside an `execute_code`
     *  sandbox: the id of the parent `execute_code` tool call. The executor then
     *  records a `code_mode_call` instead of a top-level tool_call/tool_result. */
    codeModeParentToolCallId?: string;
}
interface ToolRender {
    title: string;
    body?: string;
    diff?: {
        path: string;
        before: string;
        after: string;
    };
}
interface RunWaitRequest {
    runId: string;
    timerId: string;
    condition: "time" | "job";
    jobId?: string;
    wakeAt: number;
}
interface ToolOutput {
    content: string;
    rawBytes: number;
    reducedBytes: number;
    /** Internal control signal: persist the wait and end this agent turn. */
    waitRequest?: RunWaitRequest;
    /** Structured facts about the execution (bash: exit code, timeout), for the trace. */
    meta?: ToolOutputMeta;
}
interface ToolOutputMeta {
    exitCode?: number | null;
    timedOut?: boolean;
}
interface ToolSpec<Args = any> {
    name: string;
    description: string;
    parameters: Record<string, unknown>;
    needsPermission: boolean;
    /** When true, the tool only reads state and never mutates the workspace.
     *  Read-only tools within a single turn may be executed in parallel. */
    isReadOnly?: boolean;
    /** When true, calls may run concurrently with each other and with
     *  read-only calls even though the tool is not read-only — for work
     *  that is isolated from the workspace (e.g. sandboxed subagents).
     *  Permission prompts for a concurrent batch are serialized and
     *  same-tool calls are approved together (see agent/permission-gate.ts). */
    concurrent?: boolean;
    /** In Code Mode, expose this tool directly next to `execute_code` instead
     *  of inside the sandbox API (for long-running or parallel tools that the
     *  synchronous, time-limited sandbox cannot host). */
    codeModeDirect?: boolean;
    render?: (args: Args) => ToolRender;
    run: (args: Args, ctx: ToolContext) => Promise<string | ToolOutput>;
}
type TaskStatus = "pending" | "in_progress" | "completed";
interface Task {
    id: string;
    title: string;
    status: TaskStatus;
}

/**
 * Whether the user said yes or no.
 *
 * NOTE on naming: the legacy `PermissionDecision` string type
 * (`"allow" | "allow_session" | "deny"`) also lives below as a
 * deprecated back-compat alias. New callers should return the typed
 * `PermissionDecisionResult` shape; the executor normalizes either at
 * the boundary so existing SDK callers keep working unchanged.
 */
type PermissionDecisionKind = "allow" | "deny";
/**
 * How broadly an allow applies. M2.1's enum overloaded this with the
 * decision — `"allow_session"` meant "allow + cache for the session" —
 * which made the new `"pattern"` scope from M6.2 impossible to express.
 *
 *   - `once`    — approved this single call. The executor will ask
 *                 again next time the same tool is requested.
 *   - `session` — approved for the rest of the session (existing
 *                 `"allow_session"` behavior).
 *   - `pattern` — approved because the call matched a pre-configured
 *                 glob in settings.json (M6.2). The executor does NOT
 *                 cache pattern-allow decisions — the pattern itself
 *                 is the durable record and lives in the config file.
 *
 * `scope` is meaningful only when `decision === "allow"`; for `deny`
 * it's ignored, but we keep it required so the shape stays uniform.
 */
type PermissionScope = "once" | "session" | "pattern";
interface PermissionDecisionResult {
    decision: PermissionDecisionKind;
    scope: PermissionScope;
    /** Free-text the user typed when denying ("do X instead"). Recorded on the
     *  trace's `permission_decision`; the text itself reaches the model as the
     *  next user message. */
    feedback?: string;
}
/**
 * Legacy single-string decision. Kept for SDK back-compat — callers can
 * still return `"allow" | "allow_session" | "deny"` and the executor
 * normalizes via `toPermissionResult`. Prefer
 * `PermissionDecisionResult` in new code.
 *
 * @deprecated since M2.2. Use `PermissionDecisionResult` instead.
 */
type PermissionDecisionLegacy = "allow" | "allow_session" | "deny";
/**
 * Union of the new typed result and the legacy string. Both shapes are
 * accepted by `PermissionAsker`; the executor calls
 * `toPermissionResult` to normalize.
 */
type PermissionDecision = PermissionDecisionLegacy | PermissionDecisionResult;
interface PermissionRequest {
    tool: ToolSpec;
    args: Record<string, unknown>;
    sessionKey: string;
}

interface CreateSessionOptions {
    /** Working directory for the agent. Defaults to process.cwd(). */
    cwd?: string;
    /** Override config. If omitted, loads from ~/.config/kimiflare/config.json */
    config?: Partial<KimiConfig>;
    /** Session ID for persistence. If omitted, creates a new session. */
    sessionId?: string;
    /** Which tools to enable. Defaults to ALL_TOOLS. */
    tools?: ToolSpec[];
    /** Enable local structured memory. Defaults to config value. */
    memoryEnabled?: boolean;
    /** Enable LSP integration. Defaults to config value. */
    lspEnabled?: boolean;
    /** Enable cost attribution. Defaults to config value. */
    costAttribution?: boolean;
    /** OpenRouter provider-routing preferences for this session (merged over config.openrouterProvider). */
    provider?: OpenRouterProviderPrefs;
    /** Custom permission handler. Defaults to auto-deny in plan mode, ask callback in edit mode. */
    permissionHandler?: PermissionHandler;
    /** Called when the agent detects KIMI.md drift. */
    onKimiMdStale?: () => void;
    /**
     * M6.1: enable user-configured lifecycle hooks loaded from
     * `~/.config/kimiflare/settings.json` + `<cwd>/.kimiflare/settings.json`.
     * Default `false` (SDK is a primitive; opt in if you want the
     * TUI's hook behavior).
     */
    enableHooks?: boolean;
}
interface PromptOptions {
    /** Attach images to the prompt. */
    images?: Array<{
        path: string;
    } | {
        data: string;
        mimeType: string;
    }>;
    /** Override mode for this prompt only. */
    mode?: "plan" | "edit" | "auto";
    /** Override max tool iterations for this prompt only. */
    maxToolIterations?: number;
}
type SessionEvent = {
    type: "session.start";
    sessionId: string;
    cwd: string;
} | {
    type: "session.end";
    reason: "complete" | "aborted" | "error";
    error?: string;
} | {
    type: "message.start";
    messageId: string;
    role: "user" | "assistant";
} | {
    type: "message.delta";
    messageId: string;
    text: string;
} | {
    type: "message.reasoning";
    messageId: string;
    text: string;
} | {
    type: "message.end";
    messageId: string;
} | {
    type: "tool.start";
    toolCallId: string;
    toolName: string;
    args: unknown;
} | {
    type: "tool.result";
    toolCallId: string;
    toolName: string;
    result: string;
    isError: boolean;
} | {
    type: "usage";
    inputTokens: number;
    outputTokens: number;
    reasoningTokens?: number;
    cost?: number;
} | {
    type: "permission.request";
    requestId: string;
    toolName: string;
    args: unknown;
} | {
    type: "permission.resolved";
    requestId: string;
    decision: PermissionDecision;
} | {
    type: "tasks.update";
    tasks: Task[];
} | {
    type: "warning";
    message: string;
}
/** Older turns were archived so the next request fits the model's input budget. */
 | {
    type: "context.compacted";
    tokensBefore: number;
    tokensAfter: number;
    turnsRemoved: number;
    artifactsArchived: number;
} | {
    type: "status";
    status: "idle" | "streaming" | "tool_executing" | "compacting" | "error";
};
interface SessionUsage {
    totalInputTokens: number;
    totalOutputTokens: number;
    totalCost: number;
    turnCount: number;
}
interface SessionStatus {
    isStreaming: boolean;
    isCompacting: boolean;
    pendingSteer: string[];
    pendingFollowUp: string[];
    currentMode: Mode;
}
type PermissionHandler = (req: PermissionRequest) => Promise<PermissionDecision>;
interface KimiFlareSession {
    readonly sessionId: string;
    readonly cwd: string;
    readonly isStreaming: boolean;
    readonly messages: ChatMessage[];
    /** The session's trace recorder (a no-op unless `config.trace.enabled`). Hosts
     *  such as the eval runner append their own outcome events to it. */
    readonly trace: TraceRecorder;
    /** Run a turn. Called while a turn is streaming, the text is treated as a steer. */
    prompt(text: string, options?: PromptOptions): Promise<void>;
    /** Inject guidance into the running turn: delivered at the next tool
     *  boundary, or before the final answer (the turn continues to address it).
     *  Ignored when no turn is running. */
    steer(text: string): Promise<void>;
    /** Queue a message to run as its own turn after the current (or next)
     *  prompt's turn completes successfully. */
    followUp(text: string): Promise<void>;
    abort(): Promise<void>;
    setModel(modelId: string): void;
    setMode(mode: "plan" | "edit" | "auto"): void;
    setReasoningEffort(level: "low" | "medium" | "high"): void;
    resolvePermission(requestId: string, decision: PermissionDecision): void;
    subscribe(listener: (event: SessionEvent) => void): () => void;
    getUsage(): SessionUsage;
    getStatus(): SessionStatus;
    save(): Promise<void>;
    /** Release resources. `traceEndReason` sets the `session_end` reason recorded on the trace (default "exit"). */
    dispose(options?: {
        traceEndReason?: SessionEndReason;
    }): void;
}

declare function createAgentSession(opts: CreateSessionOptions): Promise<{
    session: KimiFlareSession;
}>;

declare function createDefaultPermissionHandler(options: {
    mode: Mode;
    onRequest?: (req: PermissionRequest) => void;
}): PermissionHandler;

declare function startRpcServer(input?: Readable, output?: Writable, createSession?: typeof createAgentSession): Promise<void>;

declare function startAsterCellBridge(port?: number): Promise<ReturnType<typeof createServer>>;
interface AsterCellControlRequest {
    method: "GET" | "POST";
    path: string;
    body?: unknown;
}
declare function executeAsterCellControl(requestId: string): Promise<unknown>;

interface RunWorktree {
    runId: string;
    repositoryRoot: string;
    sourceCwd: string;
    worktreePath: string;
    cwd: string;
    branch: string;
}

type RunStatus = "queued" | "running" | "waiting" | "needs_input" | "completed" | "failed" | "cancelled" | "interrupted_unknown";
type RunEventType = "state" | "tool_intent" | "tool_result" | "usage" | "checkpoint";
type TimerCondition = "time" | "job" | "approval";
type TimerStatus = "scheduled" | "claimed" | "completed" | "failed" | "cancelled";
interface RunRecord {
    id: string;
    sessionId: string | null;
    cwd: string;
    sourceCwd: string | null;
    repositoryRoot: string | null;
    worktreePath: string | null;
    branch: string | null;
    task: string;
    status: RunStatus;
    stopReason: string | null;
    allowedTools: string[];
    maxToolIterations: number | null;
    maxRuntimeMs: number | null;
    maxTotalTokens: number | null;
    maxCostUsd: number | null;
    totalPromptTokens: number;
    totalCompletionTokens: number;
    totalCostUsd: number;
    costUnknownResponses: number;
    createdAt: number;
    updatedAt: number;
    startedAt: number | null;
    finishedAt: number | null;
}
interface RunBudgetUpdate {
    maxToolIterations?: number | null;
    maxRuntimeMs?: number | null;
    maxTotalTokens?: number | null;
    maxCostUsd?: number | null;
}
interface RunEvent {
    sequence: number;
    runId: string;
    type: RunEventType;
    toolCallId: string | null;
    toolName: string | null;
    metadata: Record<string, unknown>;
    createdAt: number;
}
interface RunTimer {
    id: string;
    runId: string;
    condition: TimerCondition;
    jobId: string | null;
    wakeAt: number;
    intervalMs: number;
    status: TimerStatus;
    attempts: number;
    maxAttempts: number;
    leaseUntil: number | null;
    lastError: string | null;
    createdAt: number;
}
/**
 * SQLite persistence for unattended runs, tool-boundary checkpoints, and
 * durable wake timers. Constructing a store only opens/migrates the schema;
 * call reconcileInterruptedRuns explicitly once at supervisor startup.
 */
declare class RunStore {
    private readonly db;
    constructor(dbPath?: string);
    close(): void;
    createRun(input: {
        id?: string;
        task: string;
        cwd: string;
        sessionId?: string;
        worktree?: RunWorktree;
        allowedTools?: string[];
        maxToolIterations?: number | null;
        maxRuntimeMs?: number | null;
        maxTotalTokens?: number | null;
        maxCostUsd?: number | null;
    }): RunRecord;
    getRun(id: string): RunRecord | undefined;
    listRuns(options?: {
        status?: RunStatus;
        limit?: number;
    }): RunRecord[];
    transition(id: string, status: RunStatus, reason?: string): RunRecord;
    /** Resume a budget-paused run and atomically apply any revised limits. */
    resumeWithBudgets(id: string, budgets: RunBudgetUpdate): RunRecord;
    /** Persist a tool intent before execution. Only hashes are retained, never raw arguments. */
    recordToolIntent(runId: string, toolCallId: string, toolName: string, args: unknown): void;
    /** Persist completion metadata after execution without storing tool output text. */
    recordToolResult(runId: string, toolCallId: string, toolName: string, result: {
        ok: boolean;
        content: string;
        errorCode?: string;
    }): void;
    /** Mark that the current run session was durably saved after its last tool results. */
    recordCheckpoint(runId: string): void;
    listEvents(runId: string): RunEvent[];
    countToolIterations(runId: string): number;
    recordUsage(runId: string, usage: {
        promptTokens: number;
        completionTokens: number;
        costUsd?: number;
    }): {
        run: RunRecord;
        budgetExceededReason: string | null;
    };
    /**
     * On daemon startup, requeue runs with a saved checkpoint and no unmatched
     * tool intent. Ambiguous tool actions remain interrupted and are never replayed.
     */
    reconcileInterruptedRuns(now?: number): RunRecord[];
    scheduleTimer(input: {
        runId: string;
        condition: TimerCondition;
        wakeAt: number;
        intervalMs?: number;
        jobId?: string;
        maxAttempts?: number;
    }): RunTimer;
    getTimer(id: string): RunTimer | undefined;
    nextTimerAt(): number | null;
    listFailedTimers(limit?: number): RunTimer[];
    /** Reschedule a successful job-status check without consuming failure retries. */
    rescheduleTimer(id: string, wakeAt: number): RunTimer;
    /** Atomically lease timers due now; expired claims are safely retried. */
    claimDueTimers(now?: number, limit?: number, leaseMs?: number): RunTimer[];
    completeTimer(id: string): RunTimer;
    retryTimer(id: string, error: string, now?: number): RunTimer;
    cancelTimersForRun(runId: string): number;
    cancelTimer(id: string): RunTimer;
    private updateTimer;
    private appendEvent;
    private assertRunExists;
    private assertRunActive;
}

type JobStatus = "starting" | "running" | "cancelling" | "completed" | "failed" | "timed_out" | "cancelled" | "unknown";

interface RunWakeEvent {
    runId: string;
    timerId: string;
    condition: "time" | "job";
    jobId?: string;
    jobStatus?: JobStatus | "not_found";
}
interface RunWakeSchedulerOptions {
    /** Enqueue/resume the run; must be idempotent by timerId and return promptly. */
    onWake: (event: RunWakeEvent) => void | Promise<void>;
    runsDbPath?: string;
    jobsDbPath?: string;
    leaseMs?: number;
    batchSize?: number;
    onError?: (error: Error, timer?: RunTimer) => void;
}
/**
 * Durable timer dispatcher. It holds no timer state in memory: every wake is
 * claimed from SQLite, so a restart recovers scheduled and expired leases.
 * onWake must be idempotent by timerId because delivery is at-least-once.
 */
declare class RunWakeScheduler {
    private readonly store;
    private readonly options;
    private timer;
    private running;
    private disposed;
    constructor(options: RunWakeSchedulerOptions);
    start(): void;
    stop(): void;
    dispose(): void;
    /** Deterministic entry point for tests and hosts that own their scheduler loop. */
    processDueTimers(now?: number): Promise<number>;
    private tick;
    private scheduleNextTick;
    private processTimer;
    private failRunsForFailedTimers;
}

declare function resolveSdkConfig(opts: CreateSessionOptions): Promise<KimiConfig>;

export { type AsterCellControlRequest, type ChatMessage, type CreateSessionOptions, DEFAULT_MODEL, DEFAULT_REASONING_EFFORT, type KimiConfig, type KimiFlareSession, type PermissionDecision, type PermissionHandler, type PermissionRequest, type PromptOptions, type RunEvent, type RunEventType, type RunRecord, type RunStatus, RunStore, type RunTimer, type RunWakeEvent, RunWakeScheduler, type RunWakeSchedulerOptions, type SessionEvent, type SessionStatus, type SessionUsage, type Task, type TimerCondition, type TimerStatus, type ToolSpec, createAgentSession, createDefaultPermissionHandler, executeAsterCellControl, loadConfig, resolveSdkConfig, saveConfig, startAsterCellBridge, startRpcServer };
