@combycode/llm-sdk 2.2.1 → 2.3.0

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 (49) hide show
  1. package/CHANGELOG.md +206 -0
  2. package/dist/agent/loop-internals.d.ts +4 -0
  3. package/dist/agent/loop.d.ts +35 -0
  4. package/dist/{llm/providers → catalog}/builtin-tools.d.ts +1 -1
  5. package/dist/{plugins/model-catalog → catalog}/catalog.d.ts +34 -0
  6. package/dist/helpers/client-pool.d.ts +1 -1
  7. package/dist/helpers/client-resolver.d.ts +1 -1
  8. package/dist/helpers/engine.d.ts +12 -1
  9. package/dist/helpers/mcp.d.ts +6 -1
  10. package/dist/helpers/models.d.ts +1 -1
  11. package/dist/helpers/one-shot.d.ts +2 -2
  12. package/dist/helpers/select-model.d.ts +1 -1
  13. package/dist/index.browser.js +1438 -891
  14. package/dist/index.d.ts +4 -4
  15. package/dist/index.js +1438 -891
  16. package/dist/llm/client-config.d.ts +1 -1
  17. package/dist/llm/client-internal.d.ts +11 -0
  18. package/dist/llm/client.d.ts +4 -0
  19. package/dist/llm/providers/_shared/sse.d.ts +19 -0
  20. package/dist/llm/providers/google/files.d.ts +15 -0
  21. package/dist/llm/providers/google/media.d.ts +22 -4
  22. package/dist/llm/providers/google/realtime.d.ts +15 -2
  23. package/dist/llm/providers/openai/media.d.ts +10 -1
  24. package/dist/llm/providers/openai/realtime.d.ts +15 -2
  25. package/dist/llm/server-state.d.ts +1 -1
  26. package/dist/llm/types/options.d.ts +2 -2
  27. package/dist/llm/types/request.d.ts +50 -1
  28. package/dist/plugins/context-measurer/counter/count-api.d.ts +1 -1
  29. package/dist/plugins/context-measurer/counter/heuristic.d.ts +1 -1
  30. package/dist/plugins/context-measurer/counter/hybrid.d.ts +1 -1
  31. package/dist/plugins/context-measurer/measurer.d.ts +1 -1
  32. package/dist/plugins/cost-collector/collector.d.ts +1 -1
  33. package/dist/plugins/cost-collector/cost-collector-internal.d.ts +1 -1
  34. package/dist/plugins/cost-collector/cost-collector-types.d.ts +1 -1
  35. package/dist/plugins/files/registry.d.ts +1 -1
  36. package/dist/plugins/files/strategy.d.ts +1 -1
  37. package/dist/plugins/internal-tools/registry.d.ts +1 -1
  38. package/dist/plugins/internal-tools/runner/types.d.ts +1 -1
  39. package/dist/plugins/mcp/sampling.d.ts +23 -1
  40. package/dist/plugins/media/output.d.ts +1 -1
  41. package/dist/plugins/telemetry/telemetry.d.ts +2 -133
  42. package/dist/plugins/telemetry/types.d.ts +139 -0
  43. package/dist/util/hash.d.ts +8 -0
  44. package/dist/{plugins/media → util}/source-image.d.ts +1 -1
  45. package/dist/wire/inherit.d.ts +47 -0
  46. package/dist/wire/interpreter.d.ts +236 -0
  47. package/dist/wire/registry.d.ts +18 -0
  48. package/dist/wire/transforms.d.ts +22 -0
  49. package/package.json +3 -3
