/**
 * Shared utilities for Gemini 3 native SDK support.
 *
 * Both GoogleAIStudioProvider and GoogleVertexProvider route Gemini 3 models
 * with tools to the native @google/genai SDK (bypassing the Vercel AI SDK)
 * in order to properly handle thought_signature in multi-turn tool calling.
 *
 * This module extracts the functions that are duplicated between the two
 * providers so they can share a single implementation.
 */
import type { GenerateStopReason, ThinkingConfig, ChatMessage, CollectedChunkResult, MinimalChatMessage, NativeFunctionCall, NativeFunctionResponse, NativeToolDeclarationsResult, NativeToolsConfig, TextChannel, VertexNativePart, GeminiMultimodalInput } from "../types/index.js";
import type { Tool } from "../types/index.js";
/**
 * A per-turn tool execute map that deduplicates identical tool calls.
 *
 * Gemini occasionally re-emits a tool call with identical arguments across
 * agentic steps even though the prior result is already in the conversation
 * history (BZ-3327). Re-executing produces duplicate side effects and
 * duplicate reports for the user, plus wasted tokens. This map caches the
 * result of each {tool name + args} executed within the turn and returns it
 * for any identical re-request instead of running the tool again.
 *
 * Providers build a fresh executeMap per request, so the cache scope is
 * exactly one turn. Only `.get()` is overridden (the sole access path used by
 * the native agentic loops), so iteration still yields the raw executors.
 */
export declare class DedupExecuteMap extends Map<string, Tool["execute"]> {
    private readonly resultCache;
    get(name: string): Tool["execute"] | undefined;
}
export declare function sanitizeForGoogleFunctionName(name: string): string;
/**
 * Resolve a sanitized Gemini tool name to one that is both unique within
 * the current request and at most 128 characters. When the candidate
 * collides with an already-used name we append `_2`, `_3`, … — but
 * reserve room for the suffix by truncating the base first so the
 * resolved name never exceeds Google's `function_declarations[].name`
 * limit.
 *
 * @param base       The already-sanitized candidate name.
 * @param isTaken    Predicate that returns true if `name` is already used.
 */
export declare function resolveUniqueGoogleFunctionName(base: string, isTaken: (name: string) => boolean): string;
/**
 * Sanitize a JSON Schema for Gemini's proto-based API.
 *
 * Gemini cannot handle `anyOf`/`oneOf` union types in function declarations
 * because its proto format expects a single `type` field, not a list of types.
 * This function recursively converts unions to `string` type (the most
 * permissive primitive that can represent any value as text).
 *
 * Also removes `$schema`, `additionalProperties`, and `default` keys that
 * Gemini's proto format doesn't support.
 */
export declare function sanitizeSchemaForGemini(schema: Record<string, unknown>): Record<string, unknown>;
/**
 * Sanitize Vercel AI SDK tools for Gemini compatibility.
 *
 * For the Vercel AI SDK path (non-native), tool parameters are Zod schemas that
 * get converted to JSON Schema internally by @ai-sdk/google. This conversion
 * doesn't sanitize union types (anyOf/oneOf), causing Gemini proto errors.
 *
 * This function pre-converts each tool's Zod parameters to sanitized JSON Schema
 * and re-wraps with the Vercel AI SDK's jsonSchema() helper.
 */
export declare function sanitizeToolsForGemini(tools: Record<string, Tool>): {
    tools: Record<string, Tool>;
    dropped: string[];
    /**
     * Reverse map: Google-safe sanitized name → original consumer-supplied
     * name. Lets the calling layer translate tool-call results back so the
     * sanitization stays transport-only (see CodeRabbit thread, PR #1006).
     */
    originalNameMap: Map<string, string>;
};
export declare function normalizeToolsForJsonSchemaProvider(tools: Record<string, Tool>): {
    tools: Record<string, Tool>;
    normalized: string[];
};
/**
 * Convert Vercel AI SDK tools to @google/genai FunctionDeclarations and an execute map.
 *
 * This handles both Zod schemas and plain JSON Schema objects for tool parameters.
 */
