@caeliq/llms 1.0.71 → 1.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/cjs/server.cjs +235 -233
  2. package/dist/cjs/server.cjs.map +4 -4
  3. package/dist/cursor-sdk/inbound-session.d.ts +21 -0
  4. package/dist/cursor-sdk/model-selection.d.ts +23 -0
  5. package/dist/cursor-sdk/session.d.ts +16 -5
  6. package/dist/cursor-sdk/shared.d.ts +10 -1
  7. package/dist/cursor-sdk/turn-output.d.ts +2 -0
  8. package/dist/esm/server.mjs +239 -237
  9. package/dist/esm/server.mjs.map +4 -4
  10. package/dist/routing/protocol-endpoints.d.ts +33 -0
  11. package/dist/services/provider.d.ts +8 -2
  12. package/dist/session-registry.d.ts +30 -3
  13. package/dist/tests/anthropic.third-party-tool-names.d.ts +1 -0
  14. package/dist/tests/codex.bootstrap-buffer.d.ts +1 -0
  15. package/dist/tests/codex.model-catalog.d.ts +1 -0
  16. package/dist/tests/cursor-sdk.inbound-session.d.ts +1 -0
  17. package/dist/tests/cursor-sdk.interrupt-reentry.d.ts +1 -1
  18. package/dist/tests/cursor-sdk.model-remint.d.ts +1 -0
  19. package/dist/tests/cursor-sdk.model-selection.d.ts +1 -0
  20. package/dist/tests/cursor-sdk.on-delta-thinking.d.ts +1 -1
  21. package/dist/tests/cursor-sdk.runner-recovery.d.ts +1 -1
  22. package/dist/tests/cursor-sdk.scratch-report.d.ts +1 -1
  23. package/dist/tests/cursor-sdk.turn-coordination.d.ts +1 -1
  24. package/dist/tests/fallback.request-context.routes.d.ts +1 -0
  25. package/dist/tests/path-alias.build.d.ts +1 -0
  26. package/dist/tests/request-scoped-errors.d.ts +1 -0
  27. package/dist/tests/request-scoped-errors.routes.d.ts +1 -0
  28. package/dist/tests/responses.multi-agent.routes.d.ts +1 -0
  29. package/dist/tests/responses.orphan-delegation.d.ts +1 -0
  30. package/dist/tests/support/isolate-session-registry.d.ts +1 -0
  31. package/dist/tests/web-search.cross-protocol.d.ts +1 -0
  32. package/dist/transformer/claude-auth.transformer.d.ts +11 -2
  33. package/dist/transformer/codex.transformer.d.ts +18 -0
  34. package/dist/types/llm.d.ts +39 -2
  35. package/dist/utils/anthropic-client-policy.d.ts +8 -0
  36. package/dist/utils/claude-billing.d.ts +20 -5
  37. package/dist/utils/codex-bootstrap.d.ts +68 -0
  38. package/dist/utils/codex-model-catalog.d.ts +63 -0
  39. package/dist/utils/nested-agent.d.ts +4 -0
  40. package/dist/utils/openai.responses.util.d.ts +56 -1
  41. package/dist/utils/reasoning-effort.d.ts +3 -20
  42. package/dist/utils/request-scoped-errors.d.ts +92 -0
  43. package/package.json +3 -3
@@ -10,6 +10,33 @@ export interface AnthropicSourceRequestFields {
10
10
  thinking?: Record<string, unknown>;
11
11
  outputConfig?: Record<string, unknown>;
12
12
  stopSequences?: string[];
13
+ /**
14
+ * Anthropic-defined (typed) tools such as `web_search_20250305` or
15
+ * `bash_20250124`, exactly as the client sent them. Unified `tools[]` keeps
16
+ * their function projection (by name) for routing and other providers.
17
+ */
18
+ tools?: Record<string, any>[];
19
+ /**
20
+ * Server tool blocks (`server_tool_use`, `web_search_tool_result`) of each
21
+ * assistant turn, indexed by assistant-turn ordinal, with that turn's
22
+ * joined text so the builder only re-attaches them to the same turn.
23
+ */
24
+ assistantServerBlocks?: Array<{
25
+ text: string;
26
+ blocks: any[];
27
+ } | undefined>;
28
+ }
29
+ /** Client asked for provider-hosted web search (any inbound protocol). */
30
+ export interface HostedWebSearchRequest {
31
+ allowedDomains?: string[];
32
+ /** Per-request search cap from `WEB_SEARCH_MAX_USES` (none by default). */
33
+ maxUses?: number;
34
+ userLocation?: {
35
+ city?: string;
36
+ region?: string;
37
+ country?: string;
38
+ timezone?: string;
39
+ };
13
40
  }