@@ -0,0 +1,139 @@
1
+ /** Telemetry types.
2
+ *
3
+ * Split out of `telemetry.ts` so the adapter file is implementation and this one
4
+ * is contract — matching how the rest of the codebase is laid out, and making
5
+ * the public surface of the plugin readable without paging through 1,200 lines.
6
+ */
7
+ import type { HookName } from '../../bus/hook-map';
8
+ export type SpanKind = 'llm' | 'http' | 'media' | 'agent' | 'tool' | 'mcp' | 'other';
9
+ export interface Span {
10
+ traceId: string;
11
+ spanId: string;
12
+ /** The span this one runs under. Without it every span is a sibling and a backend
13
+ * draws a flat list instead of a tree — so a run reads as "9 things happened", not
14
+ * "a turn, which called a tool, which asked a second model".
15
+ *
16
+ * Resolved in this order: the innermost container span still open on this trace
17
+ * (`agent.run` / `tool.call`), else the app's span from a supplied `traceparent`,
18
+ * else none — this span is the root. */
19
+ parentSpanId?: string;
20
+ name: string;
21
+ kind: SpanKind;
22
+ startTime: number;
23
+ endTime?: number;
24
+ durationMs?: number;
25
+ status: 'unset' | 'ok' | 'error';
26
+ attributes: Record<string, unknown>;
27
+ }
28
+ /** What kind of work an event describes. `message` is conversation content, which is not
29
+ * a span — it is the thing you want in a debug store and NOT in your metrics backend,
30
+ * which is exactly why it filters separately. */
31
+ export type TraceEventType = 'agent' | 'tool' | 'llm' | 'http' | 'mcp' | 'media' | 'message' | 'other';
32
+ /** One piece of work, carrying enough of the tree that a consumer can push it straight
33
+ * into their own tracer without reconstructing anything. */
34
+ export interface TraceEvent {
35
+ type: TraceEventType;
36
+ /** The app's trace when it supplied a `traceparent`, else ours. */
37
+ traceId: string;
38
+ spanId: string;
39
+ /** Already resolved past anything this subscriber filtered out — see `survivingParent`. */
40
+ parentSpanId?: string;
41
+ /** The conventional name (`chat gpt-5.4-nano`, `execute_tool search`). */
42
+ name: string;
43
+ startTime: number;
44
+ endTime?: number;
45
+ durationMs?: number;
46
+ status: 'unset' | 'ok' | 'error';
47
+ attributes: Record<string, unknown>;
48
+ }
49
+ /** Declarative on purpose, rather than a predicate: knowing the types up front lets a
50
+ * filtered-out event cost nothing, where a predicate would force us to build the payload
51
+ * just to let the caller throw it away. */
52
+ export interface TraceFilter {
53
+ types?: readonly TraceEventType[];
54
+ }
55
+ export type TraceHandler = (event: TraceEvent) => void;
56
+ export interface TelemetryEvent {
57
+ seq: number;
58
+ time: number;
59
+ name: HookName;
60
+ category: string;
61
+ traceId?: string;
62
+ ctx: unknown;
63
+ }
64
+ export interface TelemetryMetrics {
65
+ requests: number;
66
+ errors: number;
67
+ retries: number;
68
+ rateLimitHits: number;
69
+ completions: number;
70
+ mediaGenerated: number;
71
+ costUsd: number;
72
+ inputTokens: number;
73
+ outputTokens: number;
74
+ inFlight: number;
75
+ queueDepth: number;
76
+ latency: {
77
+ count: number;
78
+ min: number;
79
+ max: number;
80
+ avg: number;
81
+ };
82
+ }
83
+ /** OpenTelemetry Resource — identifies the SERVICE producing this telemetry, so
84
+ * a shared backend can separate streams from different apps and attribute cost
85
+ * per service (`sum by service.name`). Stamped on every span/metric/log. */
86
+ export interface TelemetryResource {
87
+ /** Primary grouping key, e.g. "billing-api". OTel default: "unknown_service". */
88
+ serviceName: string;
89
+ /** Optional namespace/group, e.g. "prod" or a team. */
90
+ serviceNamespace?: string;
91
+ /** Unique instance (pod/host/process); a good default is the engine sessionId. */
92
+ serviceInstanceId?: string;
93
+ serviceVersion?: string;
94
+ /** Arbitrary resource attributes (deployment.environment, cloud.region, …). */
95
+ attributes?: Record<string, string>;
96
+ }
97
+ export interface TelemetryAdapterOptions {
98
+ /** Cap on retained events (ring buffer). Default 2000. */
99
+ maxEvents?: number;
100
+ /** Service identity stamped on all exported telemetry. */
101
+ resource?: TelemetryResource;
102
+ /** Whether provider error TEXT may be stored in telemetry. Default `true`
103
+ * (unchanged behaviour, and the same default as the OpenAI Agents SDK's
104
+ * `trace_include_sensitive_data`).
105
+ *
106
+ * A provider's `error.message` / `error.raw` can echo request content back —
107
+ * a moderation refusal quotes the prompt, a validation error names the offending
108
+ * field and value. URLs and headers are always redacted regardless; this switch
109
+ * governs the free-text payload. Set `false` when telemetry leaves your trust
110
+ * boundary (a shared collector, a vendor APM) and the message is replaced by a
111
+ * fixed `[redacted]` string while name/code/status are kept for triage. */
112
+ includeSensitiveData?: boolean;
113
+ /** Which event types to hand to `onTrace`. Omitted → everything.
114
+ *
115
+ * Filtering SPLICES the tree rather than punching holes in it: drop `http` and the
116
+ * spans under it re-parent to the nearest surviving ancestor. Dropping without that
117
+ * leaves orphans, and a backend draws an orphan as a second root — worse than not
118
+ * filtering at all. */
119
+ types?: readonly TraceEventType[];
120
+ /** Whether conversation content rides along on `message` events. Default `'none'`:
121
+ * prompts and completions are the debugging gold AND the PII, so sending them is a
122
+ * decision to make on purpose rather than inherit. `'full'` adds the Opt-In
123
+ * `gen_ai.input.messages` / `gen_ai.output.messages` attributes; `'none'` still
124
+ * reports the shape (counts and sizes), which is enough to spot a runaway prompt. */
125
+ content?: 'none' | 'full';
126
+ /** Fraction of TRACES to emit, 0..1. Default 1.
127
+ *
128
+ * Per trace, never per span: sampling spans independently shreds every tree it touches
129
+ * — a tool call with no run, a model call with no tool. The decision is a hash of the
130
+ * trace id, so it is stable across processes and two services sharing a trace agree
131
+ * without coordinating.
132
+ *
133
+ * This is HEAD sampling: the choice is made when the trace first appears, before we
134
+ * know whether it ends in an error. Keeping all errors needs tail sampling, which
135
+ * needs buffering; do that in your collector, which is built for it. */
136
+ sample?: number;
137
+ /** Convenience for the common case of a single sink — same as calling `onTrace`. */
138
+ onTrace?: TraceHandler;
139
+ }
@@ -0,0 +1,8 @@
1
+ /** FNV-1a, 32-bit. Deterministic, synchronous and dependency-free — used where a
2
+ * stable short id has to be derived from content rather than from a clock.
3
+ *
4
+ * Not cryptographic. Collisions are acceptable for naming and bucketing; do not
5
+ * use it for integrity or security. */
6
+ export declare function fnv1a32(input: string): number;
7
+ /** Same value as 8 lowercase hex characters. */
8
+ export declare function fnv1a32Hex(input: string): string;
@@ -5,7 +5,7 @@
5
5
  * - OpenAI: `{ image_url }` (data-URL) or `{ file_id }`
