@yanlinglabs/winter-provider-runtime 0.0.2

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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +41 -0
  3. package/README.md +109 -0
  4. package/dist/adapters/anthropic/console-oauth.d.ts +101 -0
  5. package/dist/adapters/anthropic/index.d.ts +4 -0
  6. package/dist/adapters/anthropic/messages.d.ts +76 -0
  7. package/dist/adapters/bedrock/converse.d.ts +143 -0
  8. package/dist/adapters/bedrock/crc32.d.ts +9 -0
  9. package/dist/adapters/bedrock/credentials.d.ts +32 -0
  10. package/dist/adapters/bedrock/eventstream.d.ts +65 -0
  11. package/dist/adapters/bedrock/index.d.ts +8 -0
  12. package/dist/adapters/bedrock/sigv4.d.ts +119 -0
  13. package/dist/adapters/bedrock/testing.d.ts +46 -0
  14. package/dist/adapters/content-blocks.d.ts +7 -0
  15. package/dist/adapters/google/adc.d.ts +35 -0
  16. package/dist/adapters/google/generate-content.d.ts +136 -0
  17. package/dist/adapters/google/index.d.ts +8 -0
  18. package/dist/adapters/google/jwt-rs256.d.ts +36 -0
  19. package/dist/adapters/google/vertex.d.ts +15 -0
  20. package/dist/adapters/index.d.ts +34 -0
  21. package/dist/adapters/oauth/device-code.d.ts +32 -0
  22. package/dist/adapters/oauth/refresh.d.ts +40 -0
  23. package/dist/adapters/openai/azure.d.ts +38 -0
  24. package/dist/adapters/openai/chat-completions.d.ts +86 -0
  25. package/dist/adapters/openai/codex-config.d.ts +42 -0
  26. package/dist/adapters/openai/codex-oauth.d.ts +47 -0
  27. package/dist/adapters/openai/index.d.ts +20 -0
  28. package/dist/adapters/openai/local.d.ts +16 -0
  29. package/dist/adapters/openai/pkce.d.ts +111 -0
  30. package/dist/adapters/openai/quota.d.ts +99 -0
  31. package/dist/adapters/openai/responses.d.ts +142 -0
  32. package/dist/adapters/openai/shared.d.ts +359 -0
  33. package/dist/adapters/openai/testing.d.ts +59 -0
  34. package/dist/adapters/openai/xai-derived-shapes.d.ts +67 -0
  35. package/dist/adapters/openai/xai-oauth.d.ts +102 -0
  36. package/dist/adapters/openai/xai-oauth.testing.d.ts +62 -0
  37. package/dist/adapters/privileged-headers.d.ts +51 -0
  38. package/dist/adapters/refusals.d.ts +10 -0
  39. package/dist/address-classifier.d.ts +17 -0
  40. package/dist/bun-required.d.ts +54 -0
  41. package/dist/continuity/decoration.d.ts +89 -0
  42. package/dist/continuity/domains.d.ts +92 -0
  43. package/dist/continuity/fixtures.d.ts +44 -0
  44. package/dist/continuity/handoff.d.ts +94 -0
  45. package/dist/continuity/index.d.ts +10 -0
  46. package/dist/continuity/renderer.d.ts +111 -0
  47. package/dist/continuity/warnings.d.ts +46 -0
  48. package/dist/credentials/env.d.ts +6 -0
  49. package/dist/credentials/file.d.ts +21 -0
  50. package/dist/credentials/memory.d.ts +8 -0
  51. package/dist/credentials/types.d.ts +38 -0
  52. package/dist/discovery.d.ts +15 -0
  53. package/dist/endpoint-policy.d.ts +127 -0
  54. package/dist/errors.d.ts +68 -0
  55. package/dist/http.d.ts +29 -0
  56. package/dist/identity.d.ts +62 -0
  57. package/dist/index-5z94gxhk.js +43790 -0
  58. package/dist/index.d.ts +39 -0
  59. package/dist/index.js +3194 -0
  60. package/dist/registry.d.ts +136 -0
  61. package/dist/retry.d.ts +38 -0
  62. package/dist/sse.d.ts +12 -0
  63. package/dist/testing.d.ts +19 -0
  64. package/dist/testing.js +432 -0
  65. package/dist/types.d.ts +376 -0
  66. package/package.json +49 -0
