@x12i/ai-gateway 10.0.2 → 10.0.5

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
@@ -13,7 +13,7 @@ Unified gateway for LLM provider routing, structured logging, optional Activix a
13
13
  | **Activix** | Optional Mongo-backed activity rows (`ai-actions`, `bad-requests`, `skill-executions`) with root billing fields and `outer` I/O. |
14
14
  | **Trace mode** | `diagnostics.mode === 'trace'` adds `metadata.attempts[]`, `metadata.usage`, and per-attempt billing when priced. |
15
15
 
16
- Pinned dependency versions are in `package.json` (currently **Activix ^7.2**, **ai-tools ^2**).
16
+ Pinned dependency versions are in `package.json` (currently **Activix ^8.5**, **ai-tools ^2.5**, **ai-providers-router ^4.9**).
17
17
 
18
18
  ---
19
19
 
@@ -79,7 +79,7 @@ const response = await gateway.invoke({
79
79
  agentId: 'agent-456'
80
80
  },
81
81
  workingMemory: { input: 'Hello!' },
82
- config: { model: 'cheap', provider: 'openrouter' }
82
+ config: { model: 'openai/gpt-4o-mini', provider: 'openrouter', maxTokens: 256 }
83
83
  });
84
84
 
85
85
  console.log(response.content, response.metadata?.costUsd, response.metadata?.tokens);
