@axlsdk/axl 0.25.1 → 0.25.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.
package/README.md CHANGED
@@ -105,7 +105,7 @@ import { lookupOrder } from '../tools/db.js';
105
105
 
106
106
  export const supportAgent = agent({
107
107
  name: 'support',
108
- model: 'openai-responses:gpt-5.5',
108
+ model: 'openai-responses:gpt-6-sol',
109
109
  system: 'You are a customer support agent. Use tools to look up order information.',
110
110
  tools: [lookupOrder],
111
111
  });
@@ -206,13 +206,12 @@ import { agent } from '@axlsdk/axl';
206
206
 
207
207
  const researcher = agent({
208
208
  name: 'researcher',
209
- model: 'openai-responses:gpt-5.5',
209
+ model: 'openai-responses:gpt-6-sol',
210
210
  system: 'You are a research assistant.',
211
211
  tools: [calculator],
212
212
  effort: 'high',
213
213
  maxTurns: 10,
214
214
  timeout: '30s',
215
- temperature: 0.7,
216
215
  version: 'v1.2',
217
216
  });
218
217
  ```
@@ -223,8 +222,8 @@ Dynamic model and system prompt selection:
223
222
  const dynamicAgent = agent({
224
223
  model: (ctx) =>
225
224
  ctx.metadata?.tier === 'premium'
226
- ? 'openai-responses:gpt-5.5'
227
- : 'openai-responses:gpt-5-nano',
225
+ ? 'openai-responses:gpt-6-sol'
226
+ : 'openai-responses:gpt-6-luna',
228
227
  system: (ctx) => `You are a ${ctx.metadata?.role ?? 'general'} assistant.`,
229
228
  });
230
229
  ```
@@ -263,7 +262,7 @@ for precedence, nested asks, and provider timing details.
263
262
 
264
263
  ```typescript
265
264
  const router = agent({
266
- model: 'openai-responses:gpt-5-mini',
265
+ model: 'openai-responses:gpt-6-luna',
267
266
  system: 'Route to the right specialist.',
268
267
  handoffs: (ctx) => {
269
268
  const base = [
@@ -298,7 +297,7 @@ The `effort` parameter provides a unified way to control reasoning depth across
298
297
  ```typescript
299
298
  // Simple levels — works on any provider
300
299
  const reasoner = agent({
301
- model: 'anthropic:claude-opus-4-7',
300
+ model: 'anthropic:claude-opus-5-5',
302
301
  system: 'You are a careful analyst.',
303
302
  effort: 'high', // 'none' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
304
303
  });
@@ -402,8 +401,8 @@ To observe events between `ctx.ask()` calls — e.g., streaming `partial_object`
402
401
  // compiles standalone.
403
402
  const outlineSchema = z.object({ outline: z.array(z.string()) });
404
403
  const draftSchema = z.object({ draft: z.string() });
405
- const planner = agent({ model: 'openai:gpt-4o', system: 'Plan an outline.' });
406
- const writer = agent({ model: 'openai:gpt-4o', system: 'Write a draft.' });
404
+ const planner = agent({ model: 'openai-responses:gpt-6-sol', system: 'Plan an outline.' });
405
+ const writer = agent({ model: 'openai-responses:gpt-6-sol', system: 'Write a draft.' });
407
406
 