export declare function buildNativeToolDeclarations(tools: Record<string, Tool>): NativeToolDeclarationsResult;
/**
 * Build the native @google/genai config object shared by stream and generate.
 *
 * Caller is responsible for the tools-vs-JSON conflict resolution: Gemini's
 * function calling cannot be combined with `responseMimeType:
 * "application/json"`, and `responseSchema` requires that mime type. So
 * when tools are active, callers must NOT pass `wantsJsonOutput`/
 * `responseSchema` here; when JSON/schema output is requested, callers
 * must omit `toolsConfig`. The AI Studio path enforces this by forcing
 * `disableTools: true` whenever JSON/schema output is requested.
 */
export declare function buildNativeConfig(options: {
    temperature?: number;
    maxTokens?: number;
    systemPrompt?: string;
    thinkingConfig?: ThinkingConfig;
    /**
     * When true (and `toolsConfig` is undefined), set
     * `responseMimeType: "application/json"` to enforce native JSON output.
     */
    wantsJsonOutput?: boolean;
    /**
     * Pre-converted JSON Schema for native `responseSchema`. Implies
     * `wantsJsonOutput`. Ignored if `toolsConfig` is present.
     */
    responseSchema?: Record<string, unknown>;
}, toolsConfig?: NativeToolsConfig): Record<string, unknown>;
/**
 * Compute a safe, clamped maxSteps value.
 */
export declare function computeMaxSteps(rawMaxSteps?: number): number;
/**
 * Map a `@google/genai` `Candidate.finishReason` enum value onto NeuroLink's
 * unified finish reason, mirroring anthropic.ts `mapAnthropicStopReason`.
 *
 * Enum values per `@google/genai` `FinishReason`: STOP, MAX_TOKENS, SAFETY,
 * RECITATION, LANGUAGE, OTHER, BLOCKLIST, PROHIBITED_CONTENT, SPII,
 * MALFORMED_FUNCTION_CALL, IMAGE_SAFETY, UNEXPECTED_TOOL_CALL,
 * FINISH_REASON_UNSPECIFIED. Unknown / unset / non-terminal values default to
 * "stop" (a clean completion is the safe assumption).
 *
 * Returns a plain string (not a `{ unified, raw }` object): the Vertex result
 * builders and the consuming layer (neurolink.ts `finishReason || "unknown"`
 * and `finishReason === "length"`) compare against plain strings.
 *
 * MALFORMED_FUNCTION_CALL / UNEXPECTED_TOOL_CALL map to "error", NOT
 * "tool-calls": they are provider/model failures, while "tool-calls" is the
 * exclusive contract for "step budget exhausted while the model still wanted
 * tools" — consumers (e.g. curator's step-cap intercept) branch on it and
 * were rendering fake step-limit messages for 2-4-step malformed-call turns.
 */
export declare function mapGeminiFinishReason(raw: string | null | undefined): "stop" | "length" | "tool-calls" | "content-filter" | "error";
/**
 * Append a step's text to a running cross-step accumulator, ignoring empty
 * steps and inserting a single newline between non-empty contributions.
 *
 * The native Gemini loops overwrite per-step text into `lastStepText`, so when
 * the loop is force-terminated by the step cap the intermediate tool-step prose
 * is lost and a canned placeholder becomes the answer. Accumulating here mirrors
 * the Vertex-Claude loop's `aggregatedTurnText += block.text` so the gathered
 * text can be surfaced at the maxSteps-exhaustion exit instead of the placeholder.
 */
export declare function appendStepText(accumulated: string, stepText: string): string;
/**
 * Process stream chunks to extract raw response parts, function calls, and usage metadata.
 *
 * Consumes the full async iterable and returns all collected data.
 */
export declare function collectStreamChunks(stream: AsyncIterable<{
    functionCalls?: NativeFunctionCall[];
    [key: string]: unknown;
}>): Promise<CollectedChunkResult>;
/**
 * Create a push-based text channel that bridges a background producer
 * (the agentic tool-calling loop) with an async-iterable consumer.
 *
 * This enables truly incremental streaming: text parts are yielded to the
 * caller as they arrive from the network, rather than being buffered until
 * the model finishes generating.
 */
export declare function createTextChannel(): TextChannel;
/**
 * Iterate a single stream step incrementally, pushing text parts to `channel`
 * as they arrive from the network while simultaneously accumulating the full
 * `CollectedChunkResult` needed for history and token accounting.
 *
 * Used for all steps (both intermediate tool-calling steps and the final
 * text-only step).  Text parts are pushed to the channel as they arrive,
 * enabling truly incremental streaming.  The complete `rawResponseParts`
 * (including thoughtSignature) are still returned at the end for use by
 * `pushModelResponseToHistory`.
 */
