@rulvar/anthropic 1.0.0 → 1.2.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.
package/dist/index.d.ts CHANGED
@@ -6,13 +6,13 @@ interface AnthropicModelInfo {
6
6
  /**
7
7
  * Wire thinking form: current models accept only adaptive; the
8
8
  * enabled/budget form remains functional only on Opus 4.6 and Sonnet
9
- * 4.6 (docs/04, section "Thinking and sampling parameters").
9
+ * 4.6.
10
10
  */
11
11
  thinkingForm: "adaptive" | "enabled-budget";
12
- /** Minimum cacheable prefix in tokens (docs/04, section "Prompt caching"). */
12
+ /** Minimum cacheable prefix in tokens. */
13
13
  cacheMinTokens: number;
14
14
  }
15
- /** Static seed table; docs/04 section 4 names the current model set. */
15
+ /** Static seed table naming the current model set. */
16
16
  declare const ANTHROPIC_MODELS: Record<string, AnthropicModelInfo>;
17
17
  /**
18
18
  * Unknown Anthropic models are assumed current-generation: adaptive
@@ -22,7 +22,7 @@ declare const ANTHROPIC_MODELS: Record<string, AnthropicModelInfo>;
22
22
  declare function anthropicModelInfo(model: string): AnthropicModelInfo;
23
23
  //#endregion
24
24
  //#region src/adapter.d.ts
25
- /** pause_turn continuation cap (docs/06 Appendix A; docs/04, section 4.6). */
25
+ /** pause_turn continuation cap. */
26
26
  declare const DEFAULT_PAUSE_TURN_MAX_CONTINUATIONS = 5;
27
27
  /** The client sub-surface the adapter consumes; injectable for tests. */