408
407
  const wf = workflow({
409
408
  name: 'two-step',
@@ -631,7 +630,7 @@ const containsPII = (text: string) => /\b\d{3}-\d{2}-\d{4}\b/.test(text);
631
630
  const isOffTopic = (text: string) => !text.toLowerCase().includes('support');
632
631
 
633
632
  const safe = agent({
634
- model: 'openai-responses:gpt-5.5',
633
+ model: 'openai-responses:gpt-6-sol',
635
634
  system: 'You are a helpful assistant.',
636
635
  guardrails: {
637
636
  input: async (prompt, ctx) => {
@@ -786,7 +785,7 @@ const session = runtime.session('user-123', {
786
785
  history: {
787
786
  maxMessages: 100,
788
787
  summarize: true,
789
- summaryModel: 'openai-responses:gpt-5-mini',
788
+ summaryModel: 'openai-responses:gpt-6-luna',
790
789
  },
791
790
  persist: true,
792
791
  });
@@ -826,10 +825,10 @@ import {
826
825
  Four native adapters using the `provider:model` URI scheme (plus the OpenAI-compatible presets above):
827
826
 
828
827
  ```
829
- openai-responses:gpt-5.5 # OpenAI Responses API (preferred over Chat Completions)
830
- openai:gpt-5.4 # OpenAI Chat Completions
831
- anthropic:claude-opus-4-8 # Anthropic (supports effort: 'xhigh' and 'max')
832
- google:gemini-3.1-pro-preview # Google Gemini
828
+ openai-responses:gpt-6-sol # OpenAI Responses API (preferred over Chat Completions)
829
+ openai:gpt-6-sol # OpenAI Chat Completions (text; use Responses for tools)
830
+ anthropic:claude-sonnet-5-5 # Anthropic (also claude-haiku-5-5, claude-opus-5-5)
831
+ google:gemini-3.8-flash # Google Gemini
833
832
  ```
834
833
 
835
834
  See [docs/providers.md](../../docs/providers.md) for the full model list including reasoning models.
package/dist/index.cjs CHANGED
@@ -581,7 +581,7 @@ var sharedGuard = typeof installedGuard === "object" && installedGuard !== null
581
581
  var buildPath = typeof __AXL_BUILD_PATH__ === "string" ? __AXL_BUILD_PATH__ : "<source>";
582
582
  var thisCopy = {
583
583
  path: buildPath.startsWith("file:") ? (0, import_node_url.fileURLToPath)(buildPath) : buildPath,
584
- version: true ? "0.25.1" : "unknown"
584
+ version: true ? "0.25.2" : "unknown"
585
585
  };
586
586
  function warnCrossCopy(other, joined) {
587
587
  if (other === thisCopy) return;
@@ -5146,6 +5146,17 @@ var ANTHROPIC_THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
5146
5146
  function isRecord(value) {
5147
5147
  return value !== null && typeof value === "object" && !Array.isArray(value);
5148
5148
  }
5149
+ function isFinalThinkingAdaptive(capability, body) {
5150
+ const thinking = body.thinking;
5151
+ if (thinking === void 0) {
5152
+ return capability.thinking === "adaptive-default-on" || capability.thinking === "adaptive-always-on";
5153
+ }
5154
+ return isRecord(thinking) && thinking.type === "adaptive";
5155
+ }
5156
+ var NATIVE_CLAUDE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
5157
+ function isNativeClaudeEffort(value) {
5158
+ return NATIVE_CLAUDE_EFFORTS.includes(value);
5159
+ }
5149
5160
  function hasReplayedThinking(body) {
5150
5161
  return Array.isArray(body.messages) && body.messages.some(
5151
5162
  (message) => isRecord(message) && Array.isArray(message.content) && message.content.some(
@@ -5180,11 +5191,40 @@ function hasAnthropicProviderFileImage(messages) {
5180
5191
  );
5181
5192
  }
5182
5193
  var CLAUDE_CAPABILITIES = {
5194
+ // Thinking off is `between_tools` (progress updates between tool calls);
5195
+ // `disabled` is a provider 400. Forced tool choice is a 400.
5196
+ "claude-sonnet-5-5": {
5197
+ thinking: "adaptive-default-on",
5198
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
5199
+ manualBudget: false,
5200
+ stripTemperature: true,
5201
+ disableAt: "high",
5202
+ offShape: "between_tools",
5203
+ forcedToolChoice: "rejected",
5204
+ defaultOnlySampling: true,
5205
+ strictThinking: true,
5206
+ thinkingBinding: true
5207
+ },
5208
+ // Thinking off is `disabled`, only at effort high or below. Forced tool
5209
+ // choice is accepted (the provider skips thinking for that turn).
5210
+ "claude-haiku-5-5": {
5211
+ thinking: "adaptive-default-on",
5212
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
5213
+ manualBudget: false,
5214
+ stripTemperature: true,
5215
+ disableAt: "high",
5216
+ defaultOnlySampling: true,
5217
+ strictThinking: true,
5218
+ thinkingBinding: true
5219
+ },
5183
5220
  "claude-opus-5-5": {
5184
5221
  thinking: "adaptive-always-on",
5185
5222
  effortLevels: ["low", "medium", "high", "xhigh", "max"],
5186
5223
  manualBudget: false,
5187
5224
  stripTemperature: true,
5225
+ forcedToolChoice: "rejected",
5226
+ defaultOnlySampling: true,
5227
+ strictThinking: true,
5188
5228
  thinkingBinding: true
5189
5229
  },
5190
5230
  "claude-fable-5-1": {
@@ -5192,6 +5232,9 @@ var CLAUDE_CAPABILITIES = {
5192
5232
  effortLevels: ["low", "medium", "high", "xhigh", "max"],
5193
5233
  manualBudget: false,
5194
5234
  stripTemperature: true,
5235
+ forcedToolChoice: "rejected",
5236
+ defaultOnlySampling: true,
5237
+ strictThinking: true,
5195
5238
  thinkingBinding: true
5196
5239
  },
5197
5240
  "claude-fable-5": {
@@ -5286,6 +5329,15 @@ function resolveClaudeCapability(model) {
5286
5329
  }
5287
5330
  var DEFAULT_CACHE_READ_MULTIPLIER = 0.1;
5288
5331
  var ANTHROPIC_RATES = {
5332
+ // Sonnet 5.5 cache reads are 0.05x ($0.10/MTok): the pricing table, its
5333
+ // footnote, the models overview, and the prompt-caching guide agree
5334
+ // (re-checked 2026-10-07).
5335
+ "claude-sonnet-5-5": { input: 2e-6, output: 1e-5, cacheReadMultiplier: 0.05 },
5336
+ "claude-haiku-5-5": {
5337
+ input: 1e-7,
5338
+ output: 5e-7,
5339
+ longPrompt: { aboveTokens: 1e5, card: { input: 5e-7, output: 25e-7 } }
5340
+ },
5289
5341
  "claude-opus-5-5": { input: 4e-6, output: 2e-5, cacheReadMultiplier: 0.05 },
5290
5342
  "claude-fable-5-1": { input: 1e-5, output: 5e-5, cacheReadMultiplier: 0.025 },
5291
5343
  "claude-fable-5": { input: 1e-5, output: 5e-5 },
@@ -5348,14 +5400,16 @@ function estimateAnthropicCost(model, usage) {
5348
5400
  return void 0;
5349
5401
  }
5350
5402
  const cacheWriteTotal = safeTokenSum(cacheWrite5m, cacheWrite1h);
5351
- if (cacheWriteTotal === void 0 || safeTokenSum(usage.inputTokens, cacheRead, cacheWriteTotal) === void 0) {
5403
+ const promptTokens = cacheWriteTotal === void 0 ? void 0 : safeTokenSum(usage.inputTokens, cacheRead, cacheWriteTotal);
5404
+ if (cacheWriteTotal === void 0 || promptTokens === void 0) {
5352
5405
  return void 0;
5353
5406
  }
5354
5407
  if (usage.aggregateCacheWriteTokens !== void 0 && (!isValidTokenCount(usage.aggregateCacheWriteTokens) || usage.aggregateCacheWriteTokens !== cacheWriteTotal)) {
5355
5408
  return void 0;
5356
5409
  }
5357
- const { input, output } = rate;
5358
- const cacheReadRate = input * (rate.cacheReadMultiplier ?? DEFAULT_CACHE_READ_MULTIPLIER);
5410
+ const card = rate.longPrompt && promptTokens > rate.longPrompt.aboveTokens ? rate.longPrompt.card : rate;
5411
+ const { input, output } = card;
5412
+ const cacheReadRate = input * (card.cacheReadMultiplier ?? DEFAULT_CACHE_READ_MULTIPLIER);
5359
5413
  return usage.inputTokens * input + cacheRead * cacheReadRate + cacheWrite5m * input * 1.25 + cacheWrite1h * input * 2 + usage.outputTokens * output;
5360
5414
  }
5361
5415
  var THINKING_BUDGETS = {
@@ -5417,7 +5471,7 @@ function resolveClaudeThinking(model, resolved) {
5417
5471
  };
5418
5472
  }
5419
5473
  return {
5420
- thinking: { type: "disabled" },
5474
+ thinking: { type: capability.offShape ?? "disabled" },
5421
5475
  outputConfig: activeEffort ? { effort: activeEffort } : void 0,
5422
5476
  stripTemperature: true
5423
5477
  };
@@ -5823,50 +5877,106 @@ var AnthropicProvider = class {
5823
5877
  ...betas.length > 0 ? { "anthropic-beta": betas.join(",") } : {}
5824
5878
  };
5825
5879
  }
5880
+ /**
5881
+ * Whether the final body needs the binding beta header. Runs after
5882
+ * `validateAndBindFinalBody`, which has already injected `block_binding`
5883
+ * wherever it applies, so the header follows the final body rather than
5884
+ * re-deriving the injection rule.
5885
+ */
5826
5886
  requiresBindingBeta(body) {
5887
+ const capability = resolveClaudeCapability(String(body.model));
5827
5888
  const thinking = body.thinking;
5828
- return resolveClaudeCapability(String(body.model))?.thinkingBinding === true && (hasReplayedThinking(body) || isRecord(thinking) && thinking.block_binding !== void 0);
5889
+ return capability?.thinkingBinding === true && isRecord(thinking) && thinking.type === "adaptive" && thinking.block_binding !== void 0;
5829
5890
  }
5891
+ /**
5892
+ * Validate the final body (after `providerOptions`) against the effective
5893
+ * model's capability, then inject `block_binding` where it applies. Every
5894
+ * rule is gated on its own capability field and is a no-op when unset, so a
5895
+ * model only gets the guards its provider contract calls for.
5896
+ */
5830
5897
  validateAndBindFinalBody(body) {
5831
5898
  const model = typeof body.model === "string" ? body.model : "";
5832
- if (resolveClaudeCapability(model)?.thinkingBinding !== true) return;
5899
+ const capability = resolveClaudeCapability(model);
5900
+ if (!capability) return;
5833
5901
  const reject = (option, remediation) => {
5834
5902
  throw new UnsupportedModelOptionError({ provider: this.name, model, option, remediation });
5835
5903
  };
5836
- const toolChoice = body.tool_choice;
5837
- if (isRecord(toolChoice) && (toolChoice.type === "any" || toolChoice.type === "tool")) {
5838
- reject("forced tool choice", "Use toolChoice: 'auto' or 'none' on this model.");
5839
- }
5840
- if (body.thinking !== void 0 && (!isRecord(body.thinking) || body.thinking.type !== "adaptive" || body.thinking.budget_tokens !== void 0)) {
5841
- reject(
5842
- "manual or disabled thinking",
5843
- "Use thinking: { type: 'adaptive' } and output_config.effort."
5844
- );
5845
- }
5846
- const outputConfig = body.output_config;
5847
- if (outputConfig !== void 0 && (!isRecord(outputConfig) || outputConfig.effort !== void 0 && !["low", "medium", "high", "xhigh", "max"].includes(String(outputConfig.effort)))) {
5848
- reject("output_config.effort", "Use low, medium, high, xhigh, or max effort.");
5849
- }
5850
- if (body.temperature !== void 0 && body.temperature !== 1) {
5851
- reject("temperature", "Omit temperature or use the default value 1.");
5852
- }
5853
- if (body.top_p !== void 0 && (typeof body.top_p !== "number" || body.top_p < 0.99 || body.top_p > 1)) {
5854
- reject("top_p", "Omit top_p or use a value from 0.99 to 1.");
5904
+ if (capability.forcedToolChoice === "rejected") {
5905
+ const toolChoice = body.tool_choice;
5906
+ if (isRecord(toolChoice) && (toolChoice.type === "any" || toolChoice.type === "tool")) {
5907
+ reject("forced tool choice", "Use toolChoice: 'auto' or 'none' on this model.");
5908
+ }
5909
+ }
5910
+ if (capability.strictThinking) {
5911
+ const offShape = capability.thinking === "adaptive-always-on" ? void 0 : capability.offShape ?? "disabled";
5912
+ const thinking = body.thinking;
5913
+ const outputConfig = body.output_config;
5914
+ const effort = isRecord(outputConfig) ? outputConfig.effort : void 0;
5915
+ if (thinking !== void 0 && offShape === void 0) {
5916
+ if (!isRecord(thinking) || thinking.type !== "adaptive" || thinking.budget_tokens !== void 0) {
5917
+ reject(
5918
+ "manual or disabled thinking",
5919
+ "Use thinking: { type: 'adaptive' } and output_config.effort."
5920
+ );
5921
+ }
5922
+ } else if (thinking !== void 0) {
5923
+ if (!isRecord(thinking) || thinking.type !== "adaptive" && thinking.type !== offShape) {
5924
+ reject(
5925
+ "thinking.type",
5926
+ `Use thinking: { type: 'adaptive' }, or { type: '${offShape}' } to turn thinking off.`
5927
+ );
5928
+ }
5929
+ const record = thinking;
5930
+ if (record.type === "adaptive" && record.budget_tokens !== void 0) {
5931
+ reject("thinking.budget_tokens", "Use output_config.effort instead of budget_tokens.");
5932
+ }
5933
+ if (record.type === offShape) {
5934
+ const extra = Object.keys(record).filter((key) => key !== "type");
5935
+ if (extra.length > 0) {
5936
+ reject(
5937
+ `thinking.${offShape}.${extra[0]}`,
5938
+ `Send thinking: { type: '${offShape}' } with no other fields.`
5939
+ );
5940
+ }
5941
+ }
5942
+ }
5943
+ if (outputConfig !== void 0 && (!isRecord(outputConfig) || effort !== void 0 && !isNativeClaudeEffort(effort))) {
5944
+ reject("output_config.effort", "Use low, medium, high, xhigh, or max effort.");
5945
+ }
5946
+ if (offShape !== void 0 && isRecord(thinking) && thinking.type === offShape && effort !== void 0 && capability.disableAt && !isEffortAtOrBelow(effort, capability.disableAt)) {
5947
+ reject(
5948
+ "output_config.effort",
5949
+ `Thinking can be turned off only at effort '${capability.disableAt}' or below; lower the effort or use adaptive thinking.`
5950
+ );
5951
+ }
5855
5952
  }
5856
- if (body.top_k !== void 0) {
5857
- reject("top_k", "Omit top_k on this model.");
5953
+ if (capability.defaultOnlySampling) {
5954
+ if (body.temperature !== void 0 && body.temperature !== 1) {
5955
+ reject("temperature", "Omit temperature or use the default value 1.");
5956
+ }
5957
+ if (body.top_p !== void 0 && body.top_p !== 0.99) {
5958
+ reject("top_p", "Omit top_p or use the default value 0.99.");
5959
+ }
5960
+ if (body.temperature !== void 0 && body.top_p !== void 0) {
5961
+ reject("temperature and top_p", "Send at most one of temperature and top_p.");
5962
+ }
5963
+ if (body.top_k !== void 0) {
5964
+ reject("top_k", "Omit top_k on this model.");
5965
+ }
5858
5966
  }
5859
- const replayed = hasReplayedThinking(body);
5860
- if (!replayed) return;
5861
- const thinking = isRecord(body.thinking) ? body.thinking : { type: "adaptive" };
5862
- const binding = thinking.block_binding;
5863
- if (binding !== void 0 && (!isRecord(binding) || binding.prefix_mismatch_behavior !== "error" && binding.prefix_mismatch_behavior !== "drop_block")) {
5864
- reject("thinking.block_binding", "Use prefix_mismatch_behavior: 'error' or 'drop_block'.");
5967
+ if (capability.thinkingBinding === true && isFinalThinkingAdaptive(capability, body)) {
5968
+ const thinking = isRecord(body.thinking) ? body.thinking : { type: "adaptive" };
5969
+ const binding = thinking.block_binding;
5970
+ if (binding !== void 0 && (!isRecord(binding) || binding.prefix_mismatch_behavior !== "error" && binding.prefix_mismatch_behavior !== "drop_block")) {
5971
+ reject("thinking.block_binding", "Use prefix_mismatch_behavior: 'error' or 'drop_block'.");
5972
+ }
5973
+ if (hasReplayedThinking(body)) {
5974
+ body.thinking = {
5975
+ ...thinking,
5976
+ block_binding: binding ?? { prefix_mismatch_behavior: "drop_block" }
5977
+ };
5978
+ }
5865
5979
  }
5866
- body.thinking = {
5867
- ...thinking,
5868
- block_binding: binding ?? { prefix_mismatch_behavior: "drop_block" }
5869
- };
5870
5980
  }
5871
5981
  /** Extract a human-readable message from an API error response body. */
5872
5982
  extractErrorMessage(body, status) {