export declare function collectStreamChunksIncremental(stream: AsyncIterable<{
    functionCalls?: NativeFunctionCall[];
    [key: string]: unknown;
}>, channel: TextChannel): Promise<CollectedChunkResult>;
/**
 * Extract the thoughtSignature token from raw response parts.
 * Returns the last thoughtSignature found (each step may produce one).
 */
export declare function extractThoughtSignature(rawResponseParts: unknown[]): string | undefined;
/**
 * Extract text from raw response parts, filtering out non-text parts
 * (thoughtSignature, functionCall) to avoid SDK warnings.
 */
export declare function extractTextFromParts(rawResponseParts: unknown[]): string;
/**
 * Execute a batch of native function calls with retry tracking and permanent failure detection.
 *
 * @param logLabel - Label for log messages (e.g. "[GoogleAIStudio]" or "[GoogleVertex]")
 * @param stepFunctionCalls - The function calls from the model
 * @param executeMap - Map of tool name to execute function
 * @param failedTools - Mutable map tracking per-tool failure counts
 * @param allToolCalls - Mutable array accumulating all tool call records
 * @param options - Optional settings for execution tracking and cancellation,
 *                  plus an `originalNameMap` (Google-safe → consumer-supplied
 *                  identifier) so the sanitization stays transport-only and
 *                  consumers see the names they registered.
 * @returns Array of function responses for conversation history
 */
export declare function executeNativeToolCalls(logLabel: string, stepFunctionCalls: NativeFunctionCall[], executeMap: Map<string, Tool["execute"]>, failedTools: Map<string, {
    count: number;
    lastError: string;
}>, allToolCalls: Array<{
    toolName: string;
    args: Record<string, unknown>;
}>, options?: {
    toolExecutions?: Array<{
        name: string;
        input: Record<string, unknown>;
        output: unknown;
    }>;
    abortSignal?: AbortSignal;
    originalNameMap?: Map<string, string>;
}): Promise<NativeFunctionResponse[]>;
/**
 * Handle maxSteps termination by producing a final answer when the model was
 * still calling tools as the step limit was reached.
 *
 * Resolution order (always returns a non-empty string): the caller's
 * `finalText` if present; else the `lastStepText` gathered from the final step;
 * else a graceful, user-facing cap message from {@link buildToolLoopCapMessage}
 * — which replaced the legacy bracketed step-limit placeholder string.
 *
 * @param logLabel - Label for log messages (e.g. "[GoogleAIStudio]" or "[GoogleVertex]")
 * @returns The resolved final answer text (never the legacy placeholder).
 */
export declare function handleMaxStepsTermination(logLabel: string, step: number, maxSteps: number, finalText: string, lastStepText: string): string;
/**
 * Detect whether an error represents an abort/cancellation (from an
 * AbortSignal firing during a fetch/stream drain). Used by the native
 * Gemini-3 agentic loops to break gracefully rather than re-throw.
 */
export declare function isAbortError(error: unknown): boolean;
/**
 * Build a graceful, user-facing message for when a single agentic turn hits
 * the step cap without producing a final answer. Replaces the legacy
 * bracketed "Tool execution limit reached" placeholder.
 *
 * Emit this ONLY when `step >= maxSteps` genuinely terminated the loop —
 * aborted/timed-out/stalled turns must use {@link buildAbortedTurnMessage},
 * {@link buildTurnTimeoutMessage}, or {@link buildTurnStalledMessage}, never
 * this text (a killed 23-step turn once claimed "reached the 200-step limit").
 */
export declare function buildToolLoopCapMessage(maxSteps: number, toolCallCount: number): string;
/**
 * Honest message for a turn stopped by the in-loop context guard
 * (stopReason "context-cap") without a synthesized answer. Sibling of
 * {@link buildToolLoopCapMessage} — context exits must never claim a step
 * limit was reached.
 */
export declare function buildContextCapMessage(toolCallCount: number): string;
/**
 * Honest message for a turn ended by the `turnTimeoutMs` wall-clock deadline
 * (stopReason "time-limit"). Sibling of {@link buildToolLoopCapMessage} —
 * time exits must never claim a step limit was reached.
 */
