import * as readline from 'node:readline/promises';
import type { Message, GrawkusConfig } from './types.js';
import type { Mode } from './modes.js';
export declare function resolveFirstTokenTimeoutMs(config: Pick<GrawkusConfig, 'model' | 'provider'>): number;
export declare function resolveTurnFirstTokenTimeoutMs(config: Pick<GrawkusConfig, 'model' | 'provider'>, fastDirect?: boolean): number;
export declare function resolveStreamIdleTimeoutMs(fastDirect?: boolean): number;
export declare function isKnownFlakyOpenRouterModel(config: Pick<GrawkusConfig, 'model' | 'provider'>): boolean;
export declare function isTurnCancelKeySequence(chunk: Buffer): boolean;
export declare function shouldUseFastDirectReply(userQuery: string | undefined, mode: Mode, hasPriorAssistantContext?: boolean, env?: NodeJS.ProcessEnv): boolean;
export interface WorkingIndicator {
    stop(): void;
}
export declare function formatWorkingIndicatorFrame(elapsedMs: number, frameIndex?: number, message?: string): string;
/**
 * Count how many NON-OVERLAPPING occurrences of the last `windowSize`
 * characters of `fullText` appear in the full string. Bails as soon as
 * `threshold` is reached so the cost is O(threshold * windowSize) in
 * the common stuck-model case rather than O(fullText.length).
 *
 * Non-overlapping by stepping the search index by `windowSize` after
 * each match. Overlapping counts cause false positives on self-similar
 * text (markdown tables, repeated import lines, ASCII art) — the audit
 * found that `idx++` made a single copy match 3+ times when there were
 * only two genuine repeats.
 *
 * Exported so the loop-detector behavior can be unit-tested without
 * driving the full streamChat loop.
 */
export declare function countTailRepetitions(fullText: string, windowSize: number, threshold: number): number;
export interface QueryContext {
    config: GrawkusConfig;
    messages: Message[];
    cwd: string;
    rl: readline.Interface;
    sessionId: string;
    mode: Mode;
}
export interface InputGuard {
    pause(): void;
    resume(): void;
    /** Return whatever the user typed during streaming, then clear the buffer. */
    drainQueuedInput(): string;
    /**
     * Register a callback invoked when the user presses Ctrl+G (Steer).
     * The handler is responsible for aborting the active stream / chain;
     * queued type-ahead is restored into the next editable prompt.
     */
    onSteer(handler: () => void): void;
    restore(): void;
}
export declare function buildStateBlock(messages: Message[]): string | null;
/**
 * F2 — Observation Window Masking.
 *
 * Source: arxiv 2508.21433 ("The Complexity Trap: Simple Observation
 * Masking Is as Efficient as LLM Summarization for Agent Context
 * Management"). Cuts token cost ~50% on long agent loops while
 * matching or beating LLM-summarization solve rates — at ZERO extra
 * inference cost.
 *
 * Strategy: keep the last MASKING_WINDOW tool-result messages in
 * full. For older tool-results, replace `content` with a short stub
 * indicating what was there. The stub preserves `role` and
 * `tool_call_id` so the OpenAI message-schema invariants are not
 * violated.
 *
 * We DO NOT mask:
 *   - assistant turns (the reasoning chain stays intact)
 *   - user turns (task instruction + DeCRIM critique prompts)
 *   - system messages (priming + mode)
 *
 * Only `role === 'tool'` messages are masked, because the paper's
 * empirical finding is that ~84% of token cost is tool observations
 * and the model rarely needs the old verbatim output to make the
 * next decision — it needs the current state. The reasoning trace
 * across assistant turns carries the necessary memory.
 *
 * Tunable: MASKING_WINDOW = 12 (last 12 tool-results stay verbatim).
 * Conservative for our model class — the paper's Qwen3-32B run
 * regressed -11.8% with overly aggressive masking, while Gemini-Flash
 * gained +8.5%. Deepseek-v4-flash is in that capability band, so we
 * pick a generous window. Override with GRAWKUS_MASK_WINDOW.
 *
 * Threshold: we only bother masking when the total estimated payload
 * exceeds ~60K characters (rough proxy for ~15K tokens). Below that,
 * masking adds noise without saving anything material.
 */
