UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

489 lines (488 loc) • 19.2 kB
/** * Codex (OpenAI ChatGPT subscription) proxy types. * * Codex authenticates with a ChatGPT account over OAuth and talks to the * ChatGPT backend Responses API (https://chatgpt.com/backend-api/codex). * These types describe the on-disk `~/.codex/auth.json` shape we import from, * the OAuth refresh contract, and the usage/rate-limit payloads we normalise * into the shared AccountQuota model. * * Naming: all exported names carry the `Codex` prefix (rule 9). Codex quota is * stored through the same AccountQuota shape as Anthropic — its primary window * maps onto the session fields and its secondary window onto the weekly fields. */ import type { AccountCoolingReason, AccountQuota, ClaudeContentBlock, ClaudeTool, InternalResult } from "./proxy.js"; /** Token block inside `~/.codex/auth.json`. */ export type CodexAuthFileTokens = { id_token?: string; access_token: string; refresh_token?: string; account_id?: string; }; /** Shape of `~/.codex/auth.json` written by the Codex CLI. */ export type CodexAuthFile = { auth_mode?: string; OPENAI_API_KEY?: string | null; tokens?: CodexAuthFileTokens; last_refresh?: string; }; /** Result of importing a Codex credential (from auth.json or the OAuth flow). */ export type CodexImportedCredential = { accessToken: string; refreshToken?: string; idToken?: string; /** ChatGPT account id (from auth.json or decoded from the access token). */ accountId?: string; /** Epoch ms when the access token expires (decoded from the JWT `exp`). */ expiresAt?: number; /** ChatGPT plan type decoded from the token, for display only. */ planType?: string; /** Account email decoded from the id token, for the account label. */ email?: string; }; /** Raw OpenAI OAuth token endpoint response. */ export type CodexTokenResponse = { access_token?: string; refresh_token?: string; id_token?: string; token_type?: string; expires_in?: number; }; /** One rate-limit window as reported by the Codex backend (primary/secondary). */ export type CodexRateLimitWindow = { used_percent?: number | null; window_minutes?: number | null; resets_in_seconds?: number | null; /** Seconds until reset. Observed alias of `resets_in_seconds` on some * responses; accepted defensively so a cooldown lands on the real reset * instead of degrading to the transient ceiling. */ reset_after?: number | null; resets_at?: number | null; /** Current WHAM usage fields. */ reset_after_seconds?: number | null; reset_at?: number | null; }; /** Codex rate-limit block: a primary (short) and secondary (long) window. */ export type CodexRateLimits = { allowed?: boolean; limit_reached?: boolean; primary?: CodexRateLimitWindow | null; secondary?: CodexRateLimitWindow | null; }; /** Loose shape of the Codex usage endpoint response. */ export type CodexUsageResponse = { /** Legacy Codex usage payload. */ rate_limits?: CodexRateLimits | null; /** Current ChatGPT WHAM account-usage payload. */ rate_limit?: { allowed?: boolean; limit_reached?: boolean; primary_window?: CodexRateLimitWindow | null; secondary_window?: CodexRateLimitWindow | null; } | null; plan_type?: string | null; }; /** Result of a single Codex usage fetch. */ export type CodexUsageFetchResult = { ok: true; quota: AccountQuota; } | { ok: false; reason: "not_oauth" | "auth" | "rate_limited" | "http" | "network" | "parse"; }; /** Explicit plan-exhaustion evidence from a Codex error response. */ export type CodexQuotaError = { errorCode: string; resetAt: number; scope: "session" | "weekly" | "unknown"; }; /** A Codex account with its runtime cooldown/quota state hydrated from disk. */ export type CodexRuntimeAccount = { key: string; label: string; token: string; refreshToken?: string; expiresAt?: number; accountId?: string; quota?: AccountQuota; coolingUntil?: number; coolingReason?: AccountCoolingReason; /** A persisted cooldown whose window has already passed. Present only when the * account is therefore eligible again, so the success path can delete the * spent record — nothing else ever reaps it. */ expiredCooldownUntil?: number; }; /** Provider-qualified account identity used by proxy status rendering. */ export type CodexProxyStatusAccountIdentity = { provider: "anthropic"; key: string; } | { provider: "codex"; key: string; } | { provider: "vertex"; key: string; } | { provider: "other"; key: null; }; /** A text or image content part accepted by the Codex Responses backend. */ export type CodexContentPart = { type: "input_text"; text: string; } | { type: "output_text"; text: string; } | { type: "input_image"; image_url: string; }; /** A single item in a Codex Responses request. */ export type CodexResponsesInputItem = { role: "user" | "assistant"; content: CodexContentPart[]; } | { type: "function_call"; call_id: string; name: string; arguments: string; } | { type: "function_call_output"; call_id: string; output: string; }; /** Codex reasoning settings; supported levels depend on the selected model. */ export type CodexReasoningEffort = "none" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"; /** Request shape used to bridge Anthropic Messages traffic to Codex Responses. */ export type CodexResponsesRequest = { model: string; input: CodexResponsesInputItem[]; stream: true; store: false; /** Pins one conversation to the cache that already holds its prefix. */ prompt_cache_key?: string; reasoning?: { effort: CodexReasoningEffort; }; instructions?: string; tools?: Array<{ type: "function"; name: string; description?: string; parameters: Record<string, unknown>; }>; tool_choice?: "auto" | "required" | "none" | { type: "function"; name: string; }; }; /** Fully buffered Codex result rendered back as an Anthropic response. */ export type CodexFallbackResult = { text: string; toolCalls: NonNullable<InternalResult["toolCalls"]>; usage?: NonNullable<InternalResult["usage"]> & { /** Numeric serializer compatibility must not imply provider observation. */ inputTokensObserved?: boolean; outputTokensObserved?: boolean; cacheReadTokensObserved?: boolean; cacheCreationTokensObserved?: boolean; }; finishReason: "end_turn" | "tool_use"; }; /** Incremental Claude frames and explicit upstream cancellation. */ export type CodexFallbackStream = { frames: AsyncGenerator<string, CodexFallbackResult>; cancel: (reason?: unknown) => Promise<void>; }; /** Role a native Codex `input` message item can carry. */ export type CodexNativeRole = "developer" | "user" | "assistant"; /** A plain message item in a native Codex request's `input` array. */ export type CodexNativeMessageInputItem = { type: "message"; id?: string; role: CodexNativeRole; content: CodexContentPart[]; }; /** A tool declaration backed by a JSON Schema, dispatched as a function call. */ export type CodexNativeFunctionToolDeclaration = { type: "function"; name: string; description?: string; strict: boolean; parameters: Record<string, unknown>; }; /** Grammar constraining a custom tool's argument text. */ export type CodexNativeCustomToolFormat = { type: "grammar"; syntax: "lark" | "regex"; definition: string; }; /** A tool declaration whose arguments are grammar-constrained free text. */ export type CodexNativeCustomToolDeclaration = { type: "custom"; name: string; description?: string; format: CodexNativeCustomToolFormat; }; /** A single tool declaration, discriminated by its own `type` field. */ export type CodexNativeToolDeclaration = CodexNativeFunctionToolDeclaration | CodexNativeCustomToolDeclaration; /** * A declared tool's own kind, independent of its namespace. Claude's * `tool_use` block carries only `name` + `input`, with no such discriminator, * so response serialization threads a name -> kind map (built while flattening * `additional_tools` on the request side) to decide whether a tool_use's * `input` is a real JSON-Schema-shaped object (`function`) or the single * grammar-constrained string a custom tool was wrapped into (`custom`). */ export type CodexNativeToolKind = "function" | "custom"; /** * One declared tool's mapping outcome inside `codexOutboundFallback.ts`: * the translated Claude tool, its kind (for `toolKindByName`), and any * degrade reasons collected while flattening its schema (fed to * `recordCodexOutboundSchemaDegraded` in `proxyTracer.ts`). */ export type CodexOutboundToolMappingResult = { tool: ClaudeTool; kind: CodexNativeToolKind; reasons: string[]; }; /** A named group of tool declarations inside one `additional_tools` item. */ export type CodexNativeToolNamespace = { type: "namespace"; name: string; description: string; tools: CodexNativeToolDeclaration[]; }; /** The `input` item carrying every tool declaration for a native Codex request. */ export type CodexNativeAdditionalToolsInputItem = { type: "additional_tools"; id?: string; role: "developer"; tools: CodexNativeToolNamespace[]; }; /** A model-emitted tool call replayed in a native Codex request's history. */ export type CodexNativeFunctionCallInputItem = { type: "function_call"; call_id: string; name: string; arguments: string; }; /** A client-supplied tool result replayed in a native Codex request's history. */ export type CodexNativeFunctionCallOutputInputItem = { type: "function_call_output"; call_id: string; output: string; }; /** A call to a grammar-constrained (`type:"custom"`) tool, replayed in history. */ export type CodexNativeCustomToolCallInputItem = { type: "custom_tool_call"; id?: string; call_id: string; name: string; input: string; }; /** A client-supplied result for a `custom_tool_call`, replayed in history. */ export type CodexNativeCustomToolCallOutputInputItem = { type: "custom_tool_call_output"; call_id: string; output: string; }; /** * A reasoning item the client echoes back with `store:false`. Its content is * opaque (`encrypted_content` is readable only by the OpenAI backend), so its * shape is deliberately loose beyond the discriminator. */ export type CodexNativeReasoningInputItem = { type: "reasoning"; id?: string; encrypted_content?: string | null; summary?: unknown[]; }; /** A single item in a native Codex request's `input` array. */ export type CodexNativeInputItem = CodexNativeMessageInputItem | CodexNativeAdditionalToolsInputItem | CodexNativeFunctionCallInputItem | CodexNativeFunctionCallOutputInputItem | CodexNativeCustomToolCallInputItem | CodexNativeCustomToolCallOutputInputItem | CodexNativeReasoningInputItem; /** How a native Codex request constrains which tool the model may call. */ export type CodexNativeToolChoice = "auto" | "required" | "none" | { type: "function"; name: string; }; /** * A native Codex Responses request, as sent by the real Codex CLI. * * No top-level `session_id`/`thread_id` — real traffic carries neither at * top level, only inside `client_metadata`, which the existing * `Record<string, unknown>` field already covers. */ export type CodexNativeRequest = { model: string; input: CodexNativeInputItem[]; stream: boolean; store: boolean; tool_choice?: CodexNativeToolChoice; parallel_tool_calls?: boolean; reasoning?: { effort: CodexReasoningEffort; context?: string; }; include?: string[]; text?: { verbosity?: string; }; prompt_cache_key?: string; client_metadata?: Record<string, unknown>; }; /** * A `parseCodexNativeRequest`/`translateCodexRequestToClaude` failure. Never thrown. * `UNTRANSLATABLE_REQUEST` is a request Codex itself accepts but Anthropic would * reject, as opposed to one that is invalid on the Codex wire. * `REQUEST_TOO_LARGE` is a tool-call history over `streamLimits.ts`'s count or * size ceiling; callers map it to HTTP 413 `request_too_large`. */ export type CodexTranslationError = { code: "MALFORMED_REQUEST"; message: string; } | { code: "SUSPECTED_PARTIAL_HISTORY"; message: string; } | { code: "UNTRANSLATABLE_REQUEST"; message: string; } | { code: "REQUEST_TOO_LARGE"; message: string; }; /** One coalesced run of same-role content while flattening a native Codex `input` array. */ export type CodexOutboundMessageGroup = { role: "user" | "assistant"; blocks: ClaudeContentBlock[]; }; /** Lifecycle status of one item inside a Codex Responses `output[]` array. */ export type CodexResponseItemStatus = "in_progress" | "completed" | "incomplete"; /** Wire usage block inside a synthesized response.completed / non-stream response. */ export type CodexResponseUsage = { input_tokens: number; output_tokens: number; total_tokens: number; /** Omitted entirely when no cache field was observed on the Anthropic side. */ input_tokens_details?: { cached_tokens?: number; cache_write_tokens?: number; /** The 1-hour-TTL share of `cache_write_tokens` (a subset, not additive); * sourced from `ClaudeUsage.cacheCreation1hTokens`. Omitted when not observed. */ cache_write_1h_tokens?: number; }; /** Always omitted — ClaudeUsage carries no reasoning-token count to source it from. */ output_tokens_details?: { reasoning_tokens?: number; }; }; /** One `output_text` content part inside a Codex `message` output item. */ export type CodexResponseOutputTextPart = { type: "output_text"; text: string; annotations: []; }; /** A synthesized assistant-text item in a Codex `response.output[]` array. */ export type CodexResponseMessageItem = { id: string; type: "message"; role: "assistant"; status: CodexResponseItemStatus; content: CodexResponseOutputTextPart[]; }; /** A synthesized tool-call item in a Codex `response.output[]` array. */ export type CodexResponseFunctionCallItem = { id: string; type: "function_call"; status: CodexResponseItemStatus; call_id: string; name: string; /** Concatenation of raw partial_json fragments. Valid JSON when status is * "completed"; may be a truncated, non-JSON fragment when status is * "incomplete" (closed early by a mid-stream failure). */ arguments: string; }; /** * A synthesized call to a tool the request declared `custom` (grammar- * constrained), in the item shape the real backend emits for one — the shape * `codexUsage.ts` reads on the native route and a Codex CLI replays in its * history. `input` is the bare grammar text, never JSON. */ export type CodexResponseCustomToolCallItem = { id: string; type: "custom_tool_call"; status: CodexResponseItemStatus; call_id: string; name: string; /** Empty when status is "incomplete" and the wrapped input never parsed. */ input: string; }; /** One item in a Codex `response.output[]` array, discriminated by `type`. */ export type CodexResponseItem = CodexResponseMessageItem | CodexResponseFunctionCallItem | CodexResponseCustomToolCallItem; /** A complete Codex Responses envelope — the non-streaming body, and the shape * carried inside a terminal `response.*` SSE event's `response` field. */ export type CodexResponseEnvelope = { id: string; object: "response"; created_at: number; status: "completed" | "incomplete" | "failed"; model: string; output: CodexResponseItem[]; usage?: CodexResponseUsage; incomplete_details: { reason: "max_output_tokens"; } | null; error: { code: string; message: string; } | null; }; /** The Codex Responses SSE event-type vocabulary this codec emits. */ export type CodexResponseSSEEventType = "response.created" | "response.in_progress" | "response.output_item.added" | "response.content_part.added" | "response.output_text.delta" | "response.output_text.done" | "response.content_part.done" | "response.function_call_arguments.delta" | "response.function_call_arguments.done" | "response.custom_tool_call_input.delta" | "response.custom_tool_call_input.done" | "response.output_item.done" | "response.completed" | "response.incomplete" | "response.failed"; /** Incremental Codex-shape SSE frames and the terminal envelope they resolve to. */ export type CodexResponseStream = { frames: AsyncGenerator<string, CodexResponseEnvelope>; cancel: (reason?: unknown) => Promise<void>; }; /** Which output item kind (if any) `CodexResponsesStreamSerializer` currently has open. */ export type CodexResponseOpenItemKind = "message" | "function_call" | "custom_tool_call" | null; /** The four call sites in `codexProxyRoutes.ts`'s `dispatch()` that can trigger * an outbound fallback attempt (stage-c-trigger.md §0's corrected line map). */ export type CodexOutboundFailureClass = "no_accounts" | "pool_exhausted" | "non_retryable_transport" | "loop_fallthrough"; /** What a single insertion point in `codexProxyRoutes.ts` observed. */ export type CodexOutboundFailureInput = { failureClass: CodexOutboundFailureClass; /** Present only for `non_retryable_transport`: the transport error code * (e.g. `ECONNREFUSED`) that was NOT in the retryable allow-list. */ transportErrorCode?: string; /** Present only for `loop_fallthrough`: the raw HTTP status the last * exhausted account attempt ended on (`lastErrorStatus` at the account-loop * call site). A 401/403 there is a Codex credential failure and stays * eligible; any other 4xx except 429 rejects the request itself. */ lastStatus?: number; /** Present only for `loop_fallthrough`: the error code the last exhausted * account attempt reported, when it reported one. A code * `classifyProxyFailureCode` marks non-retryable (content policy, invalid * request) makes the failure ineligible whatever the status. */ lastErrorCode?: string; }; /** `classifyCodexOutboundFailure`'s verdict: whether this failure is eligible * to attempt an outbound fallback, and why (for tracing/logging only — the * caller still applies its own config/loop-prevention/depth gates). */ export type CodexOutboundFallbackDecision = { eligible: boolean; reason: string; }; /** Outcome of one `attemptCodexOutboundFallback` call. `not_attempted` covers * every gate failure and every target exhausting without success — the * caller falls through to its own existing (unchanged) error response in * every one of those cases, so the flag-off / all-targets-failed paths stay * byte-identical to today. */ export type CodexOutboundFallbackOutcome = { kind: "not_attempted"; } | { kind: "request_too_large"; message: string; } | { kind: "success"; response: Response; };