export declare function buildTurnTimeoutMessage(elapsedMs: number, toolCallCount: number): string;
/**
 * Honest message for a turn ended by the `stallTimeoutMs` no-progress
 * watchdog (stopReason "stalled") — typically a wedged tool or a hung
 * model call.
 */
export declare function buildTurnStalledMessage(stallTimeoutMs: number, toolCallCount: number): string;
/**
 * Honest message for a caller-aborted turn (admin kill, coding-task
 * short-circuit — stopReason "aborted").
 */
export declare function buildAbortedTurnMessage(toolCallCount: number): string;
/**
 * Wrap-up nudge injected when the remaining turn time drops inside the
 * `wrapupTimeLeadMs` window — the time-budget twin of the soft step-budget
 * nudge. Rides as a trailing text block on the tool-result user turn.
 */
export declare function buildWrapupNudgeText(useFinalResultTool: boolean): string;
/**
 * Resolve the turn's `stopReason` discriminator from the loop's exit
 * bookkeeping. Precedence: time conditions beat the generic abort flag
 * (the deadline/stall watchdogs abort through the same internal controller),
 * abort beats step-cap, and a provider-error finishReason (e.g. persistent
 * MALFORMED_FUNCTION_CALL) beats "completed".
 */
export declare function resolveTurnStopReason(params: {
    timedOut: boolean;
    stalled: boolean;
    wasAborted: boolean;
    /** Step budget ran out AND no clean/forced answer was produced. */
    cappedWithoutAnswer: boolean;
    /** Context guard stopped the loop AND no clean/forced answer was produced. */
    contextCappedWithoutAnswer?: boolean;
    /** The turn's resolved unified finishReason. */
    finishReason?: string;
}): GenerateStopReason;
/**
 * Wall-clock + progress watchdogs for a native agentic turn.
 *
 * - Deadline: `turnTimeoutMs` (caller knob) or `defaultTurnTimeoutMs` (the
 *   loop's pre-existing defensive bound) arms a whole-turn timer.
 * - Stall: when `stallTimeoutMs` is set, a low-frequency interval checks the
 *   time since the last recorded progress (chunk received, tool started or
 *   finished, step started).
 *
 * Both fire `onDeadline(kind)` exactly once each and latch a flag the loop's
 * terminal handling reads to pick the honest exit message and `stopReason`.
 * Timers are unref'd so they never hold the process open; call `dispose()`
 * in the loop's finally.
 */
export declare function createTurnClock(params: {
    /** Explicit whole-turn deadline (ms); wins over defaultTurnTimeoutMs. */
    turnTimeoutMs?: number;
    /** Loop-specific defensive default when turnTimeoutMs is unset (undefined = no deadline). */
    defaultTurnTimeoutMs?: number;
    /** No-progress watchdog (ms); undefined = disabled. */
    stallTimeoutMs?: number;
    /** Wrap-up lead (ms); only honored when turnTimeoutMs is explicitly set. */
    wrapupTimeLeadMs?: number;
    onDeadline: (kind: "timeout" | "stall") => void;
}): {
    readonly timedOut: boolean;
    readonly stalled: boolean;
    /** True when the turn ended on a time condition (deadline or stall). */
    readonly expired: boolean;
    /** The effective whole-turn deadline in ms (explicit or defensive default). */
    readonly turnTimeoutMs: number | undefined;
    elapsedMs(): number;
    /** Record progress: chunk received, tool started/finished, step started. */
    noteProgress(): void;
    /** True when an explicit deadline exists and remaining time is inside the wrap-up lead. */
    shouldNudgeWrapup(): boolean;
    dispose(): void;
};
/**
 * In-loop context guard for native agentic turns.
 *
 * The provider reports the ACTUAL prompt size of every model call
 * (usage.input_tokens + cache reads/writes on Anthropic;
 * usageMetadata.promptTokenCount on Gemini). The guard tracks that number
 * plus an estimate of what the current step appends (assistant output, tool
 * results), and tells the loop to stop calling tools once the projected next
 * prompt crosses `thresholdRatio` of the model's context window — the turn
 * then synthesizes a final answer from what it has instead of stepping into
 * a provider 400 ("prompt is too long") that destroys all completed work.
 *
 * Fail-open: until the first usage report arrives, `shouldStop()` is false.
 */