28
28
  interface AnthropicClientLike {
@@ -51,12 +51,12 @@ interface AnthropicAdapterOptions {
51
51
  /**
52
52
  * Creates the first-class Anthropic adapter (id 'anthropic'). SDK
53
53
  * autoretries are disabled (max_retries 0): the core owns retries and
54
- * wall-clock (docs/04, section "Retries belong to the core").
54
+ * wall-clock.
55
55
  */
56
56
  declare function anthropic(options?: AnthropicAdapterOptions): ProviderAdapter;
57
57
  //#endregion
58
58
  //#region src/wire.d.ts
59
- /** Bijective canonical-to-wire tool-call id map (docs/04, section 1.2). */
59
+ /** Bijective canonical-to-wire tool-call id map. */
60
60
  declare class IdMap {
61
61
  private readonly toWire;
62
62
  private readonly toCanonical;
@@ -67,10 +67,15 @@ declare class IdMap {
67
67
  }
68
68
  type Block = Record<string, unknown>;
69
69
  /**
70
+ * Returns a deep copy of the schema with the unsupported keywords
71
+ * removed at SCHEMA positions only: a property literally named
72
+ * "minimum" (a key inside `properties`) survives. The input is never
73
+ * mutated; unrecognized keywords are copied through untouched.
74
+ */
75
+ /**
70
76
  * Builds Messages API params from a ChatRequest. cacheHint compiles into
71
77
  * cache_control breakpoints; beyond the provider cap of 4 the DEEPEST
72
- * breakpoints are kept and the shallowest dropped, deterministically
73
- * (docs/04, sections 1.7 and 4.4).
78
+ * breakpoints are kept and the shallowest dropped, deterministically.
74
79
  */
75
80
  declare function buildAnthropicParams(req: ChatRequest, options: {
76
81
  ids: IdMap;
@@ -86,14 +91,14 @@ interface MappedStop {
86
91
  pauseTurn: boolean;
87
92
  }
88
93
  /**
89
- * The docs/04 section 4.7 stop-reason table. pause_turn never surfaces as
94
+ * The stop-reason table. pause_turn never surfaces as
90
95
  * a canonical finish: the adapter continues internally.
91
96
  */
92
97
  declare function mapStopReason(stopReason: string | null | undefined, stopDetails: Record<string, unknown> | null | undefined): MappedStop;
93
98
  /**
94
99
  * Normalizes Messages API usage under the Usage invariant: Anthropic
95
100
  * reports input_tokens EXCLUDING cache reads and writes, so the canonical
96
- * inputTokens is the sum of all three (docs/04, sections 1.6 and 4.4).
101
+ * inputTokens is the sum of all three.
97
102
  */
98
103
  declare function normalizeAnthropicUsage(raw: Record<string, unknown> | undefined): Usage;
99
104
  interface TurnMapping {
@@ -109,7 +114,7 @@ interface TurnMapping {
109
114
  * terminal finish unless the turn paused (pause_turn) or errored.
110
115
  * `carryRetained` holds thinking blocks from earlier pause_turn
111
116
  * continuations of the same turn so the terminal finish ships the whole
112
- * turn's retention payload (docs/04, section 2.3, M4-T02).
117
+ * turn's retention payload (M4-T02).
113
118
  */
114
119
  declare function mapAnthropicStream(stream: AsyncIterable<AnthropicStreamEvent>, ids: IdMap, emit: (event: ChatEvent) => void, options?: {
115
120
  carryRetained?: Block[];
@@ -118,7 +123,7 @@ declare function mapAnthropicStream(stream: AsyncIterable<AnthropicStreamEvent>,
118
123
  * Projects an SDK/API error into the retryable WireError vocabulary:
119
124
  * 429 rate limits surface retryAfterMs and the x-ratelimit-* buckets; 529
120
125
  * overloaded and 5xx are retryable transport; everything else is terminal
121
- * transport (docs/04, section 4.9). Adapters never sleep internally.
126
+ * transport. Adapters never sleep internally.
122
127
  */
123
128
  declare function anthropicErrorToWire(error: unknown): WireError;
124
129
  //#endregion
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ function current(contextWindow, maxOutputTokens, pricing, cacheMinTokens) {
28
28
  cacheMinTokens
29
29
  };
30
30
  }
31
- /** Static seed table; docs/04 section 4 names the current model set. */
31
+ /** Static seed table naming the current model set. */
32
32
  const ANTHROPIC_MODELS = {
33
33
  "claude-fable-5": current(1e6, 128e3, {
34
34
  in: 20,
@@ -54,6 +54,22 @@ const ANTHROPIC_MODELS = {
54
54
  cacheRead: .3,
55
55
  cacheWrite: 3.75
56
56
  }, 2048),
57
+ "claude-haiku-4-5": (() => {
58
+ const base = current(2e5, 64e3, {
59
+ in: 1,
60
+ out: 5,
61
+ cacheRead: .1,
62
+ cacheWrite: 1.25
63
+ }, 2048);
64
+ return {
65
+ ...base,
66
+ caps: {
67
+ ...base.caps,
68
+ reasoningEfforts: []
69
+ },
70
+ thinkingForm: "enabled-budget"
71
+ };
72
+ })(),
57
73
  "claude-opus-4-6": {
58
74
  ...current(2e5, 32e3, {
59
75
  in: 15,
@@ -98,10 +114,10 @@ function anthropicModelInfo(model) {
98
114
  * the SSE-to-ChatEvent stream mapper with pause_turn absorption, the full
99
115
  * stop-reason table, and usage normalization under the Usage invariant.
100
116
  *
101
- * Owning spec: docs/04-model-layer-spec.md, section "@rulvar/anthropic".
117
+ * Adapter contract: https://docs.rulvar.com/guide/adapter-authors.
102
118
  * Pure functions; the adapter glue in adapter.ts owns the SDK client.
103
119
  */
104
- /** Bijective canonical-to-wire tool-call id map (docs/04, section 1.2). */
120
+ /** Bijective canonical-to-wire tool-call id map. */
105
121
  var IdMap = class {
106
122
  toWire = /* @__PURE__ */ new Map();
107
123
  toCanonical = /* @__PURE__ */ new Map();
@@ -126,6 +142,75 @@ var IdMap = class {
126
142
  return wireId;
127
143
  }
128
144
  };
145
+ /**
146
+ * Keywords the constrained-decoding schema validator rejects with a
147
+ * live 400 ("For 'integer' type, property 'minimum' is not
148
+ * supported"): every numeric bound plus, asymmetrically, maxItems
149
+ * (minItems, the string bounds, pattern, format, enum and const all
150
+ * pass). The validator guards exactly two wire positions: tools sent
151
+ * with strict: true and output_config.format json_schema; plain tools
152
+ * accept full JSON Schema. Probed live on claude-sonnet-5, 2026-07-11.
153
+ * The engine still validates tool args and
154
+ * structured output against the UNSCRUBBED schema, so the dropped
155
+ * keywords stay enforced; only the model-side hint is lost.
156
+ */
157
+ const CONSTRAINED_DECODING_UNSUPPORTED = /* @__PURE__ */ new Set([
158
+ "minimum",
159
+ "maximum",
160
+ "exclusiveMinimum",
161
+ "exclusiveMaximum",
162
+ "multipleOf",
163
+ "maxItems"
164
+ ]);
165
+ /** Keywords whose value is a map of property NAMES to schemas. */
166
+ const SCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
167
+ "properties",
168
+ "patternProperties",
169
+ "$defs",
170
+ "definitions"
171
+ ]);
172
+ /** Keywords whose value is a schema or an array of schemas. */
173
+ const SCHEMA_VALUE_KEYWORDS = /* @__PURE__ */ new Set([
174
+ "items",
175
+ "prefixItems",
176
+ "additionalProperties",
177
+ "additionalItems",
178
+ "propertyNames",
179
+ "contains",
180
+ "anyOf",
181
+ "oneOf",
182
+ "allOf",
183
+ "not",
184
+ "if",
185
+ "then",
186
+ "else"
187
+ ]);
188
+ /**
189
+ * Returns a deep copy of the schema with the unsupported keywords
190
+ * removed at SCHEMA positions only: a property literally named
191
+ * "minimum" (a key inside `properties`) survives. The input is never
192
+ * mutated; unrecognized keywords are copied through untouched.
193
+ */
194
+ function scrubForConstrainedDecoding(schema) {
195
+ if (Array.isArray(schema)) return schema.map((entry) => scrubForConstrainedDecoding(entry));
196
+ if (schema === null || typeof schema !== "object") return schema;
197
+ const out = {};
198
+ for (const [key, value] of Object.entries(schema)) {
199
+ if (CONSTRAINED_DECODING_UNSUPPORTED.has(key)) continue;
200
+ if (SCHEMA_MAP_KEYWORDS.has(key) && value !== null && typeof value === "object") {
201
+ const map = {};
202
+ for (const [name, sub] of Object.entries(value)) map[name] = scrubForConstrainedDecoding(sub);
203
+ out[key] = map;
204
+ continue;
205
+ }
206
+ if (SCHEMA_VALUE_KEYWORDS.has(key)) {
207
+ out[key] = scrubForConstrainedDecoding(value);
208
+ continue;
209
+ }
210
+ out[key] = value;
211
+ }
212
+ return out;
213
+ }
129
214
  function cacheControl(ttl) {
130
215
  return ttl === "1h" ? {
131
216
  type: "ephemeral",
@@ -135,8 +220,7 @@ function cacheControl(ttl) {
135
220
  /**
136
221
  * Builds Messages API params from a ChatRequest. cacheHint compiles into
137
222
  * cache_control breakpoints; beyond the provider cap of 4 the DEEPEST
138
- * breakpoints are kept and the shallowest dropped, deterministically
139
- * (docs/04, sections 1.7 and 4.4).
223
+ * breakpoints are kept and the shallowest dropped, deterministically.
140
224
  */
141
225
  function buildAnthropicParams(req, options) {
142
226
  const systemBlocks = [];
@@ -203,12 +287,15 @@ function buildAnthropicParams(req, options) {
203
287
  if (systemBlocks.length > 0) params.system = systemBlocks;
204
288
  if (req.stopSequences !== void 0) params.stop_sequences = req.stopSequences;
205
289
  if (req.tools !== void 0) {
206
- params.tools = req.tools.map((tool) => ({
207
- name: tool.name,
208
- description: tool.description,
209
- input_schema: tool.parameters,
210
- ...isStrictCompatibleSchema(tool.parameters) ? { strict: true } : {}
211
- }));
290
+ params.tools = req.tools.map((tool) => {
291
+ const strict = isStrictCompatibleSchema(tool.parameters);
292
+ return {
293
+ name: tool.name,
294
+ description: tool.description,
295
+ input_schema: strict ? scrubForConstrainedDecoding(tool.parameters) : tool.parameters,
296
+ ...strict ? { strict: true } : {}
297
+ };
298
+ });
212
299
  if (req.toolChoice === "required") params.tool_choice = { type: "any" };
213
300
  else if (typeof req.toolChoice === "object") params.tool_choice = {
214
301
  type: "tool",
@@ -222,7 +309,7 @@ function buildAnthropicParams(req, options) {
222
309
  if (req.effort !== void 0) outputConfig.effort = req.effort;
223
310
  if (req.schema !== void 0) outputConfig.format = {
224
311
  type: "json_schema",
225
- schema: req.schema
312
+ schema: scrubForConstrainedDecoding(req.schema)
226
313
  };
227
314
  if (anthropicOptions.task_budget !== void 0) outputConfig.task_budget = anthropicOptions.task_budget;
228
315
  if (Object.keys(outputConfig).length > 0) params.output_config = outputConfig;
@@ -264,7 +351,7 @@ function applyCacheHint(params, hint) {
264
351
  }
265
352
  }
266
353
  /**
267
- * The docs/04 section 4.7 stop-reason table. pause_turn never surfaces as
354
+ * The stop-reason table. pause_turn never surfaces as
268
355
  * a canonical finish: the adapter continues internally.
269
356
  */
270
357
  function mapStopReason(stopReason, stopDetails) {
@@ -310,7 +397,7 @@ function mapStopReason(stopReason, stopDetails) {
310
397
  /**
311
398
  * Normalizes Messages API usage under the Usage invariant: Anthropic
312
399
  * reports input_tokens EXCLUDING cache reads and writes, so the canonical
313
- * inputTokens is the sum of all three (docs/04, sections 1.6 and 4.4).
400
+ * inputTokens is the sum of all three.
314
401
  */
315
402
  function normalizeAnthropicUsage(raw) {
316
403
  const input = typeof raw?.input_tokens === "number" ? raw.input_tokens : 0;
@@ -330,7 +417,7 @@ function normalizeAnthropicUsage(raw) {
330
417
  * terminal finish unless the turn paused (pause_turn) or errored.
331
418
  * `carryRetained` holds thinking blocks from earlier pause_turn
332
419
  * continuations of the same turn so the terminal finish ships the whole
333
- * turn's retention payload (docs/04, section 2.3, M4-T02).
420
+ * turn's retention payload (M4-T02).
334
421
  */
335
422
  async function mapAnthropicStream(stream, ids, emit, options) {
336
423
  const mapping = {
@@ -496,7 +583,7 @@ async function mapAnthropicStream(stream, ids, emit, options) {
496
583
  * Projects an SDK/API error into the retryable WireError vocabulary:
497
584
  * 429 rate limits surface retryAfterMs and the x-ratelimit-* buckets; 529
498
585
  * overloaded and 5xx are retryable transport; everything else is terminal
499
- * transport (docs/04, section 4.9). Adapters never sleep internally.
586
+ * transport. Adapters never sleep internally.
500
587
  */
501
588
  function anthropicErrorToWire(error) {
502
589
  const record = error;
@@ -551,14 +638,14 @@ function anthropicErrorToWire(error) {
551
638
  * wire mapping, with pause_turn absorption, SDK autoretries disabled, and
552
639
  * refreshCaps from the capabilities-bearing model list.
553
640
  *
554
- * Owning spec: docs/04-model-layer-spec.md, section "@rulvar/anthropic".
641
+ * Full contract: https://docs.rulvar.com/guide/providers.
555
642
  */
556
- /** pause_turn continuation cap (docs/06 Appendix A; docs/04, section 4.6). */
643
+ /** pause_turn continuation cap. */
557
644
  const DEFAULT_PAUSE_TURN_MAX_CONTINUATIONS = 5;
558
645
  /**
559
646
  * Creates the first-class Anthropic adapter (id 'anthropic'). SDK
560
647
  * autoretries are disabled (max_retries 0): the core owns retries and
561
- * wall-clock (docs/04, section "Retries belong to the core").
648
+ * wall-clock.
562
649
  */
563
650
  function anthropic(options = {}) {
564
651
  const client = options.client ?? new Anthropic({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/anthropic",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "rulvar first-class provider adapter over @anthropic-ai/sdk.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@anthropic-ai/sdk": "^0.110.0",
26
- "@rulvar/core": "1.0.0"
26
+ "@rulvar/core": "1.2.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.20.0",