import type { ResponseInput } from "openai/resources/responses/responses"; import type { CacheRetention, OpenAIResponsesHistoryPayload, ProviderPayload } from "./types"; export { isRecord } from "@sayknow-cli/utils"; export declare function normalizeSystemPrompts(systemPrompt: readonly string[] | string | undefined | null): string[]; export declare function sanitizeJsonStrings(value: unknown): unknown; export declare function toNumber(value: unknown): number | undefined; export declare function toPositiveNumber(value: unknown, fallback: number): number; export declare function toBoolean(value: unknown): boolean | undefined; export declare function normalizeToolCallId(id: string): string; type ResponsesToolItemIdPrefix = "fc" | "ctc"; export declare function normalizeResponsesToolCallId(id: string, itemPrefix?: ResponsesToolItemIdPrefix): { callId: string; itemId: string; }; /** * Truncate an OpenAI Responses API item ID to 64 characters. * IDs exceeding the limit are replaced with a hash-based ID using the given prefix. */ export declare function truncateResponseItemId(id: string, prefix: string): string; export declare function sanitizeOpenAIResponsesHistoryItemsForReplay(items: Array>): ResponseInput; /** * Neutralize leaked OpenAI Harmony / control tokens (`<|channel|>`, `<|message|>`, * `<|call|>`, `<|constrain|>`, `<|recipient|>`, `<|content|>`, ...) in replayed * history text. A subagent whose tool-call channel degenerates can dump raw * control-token scaffolding into its reply text; once that poisoned text lands in * history the Codex / Responses endpoint rejects every subsequent request with * `Request blocked (code=invalid_prompt)`, permanently wedging the session because * the offending item is re-sent on each turn. Insert a zero-width space after `<` * so the delimiter can no longer be tokenized as a reserved control token while the * text stays human-readable. * * The pattern matches the two control-token shapes only, so ordinary text and pipe * syntax is left untouched: * - simple form `<|ident|>` — a leading run of identifier chars then `|>`; and * - header form `<|role to=recipient|>` — a known Harmony role * (`system`/`developer`/`user`/`assistant`/`tool`) followed by a single * recipient assignment `to=` whose value is an unbounded run of * non-delimiter, non-whitespace chars (so long MCP/custom tool recipients like * `to=functions.` are covered). * The header branch is deliberately scoped to the known role + `to=` recipient * grammar rather than an arbitrary `key=value`, so request-boundary sanitization * never rewrites non-control delimiter text such as `<|foo bar=baz|>`. A single-line * body (no `\n`) and the required leading identifier char also leave compact * pipe/operator syntax alone — e.g. F# `value <| f |> g` (space after `<|`), * `sum<|a+b|>c` (punctuation body), and `<|foo bar|>` (no assignment) never match. * The simple branch is a strict superset of the original identifier-only pattern: * every marker the old regex caught still matches. */ export declare function neutralizeReservedControlTokens(text: string): string; /** * Shape-tolerant classifier for the poisoned-history rejection that wedges * gpt-5.6 sessions: `Request blocked (code=invalid_prompt)`. Accepts a raw * provider error, an assistant message, or any object carrying a * `providerCode` / `transportFailure` / `errorMessage` field, and returns true * when the failure is the deterministic `invalid_prompt` content fault rather * than a transient upstream error. This is the single shared contract the * provider transports and the session-level circuit breaker key on so the * classification is explicit (not inferred from a catch-all bucket) and * uniformly testable across transports. */ export declare function isInvalidPromptError(input: unknown): boolean; /** * Neutralize leaked reserved control tokens across every string in an outgoing * Responses `input` array. This is the request-boundary complement to the * replay-history sanitizer: leaked Harmony markers (`<|channel|>analysis`, ...) * can enter the payload from assistant reasoning summaries, live-converted * message/tool-output text, or user-authored content — not just replayed * history — and every gpt-5.6 request that carries one is rejected with * `Request blocked (code=invalid_prompt)`. Walking every string (rather than an * item-type allowlist) guarantees no leak source is missed as item shapes * evolve; the zero-width-space insertion is idempotent (`<\u200b|` no longer * matches `<|`) and keeps the text human-readable. */ export declare function neutralizeResponsesInputControlTokens(items: readonly T[]): T[]; export declare function createOpenAIResponsesHistoryPayload(provider: string, items: Array>, incremental?: boolean): OpenAIResponsesHistoryPayload; export declare function getOpenAIResponsesHistoryPayload(providerPayload: ProviderPayload | undefined, currentProvider: string, fallbackProvider?: string): OpenAIResponsesHistoryPayload | undefined; export declare function getOpenAIResponsesHistoryItems(providerPayload: ProviderPayload | undefined, currentProvider: string, fallbackProvider?: string): Array> | undefined; /** * Resolve cache retention preference. * * Resolution order: explicit request value → `SKC_CACHE_RETENTION` → * legacy `PI_CACHE_RETENTION` → `fallback`. Both env vars act as explicit * opt-in (`"long"`) or opt-out (any other value) so a provider-specific * `fallback` only applies when nothing else is configured. `fallback` * defaults to `"short"` to preserve the historical behaviour for callers * that don't pass one. */ export declare function resolveCacheRetention(cacheRetention?: CacheRetention, fallback?: CacheRetention): CacheRetention; export declare function isAnthropicOAuthToken(key: string): boolean;