export declare function createContextGuard(contextWindowTokens: number, thresholdRatio?: number): {
    /** Tokens at which the guard trips (ratio × window). */
    readonly thresholdTokens: number;
    /** Last observed prompt size plus estimated growth since. */
    readonly projectedNextPromptTokens: number;
    /**
     * Record a model call's reported usage. `promptTokens` must be the FULL
     * prompt size (uncached input + cache read + cache creation for
     * Anthropic). The response's own output is counted as growth — it is
     * appended to the conversation for the next call.
     */
    noteUsage(promptTokens: number, outputTokens: number): void;
    /**
     * Add growth for content appended since the last model call (tool
     * results, nudge text) using the ~4 chars/token heuristic.
     */
    noteAppendedChars(chars: number): void;
    /** True when issuing another model call risks crossing the threshold. */
    shouldStop(): boolean;
};
/**
 * Push model response parts to conversation history, preserving thoughtSignature
 * for Gemini 3 multi-turn tool calling.
 */
export declare function pushModelResponseToHistory(currentContents: Array<{
    role: string;
    parts: unknown[];
}>, rawResponseParts: unknown[], stepFunctionCalls: NativeFunctionCall[]): void;
/**
 * Convert a Zod schema (or AI SDK `jsonSchema()` wrapper) into the shape
 * `@google/genai` accepts as `responseSchema`. Mirrors the inline pipeline
 * the Vertex Gemini paths already use:
 *
 *   convertZodToJsonSchema → inlineJsonSchema → strip `$schema` → ensure
 *   every nested schema has a `type` (Vertex/Gemini reject schemas missing
 *   that field, even on nested objects).
 *
 * Lives here so the AI Studio and Vertex paths can share the same
 * sanitization without duplicating the schema-conversion churn.
 */
export declare function buildGeminiResponseSchema(schema: unknown): Record<string, unknown>;
/**
 * Map NeuroLink ChatMessage[] history into the @google/genai content format
 * and push the entries onto a contents array.
 *
 * Used by the native Vertex Gemini and Google AI Studio paths to honor
 * `options.conversationMessages` so multi-turn conversations (memory, loop
 * REPL, agent flows) actually carry prior turns into the request.
 *
 * Behavior notes:
 *  - Only `user` and `assistant` roles are forwarded; system messages are
 *    expected to be wired via `systemInstruction`, and tool-call /
 *    tool-result roles only appear inside intra-call tool loops which build
 *    their own model/function entries.
 *  - String content is wrapped as a single `{ text }` part. Empty strings
 *    are skipped to avoid sending empty parts that some Gemini regions
 *    reject.
 *  - The current user input should be appended AFTER calling this helper
 *    so the prior turns appear first in chronological order.
 */
export declare function prependConversationMessages(contents: Array<{
    role: string;
    parts: unknown[];
}>, conversationMessages?: Array<ChatMessage | MinimalChatMessage>): void;
/**
 * Build the `parts` array for the current user turn of a Gemini native
 * `generateContent` request, including inline image + PDF blobs.
 *
 * Both providers that hit the native `@google/genai` SDK — `GoogleVertex`
 * and `GoogleAIStudio` — need this. The previous AI Studio code only
 * pushed a single `{ text }` part, which silently dropped `input.images`
 * and `input.pdfFiles` on the floor: the model received text only and
 * legitimately reported "no image attached". Extracting this from the
 * Vertex copy keeps both providers on one definition.
 *
 * Accepted shapes per element (mirroring the runtime behaviour the Vertex
 * code already supported):
 *   - `Buffer` → used as-is
 *   - local file path → read via `readFileSync`, MIME guessed from extension
 *   - `data:<mime>;base64,...` URL → mime parsed, data base64-decoded
 *   - `http(s)://...` URL → fetched, mime from `content-type`
 *   - any other string → assumed to be a base64-encoded payload
 *
 * Image MIME guessing is conservative — only known extensions override the
 * default `image/jpeg`. Fetch failures are logged and the offending entry
 * is skipped rather than aborting the entire request, matching prior
 * Vertex behaviour.
 */
export declare function buildUserPartsWithMultimodal(input: GeminiMultimodalInput | undefined, textOverride?: string, logPrefix?: string): Promise<VertexNativePart[]>;
