@axlsdk/axl 0.18.2 → 0.19.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
@@ -132,6 +132,28 @@ interface Provider {
132
132
  * Stream a chat completion, yielding chunks as they arrive.
133
133
  */
134
134
  stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk>;
135
+ /**
136
+ * How this provider honors a `responseFormat: { type: 'json_schema' }` derived
137
+ * from the caller's Zod schema, for `model` (spec 22, Problem B / R10). Used by
138
+ * the runtime to warn — via a `schema_diagnostic` — when `nativeStructuredOutput`
139
+ * can't be honored, WITHOUT failing the call (portable multi-provider code must
140
+ * still run). Optional: an adapter that omits it is assumed to honor the schema
141
+ * fully (`'schema'`), so unknown adapters simply opt out of the warning.
142
+ *
143
+ * - `'schema'` — accepts a `json_schema` response format for this model (the
144
+ * strongest tier; e.g. OpenAI/Responses). Note: Axl currently sends
145
+ * NON-strict `json_schema` (it does not set `strict: true`), so on OpenAI
146
+ * this is schema-as-guidance, not hard constrained decoding — true strict
147
+ * mode needs an OpenAI-strict-subset schema transform and is a follow-up.
148
+ * - `'downgraded'` — accepted but downgraded to plain `json_object`; the schema
149
+ * shape is NOT enforced structurally (e.g. an OpenAI-compatible profile with
150
+ * `supportsJsonSchema: false`).
151
+ * - `'lossy'` — accepted but the schema is sanitized and may lose keywords
152
+ * (e.g. Gemini strips `$ref`/`$defs`/`additionalProperties`).
153
+ * - `'unsupported'` — ignored structurally; the provider relies on the prompt
154
+ * JSON instruction instead (e.g. Anthropic).
155
+ */
156
+ nativeStructuredOutputSupport?(model: string): 'schema' | 'downgraded' | 'lossy' | 'unsupported';
135
157
  }
136
158
  /**
137
159
  * Alias for Provider. Used for backward compatibility with index.ts exports.
@@ -155,6 +177,15 @@ type ResolvedThinkingOptions = {
155
177
  /** Resolve effort/thinkingBudget/includeThoughts into normalized form.
156
178
  * Validates inputs and computes derived flags used by all provider adapters. */
157
179
  declare function resolveThinkingOptions(options: Pick<ChatOptions, 'effort' | 'thinkingBudget' | 'includeThoughts'>): ResolvedThinkingOptions;
180
+ /**
181
+ * An API key, or a function that produces one per request. The function form
182
+ * supports expiring credentials (Azure-Entra, Databricks/IBM OAuth, Bedrock
183
+ * short-term tokens): the caller's callback owns refresh/caching; Axl invokes it
184
+ * once per request and does not memoize. See `docs/providers.md`.
185
+ */
186
+ type ApiKeySource = string | (() => string | Promise<string>);
187
+ /** Resolve an {@link ApiKeySource} to a concrete key for a single request. */
188
+ declare function resolveApiKey(source: ApiKeySource | undefined): Promise<string>;
158
189
 
159
190
  /** Result type for concurrent operations (spawn, map) */