@@ -87,7 +87,7 @@ console.log(response.content, response.metadata?.costUsd, response.metadata?.tok
87
87
 
88
88
  ### Providers without manual `register()`
89
89
 
90
- - **OpenRouter:** Set **`OPENROUTER_API_KEY`** in `.env`. The gateway always passes this key to the router when set. **By default, OpenRouter is preferred** for routing (including when you also have direct keys such as `OPENAI_API_KEY`). **`@x12i/ai-tools` 2.1** resolves model + provider agnostically (`openrouter` + `cheap` or `openai` + `gpt-4o` same pipeline). Profile aliases (`cheap`, `balanced`, …) set `routedViaOpenRouter` from env. You do not need `USE_OPENROUTER=true` when the key is present.
90
+ - **OpenRouter:** Set **`OPENROUTER_API_KEY`** in `.env`. The gateway always passes this key to the router when set. **By default, OpenRouter is preferred** for routing (including when you also have direct keys such as `OPENAI_API_KEY`). **`@x12i/ai-tools`** resolves concrete model ids + provider via `resolveInvokeModel()` (catalog normalization, OpenRouter vs direct transport, router proxy flags). Pass catalog model ids such as `openai/gpt-4o-mini` or `gpt-4o` not profile shortcuts (`cheapest`, `cheap/default`) unless you set `aiTools.modelsOnly: false`.
91
91
  - **`USE_OPENROUTER=false`:** Do **not** prefer OpenRouter when a direct provider API key exists — use the direct provider instead. OpenRouter is **still** used as **fallback** when the request targets a provider without a direct key (e.g. `anthropic` without `ANTHROPIC_API_KEY`). It does not disable OpenRouter while `OPENROUTER_API_KEY` is set.
92
92
  - **Direct providers:** Set `OPENAI_API_KEY`, `GROK_API_KEY`, etc. Registered lazily on first invoke.
93
93
 
@@ -188,7 +188,7 @@ Instructions must be **complete caller text** — the gateway no longer injects
188
188
  | `AI_GATEWAY_LOGS_LEVEL` | Log threshold for gateway diagnostics (`AI_GATEWAY` prefix): `error` … `verbose` |
189
189
  | `AI_GATEWAY_VERBOSE` | Full payload lines (still requires `AI_GATEWAY_LOGS_LEVEL=verbose`) |
190
190
  | `LOGXER_PACKAGE_LEVELS` | Bulk stack levels, e.g. `AI_GATEWAY:info,AI_PROVIDER_ROUTER:debug` |
191
- | `OPENROUTER_API_KEY` | OpenRouter key; always wired when set (required for profile/OpenRouter routes) |
191
+ | `OPENROUTER_API_KEY` | OpenRouter key; always wired when set (required for OpenRouter transport) |
192
192
  | `USE_OPENROUTER` | Optional; default **prefer** OpenRouter when key is set. `false` = use direct provider keys when present; OpenRouter still used as fallback when a provider has no key |
193
193
  | Other provider keys | `OPENAI_API_KEY`, `GROK_API_KEY`, etc. |
194
194
 
@@ -210,12 +210,39 @@ Exports: `GATEWAY_LOGXER_PACKAGE`, `GATEWAY_LOG_ENV_PREFIX`, `createGatewayLogge
210
210
 
211
211
  ## @x12i/ai-tools v2 (models + cost)
212
212
 
213
- - **No Catalox / Firestore**catalogs come from ai-tools open-assets JSON (optional `bundledOnly`).
214
- - **`aiTools.enabled`** — bootstrap catalog client + calculator.
215
- - **`aiTools.resolveModels`** — `mergeConfig()` resolves model ids (strict in **`mode: 'dev'`**).
216
- - **`aiTools.calculateCost`** — prices usage before Activix `completeRecord` when the router did not mark the call priced.
213
+ Engine-owned catalog bootstrap and post-call billing. Consumers read **`metadata.costUsd`** / **`costStatus`** only no direct `@x12i/ai-tools` dependency for cost.
217
214
 
218
- Gateway helpers (also exported): `resolveCostCompletionWithAiTools`, `buildTraceUsageSummary`, `enrichTraceAttemptsWithBilling`.
215
+ ### Resolution order (after every successful LLM call)
216
+
217
+ | Step | Condition | Result |
218
+ |------|-----------|--------|
219
+ | A | Router/provider returned finite **`costUsd`** (or equivalent) | **`costStatus: "priced"`**, set cost |
220
+ | B | Tokens + catalog pricing succeeds (`isAuthoritative`, not `unknownModel`, finite cost ≥ 0) | **`priced`** (+ optional breakdown) |
221
+ | C | Tokens but no price | **`unpriced`** |
222
+ | D | No usage | omit **`costUsd`** and **`costStatus`** |
223
+
224
+ Step A always wins; explicit router **`costStatus: "unpriced"`** is never overridden by catalog.
225
+
226
+ Implemented in **`resolveCostCompletionWithAiTools`** (delegates to **`CostCalculator.calculateFromRecord`** via **`buildGatewayPricingRecord`**). Target: move orchestrator to ai-tools as **`resolveInvokeBilling`** — see [AI_TOOLS_INVOKE_BILLING_ORCHESTRATOR_SPEC.md](./docs/upstream-reports/AI_TOOLS_INVOKE_BILLING_ORCHESTRATOR_SPEC.md).
227
+
228
+ ### `aiTools` config (aligned with funcx / generic engine contract)
229
+
230
+ | Flag | Default | Purpose |
231
+ |------|---------|---------|
232
+ | **`enabled`** | `true` | Bootstrap **`AiModelsCatalogClient`** + **`CostCalculator`** |
233
+ | **`calculateCost`** | `true` | Run post-call catalog pricing when router did not price |
234
+ | **`resolveModels`** | `true` | **`mergeConfig()`** → **`resolveInvokeModel()`** |
235
+ | **`modelsOnly`** | `true` | Reject profile shortcuts (`cheapest`, `cheap/default`, …) |
236
+ | **`bundledOnly`** | `false` | Offline bundled catalogs only |
237
+ | **`costIncludeBreakdown`** | `false` | Include prompt/completion breakdown on priced results |
238
+ | **`catalogLane`** | `"text"` (ai-tools default) | Catalog lane for resolution + cost lookup (`text`, `image`, …) |
239
+ | **`cacheTtlMs`** | ai-tools default (24h) | In-memory catalog cache TTL |
240
+
241
+ - **No Catalox / Firestore** — catalogs come from ai-tools open-assets JSON (optional **`bundledOnly`**).
242
+
243
+ Gateway exports the model orchestrator from `@x12i/ai-tools` ≥ **2.5.0** (`resolveInvokeModel`, …) — see [AI_TOOLS_INVOKE_MODEL_RESOLUTION_ORCHESTRATOR_SPEC.md](./docs/upstream-reports/AI_TOOLS_INVOKE_MODEL_RESOLUTION_ORCHESTRATOR_SPEC.md).
244
+
245
+ Gateway billing helpers (also exported): `resolveCostCompletionWithAiTools`, `buildGatewayPricingRecord`, `catalogPricingSucceeded`, `ensureInvokeBillingCostStatus`, `buildTraceUsageSummary`, `enrichTraceAttemptsWithBilling`.
219
246
 
220
247
  ---
221
248
 
@@ -246,9 +273,9 @@ Mongo env: `MONGO_URI` + `MONGO_LOGS_DB` or `MONGO_DB`.
246
273
 
247
274
  ## Response metadata and cost
248
275
 
249
- On every successful **`invoke()`**:
276
+ On every successful **`invoke()`** and **`invokeChat()`**:
250
277
 
251
- - **`metadata.provider`**, **`modelUsed`**, **`maxTokensRequested`**, **`effectiveModelConfig`**
278
+ - **`metadata.provider`**, **`modelUsed`**, **`maxTokensRequested`**, **`effectiveModelConfig`** (invoke only)
252
279
  - **`metadata.tokens`**, **`costStatus`**, **`costUsd`** when usage exists and pricing applies
253
280
 
254
281
  Full contract: [AI Gateway invoke execution metadata](./docs/AI_GATEWAY_INVOKE_EXECUTION_METADATA.md).
@@ -276,7 +303,7 @@ Adds **`metadata.attempts`**, **`metadata.usage`**, **`metadata.requestIds`**, a
276
303
 
277
304
  Set via constructor `mode` or env `mode` / `MODE`. **Downstream hosts should document and expose `mode`** so graph/skill callers know resolution behavior.
278
305
 
279
- Every mode requires an explicit **`model`** on the request. Unresolved catalog profiles throw (e.g. `ModelProfileUnroutableError` in dev when profile has no routable target).
306
+ Every mode requires an explicit **`model`** on the request (concrete catalog id when `aiTools.modelsOnly` is true). Unknown models throw `ModelResolutionError`. Profile keys only work when `aiTools.modelsOnly: false`.
280
307
 
281
308
  ---
282
309
 
@@ -291,7 +318,7 @@ Every mode requires an explicit **`model`** on the request. Unresolved catalog p
291
318
  | `npm run test:flex-md-esm-regression` | ESM build regression for flex-md |
292
319
  | `npm run test:prepublish` | `build` + `npm test` |
293
320
 
294
- Live tests use `LIVE_TEST_PROVIDER` / `LIVE_TEST_MODEL` (default `openrouter` + `cheap`, an ai-tools profile alias). Set `LIVE_SKIP_INVOKE=1` to skip the LLM call.
321
+ Live tests use `LIVE_TEST_PROVIDER` / `LIVE_TEST_MODEL` (default `openrouter` + `openai/gpt-4o-mini`). Set `LIVE_TEST_PROFILES=1` to run legacy profile-key invokes. Set `LIVE_SKIP_INVOKE=1` to skip the LLM call.
295
322
 
296
323
  ---
297
324
 
@@ -1,44 +1,26 @@
1
1
  /**
2
- * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
2
+ * @x12i/ai-tools invoke client bootstrap for the gateway.
3
+ * Model resolution orchestration lives in ai-tools ≥ 2.5.0 (`resolveInvokeModel`).
3
4
  */
4
- import { AiModelsCatalogClient, CostCalculator, type ModelResolutionSuccess, type ModelResolverOptions, type OpenRouterRoutingConfig } from '@x12i/ai-tools';
5
+ import { type AiToolsInvokeClient, type ModelResolutionSuccess, type OpenRouterRoutingConfig } from '@x12i/ai-tools';
5
6
  import type { Logxer } from '@x12i/logxer';
6
- import type { ChatRequest, GatewayConfig } from './types.js';
7
- export type AiToolsClientBundle = {
8
- catalog: AiModelsCatalogClient;
9
- calculator: CostCalculator;
10
- routingEnv: OpenRouterRoutingConfig;
11
- };
7
+ import type { GatewayConfig } from './types.js';
8
+ export type AiToolsClientBundle = AiToolsInvokeClient;
9
+ export { resolveInvokeModel, applyOpenRouterInvokePolicy, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
10
+ export { resolveOpenRouterApiKey, resolvePreferOpenRouter, readPreferOpenRouterFromEnv } from './openrouter-routing.js';
11
+ export type { InvokeModelResolutionDiagnostics, InvokeModelResolutionInput, InvokeModelResolutionOptions, InvokeModelResolutionResult, InvokeRouterConfigSlice, AiToolsInvokeClient, } from '@x12i/ai-tools';
12
+ /** @deprecated Use buildInvokeModelResolverOptions */
13
+ export declare function buildModelResolverOptions(config: GatewayConfig, routingEnv?: OpenRouterRoutingConfig): import("@x12i/ai-tools").ModelResolverOptions;
12
14
  /**
13
- * Per-invoke resolver options: ai-tools decides OpenRouter vs direct from env + optional gateway prefer override.
15
+ * @deprecated Use mapResolutionToRouterConfig from @x12i/ai-tools
14
16
  */
15
- export declare function buildModelResolverOptions(config: GatewayConfig, routingEnv?: OpenRouterRoutingConfig): ModelResolverOptions;
17
+ export declare function applyModelResolution(merged: {
18
+ provider?: string;
19
+ model?: string;
20
+ }, resolution: ModelResolutionSuccess, gatewayDefaultEngine?: string, inputModel?: string): void;
16
21
  /**
17
22
  * Returns catalog + calculator, or null when disabled or bootstrap fails.
18
23
  */
19
24
  export declare function getAiToolsClient(config: GatewayConfig, logger: Logxer): Promise<AiToolsClientBundle | null>;
20
25
  /** Reset singleton (tests). */
21
26
  export declare function resetAiToolsClientForTests(): void;
22
- /**
23
- * Map catalog resolution to router `{ provider, model }` (agnostic to openrouter vs vendor input).
24
- */
25
- export declare function applyModelResolution(merged: NonNullable<ChatRequest['config']>, resolution: ModelResolutionSuccess, gatewayDefaultEngine?: string, inputModel?: string): void;
26
- type RouterConfigSlice = {
27
- provider?: string;
28
- model?: string;
29
- allowOpenRouterProxy?: boolean;
30
- providerProxy?: string;
31
- };
32
- type ModelResolutionMeta = {
33
- routedViaOpenRouter?: boolean;
34
- };
35
- /**
36
- * Router invoke flags after mergeConfig + ai-tools resolution (OpenRouter vs direct transport).
37
- */
38
- export declare function applyOpenRouterInvokePolicy(merged: RouterConfigSlice, options: {
39
- openRouterApiKey?: string;
40
- preferOpenRouter?: boolean;
41
- routingEnv?: OpenRouterRoutingConfig;
42
- resolution?: ModelResolutionMeta;
43
- }): void;
44
- export {};
@@ -1,26 +1,51 @@
1
1
  /**
2
- * Lazy @x12i/ai-tools catalog + cost calculator bootstrap.
2
+ * @x12i/ai-tools invoke client bootstrap for the gateway.
3
+ * Model resolution orchestration lives in ai-tools ≥ 2.5.0 (`resolveInvokeModel`).
3
4
  */
4
- import { AiModelsCatalogClient, CostCalculator, isEffectiveOpenRouterTransport, loadOpenRouterRoutingEnv, resolveModelVendorFromResolution } from '@x12i/ai-tools';
5
+ import { getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, mapResolutionToRouterConfig, buildInvokeModelResolverOptions, CostCalculator, } from '@x12i/ai-tools';
5
6
  import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
6
7
  import { resolvePreferOpenRouter } from './openrouter-routing.js';
7
- let sharedClientPromise = null;
8
- let sharedConfigKey;
8
+ export { resolveInvokeModel, applyOpenRouterInvokePolicy, buildInvokeModelResolverOptions, enrichModelResolutionError, mapResolutionToRouterConfig, ModelProfileUnroutableError, ModelProfileInputRejectedError, MODEL_PROFILE_UNROUTABLE, getAiToolsInvokeClient, resetAiToolsInvokeClientForTests as resetAiToolsInvokeClientForTestsUpstream, createAiToolsInvokeClient, } from '@x12i/ai-tools';
9
+ export { resolveOpenRouterApiKey, resolvePreferOpenRouter, readPreferOpenRouterFromEnv } from './openrouter-routing.js';
9
10
  let bootstrapFailedLogged = false;
10
- function configKey(config) {
11
- return `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}`;
11
+ function invokeClientOptions(config) {
12
+ return {
13
+ cacheTtlMs: config.aiTools?.cacheTtlMs,
14
+ ...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
15
+ ...(config.aiTools?.costIncludeBreakdown ? { costIncludeBreakdown: true } : {}),
16
+ cacheKey: `${config.aiTools?.cacheTtlMs ?? ''}:${config.aiTools?.costIncludeBreakdown ?? ''}:${config.aiTools?.bundledOnly ?? ''}:${config.aiTools?.catalogLane ?? ''}`,
17
+ };
12
18
  }
13
- /**
14
- * Per-invoke resolver options: ai-tools decides OpenRouter vs direct from env + optional gateway prefer override.
15
- */
16
- export function buildModelResolverOptions(config, routingEnv) {
17
- const env = routingEnv ?? loadOpenRouterRoutingEnv();
18
- const prefer = resolvePreferOpenRouter(config);
19
+ function withCatalogLaneCalculator(client, config) {
20
+ const lane = config.aiTools?.catalogLane;
21
+ if (!lane)
22
+ return client;
19
23
  return {
20
- routingEnv: env,
21
- ...(prefer ? { routeViaOpenRouter: true } : {}),
24
+ ...client,
25
+ calculator: new CostCalculator(client.catalog, {
26
+ ...(config.aiTools?.costIncludeBreakdown ? { includeBreakdown: true } : {}),
27
+ resolverOptions: buildInvokeModelResolverOptions({
28
+ routingEnv: client.routingEnv,
29
+ catalogLane: lane
30
+ })
31
+ })
22
32
  };
23
33
  }
34
+ /** @deprecated Use buildInvokeModelResolverOptions */
35
+ export function buildModelResolverOptions(config, routingEnv) {
36
+ return buildInvokeModelResolverOptions({
37
+ routingEnv,
38
+ preferOpenRouter: resolvePreferOpenRouter(config),
39
+ });
40
+ }
41
+ /**
42
+ * @deprecated Use mapResolutionToRouterConfig from @x12i/ai-tools
43
+ */
44
+ export function applyModelResolution(merged, resolution, gatewayDefaultEngine, inputModel) {
45
+ const mapped = mapResolutionToRouterConfig(resolution, { provider: merged.provider, model: inputModel ?? merged.model ?? '' }, gatewayDefaultEngine);
46
+ merged.provider = mapped.provider;
47
+ merged.model = mapped.model;
48
+ }
24
49
  /**
25
50
  * Returns catalog + calculator, or null when disabled or bootstrap fails.
26
51
  */
@@ -28,102 +53,27 @@ export async function getAiToolsClient(config, logger) {
28
53
  if (config.aiTools?.enabled === false) {
29
54
  return null;
30
55
  }
31
- const key = configKey(config);
32
- if (sharedClientPromise && sharedConfigKey !== key) {
33
- sharedClientPromise = null;
34
- }
35
- sharedConfigKey = key;
36
- if (!sharedClientPromise) {
37
- sharedClientPromise = bootstrapAiTools(config, logger);
38
- }
39
- return sharedClientPromise;
40
- }
41
- /** Reset singleton (tests). */
42
- export function resetAiToolsClientForTests() {
43
- sharedClientPromise = null;
44
- sharedConfigKey = undefined;
45
- bootstrapFailedLogged = false;
46
- }
47
- async function bootstrapAiTools(config, logger) {
48
- try {
49
- const routingEnv = loadOpenRouterRoutingEnv();
50
- const catalog = new AiModelsCatalogClient({
51
- cacheTtlMs: config.aiTools?.cacheTtlMs,
52
- ...(config.aiTools?.bundledOnly ? { bundledOnly: true } : {}),
53
- resolverOptions: { routingEnv },
54
- });
55
- const calculator = new CostCalculator(catalog, {
56
- includeBreakdown: config.aiTools?.costIncludeBreakdown === true,
57
- });
56
+ const client = await getAiToolsInvokeClient(invokeClientOptions(config), {
57
+ warn: (msg, err) => {
58
+ if (!bootstrapFailedLogged) {
59
+ bootstrapFailedLogged = true;
60
+ logger.warn(msg, withActivityIdentity(undefined, {
61
+ error: err instanceof Error ? err.message : String(err),
62
+ debugKind: gatewayLogDebug.anomaly,
63
+ }));
64
+ }
65
+ },
66
+ });
67
+ if (client) {
58
68
  logger.debug('ai-tools catalog client ready', {
59
69
  debugKind: gatewayLogDebug.state,
60
70
  });
61
- return { catalog, calculator, routingEnv };
62
- }
63
- catch (error) {
64
- if (!bootstrapFailedLogged) {
65
- bootstrapFailedLogged = true;
66
- logger.warn('ai-tools catalog bootstrap failed; model resolution and catalog cost calculation disabled', withActivityIdentity(undefined, {
67
- error: error instanceof Error ? error.message : String(error),
68
- debugKind: gatewayLogDebug.anomaly,
69
- }));
70
- }
71
- return null;
72
- }
73
- }
74
- /**
75
- * Map catalog resolution to router `{ provider, model }` (agnostic to openrouter vs vendor input).
76
- */
77
- export function applyModelResolution(merged, resolution, gatewayDefaultEngine, inputModel) {
78
- const ref = resolveModelVendorFromResolution(resolution, inputModel ?? merged.model ?? '', {
79
- asOpenRouter: resolution.routedViaOpenRouter,
80
- });
81
- if (ref) {
82
- merged.provider = ref.provider;
83
- merged.model = ref.model;
84
- return;
85
- }
86
- if (resolution.routedViaOpenRouter) {
87
- merged.provider = 'openrouter';
88
- merged.model = resolution.modelId;
89
- return;
90
- }
91
- const slash = resolution.modelId.indexOf('/');
92
- if (slash > 0) {
93
- merged.provider = resolution.record?.providerId ?? resolution.modelId.slice(0, slash);
94
- merged.model = resolution.modelId.slice(slash + 1);
95
- }
96
- else {
97
- merged.model = resolution.modelId;
98
- if (resolution.record?.providerId) {
99
- merged.provider = resolution.record.providerId;
100
- }
101
- }
102
- if (!merged.provider && gatewayDefaultEngine) {
103
- merged.provider = gatewayDefaultEngine;
71
+ return withCatalogLaneCalculator(client, config);
104
72
  }
73
+ return client;
105
74
  }
106
- /**
107
- * Router invoke flags after mergeConfig + ai-tools resolution (OpenRouter vs direct transport).
108
- */
109
- export function applyOpenRouterInvokePolicy(merged, options) {
110
- if (!options.openRouterApiKey?.trim())
111
- return;
112
- const routingEnv = options.routingEnv ?? loadOpenRouterRoutingEnv();
113
- const viaOpenRouter = options.resolution?.routedViaOpenRouter === true ||
114
- (options.resolution?.routedViaOpenRouter !== false &&
115
- isEffectiveOpenRouterTransport(routingEnv, {
116
- provider: merged.provider,
117
- modelId: merged.model,
118
- routeViaOpenRouter: options.preferOpenRouter ? true : undefined,
119
- }));
120
- if (viaOpenRouter) {
121
- merged.allowOpenRouterProxy = true;
122
- if (merged.provider && merged.provider !== 'openrouter') {
123
- merged.providerProxy = 'openrouter';
124
- }
125
- }
126
- else {
127
- merged.allowOpenRouterProxy = false;
128
- }
75
+ /** Reset singleton (tests). */
76
+ export function resetAiToolsClientForTests() {
77
+ resetAiToolsInvokeClientForTestsUpstream();
78
+ bootstrapFailedLogged = false;
129
79
  }
@@ -16,7 +16,10 @@ export declare function ensureTaskTypeId(request: ChatRequest, logger: Logxer):
16
16
  export type MergeConfigOptions = {
17
17
  catalog?: AiModelsCatalogClient | null;
18
18
  routingEnv?: OpenRouterRoutingConfig;
19
+ openRouterApiKey?: string;
20
+ preferOpenRouter?: boolean;
19
21
  };
22
+ export { MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, } from '@x12i/ai-tools';
20
23
  /**
21
24
  * Merges config with defaults
22
25
  * Supports using internal system action defaults (internalSkill or skillAudit) when useInternalDefaults is set
@@ -91,13 +94,38 @@ export type ResolveCostCompletionOptions = {
91
94
  calculator?: CostCalculator | null;
92
95
  calculateCost?: boolean;
93
96
  };
94
- /** Record shape for {@link CostCalculator.calculateFromRecord} (router + merged config + usage). */
97
+ /** Optional cache/reasoning token fields for catalog pricing records. */
98
+ export type InvokeUsageExtras = {
99
+ cached?: number;
100
+ cacheWrite?: number;
101
+ reasoning?: number;
102
+ };
103
+ /**
104
+ * Best-effort cache/reasoning token counts from router usage buckets
105
+ * (for {@link buildGatewayPricingRecord} / ai-tools {@link CostCalculator.calculateFromRecord}).
106
+ */
107
+ export declare function extractUsageExtrasFromRouterResponse(routerResponse: unknown): InvokeUsageExtras;
108
+ /**
109
+ * Whether ai-tools catalog pricing is authoritative enough for Step B (`priced`).
110
+ * Matches the generic engine contract: authoritative catalog hit with finite cost ≥ 0.
111
+ */
112
+ export declare function catalogPricingSucceeded(result: AiCostResult): boolean;
113
+ /** Record shape for {@link CostCalculator.calculateFromRecord} (shared engine contract). */
95
114
  export declare function buildGatewayPricingRecord(routerResponse: unknown, tokens: {
96
115
  prompt: number;
97
116
  completion: number;
98
117
  total: number;
99
118
  }, mergedConfig?: unknown): Record<string, unknown>;
100
119
  export declare function mapAiCostResultToResolvedActivityCost(base: ResolvedActivityCost, result: AiCostResult): ResolvedActivityCost;
120
+ /**
121
+ * G8 safety net: token usage without a billing signal → `unpriced`.
122
+ * Used at invoke boundaries after {@link resolveCostCompletionWithAiTools}.
123
+ */
124
+ export declare function ensureInvokeBillingCostStatus(billing: ResolvedActivityCost, tokens: {
125
+ prompt: number;
126
+ completion: number;
127
+ total: number;
128
+ }): ResolvedActivityCost;
101
129
  /**
102
130
  * Router cost passthrough, then optional @x12i/ai-tools catalog pricing when still unpriced.
103
131
  */
@@ -149,14 +177,6 @@ export declare function pickEffectiveModelConfigFromInvokeRequest(request: Pick<
149
177
  */
150
178
  export declare function tryExtractRouterLikePayloadFromErrorChain(error: unknown, maxDepth?: number): unknown;
151
179
  export declare function pickRequestIdsFromRouterLike(gatewayAiRequestId: string | undefined, routerLike: unknown): GatewayTraceRequestIds | undefined;
152
- /** Error code hint when a bundled profile name cannot be routed to a catalog target. */
153
- export declare const MODEL_PROFILE_UNROUTABLE = "MODEL_PROFILE_UNROUTABLE";
154
- export declare class ModelProfileUnroutableError extends Error {
155
- readonly profileAlias: string;
156
- readonly provider: string | undefined;
157
- readonly code = "MODEL_PROFILE_UNROUTABLE";
158
- constructor(profileAlias: string, provider: string | undefined, cause?: unknown);
159
- }
160
180
  type ModelResolutionCandidate = {
161
181
  provider: string;
162
182
  model: string;
@@ -175,7 +195,7 @@ export declare function mapGatewayFallbackAttemptsToRouter(attempts: GatewayFall
175
195
  responsePreview?: string;
176
196
  }>;
177
197
  /**
178
- * Log profile alias vs OpenRouter model id actually sent to the router after catalog resolution.
198
+ * Log original input vs OpenRouter model id actually sent to the router after catalog resolution.
179
199
  */
180
200
  export declare function logResolvedModelRouting(logger: Logxer, request: ChatRequest, mergedConfig: ChatRequest['config']): void;
181
201
  /**