@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,119 @@
1
+ /** The signing algorithm's own name, as it appears in the string-to-sign and the Authorization header. */
2
+ export declare const SIGV4_ALGORITHM = "AWS4-HMAC-SHA256";
3
+ /** The AWS service name Bedrock signs under — both the runtime and control planes use it. */
4
+ export declare const BEDROCK_SERVICE = "bedrock";
5
+ /** The AWS credential material a signature needs. A structural mirror of `CredentialMaterial`'s `aws` arm, so this module stays free of the credential types. */
6
+ export interface AwsSigningCredentials {
7
+ accessKeyId: string;
8
+ secretAccessKey: string;
9
+ sessionToken?: string;
10
+ }
11
+ export interface SignRequestInput {
12
+ method: string;
13
+ /** The ABSOLUTE request URL, path already once-encoded (the canonical form encodes it again). */
14
+ url: string;
15
+ /** Headers to sign ALONGSIDE the derived ones. `host`, `x-amz-date`, `x-amz-content-sha256` and `x-amz-security-token` are added here and must not be passed in. */
16
+ headers: Record<string, string>;
17
+ /** The exact request body bytes, or empty for a GET. Hashed verbatim — a re-serialization would sign a different payload than the one sent. */
18
+ body: Uint8Array;
19
+ credentials: AwsSigningCredentials;
20
+ region: string;
21
+ service?: string;
22
+ /** Injected so a fixture can pin a signature. Defaults to now. */
23
+ date?: Date;
24
+ }
25
+ /**
26
+ * RFC 3986 percent-encoding with AWS's unreserved set: `A-Za-z0-9` plus `-`, `_`, `.` and `~`.
27
+ *
28
+ * DELIBERATELY STRICTER THAN `encodeURIComponent`, which leaves `!*'()` alone. AWS's own SDKs use
29
+ * `encodeURIComponent` for the path, so the two disagree on those five characters — a discrepancy
30
+ * that is unreachable for Bedrock (a model id matches `[a-zA-Z0-9-:.]+`, and an ARN adds only `/`
31
+ * and `:`), and the spec's reading is the safer one to be wrong in the direction of. Recorded here
32
+ * rather than left as a silent choice.
33
+ */
34
+ export declare function awsUriEncode(value: string, encodeSlash: boolean): string;
35
+ /**
36
+ * The canonical URI: the path, normalized then encoded a SECOND time (see the file header).
37
+ *
38
+ * `%2F` is restored to `/` after the whole path is encoded, which is what makes the separators
39
+ * survive an encoding pass that would otherwise escape them — the same manoeuvre the AWS SDKs use.
40
+ */
41
+ export declare function canonicalUri(pathname: string): string;
42
+ /** The canonical query string: every parameter encoded once, then sorted by name and, within a name, by value. */
43
+ export declare function canonicalQuery(search: string): string;
44
+ /**
45
+ * The canonical headers block and the signed-headers list.
46
+ *
47
+ * Names are lowercased and sorted; values are trimmed and their internal runs of whitespace
48
+ * collapsed to a single space (AWS's own rule, and the reason a header value with a stray double
49
+ * space still verifies).
50
+ *
51
+ * NAMES ARE DE-DUPLICATED AFTER LOWERCASING, and that is a latent-bug fix rather than a live one
52
+ * (Lane N r1 carry). Every caller today hands this a map whose keys are already lowercase, so the
53
+ * two spellings cannot both be present — but nothing in the signature *type* says so, and a future
54
+ * caller passing `{ "X-Amz-Date": …, "x-amz-date": … }` would have produced `x-amz-date` TWICE in
55
+ * both the canonical block and `SignedHeaders`. AWS would reject that with an
56
+ * `InvalidSignatureException` whose text is about the signature, not about a duplicate header, so
57
+ * the failure would read as a broken signer. `byLower` already collapsed the VALUES; only the name
58
+ * list did not.
59
+ */
60
+ export declare function canonicalHeaders(headers: Record<string, string>): {
61
+ canonical: string;
62
+ signed: string;
63
+ };
64
+ export declare function sha256Hex(bytes: Uint8Array): Promise<string>;
65
+ /** The four chained HMACs of step 3. Derived per request rather than cached: the key is scoped to a DATE, and a cache would need invalidating at midnight UTC for no measurable gain. */
66
+ export declare function signingKey(secretAccessKey: string, datestamp: string, region: string, service: string): Promise<Uint8Array>;
67
+ /** `YYYYMMDDTHHMMSSZ` — an ISO instant with every separator removed, which is the only form the header accepts. */
68
+ export declare function amzDate(date: Date): string;
69
+ export interface CanonicalRequestInput {
70
+ method: string;
71
+ pathname: string;
72
+ search: string;
73
+ headers: Record<string, string>;
74
+ payloadHash: string;
75
+ }
76
+ /** Step 1, exported whole so a fixture and the conformance fake can both assert on the exact string a signature was computed over. */
77
+ export declare function buildCanonicalRequest(input: CanonicalRequestInput): {
78
+ canonicalRequest: string;
79
+ signedHeaders: string;
80
+ };
81
+ /** Step 2, exported so a fixture can assert the exact string a signature was taken over — and so a verifier rebuilds it rather than re-deriving it differently. */
82
+ export declare function buildStringToSign(canonicalRequest: string, stamp: string, scope: string): Promise<string>;
83
+ /** Steps 3 and 4's hash: the signing key, then one HMAC over the string-to-sign, hex-encoded. */
84
+ export declare function computeSignature(secretAccessKey: string, datestamp: string, region: string, service: string, stringToSign: string): Promise<string>;
85
+ export interface SignedRequest {
86
+ /** The headers to ADD to the request. `host` is deliberately absent — it is signed but never set (see the file header). */
87
+ headers: Record<string, string>;
88
+ /** Exposed for fixtures and for the fake's recomputation; never logged. */
89
+ canonicalRequest: string;
90
+ stringToSign: string;
91
+ signature: string;
92
+ signedHeaders: string;
93
+ }
94
+ /**
95
+ * Signs a request, returning the headers to attach.
96
+ *
97
+ * `x-amz-content-sha256` is ALWAYS sent, not only where a service requires it: it is what lets a
98
+ * verifier (real AWS, or this repo's fake) check that the payload it received is the payload that
99
+ * was signed, rather than only that the two agree about the headers.
100
+ */
101
+ export declare function signRequest(input: SignRequestInput): Promise<SignedRequest>;
102
+ export interface ParsedAuthorization {
103
+ accessKeyId: string;
104
+ datestamp: string;
105
+ region: string;
106
+ service: string;
107
+ signedHeaders: string[];
108
+ signature: string;
109
+ }
110
+ /**
111
+ * Parses an `Authorization` header back into its parts.
112
+ *
113
+ * Exists so a VERIFIER can rebuild the canonical request from what the wire actually carried — the
114
+ * signed-header list included — rather than from what the signer intended to send. That distinction
115
+ * is the whole value of the conformance fake's signature check: a verifier that re-derived the
116
+ * signed-header set from its own idea of the request would agree with a buggy signer about a header
117
+ * neither of them included.
118
+ */
119
+ export declare function parseAuthorization(header: string | null | undefined): ParsedAuthorization | undefined;
@@ -0,0 +1,46 @@
1
+ /** One header to encode. Only the string type is offered: it is the only type Bedrock's own frames use, and a fixture that needed another would be testing the decoder rather than the adapter. */
2
+ export interface EventStreamHeaderInput {
3
+ name: string;
4
+ value: string;
5
+ }
6
+ /** Encodes one complete event-stream frame: prelude + prelude CRC + headers + payload + message CRC. */
7
+ export declare function encodeEventStreamMessage(headers: EventStreamHeaderInput[], payload: Uint8Array): Uint8Array;
8
+ /** A ConverseStream `event` frame carrying a JSON payload — the shape every happy-path frame takes. */
9
+ export declare function converseStreamEvent(eventType: string, payload: unknown): Uint8Array;
10
+ /** A ConverseStream `exception` frame — how Bedrock reports a failure that begins AFTER the 200 (`ThrottlingException`, `ModelStreamErrorException`, …). */
11
+ export declare function converseStreamException(exceptionType: string, payload?: unknown): Uint8Array;
12
+ /** Concatenates frames into one response body. */
13
+ export declare function concatFrames(frames: Uint8Array[]): Uint8Array;
14
+ /** What the fake's signature check is given: the LIVE request, plus the secret it should verify against. */
15
+ export interface VerifySigV4Input {
16
+ method: string;
17
+ /** The absolute request URL, exactly as the server received it. */
18
+ url: string;
19
+ /** The live request headers. MUST come from the `Request`, never from the fake base's recorded copy — that copy redacts `authorization` before a route ever runs. */
20
+ headers: Headers;
21
+ /** The exact request body bytes the server received. */
22
+ body: Uint8Array;
23
+ secretAccessKey: string;
24
+ expectedAccessKeyId?: string;
25
+ }
26
+ export type SigV4Verdict = {
27
+ ok: true;
28
+ accessKeyId: string;
29
+ } | {
30
+ ok: false;
31
+ reason: string;
32
+ };
33
+ /**
34
+ * Recomputes a request's signature and compares it with the one the client sent.
35
+ *
36
+ * THE CANONICAL REQUEST IS REBUILT FROM THE WIRE, not from any notion of what the adapter meant to
37
+ * send: the header set comes from the `SignedHeaders=` list in the Authorization header, each value
38
+ * is read off the live request, and the payload hash is taken over the bytes the server actually
39
+ * received. That is what makes the check independent of `signRequest` in the way that matters — a
40
+ * signer that forgot a header, signed a stale body, or signed the wrong host produces a signature
41
+ * this function will not reproduce.
42
+ *
43
+ * `x-amz-content-sha256` is additionally checked AGAINST THE BODY, so a client cannot sign a hash of
44
+ * one payload and send another.
45
+ */
46
+ export declare function verifySigV4(input: VerifySigV4Input): Promise<SigV4Verdict>;
@@ -0,0 +1,7 @@
1
+ import type { ContentBlockLike } from "../types.js";
2
+ /** Every `image` block in `content`, including those nested inside a `tool_result`, in wire order. */
3
+ export declare function collectImages(content: string | ContentBlockLike[], depth?: number): Array<Extract<ContentBlockLike, {
4
+ type: "image";
5
+ }>>;
6
+ /** True when `content` carries an image ANYWHERE — the capability gate's real question. */
7
+ export declare function containsImage(content: string | ContentBlockLike[]): boolean;
@@ -0,0 +1,35 @@
1
+ import type { CredentialMaterial } from "../../types.js";
2
+ /** The scope a Vertex generation needs. The one value; it is not configurable, so it cannot drift. */
3
+ export declare const GCP_CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
4
+ export interface AccessTokenSourceOptions {
5
+ /** Injected in tests so a cache-expiry fixture never has to wait for real time. */
6
+ now?: () => number;
7
+ /** Declares the token endpoint local, so a loopback fake can stand in for it. Mirrors `ConnectionProfile.local`. */
8
+ local?: boolean;
9
+ }
10
+ export interface AccessTokenSource {
11
+ /** The bearer token to send. Cached until it expires; a cache miss performs the exchange. */
12
+ token(signal?: AbortSignal): Promise<string>;
13
+ /** Test/diagnostic seam: how many exchanges have actually been performed. NEVER exposes the token. */
14
+ exchanges(): number;
15
+ }
16
+ /** A `gcp-access-token` ref: the host already holds a token, so there is nothing to exchange. */
17
+ export declare function createStaticAccessTokenSource(token: string): AccessTokenSource;
18
+ /**
19
+ * The service-account flow: sign an assertion, exchange it, cache the result until it expires.
20
+ *
21
+ * The KEY IS IMPORTED ONCE and the promise is memoised -- both because importing is not free and
22
+ * because a concurrent burst of turns would otherwise import the same key many times over. The
23
+ * imported `CryptoKey` is non-extractable (`importRs256PrivateKey` passes `false`), so even holding
24
+ * it cannot re-export the PEM.
25
+ */
26
+ export declare function createServiceAccountTokenSource(material: Extract<CredentialMaterial, {
27
+ kind: "gcp-service-account";
28
+ }>, opts?: AccessTokenSourceOptions): AccessTokenSource;
29
+ /**
30
+ * The material -> a token source.
31
+ *
32
+ * `undefined` for material this flow does not handle, so the caller can produce its own typed
33
+ * refusal naming the kind -- a `null` here would be indistinguishable from "no credential".
34
+ */
35
+ export declare function createAccessTokenSource(material: CredentialMaterial, opts?: AccessTokenSourceOptions): AccessTokenSource | undefined;
@@ -0,0 +1,136 @@
1
+ import type { WinterCatalog, WinterModelDescriptor } from "@yanlinglabs/winter-provider-catalog";
2
+ import { type RetryPolicyOptions } from "../../retry.js";
3
+ import { type EndpointPolicy } from "../../endpoint-policy.js";
4
+ import type { ProviderAdapter, ProviderContext, ProviderMessageLike, TurnRequest } from "../../types.js";
5
+ export declare const GOOGLE_ADAPTER_ID = "winter.google-generate-content";
6
+ /** The GENERATED endpoint. Immutable (R6-11); pinned to the catalog row by a test. */
7
+ export declare const GOOGLE_DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com";
8
+ export declare const GOOGLE_API_VERSION_PATH = "v1beta";
9
+ export interface GoogleAdapterOptions {
10
+ catalog?: WinterCatalog;
11
+ requestTimeoutMs?: number;
12
+ maxBodyBytes?: number;
13
+ retry?: RetryPolicyOptions;
14
+ defaultMaxOutputTokens?: number;
15
+ }
16
+ /**
17
+ * What differs between the Gemini API and Vertex: the URL, the credential, and whether a bounded
18
+ * list endpoint is in scope. Everything else -- the whole wire mapping and the whole normalizer --
19
+ * is shared.
20
+ */
21
+ export interface GoogleTransport {
22
+ readonly id: string;
23
+ readonly version: string;
24
+ /** Resolves the base URL and its endpoint policy, or throws a typed capability refusal. */
25
+ endpoint(ctx: ProviderContext): {
26
+ base: string;
27
+ policy: EndpointPolicy;
28
+ };
29
+ /**
30
+ * `identity` is the ROW's own Winter-authored second identity field (WS-13b §7/§8.4, R-FW-2),
31
+ * already `<version>`-substituted -- `{}` for every row whose vendor names none, which is every
32
+ * row this family serves today. It is a PARAMETER rather than something a transport derives,
33
+ * because a transport has no catalog and the adapter above it does.
34
+ */
35
+ headers(ctx: ProviderContext, policy: EndpointPolicy, json: boolean, identity: Record<string, string>): Promise<Record<string, string>>;
36
+ streamPath(ctx: ProviderContext, model: string): string;
37
+ countTokensPath(ctx: ProviderContext, model: string): string;
38
+ /** ABSENT when this transport has no bounded model-list endpoint in this phase's scope (Vertex). */
39
+ listPath?: (ctx: ProviderContext, pageToken: string | undefined, pageSize: number) => string;
40
+ /** Credential kinds this transport can authenticate with, for `validateCredential`'s honest `unsupported`. */
41
+ readonly credentialKinds: readonly string[];
42
+ }
43
+ /**
44
+ * Which chunk this family treats as COMPLETING, from the descriptor's own `completionEvent` evidence.
45
+ *
46
+ * THIS FAMILY HAS EXACTLY ONE HONOURABLE MARKER: the chunk carrying `finishReason`. There is no
47
+ * per-block terminator to choose instead, and `usageMetadata` -- the only other candidate a row might
48
+ * plausibly name -- is shipped on EVERY chunk, so treating it as the completion signal would report
49
+ * the turn finished from the first chunk and capture continuation state the provider had not finished
50
+ * minting. That is precisely the failure the completion-event rule exists to prevent, so a row naming
51
+ * it is REFUSED before the request rather than honoured into an early capture.
52
+ *
53
+ * Everything else falls back to `finish-reason`, and the asymmetry is deliberate: an UNRECOGNISED
54
+ * value means "this adapter does not know what the row meant", and the safe answer to that is the
55
+ * conservative default (a fallback can only ever capture later, never earlier). A value this adapter
56
+ * DOES recognise and cannot honour safely is a different thing entirely — it is a claim, and the
57
+ * honest answer to a claim that cannot be met is a refusal.
58
+ *
59
+ * Matched by MENTION rather than equality, because the field is a prose-ish
60
+ * `CapabilityEvidence<string>` whose existing catalog values read like sentences (Lane A's row says
61
+ * `response.completed`; this lane's fixtures say "the chunk carrying finishReason").
62
+ */
63
+ export declare function googleCompletionMarker(descriptor: WinterModelDescriptor | undefined): {
64
+ ok: true;
65
+ marker: "finish-reason";
66
+ } | {
67
+ ok: false;
68
+ reason: string;
69
+ };
70
+ /**
71
+ * One opaque continuation item: a `thoughtSignature` and enough addressing to put it back on the
72
+ * EXACT part it came from.
73
+ *
74
+ * The value itself is OPAQUE (Global Constraints): it reaches the provider-state sidecar and the
75
+ * next request body, and nothing else -- never a log line, never an error message, never a frame.
76
+ */
77
+ export interface GoogleThoughtSignatureItem {
78
+ /** The wire part index it arrived on. The family's own truth, recorded even though replay keys on `callId` where one exists. */
79
+ partIndex: number;
80
+ /**
81
+ * WHICH KIND of part carried it, and this is what makes replay addressable rather than positional.
82
+ *
83
+ * A `thought` part is FOREIGN reasoning and is never replayed as content (R6-8) -- so there is no
84
+ * part on the next request for its signature to ride. Before this discriminator existed, any
85
+ * non-`functionCall` signature was treated as a text item and stamped onto the first replayed text
86
+ * block: a signature minted for a thought part, re-attached to a different part, which is exactly
87
+ * the mis-attachment decision #2 exists to prevent and which a validating endpoint can reject.
88
+ * A `thought` item is now RETAINED as opaque state and attached to nothing.
89
+ */
90
+ kind: "function-call" | "text" | "thought" | "other";
91
+ /** The tool-call id this adapter minted for the `functionCall` part. Absent for every other kind. */
92
+ callId?: string;
93
+ signature: string;
94
+ }
95
+ type WirePart = Record<string, unknown>;
96
+ interface SerializeResult {
97
+ contents: Array<{
98
+ role: "user" | "model";
99
+ parts: WirePart[];
100
+ }>;
101
+ /** How many blocks from ANOTHER dialect were dropped at this boundary. Reported as a count, never as content. */
102
+ droppedForeignReasoning: number;
103
+ }
104
+ /**
105
+ * Engine messages -> `contents`.
106
+ *
107
+ * `assistant` becomes `model`; `tool` becomes `user` carrying `functionResponse` parts (this family
108
+ * has no tool role either). Adjacent same-role entries are merged, which preserves part ORDER
109
+ * exactly and keeps a two-tool-message history from producing the consecutive same-role entries the
110
+ * endpoint rejects.
111
+ */
112
+ export declare function toContents(messages: ProviderMessageLike[]): SerializeResult;
113
+ export declare function findDescriptor(catalog: WinterCatalog, providerId: string, model: string): WinterModelDescriptor | undefined;
114
+ export type GoogleEffortMapping = {
115
+ ok: true;
116
+ value: {
117
+ thinkingBudget: number;
118
+ };
119
+ } | {
120
+ ok: false;
121
+ reason: string;
122
+ };
123
+ /** Effort -> the model's VERIFIED vocabulary, or a refusal (WS-13 §8.2). Identical rule to the Anthropic adapter's; the LADDER differs because the families' budgets do. */
124
+ export declare function mapGoogleEffort(effort: TurnRequest["effort"], descriptor: WinterModelDescriptor | undefined): GoogleEffortMapping;
125
+ export declare function createGoogleFamilyAdapter(transport: GoogleTransport, opts?: GoogleAdapterOptions): ProviderAdapter;
126
+ /**
127
+ * The Gemini API transport: `generativelanguage.googleapis.com`, `x-goog-api-key`.
128
+ *
129
+ * `x-goog-user-project` is the one PRIVILEGED header this family has (R6-L names it explicitly), so
130
+ * it goes through `applyPrivilegedHeaders` and is therefore dropped for a user-supplied `baseUrl` --
131
+ * an account identifier must never be disclosed to a host the reviewed catalog never named. The
132
+ * api-key, content-type and accept headers are PROTOCOL headers and are not routed through it.
133
+ */
134
+ export declare function geminiTransport(): GoogleTransport;
135
+ export declare function createGoogleGenerateContentAdapter(opts?: GoogleAdapterOptions): ProviderAdapter;
136
+ export {};
@@ -0,0 +1,8 @@
1
+ export { GOOGLE_ADAPTER_ID, GOOGLE_API_VERSION_PATH, GOOGLE_DEFAULT_BASE_URL, createGoogleFamilyAdapter, createGoogleGenerateContentAdapter, findDescriptor, geminiTransport, googleCompletionMarker, mapGoogleEffort, toContents, } from "./generate-content.js";
2
+ export type { GoogleAdapterOptions, GoogleEffortMapping, GoogleThoughtSignatureItem, GoogleTransport } from "./generate-content.js";
3
+ export { VERTEX_ADAPTER_ID, VERTEX_API_VERSION_PATH, createVertexGeminiAdapter, vertexEndpointUrl, vertexModelPath, vertexTransport } from "./vertex.js";
4
+ export type { VertexAdapterOptions } from "./vertex.js";
5
+ export { GCP_CLOUD_PLATFORM_SCOPE, createAccessTokenSource, createServiceAccountTokenSource, createStaticAccessTokenSource } from "./adc.js";
6
+ export type { AccessTokenSource, AccessTokenSourceOptions } from "./adc.js";
7
+ export { JwtKeyError, RS256, base64UrlEncode, base64UrlEncodeText, importRs256PrivateKey, pkcs8DerFromPem, signRs256Jwt } from "./jwt-rs256.js";
8
+ export type { ServiceAccountJwtClaims } from "./jwt-rs256.js";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The signing algorithm, in the one spelling `importKey`/`sign` both accept.
3
+ *
4
+ * EXPORTED because the loopback fake's verifier needs the identical parameters, and a second literal
5
+ * there is a second thing that can drift from this one.
6
+ */
7
+ export declare const RS256: RsaHashedImportParams;
8
+ /** Raised when a PEM cannot be read or a key cannot be imported. Its message NEVER contains key material. */
9
+ export declare class JwtKeyError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /** base64url, no padding -- the only encoding a JWT uses. */
13
+ export declare function base64UrlEncode(bytes: Uint8Array): string;
14
+ export declare function base64UrlEncodeText(text: string): string;
15
+ /**
16
+ * The DER bytes of a PKCS#8 PEM block.
17
+ *
18
+ * The block TYPE is checked explicitly so a PKCS#1 key (`BEGIN RSA PRIVATE KEY`, which openssl still
19
+ * emits by default) produces a message that says what to convert rather than an opaque WebCrypto
20
+ * `DataError`.
21
+ */
22
+ export declare function pkcs8DerFromPem(pem: string): Uint8Array;
23
+ /** Imports a PKCS#8 PEM as an RS256 signing key. */
24
+ export declare function importRs256PrivateKey(pem: string): Promise<CryptoKey>;
25
+ /** The claims a Google service-account assertion carries. `iat`/`exp` are seconds since the epoch, as the spec requires. */
26
+ export interface ServiceAccountJwtClaims {
27
+ iss: string;
28
+ scope: string;
29
+ aud: string;
30
+ iat: number;
31
+ exp: number;
32
+ /** Domain-wide delegation. Omitted for an ordinary service account. */
33
+ sub?: string;
34
+ }
35
+ /** Signs `{header}.{payload}` and returns the compact JWT. */
36
+ export declare function signRs256Jwt(claims: ServiceAccountJwtClaims, key: CryptoKey): Promise<string>;
@@ -0,0 +1,15 @@
1
+ import type { ProviderAdapter } from "../../types.js";
2
+ import { type GoogleAdapterOptions, type GoogleTransport } from "./generate-content.js";
3
+ import { type AccessTokenSourceOptions } from "./adc.js";
4
+ export declare const VERTEX_ADAPTER_ID = "winter.vertex-gemini";
5
+ /** The API version segment of the location endpoint. Vertex's GenerateContent surface is `v1`, not the Gemini API's `v1beta`. */
6
+ export declare const VERTEX_API_VERSION_PATH = "v1";
7
+ export interface VertexAdapterOptions extends GoogleAdapterOptions, AccessTokenSourceOptions {
8
+ }
9
+ /** Composes the location endpoint. Exported so a fixture can assert the exact URL the ruling names, without reaching the network. */
10
+ export declare function vertexEndpointUrl(location: string): string;
11
+ /** Composes the model path under a resolved base. Exported for the same reason. */
12
+ export declare function vertexModelPath(project: string, location: string, model: string, method: string, search?: string): string;
13
+ export declare function vertexTransport(opts?: VertexAdapterOptions): GoogleTransport;
14
+ /** The Vertex adapter: the shared GenerateContent mapping over the Vertex transport. */
15
+ export declare function createVertexGeminiAdapter(opts?: VertexAdapterOptions): ProviderAdapter;
@@ -0,0 +1,34 @@
1
+ import type { WinterCatalog, WinterModelDescriptor } from "@yanlinglabs/winter-provider-catalog";
2
+ import type { ProviderAdapter } from "../types.js";
3
+ /** A per-adapter descriptor lookup: the provider-local model id (or key, or alias) a request named -> its catalog row. */
4
+ export type AdapterDescriptorLookup = (providerLocalModelId: string) => WinterModelDescriptor | undefined;
5
+ /**
6
+ * Every row served by ONE adapter id, indexed by the three spellings a request may use.
7
+ *
8
+ * Built once per registration rather than scanned per request: `streamTurn` calls the lookup on
9
+ * every turn, and a linear scan of the whole catalog per turn is a cost with no reason.
10
+ */
11
+ export declare function descriptorLookupForAdapter(catalog: WinterCatalog, adapterId: string): AdapterDescriptorLookup;
12
+ /** The adapter ids this build ships, in the order they are registered. Exported so a test can assert the catalog names no adapter this list omits. */
13
+ export declare const SHIPPED_ADAPTER_IDS: readonly ["winter.openai-responses", "winter.openai-chat-completions", "winter.codex-oauth", "winter.xai-oauth", "winter.azure-openai", "winter.local-openai", "winter.anthropic-messages", "winter.google-generate-content", "winter.vertex-gemini", "winter.bedrock-converse"];
14
+ /**
15
+ * Builds every adapter this build ships against ONE catalog.
16
+ *
17
+ * The production wiring's only adapter-construction site (`production-wiring.ts`), and the corpus's
18
+ * whole-catalog probe's too — so "which adapters does a session have?" and "which adapters did the
19
+ * probe check?" cannot answer differently.
20
+ */
21
+ export declare function createShippedAdapters(catalog: WinterCatalog): ProviderAdapter[];
22
+ export { ANTHROPIC_ADAPTER_ID, ANTHROPIC_API_VERSION, ANTHROPIC_DEFAULT_BASE_URL, ANTHROPIC_DEFAULT_MAX_TOKENS, createAnthropicMessagesAdapter, mapAnthropicEffort, toWireMessages, } from "./anthropic/index.js";
23
+ export type { AnthropicAdapterOptions } from "./anthropic/index.js";
24
+ export { GOOGLE_ADAPTER_ID, GOOGLE_API_VERSION_PATH, GOOGLE_DEFAULT_BASE_URL, VERTEX_ADAPTER_ID, VERTEX_API_VERSION_PATH, createGoogleFamilyAdapter, createGoogleGenerateContentAdapter, createVertexGeminiAdapter, geminiTransport, mapGoogleEffort, toContents, vertexEndpointUrl, vertexTransport, } from "./google/index.js";
25
+ export type { GoogleAdapterOptions, GoogleTransport, VertexAdapterOptions } from "./google/index.js";
26
+ export { BEDROCK_ADAPTER_ID, BEDROCK_ADAPTER_VERSION, createBedrockConverseAdapter, mapBedrockEffort, requireRegion, resolveAwsCredentials, signRequest, } from "./bedrock/index.js";
27
+ export type { BedrockAdapter, BedrockAdapterOptions } from "./bedrock/index.js";
28
+ export { CODEX_ORIGINATOR, DEEPSEEK_BASE_URL, OPENAI_API_BASE_URL, OPENAI_CHAT_BASE_URL, OPENROUTER_BASE_URL, createChatCompletionsAdapter, createCodexOauthAdapter, createResponsesAdapter, codexCredentialAccount, codexCredentialRef, deepSeekProfile, openRouterProfile, startCodexLogin, XAI_CONSENT_DISCLOSURE, XAI_OAUTH, XAI_OAUTH_ADAPTER_ID, createXaiOauthAdapter, startXaiLogin, xaiCredentialRef, DERIVED_XAI, DERIVED_XAI_COMMIT, DERIVED_XAI_MODELS, } from "./openai/index.js";
29
+ export type { CodexAdapterOptions, CodexLoginOptions, CodexLoginResult, XaiLoginOptions, XaiLoginResult } from "./openai/index.js";
30
+ export { createAzureOpenAIAdapter } from "./openai/azure.js";
31
+ export type { AzureAdapterOptions } from "./openai/azure.js";
32
+ export { createLocalOpenAIAdapter } from "./openai/local.js";
33
+ export type { LocalAdapterOptions } from "./openai/local.js";
34
+ export { PRIVILEGED_IDENTITY_HEADERS } from "./privileged-headers.js";
@@ -0,0 +1,32 @@
1
+ import type { LoginConfig, OAuthTokens } from "../openai/pkce.js";
2
+ export interface DeviceCodeConfig {
3
+ clientId: string;
4
+ /** Where the device/user code pair is minted (RFC 8628 §3.1). */
5
+ deviceCodeUrl: string;
6
+ tokenUrl: string;
7
+ scope: string;
8
+ /**
9
+ * WINTER'S OWN NAME, and the form field the vendor's flow carries it in. Sent on the device
10
+ * request AND on every poll. Never omitted, never another product's name (WS-13 §5, D21).
11
+ */
12
+ identity: {
13
+ field: string;
14
+ value: string;
15
+ };
16
+ /** Floor for the poll interval. The vendor's own `interval` wins when it is larger (RFC 8628 §3.5). */
17
+ pollIntervalMs?: number;
18
+ /** How much `slow_down` widens the interval. RFC 8628 §3.5's own increment is 5 seconds. */
19
+ slowDownStepMs?: number;
20
+ /** Default 15 minutes — longer than any vendor's device code lives, so the code expires before this does. */
21
+ timeoutMs?: number;
22
+ onAuthStatus?: LoginConfig["onAuthStatus"];
23
+ }
24
+ /**
25
+ * Runs the device authorization grant to completion and returns the tokens.
26
+ *
27
+ * Ends in exactly one of four ways: tokens; a vendor error code (`access_denied`,
28
+ * `expired_token`, …) as a typed `ProviderRequestError` naming ONLY the code — never the
29
+ * `error_description`, which routinely quotes the request; the timeout; or a malformed response.
30
+ * There is no fifth path where it keeps polling.
31
+ */
32
+ export declare function runDeviceCodeFlow(cfg: DeviceCodeConfig): Promise<OAuthTokens>;
@@ -0,0 +1,40 @@
1
+ import type { CredentialMaterial, CredentialRef, CredentialStore } from "../../types.js";
2
+ /** The oauth arm of `CredentialMaterial` — what a refresh reads, merges into, and writes back. */
3
+ export type OauthMaterial = Extract<CredentialMaterial, {
4
+ kind: "oauth";
5
+ }>;
6
+ /** Tokens live in ONE Keychain record per provider/account (R6-10), so a refresh is addressed by a keychain ref. */
7
+ export type KeychainRef = Extract<CredentialRef, {
8
+ kind: "keychain";
9
+ }>;
10
+ export interface RefreshOauthMaterialInput {
11
+ store: CredentialStore;
12
+ ref: KeychainRef;
13
+ tokenUrl: string;
14
+ clientId: string;
15
+ /** Injectable clock, so a fixture can assert the exact `expiresAt` instead of a range. Defaults to `Date.now`. */
16
+ now?: () => number;
17
+ /** Extra body fields the vendor's flow requires — an honest identity field, for instance. Never a credential. */
18
+ extraFields?: Record<string, string>;
19
+ /**
20
+ * The grant's body encoding. Defaults to `"form"`.
21
+ *
22
+ * P6.5 ruling R-A2-1, "artifact wins": RFC 6749 §4.1.3 requires a token endpoint to ACCEPT
23
+ * `application/x-www-form-urlencoded`, and every Winter flow posted that — but the Anthropic
24
+ * Console endpoint is only ever OBSERVED receiving `application/json`, and an unobserved encoding
25
+ * that turns out to be rejected breaks that flow completely rather than partially. So the
26
+ * encoding is a per-flow fact rather than a constant. The default keeps codex byte-identical.
27
+ */
28
+ bodyEncoding?: "form" | "json";
29
+ }
30
+ /**
31
+ * Exchanges the record's refresh token for a fresh access token, PERSISTS the merged material, and
32
+ * returns it.
33
+ *
34
+ * Failure modes, all typed `CredentialResolutionError` and all naming the ref rather than the token:
35
+ * no record, a record that is not `oauth`, a record with no refresh token (refused BEFORE any
36
+ * request — asking a vendor to refresh nothing is a pointless round trip that also tells them the
37
+ * account exists), a non-2xx, an unparseable body, and a 200 that carries no access token. In every
38
+ * failure the OLD material is left exactly as it was.
39
+ */
40
+ export declare function refreshOauthMaterial(input: RefreshOauthMaterialInput): Promise<OauthMaterial>;
@@ -0,0 +1,38 @@
1
+ import type { ProviderAdapter, ProviderContext } from "../../types.js";
2
+ import { type OpenAiAdapterOptions } from "./shared.js";
3
+ /** The `apiVersion` that selects the `/openai/v1` Responses surface rather than the deployment path. */
4
+ export declare const AZURE_PREVIEW_API_VERSION = "preview";
5
+ export interface AzureAdapterOptions extends OpenAiAdapterOptions {
6
+ /** Used when the connection profile names none. A host that configures neither gets a typed refusal. */
7
+ defaultApiVersion?: string;
8
+ }
9
+ interface AzureRouting {
10
+ apiVersion: string;
11
+ preview: boolean;
12
+ deployment?: string;
13
+ }
14
+ /**
15
+ * Reads the routing facts off the connection profile, refusing rather than defaulting.
16
+ *
17
+ * A missing `api-version` is a refusal because Azure rejects every call without one — guessing a
18
+ * version would silently pin the operator to a surface they never chose. A missing `deployment` is a
19
+ * refusal only on the classic path, where it IS the address.
20
+ */
21
+ export declare function azureRouting(ctx: ProviderContext, options: AzureAdapterOptions): AzureRouting;
22
+ /** The turn URL for a routing. `deployment` is percent-encoded: it is untrusted profile input going into a path. */
23
+ export declare function azureTurnUrl(baseUrl: string, routing: AzureRouting): string;
24
+ export declare function createAzureOpenAIAdapter(options: AzureAdapterOptions): ProviderAdapter;
25
+ /** An Azure connection profile, so a host names the three facts in one place. */
26
+ export declare function azureProfile(opts: {
27
+ baseUrl: string;
28
+ deployment?: string;
29
+ apiVersion: string;
30
+ headers?: Record<string, string>;
31
+ }): {
32
+ providerId: string;
33
+ baseUrl: string;
34
+ apiVersion: string;
35
+ deployment?: string;
36
+ headers?: Record<string, string>;
37
+ };
38
+ export {};
@@ -0,0 +1,86 @@
1
+ import type { WinterModelDescriptor } from "@yanlinglabs/winter-provider-catalog";
2
+ import type { ProviderAdapter, ProviderContext, ProviderEvent, ProviderMessageLike, TurnRequest } from "../../types.js";
3
+ import { type AuthStyle, type OpenAiAdapterOptions, type ReasoningPlan, type ResolvedEndpoint } from "./shared.js";
4
+ export declare const OPENAI_CHAT_BASE_URL = "https://api.openai.com/v1";
5
+ export declare const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
6
+ export declare const OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
7
+ /** The `native_state` item DeepSeek-class exposed reasoning rides in. A Winter-shaped wrapper, because the text is ours to place — it is not an opaque provider object. */
8
+ export interface ExposedReasoningItem {
9
+ type: "winter.exposed_reasoning";
10
+ text: string;
11
+ }
12
+ /**
13
+ * `ProviderMessageLike[]` -> chat `messages`.
14
+ *
15
+ * `system` is prepended by the caller rather than derived here, so the ordering is visible at the
16
+ * one place that owns the body.
17
+ */
18
+ export declare function mapChatMessages(messages: readonly ProviderMessageLike[], replayExposedReasoning: boolean): unknown[];
19
+ export declare function mapChatTools(tools: TurnRequest["tools"]): unknown[];
20
+ /**
21
+ * The chat request body.
22
+ *
23
+ * `max_completion_tokens` vs `max_tokens`: the newer spelling is required on reasoning models and
24
+ * rejected by many local servers, and the old one is the reverse. The descriptor's own reasoning
25
+ * evidence is what decides, which keeps the choice a recorded FACT about the model rather than a
26
+ * guess about the endpoint. Disclosed.
27
+ */
28
+ export declare function buildChatBody(req: TurnRequest, reasoning: ReasoningPlan, descriptor: WinterModelDescriptor | undefined, replayExposedReasoning: boolean): Record<string, unknown>;
29
+ /**
30
+ * Chat Completions SSE chunks -> Winter's normalized events.
31
+ *
32
+ * ARGUMENTS ARE ASSEMBLED BY `index`, NOT BY ID: only the FIRST fragment of a call carries its `id`
33
+ * and `function.name`; every later fragment carries the index alone. An adapter keyed on id would
34
+ * drop every continuation fragment and produce empty arguments for every call — silently.
35
+ */
36
+ export declare class ChatStreamMapper {
37
+ private readonly captureExposedReasoning;
38
+ private started;
39
+ private stopReason;
40
+ private exposed;
41
+ private completed;
42
+ private readonly callsByIndex;
43
+ private readonly order;
44
+ constructor(captureExposedReasoning: boolean);
45
+ map(data: string): ProviderEvent[];
46
+ /** The stream ended. A turn that reached a `finish_reason` is complete even without a `[DONE]` — many OpenAI-compatible servers never send one. */
47
+ finish(): ProviderEvent[];
48
+ private finalize;
49
+ private mapDelta;
50
+ }
51
+ export interface ChatTurnOptions extends OpenAiAdapterOptions {
52
+ /** Azure's deployment surface needs `api-key`; everything else is a bearer. */
53
+ authStyle?: AuthStyle;
54
+ }
55
+ /**
56
+ * The chat turn, from selection validation to the last event.
57
+ *
58
+ * Everything refusable is refused before the endpoint is even resolved, so a rejected selection
59
+ * leaves the fake with zero recorded requests — which is what the corpus asserts on.
60
+ */
61
+ export declare function chatTurn(req: TurnRequest, ctx: ProviderContext, options: ChatTurnOptions, fallbackBaseUrl: string | undefined, urlFor: (endpoint: ResolvedEndpoint) => string, extraProtocolHeaders?: Record<string, string>): AsyncIterable<ProviderEvent>;
62
+ export declare function createChatCompletionsAdapter(options: ChatTurnOptions): ProviderAdapter;
63
+ /**
64
+ * OpenRouter as a CONNECTION PROFILE of the chat adapter, not a fourth adapter.
65
+ *
66
+ * The attribution headers are set ONLY when the caller supplies them. They identify the operator's
67
+ * app to a third party, so defaulting them would disclose something the host never asked to
68
+ * disclose — and they are user-supplied rather than privileged precisely because OpenRouter's own
69
+ * endpoint is where they belong.
70
+ */
71
+ export declare function openRouterProfile(opts?: {
72
+ baseUrl?: string;
73
+ referer?: string;
74
+ title?: string;
75
+ }): {
76
+ providerId: string;
77
+ baseUrl: string;
78
+ headers?: Record<string, string>;
79
+ };
80
+ /** DeepSeek as a connection profile. Its `reasoning_content` handling is descriptor-driven, so there is nothing endpoint-specific to configure beyond the base URL. */
81
+ export declare function deepSeekProfile(opts?: {
82
+ baseUrl?: string;
83
+ }): {
84
+ providerId: string;
85
+ baseUrl: string;
86
+ };