6
6
  * - xAI: `{ url }` (data-URL) or `{ file_id }`
7
7
  * - Google: `inline_data {mime_type,data}` or `file_data {file_uri}` */
8
- import type { DataSource } from '../../llm/types/messages';
8
+ import type { DataSource } from '../llm/types/messages';
9
9
  export interface NormalizedImageRef {
10
10
  /** Raw base64 (no `data:` prefix), when inline. */
11
11
  base64?: string;
@@ -0,0 +1,47 @@
1
+ /** Spec inheritance: a spec version extends the previous one and overrides only
2
+ * what changed.
3
+ *
4
+ * This replaces the `variants` section. Instead of asking "which shape does this
5
+ * model id take?" at request time — which needed version arithmetic, and which
6
+ * is currently WRONG for `claude-opus-4-20250514` (the date suffix parses as the
7
+ * minor version, so a 4.0 model resolves to the 4.6+ shape) — the catalog pins a
8
+ * model to a spec id, and the spec chain carries the differences as deltas.
9
+ *
10
+ * Merge rules, in one sentence each:
11
+ * - `fields` are keyed by `to` — child replaces, `remove:true` deletes, new ones append
12
+ * - `blocks` are keyed by `name` — same, with `before`/`after` for explicit placement
13
+ * - `tables` merge per table, per key
14
+ * - `overlays`/`envelope.headers` are keyed and replaced
15
+ * - everything scalar: child wins
16
+ *
17
+ * Block ORDER is load-bearing (proved by the mutation suite), so appended blocks
18
+ * land at the end unless the delta says otherwise.
19
+ */
20
+ import type { WireSpec } from './interpreter';
21
+ export interface Deltas {
22
+ extends?: string;
23
+ /** Rules to delete from the inherited spec. */
24
+ removeFields?: string[];
25
+ removeBlocks?: string[];
26
+ /** Explicit placement for an appended block. */
27
+ placeBlocks?: Record<string, {
28
+ before?: string;
29
+ after?: string;
30
+ }>;
31
+ }
32
+ export type SpecDelta = Partial<WireSpec> & Deltas & {
33
+ id: string;
34
+ };
35
+ /** Apply one delta to a resolved spec. This is the whole of composition, and it
36
+ * is deliberately shared: a CHAIN picks the delta sequence by walking parents, a
37
+ * MATRIX picks it by selecting features. Behind the resolver they are the same
38
+ * operation — see `compose.ts`. */
39
+ export declare function applyDelta(base: WireSpec, delta: SpecDelta): WireSpec;
40
+ /** Resolve a spec id to its fully flattened form by walking `extends`. */
41
+ export declare function resolveSpec(id: string, byId: Map<string, SpecDelta>, seen?: Set<string>): WireSpec;
42
+ /** Catalog pin: model id -> spec id. `default` is what an unknown model gets. */
43
+ export interface PinTable {
44
+ default: string;
45
+ models: Record<string, string>;
46
+ }
47
+ export declare function specForModel(model: string, pins: PinTable): string;
@@ -0,0 +1,236 @@
1
+ /** Wire-spec interpreter — prototype for report 037 / 3.0.0.
2
+ *
3
+ * Turns a declarative wire spec + a NormalizedRequest into the same
4
+ * ProviderHttpRequest the hand-written `buildRequest` produces today.
5
+ *
6
+ * The point is NOT to eliminate code. It is to move the per-provider and
7
+ * per-MODEL knowledge — field names, shapes, enum values, which variant a
8
+ * model takes — out of imperative code and into reviewable data that the
9
+ * update pipeline can diff and that all three language ports can share.
10
+ *
11
+ * Structural work (turning unified messages into provider content parts) stays
12
+ * as named code in the registry. See `transforms.ts`.
13
+ */
14
+ export type Json = unknown;
15
+ /** A condition evaluated against the request + resolved model variants. */
16
+ export type Cond = {
17
+ defined: string;
18
+ } | {
19
+ truthy: string;
20
+ } | {
21
+ eq: [string, Json];
22
+ } | {
23
+ ne: [string, Json];
24
+ } | {
25
+ variant: string;
26
+ } | {
27
+ flavor: string | string[];
28
+ } | {
29
+ pred: string;
30
+ }
31
+ /** Truthiness of a field on the current $map item. */
32
+ | {
33
+ itemTruthy: string;
34
+ }
35
+ /** The current $map item equals this value (items are scalars here). */
36
+ | {
37
+ itemEq: Json;
38
+ } | {
39
+ isLast: true;
40
+ } | {
41
+ isFunctionTool: true;
42
+ } | {
43
+ builtin: string;
44
+ }
45
+ /** `req.tools` contains a builtin of this type. */
46
+ | {
47
+ hasTool: string;
48
+ }
49
+ /** `req.tools` contains at least one function tool. */
50
+ | {
51
+ hasFunctionTool: true;
52
+ }
53
+ /** Array at `path` contains `value`. */
54
+ | {
55
+ includes: [string, Json];
56
+ } | {
57
+ not: Cond;
58
+ } | {
59
+ all: Cond[];
60
+ } | {
61
+ any: Cond[];
62
+ };
63
+ export interface FieldRule {
64
+ /** Dotted path into NormalizedRequest. */
65
+ from: string;
66
+ /** Dotted path into the body. */
67
+ to: string;
68
+ /** Default when the source is absent. Emits the field even if unset. */
69
+ default?: Json;
70
+ /** Extra gate on top of the presence check. */
71
+ when?: Cond;
72
+ /** Presence test: `defined` (!== undefined) or `truthy`. Default `defined`. */
73
+ presence?: 'defined' | 'truthy';
74
+ /** Named value table to map the source value through. */
75
+ table?: string;
76
+ /** Value used when the table has no entry (rather than dropping the field). */
77
+ tableDefault?: Json;
78
+ /** Named transform applied to the source value. */
79
+ call?: string;
80
+ }
81
+ export interface BlockRule {
82
+ /** Documentation handle; also used in diff output. */
83
+ name: string;
84
+ when?: Cond;
85
+ /** Dotted target path. Omit to merge the result into the body root. */
86
+ to?: string;
87
+ /** Deep-merge into whatever is already at `to` (output_config case). */
88
+ merge?: boolean;
89
+ /** Value template. */
90
+ template?: Json;
91
+ /** Named builder invoked with (req, ctx) instead of a template. */
92
+ call?: string;
93
+ /** Named cross-field effects run after the block is written. */
94
+ effects?: string[];
95
+ }
96
+ export interface WireSpec {
97
+ id: string;
98
+ provider: string;
99
+ api: string;
100
+ /** Adapter flavor, for specs shared by several providers (openai|xai|openrouter). */
101
+ flavors?: string[];
102
+ envelope?: {
103
+ path?: Json;
104
+ /** Full URL template. Non-chat adapters (media, files, batch) address an
105
+ * absolute URL rather than a path under a shared base. */
106
+ url?: Json;
107
+ method?: string;
108
+ /** `json` (default), `multipart`, or `none` for GET/DELETE with no body.
109
+ * Multipart matters: a FormData body JSON-stringifies to `{}`, so comparing
110
+ * it as JSON would pass vacuously no matter what the fields are. */
111
+ bodyKind?: 'json' | 'multipart' | 'none';
112
+ headers?: {
113
+ name: string;
114
+ value: Json;
115
+ when?: Cond;
116
+ }[];
117
+ };
118
+ /** Model-id → variant flags. The migration target is a catalog pin; the
119
+ * `idMatch` form is what today's regex helpers do, expressed as data.
120
+ * `fn` is the escape hatch for rules a pattern cannot express (version
121
+ * arithmetic) — every use of it is a finding, not a feature. */
122
+ variants?: {
123
+ flag: string;
124
+ idMatch?: string;
125
+ fn?: string;
126
+ unless?: string;
127
+ note?: string;
128
+ }[];
129
+ /** Value tables referenced by `table:` and `$table`. */
130
+ tables?: Record<string, Record<string, Json>>;
131
+ /** Fields we deliberately never send, with the reason. */
132
+ unsupported?: {
133
+ from: string;
134
+ reason: string;
135
+ }[];
136
+ fields?: FieldRule[];
137
+ blocks?: BlockRule[];
138
+ /** Multipart form fields, in order, when `envelope.bodyKind` is 'multipart'. */
139
+ multipart?: {
140
+ name: string;
141
+ value?: Json;
142
+ file?: boolean;
143
+ when?: Cond;
144
+ }[];
145
+ /** Non-HTTP surfaces. A realtime session is not one request: it is a
146
+ * connection descriptor plus a sequence of outbound frames, so those are
147
+ * named operations rather than a single envelope+body. */
148
+ operations?: Record<string, OperationRule>;
149
+ /** Per-flavor patches applied after the blocks. This mirrors what the xai and
150
+ * openrouter adapters already do today: call the base builder, then patch the
151
+ * result. Expressing it as ops keeps the shared spec authoritative. */
152
+ overlays?: Record<string, {
153
+ ops: OverlayOp[];
154
+ }>;
155
+ }
156
+ export interface OverlayOp {
157
+ op: 'rename' | 'delete' | 'set' | 'mergeFrom' | 'call';
158
+ /** rename/delete: body path. mergeFrom: request path. */
159
+ from?: string;
160
+ /** rename/set: body path. */
161
+ to?: string;
162
+ value?: Json;
163
+ call?: string;
164
+ when?: Cond;
165
+ }
166
+ export interface Ctx {
167
+ req: any;
168
+ spec: WireSpec;
169
+ flavor: string;
170
+ /** Adapter-level configuration (baseURL, apiKey, ...) referenced by `$config`.
171
+ * Keeps the URL declarative rather than pushing it into a named transform. */
172
+ config: Record<string, unknown>;
173
+ variants: Set<string>;
174
+ body: Record<string, unknown>;
175
+ /** Per-item scope while inside a $map. */
176
+ item?: {
177
+ value: any;
178
+ index: number;
179
+ isLast: boolean;
180
+ };
181
+ /** Collected multipart fields, when the spec declares a multipart body. */
182
+ multipart?: MultipartField[];
183
+ }
184
+ export interface MultipartField {
185
+ name: string;
186
+ kind: 'file' | 'value';
187
+ value?: Json;
188
+ }
189
+ export type Transform = (value: any, ctx: Ctx) => Json;
190
+ export type Builder = (ctx: Ctx) => Json;
191
+ export type Predicate = (ctx: Ctx) => boolean;
192
+ export type Effect = (ctx: Ctx) => void;
193
+ export interface Registry {
194
+ transforms: Record<string, Transform>;
195
+ builders: Record<string, Builder>;
196
+ predicates: Record<string, Predicate>;
197
+ effects: Record<string, Effect>;
198
+ }
199
+ export declare function getPath(root: any, path: string): any;
200
+ export declare function evalCond(cond: Cond | undefined, ctx: Ctx, reg: Registry): boolean;
201
+ export declare function resolveVariants(spec: WireSpec, model: string, reg: Registry): Set<string>;
202
+ export interface BuiltRequest {
203
+ body: Record<string, unknown>;
204
+ headers?: Record<string, string>;
205
+ path?: string;
206
+ url?: string;
207
+ method?: string;
208
+ /** Present instead of a JSON body when bodyKind is 'multipart'. */
209
+ multipart?: MultipartField[];
210
+ /** True when the spec declares the request carries no body at all. */
211
+ noBody?: boolean;
212
+ }
213
+ export declare function buildFromSpec(spec: WireSpec, req: any, reg: Registry, flavor?: string,
214
+ /** Coverage hook: called with every rule that actually fired. */
215
+ onUse?: (kind: 'field' | 'block' | 'header' | 'variant' | 'overlay', name: string) => void,
216
+ /** Adapter config exposed to `$config`. */
217
+ config?: Record<string, unknown>): BuiltRequest;
218
+ export interface OperationRule {
219
+ /** Connection descriptor (realtime `connect`). */
220
+ url?: Json;
221
+ protocols?: Json;
222
+ /** Outbound frames, in order. A frame whose template evaluates away is skipped. */
223
+ frames?: {
224
+ name?: string;
225
+ when?: Cond;
226
+ template: Json;
227
+ }[];
228
+ }
229
+ export interface BuiltConnection {
230
+ url: string;
231
+ protocols?: string[];
232
+ }
233
+ /** Build the connection descriptor for an operation (realtime `connect`). */
234
+ export declare function buildConnection(spec: WireSpec, operation: string, input: any, reg: Registry, config?: Record<string, unknown>): BuiltConnection;
235
+ /** Build the outbound frames for an operation (realtime `open` / `send`). */
236
+ export declare function buildFrames(spec: WireSpec, operation: string, input: any, reg: Registry, config?: Record<string, unknown>): Json[];
@@ -0,0 +1,18 @@
1
+ /** Every wire spec the SDK ships, indexed by id.
2
+ *
3
+ * A spec says HOW to talk to a provider API: field names, enum values, defaults,
4
+ * which shape a model version takes. It is deliberately DATA, so the same file
5
+ * is consumed by this SDK and by the Python and Rust ports, and a provider
6
+ * change is one reviewable diff rather than three code changes.
7
+ *
8
+ * Generated index — regenerate rather than hand-edit when adding a spec.
9
+ *
10
+ * Specs are not yet wired into the adapters: they currently serve as the
11
+ * differential oracle that proves the hand-written adapters and this data agree
12
+ * (see tests/unit/wire). Making them authoritative is the 3.0.0 step.
13
+ */
14
+ import type { SpecDelta } from './inherit';
15
+ /** All shipped specs, keyed by `provider/api@version` id. */
16
+ export declare const WIRE_SPECS: ReadonlyMap<string, SpecDelta>;
17
+ /** Resolve a spec id to its flattened form, walking `extends`. */
18
+ export declare function getWireSpec(id: string): SpecDelta;
@@ -0,0 +1,22 @@
1
+ /** The named-code registry the wire specs delegate to.
2
+ *
3
+ * Everything here is bound to the REAL library internals, never reimplemented —
4
+ * if the interpreter's output matches the adapter's, it is because the spec
5
+ * drove the same code, not because I wrote a second copy that happens to agree.
6
+ *
7
+ * What lands in this file is the honest answer to "what cannot be data":
8
+ * structural message/content transformation, schema-shape rules, and one
9
+ * variant rule that is arithmetic rather than a pattern.
10
+ */
11
+ import type { Registry } from './interpreter';
12
+ /** Adapters whose private message builders we reuse. Instantiated once; the
13
+ * builders are pure with respect to the request. */
14
+ export interface AdapterHandles {
15
+ anthropic: any;
16
+ google: any;
17
+ openaiResponses: any;
18
+ openaiCompletions: any;
19
+ googleInteractions?: any;
20
+ openrouterMedia?: any;
21
+ }
22
+ export declare function makeRegistry(a: AdapterHandles): Registry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@combycode/llm-sdk",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "Unified, pluggable AI SDK for accessing the LLMs of every major provider (Anthropic, OpenAI, Google, xAI, OpenRouter) through one API. Cross-environment: Node, Bun, and the browser.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -43,9 +43,9 @@
43
43
  "format": "biome format --write src tests",
44
44
  "check": "biome check src tests",
45
45
  "check:fix": "biome check --write src tests",
46
- "gate": "node ../../quality-gate/gate.mjs",
46
+ "gate": "bun run scripts/gate.ts",
47
47
  "gate:selftest": "node ../../quality-gate/selftest.mjs",
48
- "gate:snapshot": "node ../../quality-gate/gate.mjs --only api-snapshot --update"
48
+ "gate:snapshot": "bun run scripts/gate.ts --only api-snapshot --update"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@biomejs/biome": "^2.4.13",