/**
 * GoalAct-style global planning block.
 *
 * Source: arxiv 2504.16563 ("Enhancing LLM-Based Agents via Global
 * Planning and Hierarchical Execution") and the later HiPlan line of
 * global-local planning work. This is intentionally not another LLM call:
 * it derives coarse phase signals from the transcript and injects a small
 * planning policy before each turn.
 */
export declare function buildGlobalPlanBlock(messages: Message[]): string | null;
export declare function isVerificationToolCall(name: string, rawArgs: string): boolean;
export declare function editedTargetsFromToolCall(toolName: string, rawArgs: string): string[];
export interface FileEditState {
    key: string;
    displayPath: string;
    hash: string;
    sizeBytes: number;
}
export declare function snapshotFileEditState(cwd: string, target: string): FileEditState | null;
export declare function snapshotFileEditStates(cwd: string, targets: Iterable<string>): Map<string, FileEditState>;
export declare function buildUnchangedFileEditReminder(filePath: string, reason: 'pre' | 'previous', sizeBytes: number): string;
export declare function recordFileEditStates(cwd: string, targets: Iterable<string>, lastStates: Map<string, FileEditState>, beforeStates?: Map<string, FileEditState>): string[];
export declare function buildEditVerificationReminder(pendingFiles: Iterable<string>, editCount: number): string | null;
export declare function maskOldToolResults(messages: Message[]): Message[];
/**
 * F5+ DeCRIM 3-stage critique prompts.
 *
 * Each prompt is designed to do exactly one job, in sequence:
 *
 *   decompose — Forces the model to extract requirements from the
 *               ORIGINAL task before judging its own work. This is
 *               the leverage point: the model can't bypass an
 *               implicit requirement if it has to name it.
 *
 *   critique  — Per-item PASS/FAIL with concrete evidence required.
 *               Asking for evidence ("file path", "command output",
 *               "test result") is much harder to fake than the
 *               generic "have you accomplished what was asked?".
 *
 *   refine    — Only the FAIL items get redone, plus any items
 *               whose PASS evidence the model now thinks was weak.
 *               If everything is solid, the model exits naturally.
 *
 * The phrasing deliberately includes "be honest" / "the user prefers
 * honest failures over confident lies" — research on prompted self-
 * criticism shows this kind of social-cost signaling reduces the
 * self-confirmation bias that otherwise dominates weak-model
 * critique. (Reflexion-style "just reflect on your work" prompts
 * have been shown to degrade weak models — generic self-questioning
 * without concrete structure produces overconfident revisions.)
 */
export declare function critiquePromptFor(stage: 'decompose' | 'critique' | 'refine'): string;
export declare function minimumToolCallsBeforeDone(mode: Mode, env?: NodeJS.ProcessEnv): number;
export declare function buildEmptyEngagementReminder(toolCallCount: number, minToolCalls: number, mode: Mode): string;
export declare function dedupFingerprint(toolName: string, rawArgs: string): string;
type RecoveryReason = 'repeat' | 'timeout';
export declare function isTimeoutObservation(content: string): boolean;
export declare function buildRecoveryReminder(toolName: string, rawArgs: string, toolResultContent: string, reason?: RecoveryReason): string;
/**
 * F4 — Rewrite stale duplicate tool-result messages in place.
 *
 * Called once per tool-execution batch. For each call whose
 * fingerprint we've seen before in this chain, find the previous
 * tool-result message and replace its `content` with a 1-line stub
 * pointing at the newer message. The new result stays untouched so
 * the model's next turn reads complete, fresh data.
 *
 * NOT called for the FIRST occurrence of a fingerprint — only when
 * a repeat fires. So a one-time `read` of a file is never touched.
 *
 * The map is keyed by fingerprint → array-index of the tool result
 * in ctx.messages. We update the index to the newest occurrence after
 * each rewrite, so the NEXT repeat collapses the second one (not the
 * first, which is already stubbed).
 */
export declare function dedupRepeatedToolCalls(messages: Message[], toolCalls: {
    id: string;
    type: 'function';
    function: {
        name: string;
        arguments: string;
    };
}[], toolResults: Message[], dedupMap: Map<string, number>): string[];
/**
 * Main query loop: sends messages to the API, handles tool calls, loops until done.
 */
export declare function runQuery(ctx: QueryContext): Promise<void>;
export {};