14
41
  export interface ClientProtocolContext {
15
42
  protocol: ClientProtocol;
@@ -25,6 +52,12 @@ export interface ClientProtocolContext {
25
52
  scenarioType?: RouterScenarioType;
26
53
  /** Source-only Anthropic semantics retained before destination routing. */
27
54
  anthropicSource?: AnthropicSourceRequestFields;
55
+ /**
56
+ * Hosted web search requested by an Anthropic `web_search_*` tool, a
57
+ * Responses `web_search` tool or Chat `web_search_options`; Unified carries
58
+ * the `web_search` function projection.
59
+ */
60
+ hostedWebSearch?: HostedWebSearchRequest;
28
61
  /** Client fingerprint captured before Anthropic normalization. */
29
62
  anthropicClientKind?: AnthropicClientKind;
30
63
  /** In-scope Anthropic destination/auth variant selected after routing. */
@@ -1,4 +1,4 @@
1
- import { LLMProvider, RegisterProviderRequest, ModelRoute, RequestRouteInfo } from "../types/llm";
1
+ import { LLMProvider, RegisterProviderRequest, ModelRoute, ProviderUpdate, RequestRouteInfo } from "../types/llm";
2
2
  import { ConfigService } from "./config";
3
3
  import { TransformerService } from "./transformer";
4
4
  export declare class ProviderService {
@@ -12,10 +12,16 @@ export declare class ProviderService {
12
12
  private initializeFromProvidersArray;
13
13
  /** Match TransformerService: instances used in provider use[] need the service logger. */
14
14
  private attachTransformerLogger;
15
+ /**
16
+ * Store request-scoped error rules under the canonical key only, so an
17
+ * update under any spelling cannot be shadowed by a stale alias. Rules are
18
+ * validated here to surface config mistakes at registration time.
19
+ */
20
+ private withCanonicalScopedErrorRules;
15
21
  registerProvider(request: RegisterProviderRequest): LLMProvider;
16
22
  getProviders(): LLMProvider[];
17
23
  getProvider(name: string): LLMProvider | undefined;
18
- updateProvider(id: string, updates: Partial<LLMProvider>): LLMProvider | null;
24
+ updateProvider(id: string, updates: ProviderUpdate): LLMProvider | null;
19
25
  deleteProvider(id: string): boolean;
20
26
  toggleProvider(name: string, _enabled: boolean): boolean;
21
27
  resolveModelRoute(modelName: string): RequestRouteInfo | null;
@@ -1,26 +1,42 @@
1
1
  /**
2
2
  * Persistent registry for every session identity CCR mints.
3
3
  *
4
- * Two families, one mechanism — the stable lookup key already exists in each
4
+ * Several families, one mechanism — the stable lookup key already exists in each
5
5
  * case (Zen conversation id, cursor buildSessionKey hash); only the minted
6
6
  * value used to live in process memory and died on restart:
7
7
  *
8
8
  * - "zen": conversationKey -> x-opencode-session (ses_…)
9
9
  * - "cursor": sessionKey -> { agentId, workspaceDir, model }
10
+ * - "cursor-inbound": hashed protocol conversation key -> internal Cursor id
11
+ * (cursor-sdk/inbound-session.ts; written on mint, claim, new user text,
12
+ * and at most hourly as a TTL refresh)
10
13
  *
11
14
  * The file lives under CCR_HOME (the mounted ~/.claude-code-router volume),
12
15
  * so bindings survive both restarts and image rebuilds. Plain JSON: values
13
16
  * are short strings, not blobs (unlike cursor-opencode-provider's pb.gz,
14
17
  * which persists raw Cursor protocol state we never see — the SDK owns that).
15
18
  *
16
- * Synchronous API, tiny file (capped entries), persistence on mint/delete
17
- * only — never on the hot read path.
19
+ * Synchronous API, tiny file (entries capped per family), persistence on
20
+ * writes only — never on the read path.
18
21
  */
19
22
  export type PersistedSession = {
20
23
  /** The fixed CCR-minted id (ses_… for zen; SDK agentId for cursor). */
21
24
  sessionId: string;
22
25
  workspaceDir?: string;
23
26
  model?: string;
27
+ /** `id|sorted params` from cursorModelFingerprint. Missing means pre-variant binding. */
28
+ modelFingerprint?: string;
29
+ /**
30
+ * Cursor inbound opening row: a follow-up turn claimed it. A repeated
31
+ * opening then needs a fresh id.
32
+ */
33
+ progressed?: boolean;
34
+ /**
35
+ * Cursor inbound opening row that replaced an unclaimed opening of an
36
+ * identical earlier conversation. A follow-up cannot tell the two apart,
37
+ * so it gets a fresh agent instead of claiming this one.
38
+ */
39
+ contested?: boolean;
24
40
  updatedAt: number;
25
41
  };
26
42
  export declare const SESSION_REGISTRY_TTL_MS: number;
@@ -28,6 +44,17 @@ export declare const SESSION_REGISTRY_TTL_MS: number;
28
44
  export declare function getPersistedSession(family: string, key: string, now?: number): PersistedSession | undefined;
29
45
  /** Record a newly minted fixed id. Overwrites any prior binding. */
30
46
  export declare function putPersistedSession(family: string, key: string, value: Omit<PersistedSession, "updatedAt">, now?: number): PersistedSession;
47
+ /**
48
+ * Apply several writes to one family with a single file rewrite: `put`
49
+ * entries are stored (overwriting), then `remove` keys are dropped.
50
+ */
51
+ export declare function updatePersistedSessions(family: string, changes: {
52
+ put?: Array<{
53
+ key: string;
54
+ value: Omit<PersistedSession, "updatedAt">;
55
+ }>;
56
+ remove?: string[];
57
+ }, now?: number): void;
31
58
  /** Forget a binding (session retire, Zen bucket re-roll). */
32
59
  export declare function deletePersistedSession(family: string, key: string): void;
33
60
  /** Drop expired bindings; returns the number pruned. */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ import "./support/isolate-session-registry";
@@ -1 +1 @@
1
- export {};
1
+ import "./support/isolate-session-registry";
@@ -0,0 +1 @@
1
+ import "./support/isolate-session-registry";
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- export {};
1
+ import "./support/isolate-session-registry";
@@ -1 +1 @@
1
- export {};
1
+ import "./support/isolate-session-registry";
@@ -1 +1 @@
1
- export {};
1
+ import "./support/isolate-session-registry";
@@ -1 +1 @@
1
- export {};
1
+ import "./support/isolate-session-registry";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -5,6 +5,14 @@ import { ClaudeModelCatalogEntry } from "../utils/claude-model-catalog";
5
5
  /** Anthropic beta required for Claude subscription / Claude Code OAuth Bearer auth. */
6
6
  export declare const CLAUDE_OAUTH_REQUIRED_BETA = "oauth-2025-04-20";
7
7
  export declare function mergeAnthropicBetaValues(...values: Array<string | undefined | null>): string;
8
+ /**
9
+ * Beta tokens a third-party client sent for its Anthropic-defined computer use
10
+ * tool (e.g. `computer-use-2025-11-24` for `computer_20251124`). The emulated
11
+ * profile otherwise ignores client betas; the client already chose the one
12
+ * matching its tool version, so only that family is carried over, and only
13
+ * when the request actually declares a `computer_*` tool.
14
+ */
15
+ export declare function clientComputerUseBetas(clientBeta: string | undefined, typedTools: Array<Record<string, any>> | undefined): string | undefined;
8
16
  /** Read a named header value from a Fastify/Node headers object (case-insensitive). */
9
17
  export declare function readHeaderValue(headers: Record<string, unknown> | undefined, name: string): string | undefined;
10
18
  /** True when the client's User-Agent identifies it as the genuine Claude Code CLI. */
@@ -67,8 +75,9 @@ export declare class ClaudeAuthTransformer implements Transformer {
67
75
  /**
68
76
  * Body/URL/wire-format conversion belong to AnthropicTransformer's
69
77
  * provider pair, which already ran (response-side order is reversed, so
70
- * it runs before this stage). This stage only inspects the resulting
71
- * response for subscription-specific overage observability.
78
+ * it runs before this stage). This stage inspects the resulting response
79
+ * for subscription-specific overage observability and restores tool names
80
+ * its own legacy branch renamed.
72
81
  */
73
82
  transformResponseOut(response: Response, context?: TransformerContext): Promise<Response>;
74
83
  /**
@@ -1,4 +1,5 @@
1
1
  import { Transformer } from "../types/transformer";
2
+ import { type CodexBootstrapOptions } from "../utils/codex-bootstrap";
2
3
  /**
3
4
  * ChatGPT/Codex backend auth + Responses-wire constraints.
4
5
  *
@@ -8,10 +9,26 @@ import { Transformer } from "../types/transformer";
8
9
  * this transformer only stamps auth, Codex headers, `store: false`, and
9
10
  * `stream: true`.
10
11
  */
12
+ export interface CodexTransformerOptions extends CodexBootstrapOptions {
13
+ streamBootstrapMaxFrames?: number;
14
+ streamBootstrapMaxBytes?: number;
15
+ streamBootstrapTimeoutMs?: number;
16
+ /**
17
+ * Hold pre-generation SSE frames uncommitted so an in-stream
18
+ * `server_is_overloaded` / quota rejection throws a fallback-eligible
19
+ * error *before* downstream headers commit, instead of reaching the
20
+ * client as a failed stream. Default false (opt-in).
21
+ */
22
+ streamBootstrapBuffering?: boolean;
23
+ }
11
24
  export declare class CodexTransformer implements Transformer {
25
+ static TransformerName: string;
12
26
  name: string;
13
27
  requestPhase: "headers";
14
28
  logger?: any;
29
+ private readonly bootstrapOptions;
30
+ constructor(options?: CodexTransformerOptions);
31
+ private get bootstrapEnabled();
15
32
  private streamIntent;
16
33
  transformRequestIn(request: any, provider: any, context?: any): Promise<Record<string, any>>;
17
34
  auth(request: any, provider: any): Promise<any>;
@@ -29,6 +46,7 @@ export declare class CodexTransformer implements Transformer {
29
46
  req?: {
30
47
  id?: string;
31
48
  };
49
+ signal?: AbortSignal;
32
50
  }): Promise<Response>;
33
51
  private normalizeCodexTransport;
34
52
  private ensureSseContentType;
@@ -6,6 +6,7 @@ import type { ChatCompletionTool } from "openai/resources/chat/completions";
6
6
  import type { Tool as AnthropicTool } from "@anthropic-ai/sdk/resources/messages";
7
7
  import { Transformer } from "./transformer";
8
8
  import type { ProviderTokenizerConfig } from "./tokenizer";
9
+ import type { RequestScopedErrorRule } from "../utils/request-scoped-errors";
9
10
  export type TransformerConfigEntry = string | [string, Record<string, any>?];
10
11
  export interface UrlCitation {
11
12
  url: string;
@@ -18,6 +19,16 @@ export interface Annotation {
18
19
  type: "url_citation";
19
20
  url_citation?: UrlCitation;
20
21
  }
22
+ /**
23
+ * A provider-executed web search (e.g. Anthropic's server `web_search`),
24
+ * surfaced so clients can render the call itself (Responses `web_search_call`).
25
+ * Internal: Chat Completions clients never receive it.
26
+ */
27
+ export interface WebSearchCall {
28
+ id: string;
29
+ query: string;
30
+ status: "completed" | "failed";
31
+ }
21
32
  export interface TextContent {
22
33
  type: "text";
23
34
  text: string;
@@ -137,6 +148,12 @@ export interface UnifiedChatRequest {
137
148
  anthropic_output_config?: Record<string, any>;
138
149
  anthropic_metadata?: Record<string, any>;
139
150
  anthropic_stop_sequences?: string[];
151
+ /**
152
+ * Anthropic-defined (typed) tools for legacy direct claude-auth callers,
153
+ * like the other anthropic_* fields. Routed requests carry them in
154
+ * `protocolContext.anthropicSource.tools` so no other provider sees them.
155
+ */
156
+ anthropic_tools?: Record<string, any>[];
140
157
  reasoning_effort?: string;
141
158
  /**
142
159
  * Responses opaque `include` list (e.g. `reasoning.encrypted_content`).
@@ -168,6 +185,7 @@ export interface UnifiedChatResponse {
168
185
  };
169
186
  }>;
170
187
  annotations?: Annotation[];
188
+ web_search_calls?: WebSearchCall[];
171
189
  }
172
190
  export interface StreamChunk {
173
191
  id: string;
@@ -195,6 +213,7 @@ export interface StreamChunk {
195
213
  thought_signature?: string;
196
214
  }>;
197
215
  annotations?: Annotation[];
216
+ web_search_calls?: WebSearchCall[];
198
217
  };
199
218
  finish_reason?: string | null;
200
219
  }>;
@@ -241,6 +260,12 @@ export interface LLMProvider {
241
260
  models: string[];
242
261
  /** Optional GCP / Antigravity project id (provider-specific). */
243
262
  project_id?: string;
263
+ /**
264
+ * Request-scoped error rules for this provider (CLIProxyAPI port).
265
+ * Evaluated before global rules; first match decides stop vs continue.
266
+ * The provider service stores rules only under this canonical key.
267
+ */
268
+ request_scoped_errors?: RequestScopedErrorRule[];
244
269
  transformer?: {
245
270
  [key: string]: {
246
271
  use?: Transformer[];
@@ -250,7 +275,18 @@ export interface LLMProvider {
250
275
  passthrough?: any;
251
276
  };
252
277
  }
253
- export type RegisterProviderRequest = LLMProvider;
278
+ /**
279
+ * Accepted input spellings of the request-scoped error rule list. The
280
+ * provider service folds them into `request_scoped_errors`.
281
+ */
282
+ export interface RequestScopedErrorsCarrier {
283
+ request_scoped_errors?: RequestScopedErrorRule[];
284
+ requestScopedErrors?: RequestScopedErrorRule[];
285
+ "request-scoped-errors"?: RequestScopedErrorRule[];
286
+ }
287
+ export type RegisterProviderRequest = LLMProvider & RequestScopedErrorsCarrier;
288
+ /** Provider update payload; any rule-list spelling replaces the stored rules. */
289
+ export type ProviderUpdate = Partial<LLMProvider> & RequestScopedErrorsCarrier;
254
290
  export interface ModelRoute {
255
291
  provider: string;
256
292
  model: string;
@@ -261,7 +297,8 @@ export interface RequestRouteInfo {
261
297
  originalModel: string;
262
298
  targetModel: string;
263
299
  }
264
- export interface ConfigProvider {
300
+ /** Request-scoped error rules use any RequestScopedErrorsCarrier spelling. */
301
+ export interface ConfigProvider extends RequestScopedErrorsCarrier {
265
302
  name: string;
266
303
  api_base_url: string;
267
304
  api_key: string;
@@ -1,4 +1,5 @@
1
1
  import { UnifiedChatRequest } from "../types/llm";
2
+ import type { AnthropicSourceRequestFields, HostedWebSearchRequest } from "../routing/protocol-endpoints";
2
3
  export type AnthropicClientKind = "claude_desktop" | "claude_code" | "other";
3
4
  export type AnthropicProviderMode = "api_key" | "claude_oauth" | "out_of_scope";
4
5
  export interface AnthropicClientFingerprintSignals {
@@ -20,6 +21,8 @@ export interface AnthropicClientPolicyContext {
20
21
  anthropicPolicyApplied?: boolean;
21
22
  anthropicSystemTransformed?: boolean;
22
23
  claudeAuthToolNameMap?: Map<string, string>;
24
+ anthropicSource?: AnthropicSourceRequestFields;
25
+ hostedWebSearch?: HostedWebSearchRequest;
23
26
  }
24
27
  export declare function readHeaderValue(headers: Record<string, unknown> | undefined, name: string): string | undefined;
25
28
  /**
@@ -55,3 +58,8 @@ export declare function applyNativeClaudeOAuthCacheTtl(body: any, context: Anthr
55
58
  * before the provider's Anthropic body builder runs.
56
59
  */
57
60
  export declare function applyThirdPartyAnthropicPolicy(request: UnifiedChatRequest, context: AnthropicClientPolicyContext, configService: any): Promise<void>;
61
+ /**
62
+ * Names of Anthropic-defined tools in this request: typed tools sent by an
63
+ * Anthropic client, and hosted web search requested over another protocol.
64
+ */
65
+ export declare function anthropicFixedToolNames(context: AnthropicClientPolicyContext): string[];
@@ -71,8 +71,23 @@ export declare function prefixClaudeToolName(name: string): string;
71
71
  /** Restore a Claude Code tool name to the caller's original spelling. */
72
72
  export declare function unprefixClaudeToolName(name: string): string;
73
73
  /** Rewrite tool names in a Unified request in place for the OAuth wire path. */
74
- export declare function prefixClaudeToolNames(request: UnifiedChatRequest, nameMap?: Map<string, string>): void;
75
- /** Rewrite tool names in a Unified/OpenAI-shaped response in place. */
76
- export declare function unprefixClaudeToolNames(value: any, nameMap?: Map<string, string>): void;
77
- /** Rewrite one OpenAI SSE data payload's tool names. */
78
- export declare function unprefixClaudeToolNamesInSseData(value: any, nameMap?: Map<string, string>): void;
74
+ export declare function prefixClaudeToolNames(request: UnifiedChatRequest, nameMap?: Map<string, string>, fixedNames?: Iterable<string>): void;
75
+ /**
76
+ * Rewrite tool names in a response payload in place and report whether any
77
+ * name changed. Accepts both shapes a response takes after the provider leg:
78
+ * Unified/OpenAI (`choices[].message|delta.tool_calls`) and Anthropic
79
+ * Messages wire (`content[]` tool_use blocks of a message, or the
80
+ * `content_block_start` stream event that carries a tool_use block's name).
81
+ *
82
+ * With a request-local `nameMap`, only names CCR itself prefixed are restored,
83
+ * which keeps the operation idempotent: a caller's own `mcp__server__tool`
84
+ * name is never stripped a second time. Without one, fall back to the
85
+ * spelling heuristic.
86
+ */
87
+ export declare function unprefixClaudeToolNames(value: any, nameMap?: Map<string, string>): boolean;
88
+ /**
89
+ * Restore CCR-prefixed tool names in a JSON or SSE response. SSE events and
90
+ * JSON bodies without a renamed tool are forwarded byte-identical, so
91
+ * exact-protocol responses keep the provider's usage and framing untouched.
92
+ */
93
+ export declare function restoreClaudeToolNamesInResponse(response: Response, nameMap: Map<string, string>, logger?: any): Promise<Response>;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Codex stream bootstrap buffering (CLIProxyAPI `stream-bootstrap-buffering` port).
3
+ *
4
+ * The ChatGPT backend smuggles overload/quota rejections *inside* an HTTP 200
5
+ * SSE stream — right after the handshake events — instead of returning a
6
+ * retryable status on the wire. Once downstream headers are committed that
7
+ * failure can only be delivered to the client; holding the pre-generation
8
+ * bootstrap frames uncommitted keeps the window open for a transparent
9
+ * fallback to the next model.
10
+ *
11
+ * Held (never released on their own): handshake frames
12
+ * (`response.created`, `response.in_progress`, `response.queued`), `*.added`
13
+ * announcements, empty `output_text.delta` heartbeats, SSE comments, `event:`
14
+ * lines and blank separators. Anything else — text deltas, tool-call deltas,
15
+ * `*.done` / `*.completed` / `*.failed` / `error` frames, or an unparsable
16
+ * `data:` line — releases the buffer immediately. The hold is bounded by a
17
+ * frame budget and a byte budget, never by nothing.
18
+ */
19
+ export interface CodexBootstrapOptions {
20
+ /** Max held `data:` lines before release. Default 48. */
21
+ maxFrames?: number;
22
+ /** Max held bytes before release. Default 1 MiB. */
23
+ maxBytes?: number;
24
+ /**
25
+ * Max hold time in ms before release. Default 0 (unlimited — the budget
26
+ * bounds what is held, not how long). Evaluated between reads; a peer that
27
+ * stops mid-line is still bounded by the request context, not this timer.
28
+ */
29
+ timeoutMs?: number;
30
+ }
31
+ /**
32
+ * Capacity failure classes, as Codex CLI distinguishes them: the server is
33
+ * overloaded (503), the request rate is limited (429), or the account's quota
34
+ * or plan does not cover it (429). Each is worth another model; a request
35
+ * failure is not.
36
+ */
37
+ export type CodexOverloadKind = "overload" | "rate_limit" | "quota";
38
+ export type CodexBootstrapRelease = "generated" | "budget" | "timeout" | "ended" | "overload";
39
+ export interface CodexBootstrapResult {
40
+ /** Replayable response: held bytes followed by the live remainder. */
41
+ response: Response;
42
+ overloaded: boolean;
43
+ overloadKind?: CodexOverloadKind;
44
+ /** Raw bootstrap text that carried the overload signal (truncated). */
45
+ overloadText?: string;
46
+ /** Structured error fields retained for internal request-scoped matching. */
47
+ overloadErrorText?: string;
48
+ /** Retry-After value (seconds or HTTP date) the failed event advised. */
49
+ overloadRetryAfter?: string;
50
+ heldBytes: number;
51
+ heldFrames: number;
52
+ releasedBy: CodexBootstrapRelease;
53
+ }
54
+ export declare const DEFAULT_BOOTSTRAP_MAX_FRAMES = 48;
55
+ export declare const DEFAULT_BOOTSTRAP_MAX_BYTES: number;
56
+ /** Capacity class of a structured Responses error; `message` is ignored. */
57
+ export declare function classifyCodexError(error: {
58
+ code?: unknown;
59
+ type?: unknown;
60
+ message?: unknown;
61
+ }): CodexOverloadKind | undefined;
62
+ /**
63
+ * Buffer the Codex SSE bootstrap. Never throws on upstream content: overload
64
+ * is reported on the result so the caller can raise a fallback-eligible error
65
+ * *before* downstream headers commit. The returned response replays held
66
+ * bytes first, then the untouched remainder of the original stream.
67
+ */
68
+ export declare function bufferCodexBootstrapStream(response: Response, options?: CodexBootstrapOptions, signal?: AbortSignal): Promise<CodexBootstrapResult>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * OpenAI model metadata imported from Codex CLI's bundled catalog
3
+ * (codex-rs `models-manager/models.json`), so CCR sends what Codex sends:
4
+ *
5
+ * - reasoning effort: only the model's supported levels. `ultra` is a picker
6
+ * alias and never reaches the wire (`ModelInfo::resolve_reasoning_effort`).
7
+ * - Responses Lite: the request shape Codex uses for `use_responses_lite`
8
+ * models (`core/src/client.rs::build_responses_request`).
9
+ * - verbosity: only models with `support_verbosity`.
10
+ *
11
+ * Refresh the table when Codex adds or retires catalog models.
12
+ */
13
+ import { type ReasoningSummaryLevel } from "./reasoning-effort";
14
+ import type { ThinkLevel } from "../types/llm";
15
+ export type CodexTextVerbosity = "low" | "medium" | "high";
16
+ export interface CodexModelSpec {
17
+ /** `supported_reasoning_levels`, lowest first. */
18
+ efforts: readonly ThinkLevel[];
19
+ /** `multi_agent_reasoning_effort`: what `ultra` resolves to, when set. */
20
+ ultraEffort?: ThinkLevel;
21
+ /** `use_responses_lite`. */
22
+ responsesLite: boolean;
23
+ /** `support_verbosity`. */
24
+ verbosity: boolean;
25
+ }
26
+ /**
27
+ * GPT-6 Luna slugs (`gpt-6-luna`, `gpt-6.1-luna`, `openai/gpt-6-luna`,
28
+ * `codex,gpt-6.1-luna`). Anchored so `gpt-6-sol` / `gpt-6-astra` /
29
+ * `gpt-5.6-luna` do not match.
30
+ */
31
+ export declare function isGpt6LunaModel(model: unknown): boolean;
32
+ /**
33
+ * Catalog metadata for a model id, or undefined for models Codex does not
34
+ * list. GPT-6 slugs newer than the table get the family's shape (Luna tops
35
+ * out at `max`), so a new minor release keeps working before a refresh.
36
+ */
37
+ export declare function codexModelSpec(model: unknown): CodexModelSpec | undefined;
38
+ /**
39
+ * The effort a catalog model is sent. `ultra` follows Codex: the model's
40
+ * multi-agent effort, else `max`, else the highest level below `ultra`. Any
41
+ * other unsupported level moves to the nearest end of the supported range
42
+ * (`none` / `minimal` → the lowest). Unknown tokens and unlisted models pass
43
+ * through unchanged.
44
+ */
45
+ export declare function resolveCodexReasoningEffort(model: unknown, effort: ThinkLevel | undefined): ThinkLevel | undefined;
46
+ /**
47
+ * Apply `resolveCodexReasoningEffort` to a Responses/Unified request in
48
+ * place. Covers convert (`openai-responses`) and same-protocol wire-keep
49
+ * (`codex`). A disabled-reasoning request raised to a supported level is
50
+ * re-enabled, since the model cannot run without reasoning.
51
+ */
52
+ export declare function applyCodexReasoningEffort(request: {
53
+ model?: unknown;
54
+ reasoning?: {
55
+ effort?: unknown;
56
+ enabled?: boolean;
57
+ } | null;
58
+ }): void;
59
+ /**
60
+ * `text.verbosity` implied by `REASONING_AUTO_SUMMARY`: detailed thinking
61
+ * pairs with verbose answers, concise with terse ones.
62
+ */
63
+ export declare function verbosityForReasoningSummary(summary: ReasoningSummaryLevel | undefined): CodexTextVerbosity | undefined;
@@ -2,6 +2,8 @@
2
2
  export declare function firstUserText(request: unknown): string;
3
3
  export declare function isHarnessUserNoise(text: string): boolean;
4
4
  export declare function userMessageTextParts(content: unknown): string[];
5
+ /** Substantive user turns, in order. Reminders and caveats are not identity. */
6
+ export declare function substantiveUserTexts(request: unknown): string[];
5
7
  /**
6
8
  * First user text that distinguishes a worker transcript.
7
9
  * Shared reminder/caveat preambles are skipped so parallel Tasks do not collide.
@@ -9,6 +11,8 @@ export declare function userMessageTextParts(content: unknown): string[];
9
11
  export declare function firstSubstantiveUserText(request: unknown): string;
10
12
  /** Statusline / spinner polls — must not supersede or become a cache baseline. */
11
13
  export declare function isStatuslinePollTurn(request: unknown): boolean;
14
+ /** Explicit worker-fork boundary, including inside inherited history. */
15
+ export declare function isForkOpeningText(text: string): boolean;
12
16
  /**
13
17
  * Nested/worker agent on any inbound protocol.
14
18
  *