160
191
  type Result<T> = {
@@ -169,6 +200,14 @@ type BudgetResult<T> = {
169
200
  value: T | null;
170
201
  budgetExceeded: boolean;
171
202
  totalCost: number;
203
+ /**
204
+ * True when the block ran a model with no usable cost (unpriced / pricing-table
205
+ * miss) that did measurable work. `totalCost` is then a LOWER BOUND — the unknown
206
+ * component is omitted. NOTE: this is observability only; cost limits / `hard_stop`
207
+ * are NOT enforced on unpriced spend (the enforcement rail never sees it). Token
208
+ * budgets are the future fix for governing unpriced models.
209
+ */
210
+ unpriced: boolean;
172
211
  };
173
212
  /** Human decision from awaitHuman */
174
213
  type HumanDecision = {
@@ -226,9 +265,34 @@ type AwaitHumanOptions = {
226
265
  prompt: string;
227
266
  metadata?: Record<string, unknown>;
228
267
  };
268
+ /**
269
+ * Controls how the output `schema` is rendered into the MODEL-FACING prompt
270
+ * (spec 22, Problem B). Independent of the `.parse` gate — whichever mode you
271
+ * pick, the full Zod `schema` still validates the reply.
272
+ *
273
+ * - `'json-schema'` (default) — append the compact, `$ref`-hoisted JSON-Schema
274
+ * guidance (`Respond with valid JSON matching this schema: …`).
275
+ * - `'none'` — append NOTHING; the schema is the parse gate only. Fires a
276
+ * `schema_prompt_none_no_guidance` diagnostic (the model gets zero shape hint).
277
+ * - `{ render }` — append exactly this string, or the string your function
278
+ * returns from the schema. Object form (not a bare string) so custom text can
279
+ * never collide with the `'none'` / `'json-schema'` sentinels.
280
+ */
281
+ type SchemaPromptOption = 'json-schema' | 'none' | {
282
+ render: string | ((schema: z.ZodType<unknown>) => string);
283
+ };
229
284
  /** Ask options */
230
285
  type AskOptions<T = unknown> = {
231
286
  schema?: z.ZodType<T>;
287
+ /** How the `schema` is rendered into the model-facing prompt. Default
288
+ * `'json-schema'`. Does not affect the `.parse` gate. See `SchemaPromptOption`. */
289
+ schemaPrompt?: SchemaPromptOption;
290
+ /** Opt into the provider's NATIVE structured-output path (`json_schema`),
291
+ * deriving the provider schema from `schema` (no second, contradictable
292
+ * schema). Providers that can't honor it downgrade/ignore it and a
293
+ * `schema_diagnostic` warns; the call still proceeds. Only takes effect when
294
+ * a `schema` is set and no tools are registered. Default `false`. */
295
+ nativeStructuredOutput?: boolean;
232
296
  retries?: number;
233
297
  /** Post-schema business rule validation. Receives the parsed typed object after schema
234
298
  * validation succeeds. Only runs when `schema` is set. Retries with accumulating context
@@ -259,6 +323,12 @@ type AskOptions<T = unknown> = {
259
323
  type DelegateOptions<T = unknown> = {
260
324
  /** Zod schema for structured output from the selected agent. */
261
325
  schema?: z.ZodType<T>;
326
+ /** How the `schema` is rendered into the selected agent's prompt. Forwarded to
327
+ * the terminal `ctx.ask()`. See `SchemaPromptOption`. */
328
+ schemaPrompt?: SchemaPromptOption;
329
+ /** Opt the terminal `ctx.ask()` into the provider's native structured-output
330
+ * path. Forwarded to the selected agent's ask. */
331
+ nativeStructuredOutput?: boolean;
262
332
  /** Model URI for the internal router agent (default: first candidate's model). */
263
333
  routerModel?: string;
264
334
  /** Additional metadata passed to the router and selected agent. */
@@ -284,7 +354,7 @@ type ExecutionStatus = 'running' | 'completed' | 'failed' | 'waiting';
284
354
  * derive `AxlEventType` from this tuple and use it to validate stream filters,
285
355
  * redaction tables, and exhaustiveness assertions.
286
356
  */
287
- declare const AXL_EVENT_TYPES: readonly ["workflow_start", "workflow_end", "ask_start", "ask_end", "agent_call_start", "agent_call_end", "token", "tool_call_start", "tool_call_end", "tool_approval", "tool_denied", "delegate", "handoff_start", "handoff_return", "pipeline", "partial_object", "string_delta", "verify", "log", "memory_remember", "memory_recall", "memory_forget", "checkpoint_save", "checkpoint_replay", "await_human", "await_human_resolved", "guardrail", "schema_check", "validate", "done", "error"];
357
+ declare const AXL_EVENT_TYPES: readonly ["workflow_start", "workflow_end", "ask_start", "ask_end", "agent_call_start", "agent_call_end", "token", "tool_call_start", "tool_call_end", "tool_approval", "tool_denied", "delegate", "handoff_start", "handoff_return", "pipeline", "partial_object", "string_delta", "verify", "schema_diagnostic", "log", "memory_remember", "memory_recall", "memory_forget", "checkpoint_save", "checkpoint_replay", "await_human", "await_human_resolved", "guardrail", "schema_check", "validate", "done", "error"];
288
358
  /** Discriminator union derived from `AXL_EVENT_TYPES`. */
289
359
  type AxlEventType = (typeof AXL_EVENT_TYPES)[number];
290
360
  /** Resolved model parameters sent to the provider for an LLM call. */
@@ -343,6 +413,13 @@ type AgentCallEndData = {
343
413
  * abort, etc). Mutually exclusive with `response` content. Subject to
344
414
  * `config.trace.redact` (vendor errors can echo prompt text). */
345
415
  error?: string;
416
+ /** HTTP status when the thrown error was a `ProviderError` (`0` for network
417
+ * failures). Omitted for non-provider errors. The raw error body is
418
+ * intentionally NOT placed on the event (it's redaction-eligible). */
419
+ status?: number;
420
+ /** Semantic failover hint from a thrown `ProviderError` (see
421
+ * `ProviderError.retryable`). Omitted for non-provider errors. */
422
+ retryable?: boolean;
346
423
  };
347
424
  /** Data shape for `tool_call_end` events. */
348
425
  type ToolCallData = {
@@ -434,6 +511,51 @@ type StringDeltaData = {
434
511
  */
435
512
  delta: string;
436
513
  };
514
+ /**
515
+ * Data for the `schema_diagnostic` event (spec 22, Problem E) — a `kind`-
516
+ * discriminated payload mirroring `pipeline`'s two-level union. Each variant
517
+ * surfaces one silent structured-output cliff:
518
+ *
519
+ * - `prompt_schema_oversized` — an appended prompt schema (or a tool-def
520
+ * schema) exceeds the token threshold; the schema is a recurring input cost.
521
+ * - `dropped_refinements` — the schema carries `.refine()`/`.superRefine()`
522
+ * rules `z.toJSONSchema` drops, so the model is never told and `.parse` may
523
+ * reject → wasted retries. `paths` are structural field paths (not PII).
524
+ * - `streaming_disabled` — progressive `partial_object` streaming is off
525
+ * because the schema root isn't a `ZodObject` (`non-object`) or tools are
526
+ * present (`tools`).
527
+ * - `schema_prompt_none_no_guidance` — `schemaPrompt:'none'` was set with a
528
+ * schema and no override, so the model gets zero shape guidance (parse gate
529
+ * only). Emitted from the Problem-B path (spec 22 §5.3).
530
+ */
531
+ type SchemaDiagnosticData = {
532
+ kind: 'prompt_schema_oversized';
533
+ estimatedTokens: number;
534
+ threshold: number;
535
+ site: 'prompt' | 'tool';
536
+ tool?: string;
537
+ } | {
538
+ kind: 'dropped_refinements';
539
+ count: number;
540
+ paths?: string[];
541
+ site: 'prompt' | 'tool';
542
+ tool?: string;
543
+ } | {
544
+ kind: 'streaming_disabled';
545
+ rootType: string;
546
+ cause: 'non-object' | 'tools';
547
+ } | {
548
+ kind: 'schema_prompt_none_no_guidance';
549
+ } | {
550
+ /** `nativeStructuredOutput` was requested but the resolved provider can't
551
+ * honor the derived schema — it downgrades it to plain JSON mode
552
+ * (`downgraded`), sanitizes it lossily (`lossy`, e.g. Gemini strips
553
+ * keywords), or ignores it structurally and relies on the prompt
554
+ * (`unsupported`, e.g. Anthropic). The call proceeds regardless (O5). */
555
+ kind: 'native_output_unsupported';
556
+ provider?: string;
557
+ support: 'downgraded' | 'lossy' | 'unsupported';
558
+ };
437
559
  /** Data shape for legacy `guardrail` events. Replaced by `pipeline` in PR 2. */
438
560
  type GuardrailData = {
439
561
  guardrailType: 'input' | 'output';
@@ -556,7 +678,9 @@ type AxlEventBase = {
556
678
  * - **Leaf cost** (`agent_call_end`, `tool_call_end`, `memory_remember`,
557
679
  * `memory_recall`): the authoritative charge for this single
558
680
  * provider call / tool invocation / embedder call. Summing these
559
- * across an execution gives the true spend.
681
+ * across an execution gives the spend — a LOWER BOUND when any call
682
+ * used an unpriced model (its `cost` is `undefined` and contributes
683
+ * nothing; the owning `ask_end` sets `unpriced: true`).
560
684
  *
561
685
  * - **Per-ask rollup** (`ask_end`): the SUM of leaf costs emitted
562
686
  * within this ask's frame, EXCLUDING nested asks (which roll up
@@ -658,6 +782,12 @@ type AxlEvent = (AxlEventBase & {
658
782
  /** Sum of `agent_call_end.cost` + `tool_call_end.cost` WITHIN THIS ASK,
659
783
  * excluding nested asks. Nested asks contribute to their own ask_end. */
660
784
  cost: number;
785
+ /** True when a cost-bearing leaf in this ask did measurable work but had
786
+ * no usable cost (unpriced model / pricing-table miss). When set, `cost`
787
+ * is a LOWER BOUND — the unknown component is not included. Absent ⇒
788
+ * `cost` is exact. Surfaced so dashboards don't render a misleading
789
+ * exact `$0.00` for an ask that used an unpriced model. */
790
+ unpriced?: boolean;
661
791
  duration: number;
662
792
  }) | (AxlEventBase & AskScoped & {
663
793
  type: 'agent_call_start';
@@ -671,8 +801,9 @@ type AxlEvent = (AxlEventBase & {
671
801
  type: 'agent_call_end';
672
802
  agent: string;
673
803
  model: string;
674
- /** Authoritative turn-level cost. */
675
- cost: number;
804
+ /** Authoritative turn-level cost. `undefined` for an unpriced model
805
+ * (pricing-table miss) and absent on the error path (no usage). */
806
+ cost?: number;
676
807
  duration: number;
677
808
  tokens?: {
678
809
  input?: number;
@@ -763,6 +894,9 @@ type AxlEvent = (AxlEventBase & {
763
894
  }) | (AxlEventBase & AskScoped & {
764
895
  type: 'verify';
765
896
  data: VerifyData;
897
+ }) | (AxlEventBase & AskScoped & {
898
+ type: 'schema_diagnostic';
899
+ data: SchemaDiagnosticData;
766
900
  }) | (AxlEventBase & Partial<AskScoped> & {
767
901
  type: 'guardrail';
768
902
  data?: GuardrailData;
@@ -849,7 +983,17 @@ type ExecutionInfo = {
849
983
  * here (stream-only); aggregate `tokens: { input, output, reasoning? }`
850
984
  * on `agent_call_end` is the persisted token representation. */
851
985
  events: AxlEvent[];
986
+ /** Total spend for the execution. A LOWER BOUND when an unpriced model ran
987
+ * (those calls contribute nothing) — read {@link ExecutionInfo.unpriced} to
988
+ * know whether it's exact, rather than scanning the event timeline. */
852
989
  totalCost: number;
990
+ /** True when any cost-bearing leaf in this execution did billable work but had
991
+ * no usable cost (unpriced model / pricing-table miss): `totalCost` is then a
992
+ * LOWER BOUND, not exact. `false` means every cost-bearing call was priced.
993
+ * The aggregate counterpart of `ask_end.unpriced` / `BudgetResult.unpriced`,
994
+ * computed via `isUnpricedLeaf`. `undefined` only on executions recorded
995
+ * before this field existed (back-compat). */
996
+ unpriced?: boolean;
853
997
  startedAt: number;
854
998
  completedAt?: number;
855
999
  duration: number;
@@ -892,6 +1036,8 @@ type AgentCallInfo = {
892
1036
  response: string;
893
1037
  model: string;
894
1038
  cost: number;
1039
+ /** True when `cost` is a lower bound because the call tree included unpriced work. */
1040
+ unpriced: boolean;
895
1041
  duration: number;
896
1042
  promptVersion?: string;
897
1043
  temperature?: number;
@@ -983,6 +1129,12 @@ type AgentConfig = {
983
1129
  stop?: string[];
984
1130
  /** Provider-specific options merged into API requests. Not portable across providers. */
985
1131
  providerOptions?: Record<string, unknown>;
1132
+ /** Default rendering of a `ctx.ask` output schema into this agent's prompt.
1133
+ * Overridable per-call via `AskOptions.schemaPrompt`. See `SchemaPromptOption`. */
1134
+ schemaPrompt?: SchemaPromptOption;
1135
+ /** Default opt-in to the provider's native structured-output path for this
1136
+ * agent. Overridable per-call via `AskOptions.nativeStructuredOutput`. */
1137
+ nativeStructuredOutput?: boolean;
986
1138
  maxTurns?: number;
987
1139
  timeout?: string;
988
1140
  maxContext?: number;
@@ -1496,6 +1648,278 @@ declare class RateLimiter {
1496
1648
  private clearWaiterTimers;
1497
1649
  }
1498
1650
 
1651
+ /**
1652
+ * A profile value that may legitimately differ per model within one provider.
1653
+ * Mirrors the model-aware shape of OpenAI's `clampReasoningEffort(model, …)`.
1654
+ * Use a function ONLY where the provider's constraints are genuinely per-model
1655
+ * (e.g. xAI reasoning vs chat variants, Mistral `magistral-*`); a plain value
1656
+ * otherwise.
1657
+ */
1658
+ type PerModel<T> = T | ((model: string) => T);
1659
+ /** Resolve a {@link PerModel} value against a concrete model id. */
1660
+ declare function resolvePerModel<T>(value: PerModel<T> | undefined, model: string, fallback: T): T;
1661
+ /**
1662
+ * Auth header shape.
1663
+ * - `'bearer'` → `Authorization: Bearer <key>` (OpenAI and most providers).
1664
+ * - `'api-key'` → `api-key: <key>` (Azure OpenAI key auth — no scheme).
1665
+ * - object → arbitrary header name + optional scheme prefix.
1666
+ */
1667
+ type AuthHeader = 'bearer' | 'api-key' | {
1668
+ header: string;
1669
+ scheme?: string;
1670
+ };
1671
+ /** `[inputRatePerToken, outputRatePerToken, cachedInputMultiplier]`. */
1672
+ type PricingTable = Record<string, [number, number, number]>;
1673
+ /**
1674
+ * How per-call cost is derived. A miss is **never** silently `0` — it surfaces
1675
+ * as `undefined` ("unmeasured") so `ctx.budget()` doesn't treat paid models as
1676
+ * free. See spec §6.
1677
+ */
1678
+ type PricingSource = {
1679
+ kind: 'table';
1680
+ table: PricingTable;
1681
+ }
1682
+ /** Provider reports per-call cost in `usage.cost` (OpenRouter credits ≈ USD). */
1683
+ | {
1684
+ kind: 'from-response';
1685
+ }
1686
+ /** Local/self-hosted: cost is genuinely 0. */
1687
+ | {
1688
+ kind: 'zero';
1689
+ }
1690
+ /** Non-token billing (credits/DBU/neurons): cost is unknowable → `undefined`. */
1691
+ | {
1692
+ kind: 'unknown';
1693
+ };
1694
+ /** How reasoning/thinking text is captured from a response (chat + stream). */
1695
+ type ReasoningCapture =
1696
+ /** None — OpenAI Chat Completions has no reasoning surface. */
1697
+ 'none'
1698
+ /** `message.reasoning_content` / `delta.reasoning_content` (DeepSeek, Fireworks, Together…). */
1699
+ | 'reasoning_content'
1700
+ /** `message.reasoning` / `delta.reasoning` (Groq, Cloudflare). */
1701
+ | 'reasoning'
1702
+ /** OpenRouter: `message.reasoning` text + `reasoning_details[]` for round-tripping. */
1703
+ | 'reasoning_details'
1704
+ /** Inline `<think>…</think>` tags inside content (local R1-style models). */
1705
+ | 'think_tags';
1706
+ /**
1707
+ * Stateful, TURN-AWARE round-trip of captured reasoning on the next request.
1708
+ * `'on-tool-call-turns'`: echo the captured reasoning back (via
1709
+ * `providerMetadata`) on an assistant message that carried `tool_calls`, and
1710
+ * only then. DeepSeek's `deepseek-reasoner` 400s on a tool loop if reasoning is
1711
+ * not echoed on the tool-call turn; on a plain turn it must NOT be echoed.
1712
+ */
1713
+ type ReasoningRoundTrip = 'none' | 'on-tool-call-turns';
1714
+ /**
1715
+ * Mutates the request body to encode the resolved effort/thinking options into
1716
+ * this provider's wire vocabulary. Returns `{ stripTemperature }` when the
1717
+ * provider rejects/ignores `temperature` while reasoning is active.
1718
+ */
1719
+ type ReasoningEmit = (body: Record<string, unknown>, resolved: ResolvedThinkingOptions, model: string) => {
1720
+ stripTemperature?: boolean;
1721
+ } | void;
1722
+ /** Per-profile reasoning behavior — decoupled from any model-name regex. */
1723
+ type ReasoningProfile = {
1724
+ /** Write the unified `effort`/`thinkingBudget` into the request body. */
1725
+ emit: ReasoningEmit;
1726
+ /** Read reasoning text out of responses/streams. */
1727
+ capture: ReasoningCapture;
1728
+ /** Echo captured reasoning back on subsequent requests. Default `'none'`. */
1729
+ roundTrip?: ReasoningRoundTrip;
1730
+ };
1731
+ /** Wire-quirk capability flags. Each defaults to the OpenAI-correct value. */
1732
+ type CapabilityFlags = {
1733
+ /**
1734
+ * Emit `messages[].name` when set on a ChatMessage. OpenAI: true. Groq 400s
1735
+ * on it → false. Message-level (not reachable via `providerOptions`).
1736
+ */
1737
+ emitsMessageName?: boolean;
1738
+ /**
1739
+ * Engine-COMPUTED request params to strip before sending (provider rejects
1740
+ * them). `PerModel` because constraints are often per-model (e.g. `stop`
1741
+ * 400s only on Grok reasoning variants). Stripping runs AFTER the
1742
+ * `providerOptions` merge but EXEMPTS keys the user set explicitly there —
1743
+ * re-introducing a forbidden param via `providerOptions` is "you-asked-for-it".
1744
+ */
1745
+ forbiddenParams?: PerModel<string[]>;
1746
+ /**
1747
+ * Provider honors `response_format` `json_schema` strict mode. When false and
1748
+ * a `json_schema` format is requested, fall back to `{ type: 'json_object' }`.
1749
+ */
1750
+ supportsJsonSchema?: PerModel<boolean>;
1751
+ /**
1752
+ * Provider returns usage in the final stream chunk via
1753
+ * `stream_options.include_usage`. When false, don't request it.
1754
+ */
1755
+ supportsStreamUsage?: boolean;
1756
+ };
1757
+ /**
1758
+ * A provider definition. The engine reads ONLY this — adding a provider is
1759
+ * adding a profile, not new engine code.
1760
+ */
1761
+ type ProviderProfile = {
1762
+ /** Machine name = `Provider.name` and the `provider:` URI scheme key. */
1763
+ name: string;
1764
+ /** Human label used in error messages (defaults to {@link name}). */
1765
+ label?: string;
1766
+ /** Base URL used when neither a constructor arg nor env var is set. */
1767
+ defaultBaseUrl: string;
1768
+ /**
1769
+ * Require an explicit base URL (config `baseUrl` or `envBaseUrl`) — throw at
1770
+ * construction if neither is set. For providers like Azure whose base URL is
1771
+ * account/resource-specific and has no usable default.
1772
+ */
1773
+ requireExplicitBaseUrl?: boolean;
1774
+ /** Env var consulted for the base URL (after the constructor arg). */
1775
+ envBaseUrl?: string;
1776
+ /** Env var consulted for the API key (after the constructor arg). */
1777
+ envApiKey?: string;
1778
+ /** Auth header shape. Default `'bearer'`. */
1779
+ authHeader?: AuthHeader;
1780
+ /** Extra static headers merged into every request. */
1781
+ headers?: Record<string, string>;
1782
+ /** Allow an empty API key (local servers) → no auth header. Default false. */
1783
+ allowMissingApiKey?: boolean;
1784
+ /** How cost is derived. */
1785
+ pricing: PricingSource;
1786
+ /** Reasoning behavior. */
1787
+ reasoning: ReasoningProfile;
1788
+ /** Wire-quirk capability flags. */
1789
+ capabilities?: CapabilityFlags;
1790
+ /** Map a message role → wire role for a model. Default identity. */
1791
+ roleFor?: (role: ChatRole, model: string) => string;
1792
+ /** Output-token field name. Default `'max_completion_tokens'`. */
1793
+ maxTokensField?: 'max_completion_tokens' | 'max_tokens';
1794
+ /** Send `parallel_tool_calls: true` when tools are present. Default off. */
1795
+ parallelToolCalls?: PerModel<boolean>;
1796
+ /**
1797
+ * Static body fields merged BEFORE `providerOptions` (e.g. OpenRouter
1798
+ * `usage: { include: true }`). Treated as engine-computed for forbiddenParams.
1799
+ */
1800
+ requestDefaults?: Record<string, unknown>;
1801
+ };
1802
+ /**
1803
+ * Look up per-token pricing by exact match, then longest-prefix match (for
1804
+ * versioned snapshots like `gpt-4o-2024-05-13`). Returns `undefined` on a miss —
1805
+ * callers MUST treat that as "unknown cost", never as free.
1806
+ */
1807
+ declare function priceFromTable(table: PricingTable, model: string, promptTokens: number, completionTokens: number, cachedTokens?: number): number | undefined;
1808
+ /**
1809
+ * Incremental scanner that separates streamed content into visible text and
1810
+ * `<think>…</think>` reasoning, tolerating tags split across chunks.
1811
+ */
1812
+ declare class ThinkTagScanner {
1813
+ private buffer;
1814
+ private inside;
1815
+ push(chunk: string): {
1816
+ text: string;
1817
+ thinking: string;
1818
+ };
1819
+ /** Flush any residual buffer at stream end (treat as the current channel). */
1820
+ flush(): {
1821
+ text: string;
1822
+ thinking: string;
1823
+ };
1824
+ }
1825
+ /** Strip `<think>…</think>` from a complete (non-streamed) content string. */
1826
+ declare function extractThinkTags(content: string): {
1827
+ content: string;
1828
+ thinking: string;
1829
+ };
1830
+ /**
1831
+ * Build a {@link ReasoningEmit} that writes a top-level `reasoning_effort`
1832
+ * string (OpenAI-style; used by xAI, Groq, Mistral). `map` returns the wire
1833
+ * value for the resolved options + model, or `undefined` to emit nothing
1834
+ * (e.g. reasoning disabled, or a model that rejects the field).
1835
+ */
1836
+ declare function reasoningEffortEmit(map: (resolved: ResolvedThinkingOptions, model: string) => string | undefined): ReasoningEmit;
1837
+ /**
1838
+ * Build a {@link ReasoningEmit} that writes OpenRouter/Vercel's nested
1839
+ * `reasoning` object. effort and max_tokens are MUTUALLY EXCLUSIVE there, so a
1840
+ * positive `thinkingBudget` emits `{ max_tokens }`, an active effort emits
1841
+ * `{ effort }`, and disabled reasoning emits `{ enabled: false }`.
1842
+ */
1843
+ declare function reasoningObjectEmit(mapEffort: (effort: Exclude<Effort, 'none'>, model: string) => string): ReasoningEmit;
1844
+ type OpenAICompatibleOptions = {
1845
+ profile: ProviderProfile;
1846
+ /** API key or a per-request resolver (expiring tokens). See {@link ApiKeySource}. */
1847
+ apiKey?: ApiKeySource;
1848
+ baseUrl?: string;
1849
+ /** Override the profile's auth header shape (e.g. Azure Entra bearer tokens). */
1850
+ authHeader?: AuthHeader;
1851
+ rateLimit?: RateLimitConfig;
1852
+ };
1853
+ /**
1854
+ * Generic provider over the OpenAI `/v1/chat/completions` wire format, raw
1855
+ * `fetch`, zero SDK dependencies. Parameterized by a {@link ProviderProfile}.
1856
+ */
1857
+ declare class OpenAICompatibleProvider implements Provider {
1858
+ readonly name: string;
1859
+ /** `json_schema` is honored natively when the profile's `supportsJsonSchema`
1860
+ * capability is true for this model (the default); otherwise the engine
1861
+ * downgrades the request to plain `json_object` (see `buildRequestBody`),
1862
+ * which does NOT enforce the schema shape. */
1863
+ nativeStructuredOutputSupport(model: string): 'schema' | 'downgraded';
1864
+ protected readonly profile: ProviderProfile;
1865
+ protected readonly baseUrl: string;
1866
+ /** A key string, or a resolver invoked per request (expiring tokens). */
1867
+ protected readonly apiKeySource: ApiKeySource;
1868
+ protected readonly authHeader?: AuthHeader;
1869
+ protected readonly governor?: RateLimiter;
1870
+ constructor(options: OpenAICompatibleOptions);
1871
+ /** Resolve the API key for one request, validating against allowMissingApiKey. */
1872
+ protected resolveKey(): Promise<string>;
1873
+ chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse>;
1874
+ stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk>;
1875
+ protected buildHeaders(apiKey: string): Record<string, string>;
1876
+ protected buildRequestBody(messages: ChatMessage[], options: ChatOptions, stream: boolean): Record<string, unknown>;
1877
+ protected formatMessage(msg: ChatMessage, model: string): Record<string, unknown>;
1878
+ protected extractErrorMessage(body: string, status: number): string;
1879
+ private toUsage;
1880
+ private computeCost;
1881
+ protected parseResponse(json: OpenAIChatResponse, model: string): ProviderResponse;
1882
+ private roundTripMetadata;
1883
+ protected parseSSEStream(body: ReadableStream<Uint8Array>, model: string): AsyncGenerator<StreamChunk>;
1884
+ }
1885
+ type OpenAIUsage = {
1886
+ prompt_tokens: number;
1887
+ completion_tokens: number;
1888
+ total_tokens: number;
1889
+ completion_tokens_details?: {
1890
+ reasoning_tokens?: number;
1891
+ };
1892
+ prompt_tokens_details?: {
1893
+ cached_tokens?: number;
1894
+ };
1895
+ /** OpenRouter / Vercel Gateway: per-call cost in USD. */
1896
+ cost?: number;
1897
+ };
1898
+ type OpenAIChatMessage = {
1899
+ content: string | null | Array<string | {
1900
+ text?: string;
1901
+ content?: string;
1902
+ }>;
1903
+ reasoning_content?: string;
1904
+ reasoning?: string;
1905
+ reasoning_details?: unknown;
1906
+ tool_calls?: Array<{
1907
+ id: string;
1908
+ type: 'function';
1909
+ function: {
1910
+ name: string;
1911
+ arguments: string;
1912
+ };
1913
+ }>;
1914
+ };
1915
+ type OpenAIChatResponse = {
1916
+ choices: Array<{
1917
+ message: OpenAIChatMessage;
1918
+ finish_reason: string;
1919
+ }>;
1920
+ usage?: OpenAIUsage;
1921
+ };
1922
+
1499
1923
  /** A pending human decision awaiting resolution. */
1500
1924
  type PendingDecision = {
1501
1925
  executionId: string;
@@ -1737,8 +2161,13 @@ type MemoryConfig = {
1737
2161
 
1738
2162
  /** Provider configuration */
1739
2163
  type ProviderConfig = {
1740
- apiKey?: string;
2164
+ /** API key, or a function resolving one per request (for expiring tokens —
2165
+ * Azure-Entra, Databricks/IBM OAuth, Bedrock short-term). A plain string is
2166
+ * the common case. See {@link ApiKeySource}. */
2167
+ apiKey?: ApiKeySource;
1741
2168
  baseUrl?: string;
2169
+ /** OpenAI-compatible presets only: override the profile auth header shape. */
2170
+ authHeader?: AuthHeader;
1742
2171
  /**
1743
2172
  * Opt-in client-side rate governor for this provider's HTTP calls (see
1744
2173
  * {@link RateLimitConfig}). Bounds in-flight request concurrency (and,
@@ -1842,6 +2271,22 @@ type ContextManagementConfig = {
1842
2271
  summaryModel?: string;
1843
2272
  reserveTokens?: number;
1844
2273
  };
2274
+ /** Schema-capability diagnostics configuration (spec 22, Problem E). */
2275
+ type DiagnosticsConfig = {
2276
+ /**
2277
+ * Token threshold above which an appended prompt schema — or a provider
2278
+ * tool-definition schema — emits a `prompt_schema_oversized`
2279
+ * `schema_diagnostic`. Measured with the same ~4-chars/token estimator used
2280
+ * for context management. Default `4000`.
2281
+ */
2282
+ schemaOversizedTokens?: number;
2283
+ /**
2284
+ * Silence the one-time `console.warn` diagnostics (the structured
2285
+ * `schema_diagnostic` events still fire). Also silenceable process-wide via
2286
+ * `AXL_DIAGNOSTICS_SILENT=true`.
2287
+ */
2288
+ silent?: boolean;
2289
+ };
1845
2290
 
1846
2291
  /** Full Axl configuration */
1847
2292
  type AxlConfig = {
@@ -1855,6 +2300,7 @@ type AxlConfig = {
1855
2300
  trace?: TraceConfig;
1856
2301
  defaults?: DefaultsConfig;
1857
2302
  contextManagement?: ContextManagementConfig;
2303
+ diagnostics?: DiagnosticsConfig;
1858
2304
  memory?: MemoryConfig;
1859
2305
  telemetry?: TelemetryConfig;
1860
2306
  };
@@ -2110,7 +2556,18 @@ declare class MemoryManager {
2110
2556
 
2111
2557
  /** Convert a Zod schema to JSON Schema. Exported for Studio tool introspection.
2112
2558
  * Wraps Zod v4's built-in `z.toJSONSchema()`, stripping the `$schema` key
2113
- * since tool parameter schemas are embedded objects, not standalone documents. */
2559
+ * since tool parameter schemas are embedded objects, not standalone documents.
2560
+ *
2561
+ * **Inline** rendering: shared subschemas are duplicated in place rather than
2562
+ * hoisted into `$defs`/`$ref`. This is the safe rendering for provider tool
2563
+ * definitions — notably Gemini's `sanitizeSchemaForGemini` strips `$ref`/`$defs`,
2564
+ * which would silently reduce a hoisted schema to a typeless `{}`. (Note: a
2565
+ * genuinely RECURSIVE schema still emits `$ref`/`$defs` even inline — recursion
2566
+ * can't be inlined — so such tool schemas remain lossy on Gemini regardless;
2567
+ * that's a pre-existing provider limitation, independent of this rendering.)
2568
+ * The extra `reused:'ref'` compaction lives in the private
2569
+ * `renderSchemaForPrompt` and is scoped to prompt text, where `$ref` is opaque
2570
+ * bytes on the wire. */
2114
2571
  declare function zodToJsonSchema(schema: z.ZodType): Record<string, unknown>;
2115
2572
  /**
2116
2573
  * Extract JSON from LLM response content.
@@ -2118,6 +2575,30 @@ declare function zodToJsonSchema(schema: z.ZodType): Record<string, unknown>;
2118
2575
  * and content with leading/trailing text around a JSON object/array.
2119
2576
  */
2120
2577
  declare function extractJson(content: string): string;
2578
+ /**
2579
+ * Active `ctx.budget()` accounting state. One mutable object per budget block,
2580
+ * shared by reference across the block's async branches (spawn/race/map).
2581
+ */
2582
+ type BudgetContextState = {
2583
+ totalCost: number;
2584
+ limit: number;
2585
+ exceeded: boolean;
2586
+ policy: string;
2587
+ abortController?: AbortController;
2588
+ /**
2589
+ * Set true when a cost-bearing leaf inside this budget did measurable work
2590
+ * (positive tokens) but produced no usable cost (unpriced model / pricing-table
2591
+ * miss). `totalCost` then UNDERSTATES real spend — it's a lower bound. The budget
2592
+ * rail REPORTS this (via {@link BudgetResult.unpriced} / `getBudgetStatus`) but
2593
+ * does NOT enforce on it: `_accumulateBudgetCost` never receives the unknown
2594
+ * component, so a cost limit / hard_stop cannot trip on unpriced spend.
2595
+ */
2596
+ unpriced: boolean;
2597
+ /** Monotonic count of unpriced leaves observed in this budget scope. */
2598
+ unpricedCount: number;
2599
+ /** Dedup guard so the unpriced warning fires at most once per budget block. */
2600
+ unpricedWarned: boolean;
2601
+ };
2121
2602
  type WorkflowContextInit = {
2122
2603
  input: unknown;
2123
2604
  executionId: string;
@@ -2137,13 +2618,7 @@ type WorkflowContextInit = {
2137
2618
  callId?: string;
2138
2619
  }, meta: CallbackMeta) => void;
2139
2620
  pendingDecisions?: Map<string, (d: HumanDecision) => void>;
2140
- budgetContext?: {
2141
- totalCost: number;
2142
- limit: number;
2143
- exceeded: boolean;
2144
- policy: string;
2145
- abortController?: AbortController;
2146
- };
2621
+ budgetContext?: BudgetContextState;
2147
2622
  stateStore?: StateStore;
2148
2623
  signal?: AbortSignal;
2149
2624
  workflowName?: string;
@@ -2393,6 +2868,23 @@ declare class WorkflowContext<TInput = unknown> {
2393
2868
  * stamping the agent name for observability + future per-agent scoping.
2394
2869
  */
2395
2870
  private pushAssistantToSessionHistory;
2871
+ /**
2872
+ * Emit `schema_diagnostic` events for the silent structured-output cliffs
2873
+ * (spec 22, Problems B + E). Called once per ask. Covers:
2874
+ * - the appended **prompt** schema (oversized; dropped refinements; or the
2875
+ * zero-guidance `schemaPrompt:'none'` footgun),
2876
+ * - each user **tool-def** schema (oversized, dropped refinements),
2877
+ * - the **streaming** gate (disabled by a non-object root or by tools),
2878
+ * - **native structured output** the resolved provider can't honor (R10).
2879
+ *
2880
+ * The structured event always fires (persisted + streamed). For the genuinely
2881
+ * surprising, low-frequency cliffs it ALSO fires a one-time deduped
2882
+ * `console.warn` so the median consumer — who never wires up `ctx.events` and
2883
+ * runs with the trace console off — still sees it. `oversized` is event-only
2884
+ * (O3: it's tunable/noisy); the `tools` streaming cause is event-only too
2885
+ * (expected behavior for any tool-using agent, not a surprise).
2886
+ */
2887
+ private emitSchemaDiagnostics;
2396
2888
  private buildToolDefs;
2397
2889
  /**
2398
2890
  * Summarize old messages to fit within context window.
@@ -2457,11 +2949,20 @@ declare class WorkflowContext<TInput = unknown> {
2457
2949
  private medianVote;
2458
2950
  verify<T>(fn: (retry?: VerifyRetry<T>) => Promise<unknown>, schema: z.ZodType<T>, options?: VerifyOptions<T>): Promise<T>;
2459
2951
  budget<T>(options: BudgetOptions, fn: () => Promise<T>): Promise<BudgetResult<T>>;
2460
- /** Get the current budget status, or null if not inside a budget block. */
2952
+ /**
2953
+ * Get the current budget status, or null if not inside a budget block. Inside
2954
+ * nested budgets this reflects the **innermost active** block (the nested block's
2955
+ * `finally` restores the parent before control returns to it).
2956
+ *
2957
+ * `unpriced` is true when this block ran a model with no usable cost (unpriced /
2958
+ * pricing-table miss): `spent` is then a LOWER BOUND and `remaining` an upper bound —
2959
+ * the limit cannot be enforced on the unmeasured spend. See {@link BudgetResult.unpriced}.
2960
+ */
2461
2961
  getBudgetStatus(): {
2462
2962
  spent: number;
2463
2963
  limit: number;
2464
2964
  remaining: number;
2965
+ unpriced: boolean;
2465
2966
  } | null;
2466
2967
  race<T>(fns: Array<() => Promise<T>>, options?: RaceOptions<T>): Promise<T>;
2467
2968
  private _raceImpl;
@@ -3305,6 +3806,7 @@ declare class AxlRuntime extends EventEmitter {
3305
3806
  trackCost<T>(fn: () => Promise<T>): Promise<{
3306
3807
  result: T;
3307
3808
  cost: number;
3809
+ unpriced: boolean;
3308
3810
  }>;
3309
3811
  /**
3310
3812
  * Track cost and execution metadata across any runtime operations within the given function.
@@ -3343,6 +3845,9 @@ declare class AxlRuntime extends EventEmitter {
3343
3845
  }): Promise<{
3344
3846
  result: T;
3345
3847
  cost: number;
3848
+ /** True when any tracked call was unpriced — `cost` is then a LOWER BOUND.
3849
+ * Aggregate counterpart of `ExecutionInfo.unpriced`, via `isUnpricedLeaf`. */
3850
+ unpriced: boolean;
3346
3851
  traces?: AxlEvent[];
3347
3852
  metadata: {
3348
3853
  models: string[];
@@ -3662,6 +4167,30 @@ declare function stringStreamFromEvents(source: AsyncIterable<AxlEvent>, opts?:
3662
4167
  * `AXL_EVENT_TYPES`.
3663
4168
  */
3664
4169
  declare const COST_BEARING_LEAF_TYPES: readonly ["agent_call_end", "tool_call_end", "memory_remember", "memory_recall"];
4170
+ /**
4171
+ * True when an event reports a POSITIVE token count — i.e. it did measurable,
4172
+ * billable work. This is the discriminator the unpriced-cost signal (T2.5) uses:
4173
+ * a cost-bearing leaf with positive tokens but no usable cost is an unpriced
4174
+ * model / pricing-table miss, whereas a failed call (no usage) or a no-usage
4175
+ * streamed `done` (zero tokens) is NOT "unpriced". Used by the core ask-cost
4176
+ * rollup AND the Studio aggregators, so the per-ask flag and the dashboard count
4177
+ * stay in lockstep.
4178
+ */
4179
+ declare function hasPositiveTokens(event: {
4180
+ tokens?: {
4181
+ input?: number;
4182
+ output?: number;
4183
+ reasoning?: number;
4184
+ } | null;
4185
+ }): boolean;
4186
+ /**
4187
+ * True when a cost value is usable for accounting: a finite, non-negative
4188
+ * number. A NON-usable cost (`undefined`, `NaN`, `Infinity`, negative) on a
4189
+ * work-bearing leaf is what marks it "unpriced / unknown". Shared by the core
4190
+ * ask-cost rollup and the Studio aggregators so their unpriced detection stays
4191
+ * exactly in lockstep (incl. the non-finite edge).
4192
+ */
4193
+ declare function isUsableCost(cost: unknown): cost is number;
3665
4194
  /**
3666
4195
  * Cost contribution of `event` to a running total.
3667
4196
  *
@@ -3689,6 +4218,34 @@ declare function eventCostContribution(event: AxlEvent): number;
3689
4218
  * just one that happens to contribute zero.
3690
4219
  */
3691
4220
  declare function isCostBearingLeaf(event: AxlEvent): boolean;
4221
+ /**
4222
+ * True when the event is the "unpriced lower-bound" signal: a cost-bearing leaf
4223
+ * that did measurable, billable work (POSITIVE tokens) but produced no usable
4224
+ * cost (an unpriced model / pricing-table miss). This is the SINGLE source of
4225
+ * truth for that discriminator — the core ask-cost rollup, the runtime's
4226
+ * `ExecutionInfo.unpriced` / `trackExecution().unpriced` aggregation, and
4227
+ * Studio's `unpricedCalls` count + cost reducer ALL route through it, so they
4228
+ * cannot drift.
4229
+ *
4230
+ * It is the conjunction of the three primitives:
4231
+ * `isCostBearingLeaf` ∧ `!isUsableCost(cost)` ∧ `hasPositiveTokens`.
4232
+ * The positive-token term is what distinguishes an unpriced model from a FAILED
4233
+ * call (no usage) or a no-usage streamed `done` (zero tokens) — neither is
4234
+ * "unpriced". An `ask_end` rollup carries a numeric cost, and `tool_call_end`
4235
+ * carries no tokens, so both are naturally excluded.
4236
+ *
4237
+ * Structural param (like {@link hasPositiveTokens} / {@link isUsableCost}) so
4238
+ * Studio's pre-typed trace-event shapes can pass through without casting.
4239
+ */
4240
+ declare function isUnpricedLeaf(event: {
4241
+ type?: string;
4242
+ cost?: unknown;
4243
+ tokens?: {
4244
+ input?: number;
4245
+ output?: number;
4246
+ reasoning?: number;
4247
+ } | null;
4248
+ }): boolean;
3692
4249
  /**
3693
4250
  * True when the event originates from the root ask (`depth === 0`)
3694
4251
  * or has no ask correlation at all (workflow / done / error / log).
@@ -3852,32 +4409,104 @@ declare class ToolDenied extends AxlError {
3852
4409
  }
3853
4410
 
3854
4411
  /**
3855
- * OpenAI-compatible provider using raw fetch (no SDK dependency).
4412
+ * Typed provider errors + shared HTTP-failure classification.
3856
4413
  *
3857
- * Supports:
3858
- * - Chat completions
3859
- * - Tool calling
3860
- * - Streaming via SSE
3861
- * - Structured output via response_format (JSON mode / JSON schema)
3862
- * - Reasoning models (o1/o3/o4-mini) with developer role and reasoning_effort
4414
+ * Every adapter's `if (!res.ok)` site throws a {@link ProviderError} (built via
4415
+ * {@link buildProviderError}) instead of a bare `Error`, and `fetchWithRetry`
4416
+ * normalizes thrown network failures (DNS, reset, TLS) into a `ProviderError`
4417
+ * with `status: 0`. The `.message` text is preserved verbatim from each
4418
+ * adapter's own `extractErrorMessage`, so existing `.message` assertions keep
4419
+ * working — only the thrown *type* changes.
4420
+ *
4421
+ * RETRY MODEL — two SEPARATE sets (do not conflate):
4422
+ * - `ProviderError.retryable` (here, via {@link isRetryableStatus}) is the
4423
+ * BROADER *semantic failover hint* for higher layers (e.g. "should I try a
4424
+ * different model/provider?"). It is NOT consulted by the transport loop.
4425
+ * - `retry.ts`'s `RETRYABLE_STATUS_CODES` ({429, 503, 529}) is the NARROW
4426
+ * *transport auto-retry* set. It stays narrow on purpose — widening it would
4427
+ * silently change auto-retry for every provider. See the cross-link comment
4428
+ * there.
4429
+ *
4430
+ * This module must NOT import `retry.ts` (retry.ts imports `parseRetryAfter`
4431
+ * from here — keep the dependency one-way to avoid a cycle).
3863
4432
  */
3864
- declare class OpenAIProvider implements Provider {
3865
- readonly name = "openai";
3866
- private baseUrl;
3867
- private apiKey;
3868
- private governor?;
4433
+
4434
+ /**
4435
+ * Error thrown by provider adapters on any non-2xx HTTP response, and by
4436
+ * `fetchWithRetry` on an exhausted/normalized network failure (`status: 0`).
4437
+ *
4438
+ * `instanceof Error` and `instanceof AxlError` both hold; `code` is always
4439
+ * `'PROVIDER_ERROR'`. The constructor message is passed through verbatim (no
4440
+ * prefix) so callers asserting on the raw provider message are unaffected.
4441
+ */
4442
+ declare class ProviderError extends AxlError {
4443
+ /** Adapter/profile name, e.g. `'openai'`, `'anthropic'`, `'google'`. */
4444
+ readonly provider: string;
4445
+ /** HTTP status code; `0` for non-HTTP (network) failures. */
4446
+ readonly status: number;
4447
+ /** Semantic failover hint (see module doc) — NOT the transport-retry set. */
4448
+ readonly retryable: boolean;
4449
+ /** Parsed `Retry-After` delay in ms (RAW/unclamped), when the header was present. */
4450
+ readonly retryAfterMs?: number;
4451
+ /** Provider request id from response headers, when present. */
4452
+ readonly requestId?: string;
4453
+ /**
4454
+ * Raw provider error body. Lives ONLY on the error object — it is
4455
+ * intentionally never placed on the event stream (it can echo prompt text
4456
+ * and is redaction-eligible). See `docs/security.md`.
4457
+ */
4458
+ readonly body?: string;
4459
+ constructor(args: {
4460
+ provider: string;
4461
+ status: number;
4462
+ retryable: boolean;
4463
+ message: string;
4464
+ retryAfterMs?: number;
4465
+ requestId?: string;
4466
+ body?: string;
4467
+ });
4468
+ }
4469
+ /**
4470
+ * Classify an HTTP status as a semantic failover hint (see {@link ProviderError}).
4471
+ *
4472
+ * Grounded in observed provider behavior. Unmapped codes default to `false`
4473
+ * (conservative); add a row only for a status we've observed a real provider
4474
+ * emit. Status `0` (non-HTTP / network failure) is retryable.
4475
+ */
4476
+ declare function isRetryableStatus(status: number): boolean;
4477
+
4478
+ declare const OPENAI_PRICING: PricingTable;
4479
+ /**
4480
+ * Estimate OpenAI call cost from token usage. Exact match first, then
4481
+ * longest-prefix match for versioned models (e.g. `gpt-4o-2024-05-13`).
4482
+ *
4483
+ * Returns `undefined` when the model is not in the table — callers must treat
4484
+ * that as "unknown cost", never as free (a silent `0` would break
4485
+ * `ctx.budget()` and mislead cost dashboards). See spec §6.
4486
+ */
4487
+ declare function estimateOpenAICost(model: string, promptTokens: number, completionTokens: number, cachedTokens?: number): number | undefined;
4488
+ /**
4489
+ * OpenAI Chat Completions reasoning emit. Computes `reasoning_effort` for
4490
+ * o-series / GPT-5.x models from the unified effort/thinkingBudget knobs and
4491
+ * signals when to strip `temperature` (always for o-series; for GPT-5.x only
4492
+ * when reasoning is active). Non-reasoning models get neither.
4493
+ */
4494
+ declare const openaiReasoningEmit: ReasoningEmit;
4495
+ /** Canonical OpenAI Chat Completions profile. */
4496
+ declare const OPENAI_PROFILE: ProviderProfile;
4497
+ /**
4498
+ * OpenAI provider (Chat Completions) — the canonical {@link OpenAICompatibleProvider}
4499
+ * profile. Preserved as a named export with its original constructor signature.
4500
+ *
4501
+ * Supports chat, tool calling, SSE streaming, structured output, and reasoning
4502
+ * models (o1/o3/o4-mini + GPT-5.x) via `reasoning_effort`.
4503
+ */
4504
+ declare class OpenAIProvider extends OpenAICompatibleProvider {
3869
4505
  constructor(options?: {
3870
- apiKey?: string;
4506
+ apiKey?: ApiKeySource;
3871
4507
  baseUrl?: string;
3872
4508
  rateLimit?: RateLimitConfig;
3873
4509
  });
3874
- chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse>;
3875
- stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk>;
3876
- private buildRequestBody;
3877
- /** Extract a human-readable message from an API error response body. */
3878
- private extractErrorMessage;
3879
- private formatMessage;
3880
- private parseSSEStream;
3881
4510
  }
3882
4511
 
3883
4512
  /**
@@ -3889,14 +4518,20 @@ declare class OpenAIProvider implements Provider {
3889
4518
  */
3890
4519
  declare class OpenAIResponsesProvider implements Provider {
3891
4520
  readonly name = "openai-responses";
4521
+ /** The Responses API honors `json_schema` (structured outputs) natively. */
4522
+ nativeStructuredOutputSupport(): 'schema';
3892
4523
  private baseUrl;
3893
- private apiKey;
4524
+ private apiKeySource;
3894
4525
  private governor?;
3895
4526
  constructor(options?: {
3896
- apiKey?: string;
4527
+ apiKey?: ApiKeySource;
3897
4528
  baseUrl?: string;
3898
4529
  rateLimit?: RateLimitConfig;
3899
4530
  });
4531
+ /** Resolve the API key for one request (supports an expiring-token callback). */
4532
+ private resolveKey;
4533
+ /** Build request headers from a resolved key (used by both chat and stream). */
4534
+ private buildHeaders;
3900
4535
  chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse>;
3901
4536
  stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk>;
3902
4537
  private buildRequestBody;
@@ -3931,15 +4566,19 @@ declare class OpenAIResponsesProvider implements Provider {
3931
4566
  */
3932
4567
  declare class AnthropicProvider implements Provider {
3933
4568
  readonly name = "anthropic";
4569
+ /** Anthropic ignores native `json_schema` structurally — Axl uses a system
4570
+ * prompt JSON instruction + client-side Zod validation (see buildRequest). */
4571
+ nativeStructuredOutputSupport(): 'unsupported';
3934
4572
  private baseUrl;
3935
- private apiKey;
3936
- private currentModel?;
4573
+ private apiKeySource;
3937
4574
  private governor?;
3938
4575
  constructor(options?: {
3939
- apiKey?: string;
4576
+ apiKey?: ApiKeySource;
3940
4577
  baseUrl?: string;
3941
4578
  rateLimit?: RateLimitConfig;
3942
4579
  });
4580
+ /** Resolve the API key for one request (supports an expiring-token callback). */
4581
+ private resolveKey;
3943
4582
  chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse>;
3944
4583
  stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk>;
3945
4584
  private buildHeaders;
@@ -3995,15 +4634,21 @@ declare class AnthropicProvider implements Provider {
3995
4634
  */
3996
4635
  declare class GeminiProvider implements Provider {
3997
4636
  readonly name = "google";
4637
+ /** Gemini accepts a `responseSchema` but `sanitizeSchemaForGemini` strips
4638
+ * keywords it doesn't support (`$ref`/`$defs`/`additionalProperties`/…), so a
4639
+ * derived schema can lose constraints — lossy, not faithful. */
4640
+ nativeStructuredOutputSupport(): 'lossy';
3998
4641
  private baseUrl;
3999
- private apiKey;
4642
+ private apiKeySource;
4000
4643
  private callCounter;
4001
4644
  private governor?;
4002
4645
  constructor(options?: {
4003
- apiKey?: string;
4646
+ apiKey?: ApiKeySource;
4004
4647
  baseUrl?: string;
4005
4648
  rateLimit?: RateLimitConfig;
4006
4649
  });
4650
+ /** Resolve the API key for one request (supports an expiring-token callback). */
4651
+ private resolveKey;
4007
4652
  chat(messages: ChatMessage[], options: ChatOptions): Promise<ProviderResponse>;
4008
4653
  stream(messages: ChatMessage[], options: ChatOptions): AsyncGenerator<StreamChunk>;
4009
4654
  private buildHeaders;
@@ -4040,6 +4685,120 @@ declare class GeminiProvider implements Provider {
4040
4685
  private parseSSEStream;
4041
4686
  }
4042
4687
 
4688
+ /**
4689
+ * OpenRouter — one key fronts 300+ models across vendors.
4690
+ *
4691
+ * - Reasoning is the unified `reasoning` object (`{ effort }` XOR `{ max_tokens }`,
4692
+ * or `{ enabled: false }`); reasoning text comes back in `message.reasoning`
4693
+ * with a structured `reasoning_details[]` we round-trip on tool-call turns.
4694
+ * - Cost is provider-reported: `usage: { include: true }` makes OpenRouter return
4695
+ * `usage.cost` (USD), so Axl keeps cost-as-a-primitive working across the whole
4696
+ * catalog where a static table never could.
4697
+ * - Model ids are `vendor/model` slugs (e.g. `anthropic/claude-opus-4.7`); the
4698
+ * registry's first-colon split keeps `openrouter:anthropic/claude-…` intact.
4699
+ *
4700
+ * Capability (json_schema, tool calling) is per-MODEL on a marketplace — the
4701
+ * profile defaults are optimistic; use `providerOptions` for per-call overrides.
4702
+ */
4703
+ declare const OPENROUTER_PROFILE: ProviderProfile;
4704
+
4705
+ /**
4706
+ * Azure OpenAI (v1 API).
4707
+ *
4708
+ * The mid-2026 v1 surface drops the old `/deployments/{id}/` path and
4709
+ * `api-version` query param: the deployment name goes in the `model` field and
4710
+ * the base URL is `https://{resource}.openai.azure.com/openai/v1`. It is
4711
+ * resource-specific, so `requireExplicitBaseUrl` makes the constructor throw if
4712
+ * neither `providers.azure.baseUrl` nor `AZURE_OPENAI_BASE_URL` is set.
4713
+ *
4714
+ * Auth: API-key auth uses the `api-key` header (NOT `Authorization: Bearer`).
4715
+ * Entra/AAD bearer-token auth uses the async key callback plus an `authHeader:
4716
+ * 'bearer'` override.
4717
+ *
4718
+ * Azure serves OpenAI models, so the OpenAI reasoning logic and pricing table
4719
+ * are reused. Caveat: deployment names are arbitrary, so o-series/GPT-5
4720
+ * detection and pricing only fire when the deployment is named after the model;
4721
+ * otherwise effort is a no-op and cost is `undefined` (honest, not a fake $0).
4722
+ */
4723
+ declare const AZURE_PROFILE: ProviderProfile;
4724
+
4725
+ /**
4726
+ * xAI (Grok). OpenAI-compatible at `https://api.x.ai/v1`.
4727
+ *
4728
+ * - `reasoning_effort` (`low` for effort `low`, else `high`) for grok-3-mini;
4729
+ * omitted for grok-4* (auto-reasoning) and non-reasoning models.
4730
+ * - `stop` + penalty params are stripped on reasoning models (they 400).
4731
+ * Penalties aren't engine-computed, so they only matter if a user adds them.
4732
+ */
4733
+ declare const XAI_PROFILE: ProviderProfile;
4734
+
4735
+ /**
4736
+ * DeepSeek. OpenAI-compatible at `https://api.deepseek.com/v1`.
4737
+ *
4738
+ * - Reasoning text arrives in `message.reasoning_content` (captured to
4739
+ * thinking) and MUST be echoed back on tool-call turns or the API 400s mid
4740
+ * tool-loop — the turn-aware `on-tool-call-turns` round-trip handles that.
4741
+ * - No strict `json_schema`; falls back to `json_object`.
4742
+ * - Reasoner models reject sampling params (temperature stripped; `top_p` and
4743
+ * penalties stripped if engine-set — they are user-only here, so listed for
4744
+ * intent + future-proofing).
4745
+ */
4746
+ declare const DEEPSEEK_PROFILE: ProviderProfile;
4747
+
4748
+ /**
4749
+ * Mistral. OpenAI-compatible at `https://api.mistral.ai/v1`.
4750
+ *
4751
+ * `reasoning_effort` is emitted as `'high'` for the small/medium families when
4752
+ * effort is active, and omitted otherwise (other models 422; `magistral-*`
4753
+ * always-reason and reject the field).
4754
+ */
4755
+ declare const MISTRAL_PROFILE: ProviderProfile;
4756
+
4757
+ /**
4758
+ * Groq — fastest inference (LPU), OpenAI-compatible at
4759
+ * `https://api.groq.com/openai/v1`. Open-weight models only.
4760
+ *
4761
+ * - 400s on `messages[].name` → not emitted.
4762
+ * - `reasoning_effort` (low/medium/high; `xhigh`/`max`→`high`) for gpt-oss only;
4763
+ * a no-op on other models (whose vocab differs / who reject the field).
4764
+ * - Reasoning text comes back in `message.reasoning` (gpt-oss).
4765
+ */
4766
+ declare const GROQ_PROFILE: ProviderProfile;
4767
+
4768
+ /**
4769
+ * AWS Bedrock — OpenAI-compatible surface (gpt-oss), bearer-token auth.
4770
+ *
4771
+ * - Base URL is region-specific, so `requireExplicitBaseUrl` forces a real value
4772
+ * (`providers.bedrock.baseUrl` or `BEDROCK_BASE_URL`). Prefer the current
4773
+ * `bedrock-mantle` endpoint: `https://bedrock-mantle.{region}.api.aws/v1`
4774
+ * (alternative, SigV4-capable runtime: `https://bedrock-runtime.{region}.amazonaws.com/openai/v1`).
4775
+ * - Auth: a bearer API key (`AWS_BEARER_TOKEN_BEDROCK`); short-term tokens can use
4776
+ * the async `apiKey` callback (T2.2). No SigV4.
4777
+ * - Model ids are AWS-namespaced. The **mantle** endpoint uses UNSUFFIXED ids
4778
+ * (`bedrock:openai.gpt-oss-120b`, `bedrock:openai.gpt-oss-20b`); the runtime
4779
+ * endpoint uses the version-suffixed form (`openai.gpt-oss-120b-1:0`). Match the
4780
+ * id to your endpoint. (The registry splits the URI on the first colon, so a
4781
+ * suffixed id round-trips fine.)
4782
+ * - Pricing: `unknown` — Bedrock returns the standard OpenAI usage object with no
4783
+ * `cost` field, so per-call cost can't be derived (surfaced honestly, never $0).
4784
+ *
4785
+ * Scope: gpt-oss for Tier 2. Claude-on-Bedrock is a Tier-3 native-adapter mode.
4786
+ */
4787
+ declare const BEDROCK_PROFILE: ProviderProfile;
4788
+
4789
+ declare const OLLAMA_PROFILE: ProviderProfile;
4790
+ declare const VLLM_PROFILE: ProviderProfile;
4791
+ declare const LMSTUDIO_PROFILE: ProviderProfile;
4792
+ declare const LLAMACPP_PROFILE: ProviderProfile;
4793
+ declare const SGLANG_PROFILE: ProviderProfile;
4794
+
4795
+ /**
4796
+ * Built-in OpenAI-compatible presets, each served by the generic
4797
+ * {@link OpenAICompatibleProvider}. The registry registers one factory per
4798
+ * profile under `profile.name` (the `provider:` URI key).
4799
+ */
4800
+ declare const BUILTIN_PROFILES: ProviderProfile[];
4801
+
4043
4802
  /**
4044
4803
  * In-memory implementation of StateStore.
4045
4804
  * Fast for development and testing, but lost on process restart.
@@ -4485,4 +5244,4 @@ declare class NoopSpanManager implements SpanManager {
4485
5244
  */
4486
5245
  declare function createSpanManager(config?: TelemetryConfig): Promise<SpanManager>;
4487
5246
 
4488
- export { AXL_EVENT_TYPES, type Agent, type AgentCallEndData, type AgentCallInfo, type AgentCallParams, type AgentCallStartData, type AgentConfig, AnthropicProvider, type AnyWorkflow, type AskOptions, type AskScoped, type AwaitHumanData, type AwaitHumanOptions, type AwaitHumanResolvedData, type AxlConfig, AxlError, type AxlEvent, type AxlEventBase, AxlEventBus, type AxlEventOf, type AxlEventType, AxlRuntime, AxlStream, BudgetExceededError, type BudgetOptions, type BudgetResult, CONFIG_CANDIDATES, COST_BEARING_LEAF_TYPES, type CallbackMeta, type ChatMessage, type ChatOptions, type CheckpointEventData, type CoalescedPartialObject, type CreateContextOptions, type DelegateData, type DelegateOptions, type Effort, type EmbedResult, type EmbedUsage, type Embedder, type EvalExecuteWorkflow, type EvalHistoryEntry, type EventStreamOptions, EventStreamOverflowError, type ExecuteOptions, type ExecutionInfo, type ExecutionState, GeminiProvider, type GuardrailBlockHandler, type GuardrailData, GuardrailError, type GuardrailResult, type GuardrailsConfig, type HandoffDescriptor, type HandoffRecord, type HandoffReturnData, type HandoffStartData, type HumanDecision, InMemoryVectorStore, type InputGuardrail, type MapOptions, MaxTurnsError, McpManager, type McpServer, type McpServerConfig, type McpToolDefinition, type McpToolResult, type MemoryConfig, type MemoryEventData, MemoryManager, MemoryStore, NoConsensus, NoopSpanManager, OpenAIEmbedder, OpenAIProvider, OpenAIResponsesProvider, type OutputGuardrail, type OutputValidator, type PendingDecision, type Provider, type ProviderAdapter, ProviderRegistry, type ProviderResponse, QuorumNotMet, REDACTED, REDACTION_RULES, type RaceOptions, type RateLimitConfig, RateLimiter, type RecallOptions, type RecallResult, RedisStore, type RememberOptions, type RememberResult, type ResolvedThinkingOptions, type Result, type RetryPolicy, SQLiteStore, type SchemaCheckData, Session, type SessionOptions, type SpanHandle, type SpanManager, type SpawnOptions, SqliteVectorStore, type StateStore, type StreamChunk, type StringDeltaData, type StringStreamEvent, type StringStreamFilter, type TelemetryConfig, TimeoutError, type Tool, type ToolApprovalData, type ToolCallData, type ToolCallMessage, type ToolCallStartData, type ToolChoice, type ToolConfig, ToolDenied, type ToolDeniedData, type ToolHooks, type ValidateData, type ValidateResult, ValidationError, type VectorEntry, type VectorResult, type VectorStore, type VerifyData, VerifyError, type VerifyOptions, type VerifyRetry, type VoteOptions, type Workflow, type WorkflowConfig, WorkflowContext, type WorkflowContextInit, type WorkflowEndData, type WorkflowStartData, agent, createSpanManager, defineConfig, eventCostContribution, expandGlob, extractJson, findConfig, importModule, isCostBearingLeaf, isRootLevel, needsTsxLoader, parsePartialJson, pickDefault, pickExport, redactEvent, registerConditions, resolveRuntime, resolveThinkingOptions, stringStreamFromEvents, tool, workflow, zodToJsonSchema };
5247
+ export { AXL_EVENT_TYPES, AZURE_PROFILE, type Agent, type AgentCallEndData, type AgentCallInfo, type AgentCallParams, type AgentCallStartData, type AgentConfig, AnthropicProvider, type AnyWorkflow, type ApiKeySource, type AskOptions, type AskScoped, type AuthHeader, type AwaitHumanData, type AwaitHumanOptions, type AwaitHumanResolvedData, type AxlConfig, AxlError, type AxlEvent, type AxlEventBase, AxlEventBus, type AxlEventOf, type AxlEventType, AxlRuntime, AxlStream, BEDROCK_PROFILE, BUILTIN_PROFILES, BudgetExceededError, type BudgetOptions, type BudgetResult, CONFIG_CANDIDATES, COST_BEARING_LEAF_TYPES, type CallbackMeta, type CapabilityFlags, type ChatMessage, type ChatOptions, type CheckpointEventData, type CoalescedPartialObject, type CreateContextOptions, DEEPSEEK_PROFILE, type DelegateData, type DelegateOptions, type Effort, type EmbedResult, type EmbedUsage, type Embedder, type EvalExecuteWorkflow, type EvalHistoryEntry, type EventStreamOptions, EventStreamOverflowError, type ExecuteOptions, type ExecutionInfo, type ExecutionState, GROQ_PROFILE, GeminiProvider, type GuardrailBlockHandler, type GuardrailData, GuardrailError, type GuardrailResult, type GuardrailsConfig, type HandoffDescriptor, type HandoffRecord, type HandoffReturnData, type HandoffStartData, type HumanDecision, InMemoryVectorStore, type InputGuardrail, LLAMACPP_PROFILE, LMSTUDIO_PROFILE, MISTRAL_PROFILE, type MapOptions, MaxTurnsError, McpManager, type McpServer, type McpServerConfig, type McpToolDefinition, type McpToolResult, type MemoryConfig, type MemoryEventData, MemoryManager, MemoryStore, NoConsensus, NoopSpanManager, OLLAMA_PROFILE, OPENAI_PRICING, OPENAI_PROFILE, OPENROUTER_PROFILE, type OpenAICompatibleOptions, OpenAICompatibleProvider, OpenAIEmbedder, OpenAIProvider, OpenAIResponsesProvider, type OutputGuardrail, type OutputValidator, type PendingDecision, type PerModel, type PricingSource, type PricingTable, type Provider, type ProviderAdapter, ProviderError, type ProviderProfile, ProviderRegistry, type ProviderResponse, QuorumNotMet, REDACTED, REDACTION_RULES, type RaceOptions, type RateLimitConfig, RateLimiter, type ReasoningCapture, type ReasoningEmit, type ReasoningProfile, type ReasoningRoundTrip, type RecallOptions, type RecallResult, RedisStore, type RememberOptions, type RememberResult, type ResolvedThinkingOptions, type Result, type RetryPolicy, SGLANG_PROFILE, SQLiteStore, type SchemaCheckData, type SchemaDiagnosticData, type SchemaPromptOption, Session, type SessionOptions, type SpanHandle, type SpanManager, type SpawnOptions, SqliteVectorStore, type StateStore, type StreamChunk, type StringDeltaData, type StringStreamEvent, type StringStreamFilter, type TelemetryConfig, ThinkTagScanner, TimeoutError, type Tool, type ToolApprovalData, type ToolCallData, type ToolCallMessage, type ToolCallStartData, type ToolChoice, type ToolConfig, ToolDenied, type ToolDeniedData, type ToolHooks, VLLM_PROFILE, type ValidateData, type ValidateResult, ValidationError, type VectorEntry, type VectorResult, type VectorStore, type VerifyData, VerifyError, type VerifyOptions, type VerifyRetry, type VoteOptions, type Workflow, type WorkflowConfig, WorkflowContext, type WorkflowContextInit, type WorkflowEndData, type WorkflowStartData, XAI_PROFILE, agent, createSpanManager, defineConfig, estimateOpenAICost, eventCostContribution, expandGlob, extractJson, extractThinkTags, findConfig, hasPositiveTokens, importModule, isCostBearingLeaf, isRetryableStatus, isRootLevel, isUnpricedLeaf, isUsableCost, needsTsxLoader, openaiReasoningEmit, parsePartialJson, pickDefault, pickExport, priceFromTable, reasoningEffortEmit, reasoningObjectEmit, redactEvent, registerConditions, resolveApiKey, resolvePerModel, resolveRuntime, resolveThinkingOptions, stringStreamFromEvents, tool, workflow, zodToJsonSchema };