@@ -0,0 +1,42 @@
1
+ export declare const CODEX: {
2
+ /** OAuth application id — shared by all Codex CLI clients. */
3
+ readonly clientId: "app_EMoamEEZ73f0CkXaXp7hrann";
4
+ /** Authorization endpoint: `{issuer}/oauth/authorize`. */
5
+ readonly authorizeUrl: "https://auth.openai.com/oauth/authorize";
6
+ /** Token exchange + refresh endpoint. */
7
+ readonly tokenUrl: "https://auth.openai.com/oauth/token";
8
+ /** Local callback port (primary). */
9
+ readonly callbackPort: 1455;
10
+ /** The fallback port, per codex-rs's own allow-list. A login refuses rather than drifting onto an arbitrary port. */
11
+ readonly fallbackCallbackPort: 1457;
12
+ /** OAuth scopes — codex-rs's own scope string from `build_authorize_url`. */
13
+ readonly scope: "openid profile email offline_access api.connectors.read api.connectors.invoke";
14
+ /** Base URL for the ChatGPT Codex backend; `/responses` is appended for a turn. */
15
+ readonly backendUrl: "https://chatgpt.com/backend-api/codex";
16
+ readonly headers: Record<string, string>;
17
+ };
18
+ /** The DEFAULT `originator` value, exported so a fixture asserts on it by name rather than by string literal. */
19
+ export declare const CODEX_ORIGINATOR: string;
20
+ /**
21
+ * The date `CODEX_MODELS` was last checked against the live `/models` catalogue, carried from the
22
+ * port. It lives next to the data it dates so the two cannot drift apart.
23
+ */
24
+ export declare const CODEX_MODELS_VERIFIED = "2026-07-31";
25
+ /**
26
+ * The static Codex model set.
27
+ *
28
+ * The backend returns only these slugs for ChatGPT-account auth; other OpenAI model ids answer
29
+ * HTTP 400 ("not supported when using Codex with a ChatGPT account"), so this is an ALLOW-LIST in
30
+ * effect and not merely a convenience. `contextWindow` is 272000 for every model the catalogue
31
+ * offers — a number the port records as having been WRONG once (372000, hand-transcribed), which
32
+ * put an auto-compaction threshold above the backend's own hard ceiling and killed compaction
33
+ * silently. Re-derive it from the live catalogue; never edit it by hand.
34
+ */
35
+ export declare const CODEX_MODELS: ReadonlyArray<{
36
+ id: string;
37
+ contextWindow: number;
38
+ supportsVision: boolean;
39
+ }>;
40
+ export declare const DEFAULT_CODEX_MODEL = "gpt-5.6-sol";
41
+ /** The Keychain account a codex credential occupies: `codex-oauth:<accountId>` (R6-10's one-record-per-provider/account rule). */
42
+ export declare function codexCredentialAccount(accountId: string): string;
@@ -0,0 +1,47 @@
1
+ import type { CredentialRef, CredentialStore, ProviderAdapter } from "../../types.js";
2
+ import { QuotaManager } from "./quota.js";
3
+ import { type OpenAiAdapterOptions } from "./shared.js";
4
+ export interface CodexAdapterOptions extends OpenAiAdapterOptions {
5
+ /** The token endpoint. Injectable for a fixture; the production value is `CODEX.tokenUrl`. */
6
+ tokenUrl?: string;
7
+ /** Shared across turns so the subscription state survives one. A fixture may inject its own clock. */
8
+ quota?: QuotaManager;
9
+ }
10
+ /** How a codex credential is stored: one `oauth` material under `codex-oauth:<accountId>`. */
11
+ export declare function codexCredentialRef(accountId: string, service?: string): Extract<CredentialRef, {
12
+ kind: "keychain";
13
+ }>;
14
+ export interface CodexLoginOptions {
15
+ openUrl: (url: string) => Promise<void>;
16
+ /** Overridden by a fixture; production uses `CODEX`'s own values. */
17
+ authorizeUrl?: string;
18
+ tokenUrl?: string;
19
+ callbackPort?: number;
20
+ fallbackCallbackPort?: number;
21
+ timeoutMs?: number;
22
+ /** The Keychain service the record lands in — `config.keychainService` from the host. */
23
+ service?: string;
24
+ onAuthStatus?: (status: {
25
+ isAuthenticating: boolean;
26
+ output?: string[];
27
+ error?: string;
28
+ }) => void;
29
+ }
30
+ export interface CodexLoginResult {
31
+ ref: Extract<CredentialRef, {
32
+ kind: "keychain";
33
+ }>;
34
+ accountId: string;
35
+ expiresAt: number;
36
+ }
37
+ /**
38
+ * The host-invoked login. Runs the PKCE loopback flow and PERSISTS the result through the
39
+ * credential store, returning the ref a session should be configured with.
40
+ *
41
+ * An account id is required to name the record: without one there is no per-account slot, and R6-10
42
+ * is explicit that a credential occupies one record per provider/account rather than a shared global
43
+ * slot. A token exchange that returned no id token is therefore a refusal, not a fallback to some
44
+ * default name.
45
+ */
46
+ export declare function startCodexLogin(store: CredentialStore, options: CodexLoginOptions): Promise<CodexLoginResult>;
47
+ export declare function createCodexOauthAdapter(options: CodexAdapterOptions): ProviderAdapter;
@@ -0,0 +1,20 @@
1
+ export { createResponsesAdapter, buildResponsesBody, mapResponsesInput, mapResponsesTools, responsesTurn, streamResponsesTurn, privilegedHeaders, ResponsesStreamMapper, OPENAI_API_BASE_URL } from "./responses.js";
2
+ export type { ResponsesTurnPlan } from "./responses.js";
3
+ export { createChatCompletionsAdapter, buildChatBody, mapChatMessages, mapChatTools, chatTurn, deepSeekProfile, openRouterProfile, ChatStreamMapper, DEEPSEEK_BASE_URL, OPENAI_CHAT_BASE_URL, OPENROUTER_BASE_URL, } from "./chat-completions.js";
4
+ export type { ChatTurnOptions, ExposedReasoningItem } from "./chat-completions.js";
5
+ export { createCodexOauthAdapter, codexCredentialRef, startCodexLogin } from "./codex-oauth.js";
6
+ export type { CodexAdapterOptions, CodexLoginOptions, CodexLoginResult } from "./codex-oauth.js";
7
+ export { CODEX, CODEX_MODELS, CODEX_MODELS_VERIFIED, CODEX_ORIGINATOR, DEFAULT_CODEX_MODEL, codexCredentialAccount } from "./codex-config.js";
8
+ export { QuotaManager, quotaEvent } from "./quota.js";
9
+ export type { QuotaState } from "./quota.js";
10
+ export { base64Url, buildAuthorizeUrl, decodeAccountId, generatePkce, refreshTokens, runLoginFlow } from "./pkce.js";
11
+ export type { LoginConfig, OAuthTokens } from "./pkce.js";
12
+ export { XAI_CONSENT_DISCLOSURE, XAI_OAUTH, XAI_OAUTH_ADAPTER_ID, createXaiOauthAdapter, startXaiLogin, xaiCredentialRef } from "./xai-oauth.js";
13
+ export type { XaiLoginOptions, XaiLoginResult } from "./xai-oauth.js";
14
+ export { DERIVED_XAI, DERIVED_XAI_COMMIT, DERIVED_XAI_MODELS } from "./xai-derived-shapes.js";
15
+ export { createLocalOpenAIAdapter } from "./local.js";
16
+ export type { LocalAdapterOptions } from "./local.js";
17
+ export { createAzureOpenAIAdapter, azureProfile, azureRouting, azureTurnUrl, AZURE_PREVIEW_API_VERSION } from "./azure.js";
18
+ export type { AzureAdapterOptions } from "./azure.js";
19
+ export { EFFORT_LADDER, EventQueue, assertRepresentableTools, assertWithinLimits, buildHeaders, capabilitiesFrom, capabilityRefusal, fetchOpenAiModels, mapEffortAgainst, pumpEvents, resolveEndpoint, resolveReasoning, snapNumericEffort, } from "./shared.js";
20
+ export type { AuthStyle, DescriptorLookup, OpenAiAdapterOptions, ReasoningPlan, ResolvedEndpoint } from "./shared.js";
@@ -0,0 +1,16 @@
1
+ import type { ProviderAdapter } from "../../types.js";
2
+ import { type ChatTurnOptions } from "./chat-completions.js";
3
+ export interface LocalAdapterOptions extends ChatTurnOptions {
4
+ /** Which surface this server speaks. Almost every local server is `chat`; a few expose Responses. */
5
+ surface?: "chat" | "responses";
6
+ /**
7
+ * The registered adapter id.
8
+ *
9
+ * Overridable because the catalog's twelve local providers currently name
10
+ * `winter.openai-chat-completions` as their `adapterId`, and a registry resolves an adapter BY
11
+ * THAT ID. A host that wants the `/api/tags` fallback registers this adapter under whichever id
12
+ * its catalog rows actually point at.
13
+ */
14
+ id?: string;
15
+ }
16
+ export declare function createLocalOpenAIAdapter(options: LocalAdapterOptions): ProviderAdapter;
@@ -0,0 +1,111 @@
1
+ export interface OAuthTokens {
2
+ accessToken: string;
3
+ refreshToken?: string;
4
+ idToken?: string;
5
+ accountId?: string;
6
+ /** Epoch milliseconds. */
7
+ expiresAt: number;
8
+ }
9
+ export interface LoginConfig {
10
+ clientId: string;
11
+ authorizeUrl: string;
12
+ tokenUrl: string;
13
+ /** 1455 in production (codex-rs parity); `0` gives an ephemeral port, which is what a fixture uses. */
14
+ callbackPort: number;
15
+ /** Tried when `callbackPort` is already taken. Omitted for an ephemeral port. */
16
+ fallbackCallbackPort?: number;
17
+ /**
18
+ * The loopback callback's PATH, which is part of what a registered `redirect_uri` matches.
19
+ *
20
+ * P6.5 lane A2: this was a hard-coded `/auth/callback` when codex was the only flow, and the
21
+ * Anthropic Console client registers `/callback` instead (derived-shapes-p6b.md §2.2). Optional
22
+ * and defaulted, so codex's own value is unchanged and no existing caller sees a difference.
23
+ */
24
+ callbackPath?: string;
25
+ /**
26
+ * Names the flow in this file's user-facing failures ("the … login timed out").
27
+ *
28
+ * Defaulted to codex for the same reason: those strings were written when there was one flow, and
29
+ * an Anthropic user told their sign-in failed because "the codex login timed out" would be reading
30
+ * a message about a product they are not using.
31
+ */
32
+ label?: string;
33
+ /**
34
+ * P7a pre-publish (N1): the EXPORTED function a caller invoked, for `BunRequiredError.functionName`.
35
+ *
36
+ * Separate from `label` on purpose, because the two answer different questions and round 3
37
+ * conflated them. `label` is USER-FACING PROSE ("the Anthropic Console login timed out") and its
38
+ * own doc above reserves it for that; `functionName` is a DEVELOPER-FACING identifier that must be
39
+ * the symbol in the caller's code (`bun-required.ts`: "never the internal helper that reaches for
40
+ * Bun"). Round 3 fixed the identifier by writing it into `label`, which made a codex user read
41
+ * "the startCodexLogin login timed out" -- a function name where a product name goes.
42
+ *
43
+ * Absent -> the guard names `runLoginFlow`, which is honest for a direct caller of this helper.
44
+ */
45
+ functionName?: string;
46
+ /**
47
+ * The token exchange's body encoding. Defaults to `"form"`.
48
+ *
49
+ * P6.5 ruling R-A2-1 — see `adapters/oauth/refresh.ts` for the whole reasoning. Short version:
50
+ * RFC 6749 §4.1.3 requires a token endpoint to accept form encoding, but the Anthropic Console
51
+ * endpoint is only ever OBSERVED receiving JSON, and guessing wrong there breaks the login
52
+ * outright. The default keeps codex byte-identical.
53
+ */
54
+ bodyEncoding?: "form" | "json";
55
+ /**
56
+ * Whether the authorization-code grant carries `state`. Defaults to `false`.
57
+ *
58
+ * `state` is NOT an RFC 6749 token-request parameter — it is the authorize-request nonce echoed
59
+ * back on the redirect, and this flow has already compared it before accepting the code. The
60
+ * Anthropic Console client sends it on the grant anyway, so Winter matches that endpoint's observed
61
+ * request; every other flow must not, because sending a parameter a vendor never sees from its own
62
+ * client is a gratuitous difference on the one request whose failure mode is "your login is
63
+ * broken". It was briefly unconditional and the controller reverted it: codex's token request is
64
+ * byte-identical to what it was before this lane, and a fixture pins the exact body to keep it so.
65
+ */
66
+ includeStateInTokenRequest?: boolean;
67
+ scope: string;
68
+ timeoutMs?: number;
69
+ /** Opens the browser. HOST-supplied: the SDK never shells out to a browser itself. */
70
+ openUrl: (url: string) => Promise<void>;
71
+ onAuthStatus?: (status: {
72
+ isAuthenticating: boolean;
73
+ output?: string[];
74
+ error?: string;
75
+ }) => void;
76
+ }
77
+ /** base64url over raw bytes, no padding. Hand-rolled because `btoa` needs a binary string and the padding has to go. */
78
+ export declare function base64Url(bytes: Uint8Array): string;
79
+ /** A PKCE verifier/challenge pair. 48 random bytes is 64 base64url characters — inside RFC 7636's 43-128 range. */
80
+ export declare function generatePkce(): Promise<{
81
+ verifier: string;
82
+ challenge: string;
83
+ }>;
84
+ /**
85
+ * Reads the ChatGPT account id out of an id token WITHOUT verifying it.
86
+ *
87
+ * That is safe for exactly one reason, and only that one: the value is used as a LOCATOR — the
88
+ * Keychain account name and a request header — never as an authorization decision. The token came
89
+ * from a TLS-authenticated token endpoint over a PKCE-bound exchange; nothing here treats a claim
90
+ * in it as a permission.
91
+ */
92
+ export declare function decodeAccountId(idToken: string): string | undefined;
93
+ export declare function refreshTokens(tokenUrl: string, clientId: string, refreshToken: string): Promise<OAuthTokens>;
94
+ /**
95
+ * Runs the loopback authorization-code flow.
96
+ *
97
+ * The callback server binds `127.0.0.1` ONLY: an authorization code is a single-use credential, and
98
+ * a server on `0.0.0.0` would make it reachable from the LAN for as long as the login is open.
99
+ * `state` is compared before the code is accepted, which is what makes a CSRF-planted callback fail
100
+ * instead of completing someone else's login into this process.
101
+ */
102
+ export declare function runLoginFlow(cfg: LoginConfig): Promise<OAuthTokens>;
103
+ /** The authorize URL a login would open, for a fixture that wants to drive the callback without a browser. */
104
+ export declare function buildAuthorizeUrl(cfg: {
105
+ authorizeUrl: string;
106
+ clientId: string;
107
+ redirectUri: string;
108
+ scope: string;
109
+ state: string;
110
+ challenge: string;
111
+ }): string;
@@ -0,0 +1,99 @@
1
+ import type { ProviderEvent } from "../../types.js";
2
+ /**
3
+ * `resumeAt` is OPTIONAL, and that is the whole of finding I1's fix.
4
+ *
5
+ * A 429 without a `Retry-After` header is a limit whose WINDOW IS UNKNOWN — the account is refused,
6
+ * and nothing has said when it resumes. Modelling that as `resumeAt: <some number>` forced the
7
+ * caller to invent one (the previous code handed it Winter's own jittered backoff, which then rode
8
+ * the pinned `SDKRateLimitInfo.resetsAt` as a claim about the subscription), and modelling it as
9
+ * `resumeAt: 0` made it indistinguishable from "not limited". So `limited` is tracked as its own
10
+ * flag, independent of any window, and a window-unknown limit omits `resetsAt` entirely.
11
+ */
12
+ export type QuotaState = {
13
+ kind: "ok";
14
+ } | {
15
+ kind: "limited";
16
+ resumeAt?: number;
17
+ };
18
+ export interface QuotaManagerOptions {
19
+ maxConcurrent?: number;
20
+ /** Injected so a fixture never waits out a real limit window. */
21
+ now?: () => number;
22
+ /**
23
+ * How `waitIfLimited` waits. Injectable for the same reason `RetryPolicyOptions.sleep` is, and it
24
+ * is NOT redundant with it: in production the retry backoff and the quota window are the same
25
+ * wait (the backoff consumes the window), but a fixture that mocks only the retry sleep still
26
+ * spends the real window here — twice per 429 scenario.
27
+ */
28
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
29
+ }
30
+ /**
31
+ * Tracks a ChatGPT account's quota state and caps concurrent turns against it.
32
+ *
33
+ * `resumeAt` is epoch MILLISECONDS internally and is rendered as epoch SECONDS on the event, which
34
+ * is the pinned `SDKRateLimitInfo.resetsAt` convention. Only fields the pin actually declares are
35
+ * emitted: `status` and `resetsAt`. Inventing a `rateLimitType` (whose six members are all
36
+ * consumer-subscription shapes) would be a claim about which window was hit, and the backend's 429
37
+ * does not say.
38
+ */
39
+ export declare class QuotaManager {
40
+ private readonly maxConcurrent;
41
+ private readonly now;
42
+ private readonly sleep;
43
+ private active;
44
+ private waiters;
45
+ /** Set by a refusal, cleared only by `noteRecovered()`. Survives the window elapsing — see `hasPendingLimit`. */
46
+ private limited;
47
+ /** `0` means "limited, window unknown". Never a synthesised value. */
48
+ private limitedUntil;
49
+ private listeners;
50
+ private totals;
51
+ constructor(opts?: QuotaManagerOptions);
52
+ /**
53
+ * The state a `rate_limit` event is rendered from.
54
+ *
55
+ * A KNOWN window that has elapsed reads `ok` — the account is presumed serving again, which is
56
+ * what the backend told us. A window-UNKNOWN limit stays `limited` until something says otherwise,
57
+ * because nothing has: guessing that it lapsed would be the same invention `resumeAt` refuses.
58
+ */
59
+ state(): QuotaState;
60
+ /**
61
+ * True from a refusal until `noteRecovered()`, WHATEVER the clock says.
62
+ *
63
+ * `state()` cannot answer this (finding I2): by the time a retried turn completes, the retry
64
+ * backoff has slept the whole window, so `state()` reads `ok` and "was this turn rate-limited?"
65
+ * silently becomes "no" — which made the recovery event unreachable under a real clock while a
66
+ * fixture with a 1 ms mocked sleep passed.
67
+ */
68
+ hasPendingLimit(): boolean;
69
+ onStateChange(callback: (state: QuotaState) => void): () => void;
70
+ private emit;
71
+ /**
72
+ * Records that the backend refused for quota reasons.
73
+ *
74
+ * `retryAfterMs` MUST come from the refused response's own `Retry-After` header, never from a
75
+ * computed backoff (finding I1). Absent means the window is unknown, which is recorded as exactly
76
+ * that: limited, with no resume time.
77
+ */
78
+ noteRateLimit(retryAfterMs: number | undefined): void;
79
+ /** The account is serving again. Silent when it never stopped — a listener must not see a state change that did not happen. */
80
+ noteRecovered(): void;
81
+ accumulate(inputTokens: number, outputTokens: number): void;
82
+ usage(): {
83
+ inputTokens: number;
84
+ outputTokens: number;
85
+ };
86
+ acquire(): Promise<void>;
87
+ release(): void;
88
+ /** Waits out a known limit window, ABORTABLY: an interrupt must not have to outlast a quota reset. */
89
+ waitIfLimited(signal?: AbortSignal): Promise<void>;
90
+ }
91
+ /**
92
+ * A quota state -> the pinned-shaped `rate_limit` event.
93
+ *
94
+ * `kind: "subscription-quota"` is the type-level statement that this is NOT an HTTP 429 (R6-B), and
95
+ * it is the only `kind` the seam admits.
96
+ */
97
+ export declare function quotaEvent(state: QuotaState): Extract<ProviderEvent, {
98
+ type: "rate_limit";
99
+ }>;
@@ -0,0 +1,142 @@
1
+ import type { WinterModelDescriptor } from "@yanlinglabs/winter-provider-catalog";
2
+ import type { ProviderAdapter, ProviderContext, ProviderEvent, ProviderMessageLike, TurnRequest } from "../../types.js";
3
+ import { EventQueue, type OpenAiAdapterOptions, type ReasoningPlan, type ResolvedEndpoint } from "./shared.js";
4
+ export declare const OPENAI_API_BASE_URL = "https://api.openai.com/v1";
5
+ /**
6
+ * `ProviderMessageLike[]` -> the Responses `input` array.
7
+ *
8
+ * The STRUCTURED content form is mandatory, and that is a live finding rather than a style choice:
9
+ * the flat `{ role, content: "string" }` form was rejected with an HTTP 400 by the codex backend
10
+ * (Norma, 2026-06-13). Assistant content is `output_text`, everything else `input_text`.
11
+ *
12
+ * NATIVE STATE LEADS ITS MESSAGE. §5.3 requires the provider's completed output items to be replayed
13
+ * in their original order among messages and tool calls; a turn's real order is reasoning item(s)
14
+ * first, then the message / function_call it produced. The renderer has already dropped any state
15
+ * from a foreign continuation domain before this sees it, so what arrives here is replayable by
16
+ * construction.
17
+ */
18
+ export declare function mapResponsesInput(messages: readonly ProviderMessageLike[]): unknown[];
19
+ export declare function mapResponsesTools(tools: TurnRequest["tools"]): unknown[];
20
+ /**
21
+ * The Responses request body.
22
+ *
23
+ * `instructions` is sent ONLY when the caller supplied a non-empty system prompt — a deliberate
24
+ * deviation from the port, which sent a default string. Global Constraints forbid vendor prompt
25
+ * text, and inventing a Winter one would put an instruction in front of the model that no caller
26
+ * asked for; codex-rs itself skips the field when it is empty.
27
+ */
28
+ export declare function buildResponsesBody(req: TurnRequest, reasoning: ReasoningPlan, descriptor: WinterModelDescriptor | undefined): Record<string, unknown>;
29
+ /**
30
+ * Responses SSE frames -> Winter's normalized `ProviderEvent`s.
31
+ *
32
+ * One instance per turn: the reasoning-item accumulator, the seen-a-call flag and the
33
+ * did-this-call-stream-its-arguments map are all per-turn state.
34
+ */
35
+ /**
36
+ * Which stream event this family treats as COMPLETING, from the descriptor's own `completionEvent`
37
+ * evidence (review round 1, C — ledger line 36).
38
+ *
39
+ * Lane B's pattern, applied here: `anthropicCaptureEvent` and the Google resolver both read this
40
+ * field, and this family hard-coded `"response.completed"` — so a catalog row that DECLARED a
41
+ * different terminator was silently ignored, and the evidence existed for two families out of four.
42
+ *
43
+ * MATCHED LENIENTLY BY MENTION, for the same reason those two are: the field is a prose-ish
44
+ * `CapabilityEvidence<string>`, not an enum. `"response.completed"` stays as the fallback and is the
45
+ * conservative answer — the pinned terminator for this surface, and the one every unlisted row means.
46
+ *
47
+ * `response.incomplete` is always ALSO treated as terminal and is deliberately not configurable: it
48
+ * is a truncation, not a completion, and a row that named some other event would still have to end
49
+ * its stream somewhere.
50
+ */
51
+ export declare function responsesCompletionEvent(descriptor: WinterModelDescriptor | undefined): string;
52
+ export declare class ResponsesStreamMapper {
53
+ private readonly completionEvent;
54
+ /** The event this stream's descriptor says completes a response. Injected so the mapper never reaches for a catalog itself. */
55
+ constructor(completionEvent?: string);
56
+ private sawToolCall;
57
+ private sawRefusal;
58
+ private started;
59
+ /**
60
+ * The completed reasoning items, ordered by the response's OWN `output_index` and, for anything
61
+ * that carried none, by arrival after everything that did.
62
+ *
63
+ * A list rather than a `Map<number, unknown>` keyed on `output_index ?? 0` (minor 6): that default
64
+ * made every indexless item collide on key 0, so a stream carrying two of them replayed ONE — a
65
+ * silently truncated continuation whose next turn fails at the provider, far from here.
66
+ */
67
+ private readonly reasoningItems;
68
+ private arrivals;
69
+ /** Item ids already reported as unrepresentable, so `added` + `done` for one call is ONE error (minor 5). */
70
+ private readonly reportedUnrepresentable;
71
+ /** item_id -> call_id, so an arguments delta (which carries only the item id) can name its call. */
72
+ private readonly callIdByItem;
73
+ /** call_ids whose arguments arrived as deltas — the final item must not re-send them. */
74
+ private readonly streamedArguments;
75
+ private completed;
76
+ map(data: string): ProviderEvent[];
77
+ /**
78
+ * Called when the byte stream ended.
79
+ *
80
+ * A stream that stopped before `response.completed` is a TRUNCATED turn, and reporting it as a
81
+ * finished one would hand the caller a partial answer as if it were whole. It becomes a
82
+ * non-retryable `network` error: bytes flowed, so R6-6 forbids replaying it.
83
+ */
84
+ finish(): ProviderEvent[];
85
+ private onCreated;
86
+ private onItemAdded;
87
+ private onArgumentsDelta;
88
+ private onItemDone;
89
+ private onCompleted;
90
+ private noteRefusal;
91
+ /**
92
+ * ONE error per unrepresentable CALL, not one per lifecycle event (minor 5).
93
+ *
94
+ * A call appears twice in the stream (`output_item.added`, then `.done`), so reporting on both
95
+ * emitted two errors for one refusal — which a consumer counting failures reads as two problems.
96
+ */
97
+ private unrepresentable;
98
+ private unrepresentableError;
99
+ private failureMessage;
100
+ }
101
+ /** What a Responses-speaking adapter (plain OpenAI, codex-oauth, Azure's preview surface) has to supply beyond the request body. */
102
+ export interface ResponsesTurnPlan {
103
+ /** The provider-local model id this plan is for — the descriptor lookup's key (review round 1, C). */
104
+ model: string;
105
+ url: string;
106
+ headers: Record<string, string>;
107
+ endpoint: ResolvedEndpoint;
108
+ ctx: ProviderContext;
109
+ options: OpenAiAdapterOptions;
110
+ body: string;
111
+ beforeAttempt?: (attempt: number) => Promise<void>;
112
+ recover?: (status: number, attempt: number) => Promise<Record<string, string> | undefined>;
113
+ /** Observes the refused response before its body is read — the codex quota manager's only honest source for a limit window (finding I1). */
114
+ onRefused?: (response: Response) => void;
115
+ /** Observed after a successful turn — the codex quota manager's "we are no longer limited" hook. */
116
+ onSuccess?: () => void;
117
+ /** Observed on a rate-limited failure, BEFORE the retry sleeps. The one producer of `rate_limit` events (R6-B). */
118
+ onRateLimited?: (retry: Extract<ProviderEvent, {
119
+ type: "retry";
120
+ }>, queue: EventQueue) => void;
121
+ /** Pre-seeded observations (an `auth_status` from a token refresh that already happened). */
122
+ queue?: EventQueue;
123
+ }
124
+ /**
125
+ * Opens the stream, consumes it, and yields the normalized events.
126
+ *
127
+ * `policy.commit()` fires on the FIRST SSE event, not at header time: WS-13 §13's line is the first
128
+ * response byte consumed, and a 5xx that arrives with headers and an error body is still safely
129
+ * retryable.
130
+ */
131
+ export declare function streamResponsesTurn(plan: ResponsesTurnPlan, signal: AbortSignal | undefined): AsyncIterable<ProviderEvent>;
132
+ export declare function createResponsesAdapter(options: OpenAiAdapterOptions): ProviderAdapter;
133
+ /** PRIVILEGED headers (R6-L): identifiers that only mean something at the reviewed endpoint they were minted for. `applyPrivilegedHeaders` drops them for a user endpoint. */
134
+ export declare function privilegedHeaders(options: OpenAiAdapterOptions): Record<string, string>;
135
+ /**
136
+ * The plain-OpenAI (and Azure-preview) turn: resolve the endpoint and credential, validate the
137
+ * SELECTION before anything is sent, then stream.
138
+ *
139
+ * Everything that can be refused is refused here, synchronously enough that the fake records ZERO
140
+ * requests — which is what the effort-mapping and limit-rejection fixtures assert on.
141
+ */
142
+ export declare function responsesTurn(req: TurnRequest, ctx: ProviderContext, options: OpenAiAdapterOptions, fallbackBaseUrl: string | undefined, urlFor: (baseUrl: string) => string, extraProtocolHeaders?: Record<string, string>): AsyncIterable<ProviderEvent>;