@x12i/ai-gateway 10.4.3 → 11.0.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.
@@ -549,7 +549,6 @@ export class ActivityManager {
549
549
  // Build request object snapshots (raw = incoming; parsed = constructed messages/meta)
550
550
  const rawSnapshot = request._rawRequest ?? {
551
551
  instructions: request.instructions,
552
- context: request.context,
553
552
  prompt: request.prompt,
554
553
  messages: request.messages,
555
554
  workingMemory: request.workingMemory,
@@ -559,31 +558,26 @@ export class ActivityManager {
559
558
  const requestData = {};
560
559
  // raw snapshot (only allowed fields)
561
560
  if (rawSnapshot.instructions !== undefined ||
562
- rawSnapshot.context !== undefined ||
563
561
  rawSnapshot.prompt !== undefined) {
564
562
  requestData.raw = {
565
563
  instructions: rawSnapshot.instructions,
566
- context: rawSnapshot.context,
567
564
  prompt: rawSnapshot.prompt
568
565
  };
569
566
  }
570
567
  // parsed snapshot (only allowed fields)
571
568
  // Ensure parsed is populated if parsedSnapshot has data, even if individual fields are undefined
572
569
  if (parsedSnapshot.instructions !== undefined ||
573
- parsedSnapshot.context !== undefined ||
574
570
  parsedSnapshot.prompt !== undefined) {
575
571
  requestData.parsed = {
576
572
  instructions: parsedSnapshot.instructions,
577
- context: parsedSnapshot.context,
578
573
  prompt: parsedSnapshot.prompt
579
574
  };
580
575
  }
581
576
  else if (Object.keys(parsedSnapshot).length > 0) {
582
- // If parsedSnapshot exists but doesn't have instructions/context/prompt,
577
+ // If parsedSnapshot exists but doesn't have instructions/prompt,
583
578
  // still create parsed with what's available (mirror of raw request after processing)
584
579
  requestData.parsed = {
585
580
  instructions: rawSnapshot.instructions,
586
- context: rawSnapshot.context,
587
581
  prompt: rawSnapshot.prompt
588
582
  };
589
583
  }
@@ -631,11 +625,9 @@ export class ActivityManager {
631
625
  // Only attach if any field is present
632
626
  const hasRequest = (requestData.raw &&
633
627
  (requestData.raw.instructions !== undefined ||
634
- requestData.raw.context !== undefined ||
635
628
  requestData.raw.prompt !== undefined)) ||
636
629
  (requestData.parsed &&
637
630
  (requestData.parsed.instructions !== undefined ||
638
- requestData.parsed.context !== undefined ||
639
631
  requestData.parsed.prompt !== undefined)) ||
640
632
  requestData.messages !== undefined ||
641
633
  requestData.workingMemory !== undefined ||
@@ -750,7 +742,6 @@ export class ActivityManager {
750
742
  // Build request object snapshots (same as startActivity)
751
743
  const rawSnapshot = request._rawRequest ?? {
752
744
  instructions: request.instructions,
753
- context: request.context,
754
745
  prompt: request.prompt,
755
746
  messages: request.messages,
756
747
  workingMemory: request.workingMemory,
@@ -760,21 +751,17 @@ export class ActivityManager {
760
751
  const requestData = {};
761
752
  // raw snapshot
762
753
  if (rawSnapshot.instructions !== undefined ||
763
- rawSnapshot.context !== undefined ||
764
754
  rawSnapshot.prompt !== undefined) {
765
755
  requestData.raw = {
766
756
  instructions: rawSnapshot.instructions,
767
- context: rawSnapshot.context,
768
757
  prompt: rawSnapshot.prompt
769
758
  };
770
759
  }
771
760
  // parsed snapshot
772
761
  if (parsedSnapshot.instructions !== undefined ||
773
- parsedSnapshot.context !== undefined ||
774
762
  parsedSnapshot.prompt !== undefined) {
775
763
  requestData.parsed = {
776
764
  instructions: parsedSnapshot.instructions,
777
- context: parsedSnapshot.context,
778
765
  prompt: parsedSnapshot.prompt
779
766
  };
780
767
  }
@@ -802,20 +789,17 @@ export class ActivityManager {
802
789
  requestData.workingMemory = rawSnapshot.workingMemory;
803
790
  }
804
791
  // Add skill-specific request structure
805
- if (rawSnapshot.workingMemory || rawSnapshot.context) {
792
+ if (rawSnapshot.workingMemory) {
806
793
  requestData.skill = {
807
- variables: rawSnapshot.workingMemory,
808
- context: rawSnapshot.context
794
+ variables: rawSnapshot.workingMemory
809
795
  };
810
796
  }
811
797
  // Only attach if any field is present
812
798
  const hasRequest = (requestData.raw &&
813
799
  (requestData.raw.instructions !== undefined ||
814
- requestData.raw.context !== undefined ||
815
800
  requestData.raw.prompt !== undefined)) ||
816
801
  (requestData.parsed &&
817
802
  (requestData.parsed.instructions !== undefined ||
818
- requestData.parsed.context !== undefined ||
819
803
  requestData.parsed.prompt !== undefined)) ||
820
804
  requestData.messages !== undefined ||
821
805
  requestData.workingMemory !== undefined ||
@@ -104,10 +104,6 @@ export function buildWorkingMemory(request, existingWorkingMemory, otherMemories
104
104
  if (!workingMemory.job.objective && request.instructions) {
105
105
  workingMemory.job.objective = request.instructions;
106
106
  }
107
- if (!workingMemory.job.context && request.context) {
108
- workingMemory.job.context = request.context;
109
- }
110
- // Input field has been removed - data should come from workingMemory.input
111
107
  if (!workingMemory.job.narrative && request.prompt) {
112
108
  workingMemory.job.narrative = request.prompt;
113
109
  }
@@ -118,9 +114,6 @@ export function buildWorkingMemory(request, existingWorkingMemory, otherMemories
118
114
  if (!workingMemory.task.objective && request.instructions) {
119
115
  workingMemory.task.objective = request.instructions;
120
116
  }
121
- if (!workingMemory.task.context && request.context) {
122
- workingMemory.task.context = request.context;
123
- }
124
117
  // Input field has been removed - data should come from workingMemory.input
125
118
  if (!workingMemory.task.id && request.identity.taskId) {
126
119
  workingMemory.task.id = request.identity.taskId;
@@ -13,21 +13,17 @@ function isAIRequest(request) {
13
13
  'reasoningEncrypted' in request;
14
14
  }
15
15
  /**
16
- * Constructs messages from instructions/prompt/input/context
16
+ * Constructs messages from instructions and prompt (two-message contract).
17
17
  *
18
18
  * Uses direct message builder which handles:
19
- * - Token resolution (3-tier system)
20
19
  * - Template parsing (via Rendrix, @x12i/rendrix)
21
- * - Instruction block resolution and composition
22
- * - Flex-md format specification
23
- * - Message assembly
20
+ * - Message assembly: system (instructions) + user (prompt or input fallback)
24
21
  */
25
22
  export async function constructMessages(request, config, logger, parsedSnapshot) {
26
23
  logger.verbose('Constructing messages from request', {
27
24
  jobId: request.identity.jobId,
28
25
  agentId: request.agentId,
29
26
  hasInstructions: !!request.instructions,
30
- hasContext: !!request.context,
31
27
  hasPrompt: !!request.prompt,
32
28
  hasWorkingMemory: !!request.workingMemory
33
29
  });
@@ -7,14 +7,11 @@ import type { Logxer } from '@x12i/logxer';
7
7
  import { type MessageBuilderConfig } from './message-builder.js';
8
8
  type Request = ChatRequest | AIRequest;
9
9
  /**
10
- * Constructs messages from instructions/prompt/input/context
10
+ * Constructs messages from instructions and prompt (two-message contract).
11
11
  *
12
12
  * Uses direct message builder which handles:
13
- * - Token resolution (3-tier system)
14
13
  * - Template parsing (via Rendrix, @x12i/rendrix)
15
- * - Instruction block resolution and composition
16
- * - Flex-md format specification
17
- * - Message assembly
14
+ * - Message assembly: system (instructions) + user (prompt or input fallback)
18
15
  */
19
16
  export declare function constructMessages(request: Request, config: MessageBuilderConfig, logger: Logxer, parsedSnapshot?: any): Promise<Array<{
20
17
  role: string;
@@ -3,7 +3,7 @@
3
3
  * Handles utility functions
4
4
  */
5
5
  import * as crypto from 'crypto';
6
- import { FallbackExhaustedError } from '@x12i/ai-providers-router';
6
+ import { FallbackExhaustedError, enrichRunTaskError, providerAttemptsFromFallbackAttempts } from '@x12i/ai-providers-router';
7
7
  import { ModelResolutionError, ModelProfileInputRejectedError, ModelProfileUnroutableError, resolveInvokeModel, } from '@x12i/ai-tools';
8
8
  import { extractHttpStatusCode } from './gateway-retry.js';
9
9
  import { gatewayLogDebug, withActivityIdentity } from './gateway-log-meta.js';
@@ -980,10 +980,17 @@ export function mapGatewayFallbackAttemptsToRouter(attempts) {
980
980
  provider: a.provider,
981
981
  model: a.model,
982
982
  httpStatus: a.httpStatus,
983
+ ...(a.code !== undefined ? { code: a.code } : {}),
984
+ message: a.error,
983
985
  error: new Error(a.error),
984
- responsePreview: a.responsePreview
986
+ responsePreview: a.responsePreview,
985
987
  }));
986
988
  }
989
+ /** Apply router ≥ 4.10 provider failure enrichment (code, operatorHint, details.providerAttempts). */
990
+ export function enrichGatewayFallbackExhaustedError(exhausted) {
991
+ const routerAttempts = providerAttemptsFromFallbackAttempts(exhausted.attempts);
992
+ return enrichRunTaskError(exhausted, { providerAttempts: routerAttempts });
993
+ }
987
994
  /**
988
995
  * Log original input vs OpenRouter model id actually sent to the router after catalog resolution.
989
996
  */
@@ -1009,20 +1016,58 @@ export function logResolvedModelRouting(logger, request, mergedConfig) {
1009
1016
  }));
1010
1017
  }
1011
1018
  function mapRouterFallbackAttempts(attempts) {
1012
- return attempts.map((attempt) => ({
1013
- provider: String(attempt.provider),
1014
- ...(attempt.model !== undefined ? { model: attempt.model } : {}),
1015
- ...(attempt.httpStatus !== undefined ? { httpStatus: attempt.httpStatus } : {}),
1016
- error: attempt.error instanceof Error ? attempt.error.message : String(attempt.error),
1017
- ...(attempt.responsePreview !== undefined ? { responsePreview: attempt.responsePreview } : {})
1019
+ return attempts.map((attempt) => {
1020
+ const errorMsg = typeof attempt.message === 'string' && attempt.message.trim()
1021
+ ? attempt.message
1022
+ : attempt.error instanceof Error
1023
+ ? attempt.error.message
1024
+ : String(attempt.error);
1025
+ return {
1026
+ provider: String(attempt.provider),
1027
+ ...(attempt.model !== undefined ? { model: attempt.model } : {}),
1028
+ ...(attempt.httpStatus !== undefined ? { httpStatus: attempt.httpStatus } : {}),
1029
+ error: errorMsg,
1030
+ ...(attempt.code !== undefined ? { code: attempt.code } : {}),
1031
+ ...(attempt.responsePreview !== undefined ? { responsePreview: attempt.responsePreview } : {}),
1032
+ };
1033
+ });
1034
+ }
1035
+ function mapProviderAttemptDetailsToGateway(attempts) {
1036
+ return attempts.map((a) => ({
1037
+ provider: a.provider,
1038
+ ...(a.modelId !== undefined ? { model: a.modelId } : {}),
1039
+ ...(a.httpStatus !== undefined ? { httpStatus: a.httpStatus } : {}),
1040
+ error: a.message,
1041
+ ...(a.code !== undefined ? { code: a.code } : {}),
1018
1042
  }));
1019
1043
  }
1044
+ function extractEnrichedFieldsFromError(error) {
1045
+ if (error == null || typeof error !== 'object')
1046
+ return {};
1047
+ const e = error;
1048
+ return {
1049
+ ...(typeof e.code === 'string' ? { providerSubCode: e.code } : {}),
1050
+ ...(typeof e.operatorHint === 'string' ? { operatorHint: e.operatorHint } : {}),
1051
+ ...(e.details?.providerAttempts?.length
1052
+ ? { providerAttempts: e.details.providerAttempts }
1053
+ : {}),
1054
+ };
1055
+ }
1020
1056
  function extractFallbackAttemptsFromError(error) {
1021
- if (error instanceof FallbackExhaustedError && error.attempts.length > 0) {
1022
- return mapRouterFallbackAttempts(error.attempts);
1057
+ if (error instanceof FallbackExhaustedError) {
1058
+ if (error.details?.providerAttempts?.length) {
1059
+ return mapProviderAttemptDetailsToGateway(error.details.providerAttempts);
1060
+ }
1061
+ if (error.attempts.length > 0) {
1062
+ return mapRouterFallbackAttempts(error.attempts);
1063
+ }
1023
1064
  }
1024
1065
  if (error != null && typeof error === 'object') {
1025
1066
  const record = error;
1067
+ const details = record.details;
1068
+ if (details?.providerAttempts?.length) {
1069
+ return mapProviderAttemptDetailsToGateway(details.providerAttempts);
1070
+ }
1026
1071
  if (record.name === 'FallbackExhaustedError' && Array.isArray(record.attempts) && record.attempts.length > 0) {
1027
1072
  return mapRouterFallbackAttempts(record.attempts);
1028
1073
  }
@@ -1064,6 +1109,17 @@ export function buildInvokeRejectionMetadata(args) {
1064
1109
  const fallbackAttempts = args.error !== undefined
1065
1110
  ? tryExtractFallbackAttemptsFromErrorChain(args.error)
1066
1111
  : undefined;
1112
+ const enriched = args.error !== undefined ? extractEnrichedFieldsFromError(args.error) : {};
1113
+ const providerAttempts = enriched.providerAttempts ??
1114
+ (fallbackAttempts?.length
1115
+ ? fallbackAttempts.map((a) => ({
1116
+ provider: a.provider,
1117
+ ...(a.model ? { modelId: a.model } : {}),
1118
+ ...(a.httpStatus !== undefined ? { httpStatus: a.httpStatus } : {}),
1119
+ message: a.error,
1120
+ ...(a.code ? { code: a.code } : {}),
1121
+ }))
1122
+ : undefined);
1067
1123
  const openrouterFromPartial = partial !== undefined ? pickEnhancedOpenRouterMetadata(partial, false) : {};
1068
1124
  const openrouterFromError = args.error !== undefined ? tryExtractOpenRouterMetadataFromErrorChain(args.error) : {};
1069
1125
  const openrouterMetadata = {
@@ -1090,6 +1146,9 @@ export function buildInvokeRejectionMetadata(args) {
1090
1146
  ...(tokens !== undefined ? { tokens } : {}),
1091
1147
  ...(requestIds !== undefined ? { requestIds } : {}),
1092
1148
  ...(fallbackAttempts !== undefined ? { fallbackAttempts } : {}),
1149
+ ...(providerAttempts?.length ? { providerAttempts } : {}),
1150
+ ...(enriched.providerSubCode ? { providerSubCode: enriched.providerSubCode } : {}),
1151
+ ...(enriched.operatorHint ? { operatorHint: enriched.operatorHint } : {}),
1093
1152
  ...(mc === undefined ? { mergeConfigUnavailable: true } : {}),
1094
1153
  ...(openrouterMetadata.serverTools ? { serverTools: openrouterMetadata.serverTools } : {}),
1095
1154
  ...(openrouterMetadata.citations?.length ? { citations: openrouterMetadata.citations } : {}),
@@ -3,6 +3,8 @@
3
3
  * Handles utility functions
4
4
  */
5
5
  import type { AIInvokeRequest, ChatRequest, GatewayConfig, GatewayFallbackAttempt, GatewayInvokeRejectionMetadata, GatewayTraceAttempt, GatewayTraceMergedConfig, GatewayTraceRequestIds, GatewayTraceUsageSummary, ModelConfig } from './types.js';
6
+ import { FallbackExhaustedError } from '@x12i/ai-providers-router';
7
+ import type { ProviderFailureCode } from '@x12i/ai-providers-router';
6
8
  import type { Logxer } from '@x12i/logxer';
7
9
  import { type AiCostResult, type AiModelsCatalogClient, type CostCalculator, type OpenRouterRoutingConfig } from '@x12i/ai-tools';
8
10
  import { pickOpenRouterRuntimeMetadataSlice, enrichTraceOpenRouterRuntimeMetadata } from './openrouter-runtime-adapter/index.js';
@@ -191,9 +193,13 @@ export declare function mapGatewayFallbackAttemptsToRouter(attempts: GatewayFall
191
193
  provider: string;
192
194
  model?: string;
193
195
  httpStatus?: number;
196
+ code?: ProviderFailureCode;
197
+ message?: string;
194
198
  error: Error;
195
199
  responsePreview?: string;
196
200
  }>;
201
+ /** Apply router ≥ 4.10 provider failure enrichment (code, operatorHint, details.providerAttempts). */
202
+ export declare function enrichGatewayFallbackExhaustedError(exhausted: FallbackExhaustedError): FallbackExhaustedError;
197
203
  /**
198
204
  * Log original input vs OpenRouter model id actually sent to the router after catalog resolution.
199
205
  */
@@ -2,6 +2,17 @@
2
2
  * Gateway Validation Module
3
3
  * Basic validation for clean proxy implementation
4
4
  */
5
+ function rejectRemovedContextField(request) {
6
+ if ('context' in request && request.context !== undefined) {
7
+ const err = new Error(`The 'context' field has been removed. Context is not sent to the LLM. Merge background data into workingMemory upstream and use the prompt template for the user turn.`);
8
+ err.code = 'CONTEXT_FIELD_REMOVED';
9
+ err.details = {
10
+ field: 'context',
11
+ alternative: 'workingMemory + prompt template'
12
+ };
13
+ throw err;
14
+ }
15
+ }
5
16
  function validateMandatoryRuntimeIdentity(request) {
6
17
  const id = request.identity;
7
18
  if (id === undefined || id === null || typeof id !== 'object') {
@@ -20,6 +31,7 @@ export function validateChatRequest(request) {
20
31
  throw new Error('agentId is required');
21
32
  }
22
33
  validateMandatoryRuntimeIdentity(request);
34
+ rejectRemovedContextField(request);
23
35
  // Reject input field - it has been removed
24
36
  if ('input' in request && request.input !== undefined) {
25
37
  const err = new Error(`The 'input' field has been removed. Use workingMemory.input instead for template rendering. Prompt templates should contain {{input}} which will be resolved from workingMemory.input.`);
@@ -44,6 +56,7 @@ export function validateAIRequest(request) {
44
56
  throw new Error('agentId is required for AI requests');
45
57
  }
46
58
  validateMandatoryRuntimeIdentity(request);
59
+ rejectRemovedContextField(request);
47
60
  if (!request.actionType ||
48
61
  !GATEWAY_ACTION_TYPES.includes(request.actionType)) {
49
62
  throw new Error(`actionType is required and must be one of: ${GATEWAY_ACTION_TYPES.join(', ')}`);
@@ -11,7 +11,7 @@ import { resolveRetryConfig } from './gateway-defaults.js';
11
11
  import { buildMessages } from './message-builder.js';
12
12
  import { extractJsonFromFlexMd } from './flex-md-loader.js';
13
13
  import { enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
14
- import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, pickEnhancedOpenRouterMetadata, resolveCostCompletionWithAiTools, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, } from './gateway-utils.js';
14
+ import { attachGatewayInvokeRejectionMetadata, buildGatewayFallbackAttemptsFromTrace, buildInvokeRejectionMetadata, capActivityFullResponsePayload, formatFallbackExhaustionMessage, logResolvedModelRouting, enrichGatewayFallbackExhaustedError, mapGatewayFallbackAttemptsToRouter, DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, extractCostUsdFromRouterResponse, extractTokenUsageFromRouterResponse, mergeConfig, pickEffectiveModelConfigForMetadata, pickInvokeRoutingMetadataSlice, pickTraceMergedRouterConfig, pickEnhancedOpenRouterMetadata, resolveCostCompletionWithAiTools, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, } from './gateway-utils.js';
15
15
  import { buildTraceAttemptOpenRouterRuntimeSlice } from './openrouter-runtime-adapter/index.js';
16
16
  import { getAiToolsClient } from './ai-tools-client.js';
17
17
  import { autoRegisterProviders } from './gateway-provider-auto-register.js';
@@ -93,8 +93,54 @@ export class AIGateway {
93
93
  const startTime = Date.now();
94
94
  // Generate simple task type ID
95
95
  const taskTypeId = request.taskTypeId || `task-${Date.now()}`;
96
- // Simple message construction
97
- const messages = this.buildSimpleMessages(request);
96
+ const parsedSnapshot = {};
97
+ let messages = [];
98
+ try {
99
+ const builtMessages = await buildMessages(request, this.messageBuilderConfig, {
100
+ parsedSnapshot
101
+ });
102
+ messages = builtMessages.messages;
103
+ }
104
+ catch (error) {
105
+ const err = error instanceof Error ? error : new Error(String(error));
106
+ const endTime = Date.now();
107
+ const duration = endTime - startTime;
108
+ const errWithCode = err;
109
+ const isResolutionError = err.name === 'InstructionNotFoundError' ||
110
+ err.name === 'InstructionBackendError' ||
111
+ err.name === 'TemplateResolutionError' ||
112
+ errWithCode.code === 'PROMPT_NOT_FOUND' ||
113
+ errWithCode.code === 'PROMPT_RESOLUTION_ERROR' ||
114
+ errWithCode.code === 'PROMPT_RENDERED_EMPTY' ||
115
+ errWithCode.code === 'TEMPLATE_RESOLUTION_ERROR' ||
116
+ errWithCode.code === 'TEMPLATE_VARIABLE_MISSING' ||
117
+ errWithCode.code === 'USER_CONTENT_REQUIRED' ||
118
+ err.message.includes('Failed to resolve') ||
119
+ err.message.includes('Failed to render prompt template') ||
120
+ err.message.includes('not found') ||
121
+ err.message.includes('Instruction not found') ||
122
+ err.message.includes('Prompt not found');
123
+ if (isResolutionError && this.activityManager) {
124
+ await this.activityManager.logBadRequest(request, err, {
125
+ endTime,
126
+ duration,
127
+ error: err.message,
128
+ errorType: errWithCode.code || 'MessageBuildError',
129
+ diagnosticInfo: {
130
+ errorCode: errWithCode.code,
131
+ errorName: err.name,
132
+ failureType: 'validation-failure',
133
+ stage: 'message-building',
134
+ prompt: request.prompt,
135
+ instructions: typeof request.instructions === 'string' ? request.instructions.substring(0, 100) : '(object)'
136
+ },
137
+ failureType: 'validation-failure'
138
+ }, startTime);
139
+ }
140
+ throw err;
141
+ }
142
+ parsedSnapshot.messages = messages;
143
+ request._parsedRequest = parsedSnapshot;
98
144
  // Merge config (modelConfig > request.config > gateway defaults)
99
145
  const aiTools = await this.getAiTools();
100
146
  const mergedConfig = await mergeConfig(request, this.config, this.logger, {
@@ -247,6 +293,7 @@ export class AIGateway {
247
293
  errWithCode.code === 'PROMPT_RENDERED_EMPTY' ||
248
294
  errWithCode.code === 'TEMPLATE_RESOLUTION_ERROR' ||
249
295
  errWithCode.code === 'TEMPLATE_VARIABLE_MISSING' ||
296
+ errWithCode.code === 'USER_CONTENT_REQUIRED' ||
250
297
  err.message.includes('Failed to resolve') ||
251
298
  err.message.includes('Failed to render prompt template') ||
252
299
  err.message.includes('not found') ||
@@ -284,9 +331,6 @@ export class AIGateway {
284
331
  parsedSnapshot.messages = messages;
285
332
  // parsed.instructions and parsed.prompt are set by buildMessages to the resolved/rendered content
286
333
  // (after key resolution and Rendrix). Do not overwrite with raw request keys.
287
- if (parsedSnapshot.context === undefined) {
288
- parsedSnapshot.context = request.context;
289
- }
290
334
  // Attach parsedSnapshot to request for activity tracking
291
335
  request._parsedRequest = parsedSnapshot;
292
336
  // Merge config (modelConfig > request.config > gateway defaults)
@@ -512,7 +556,7 @@ export class AIGateway {
512
556
  actual: 'All candidates failed or returned no response.'
513
557
  }
514
558
  });
515
- const exhausted = new FallbackExhaustedError(mapGatewayFallbackAttemptsToRouter(fallbackAttempts));
559
+ const exhausted = enrichGatewayFallbackExhaustedError(new FallbackExhaustedError(mapGatewayFallbackAttemptsToRouter(fallbackAttempts)));
516
560
  exhausted.message = formatFallbackExhaustionMessage(fallbackAttempts, deduped);
517
561
  if (lastError) {
518
562
  exhausted.cause = lastError;
@@ -775,37 +819,6 @@ export class AIGateway {
775
819
  }
776
820
  });
777
821
  }
778
- /**
779
- * Build simple messages from request (instructions and prompt as literal template text; no registry).
780
- */
781
- buildSimpleMessages(request) {
782
- const messages = [];
783
- // Add instructions as system message if present
784
- if (request.instructions) {
785
- const instructions = typeof request.instructions === 'string'
786
- ? request.instructions
787
- : 'Default instructions';
788
- messages.push({ role: 'system', content: instructions });
789
- }
790
- // Add context as assistant message if present
791
- if (request.context) {
792
- const context = typeof request.context === 'string'
793
- ? request.context
794
- : JSON.stringify(request.context);
795
- messages.push({ role: 'assistant', content: context });
796
- }
797
- // Add prompt/input as user message
798
- // Input field has been removed - prompt template should contain {{input}} which resolves from workingMemory.input
799
- const userContent = request.prompt || '';
800
- if (userContent) {
801
- messages.push({ role: 'user', content: userContent });
802
- }
803
- // Add direct messages if present
804
- if (request.messages) {
805
- messages.push(...request.messages);
806
- }
807
- return messages;
808
- }
809
822
  // Provider management methods
810
823
  register(provider) {
811
824
  this.router.registerProvider(provider);
@@ -29,10 +29,6 @@ export declare class AIGateway {
29
29
  * Invoke AI request (with structured output support)
30
30
  */
31
31
  invoke<TContent = unknown>(request: AIInvokeRequest): Promise<EnhancedLLMResponse<TContent>>;
32
- /**
33
- * Build simple messages from request (instructions and prompt as literal template text; no registry).
34
- */
35
- private buildSimpleMessages;
36
32
  register(provider: any): void;
37
33
  listProviders(): string[];
38
34
  getRouter(): LLMProviderRouter;
@@ -10,7 +10,7 @@ export { LLMProviderRouter } from '@x12i/ai-providers-router';
10
10
  // Re-export factory functions
11
11
  export { createRouter, createRouterFromConfig } from '@x12i/ai-providers-router';
12
12
  // Re-export error classes
13
- export { ProviderNotFoundError, FallbackExhaustedError } from '@x12i/ai-providers-router';
13
+ export { ProviderNotFoundError, FallbackExhaustedError, OpenRouterApiKeyMissingError, } from '@x12i/ai-providers-router';
14
14
  // Re-export all from router (includes everything it exports)
15
15
  export * from '@x12i/ai-providers-router';
16
16
  // Export enhanced gateway
@@ -19,7 +19,7 @@ export { InstructionNotFoundError, InstructionBackendError, ModelRequiredError,
19
19
  export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
20
20
  export { autoRegisterProviders } from './gateway-provider-auto-register.js';
21
21
  export { registerOpenRouterRuntime, shouldUseOpenRouterRuntime, mapGatewayServerTools, } from './openrouter-runtime-adapter/index.js';
22
- export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
22
+ export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, enrichGatewayFallbackExhaustedError, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
23
23
  export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
24
24
  export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';
25
25
  export { contractSpecToFieldKeys, enrichParsedContentForOutputContract, resolveOutputContractFieldKeys } from './output-contract-normalizer.js';
@@ -9,7 +9,7 @@ export { LLMProviderRouter } from '@x12i/ai-providers-router';
9
9
  export type { RouterConfig, HealthCheckResult } from '@x12i/ai-providers-router';
10
10
  export { createRouter, createRouterFromConfig } from '@x12i/ai-providers-router';
11
11
  export type { CreateRouterConfig } from '@x12i/ai-providers-router';
12
- export { ProviderNotFoundError, FallbackExhaustedError } from '@x12i/ai-providers-router';
12
+ export { ProviderNotFoundError, FallbackExhaustedError, OpenRouterApiKeyMissingError, } from '@x12i/ai-providers-router';
13
13
  export type { RequestInterceptor, ResponseInterceptor } from '@x12i/ai-providers-router';
14
14
  export type { UsageTracker } from '@x12i/ai-providers-router';
15
15
  export * from '@x12i/ai-providers-router';
@@ -19,7 +19,7 @@ export { normalizeInvokeModelAtIngress } from './invoke-model-ingress.js';
19
19
  export { autoRegisterProviders } from './gateway-provider-auto-register.js';
20
20
  export { registerOpenRouterRuntime, shouldUseOpenRouterRuntime, mapGatewayServerTools, } from './openrouter-runtime-adapter/index.js';
21
21
  export type { GatewayConfig, ProviderModelRef, ModelConfig, GatewayModelConfig, GatewayServerToolsConfig, GatewayServerToolMode, GatewayServerToolUsage, GatewayNestedServerTool, GatewayWebSearchToolConfig, GatewayWebFetchToolConfig, GatewayDatetimeToolConfig, GatewayImageGenerationToolConfig, GatewayApplyPatchToolConfig, GatewayFusionToolConfig, GatewayAdvisorToolConfig, GatewaySubagentToolConfig, GatewayOpenRouterErrorMetadata, GatewayOpenRouterConfig, GatewayOpenRouterRuntimeConfig, GatewayServerToolUsageMap, GatewayCitation, GatewayGeneratedImage, GatewayPatchProposal, GatewayOpenRouterRuntimeMetadata, RetryConfig, ChatRequest, AIInvokeRequest, AIRequest, GatewayActionType, GatewayInvokeRejectionMetadata, GatewayFallbackAttempt, GatewayTraceRequestIds, GatewayTraceAttempt, GatewayTraceUsageSummary, GatewayTraceMergedConfig, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions, SmartInputConfig, SmartInputRenderOptions } from './types.js';
22
- export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
22
+ export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, tryExtractOpenRouterMetadataFromErrorChain, tryExtractFallbackAttemptsFromErrorChain, pickRequestIdsFromRouterLike, resolveActivityCostCompletion, resolveCostCompletionForActivity, resolveCostCompletionWithAiTools, buildGatewayPricingRecord, mapAiCostResultToResolvedActivityCost, catalogPricingSucceeded, extractUsageExtrasFromRouterResponse, buildTraceUsageSummary, enrichTraceAttemptsWithBilling, pickEnhancedOpenRouterMetadata, hasNonZeroTokenUsage, MODEL_PROFILE_UNROUTABLE, ModelProfileUnroutableError, ModelProfileInputRejectedError, buildGatewayFallbackAttemptsFromTrace, formatFallbackExhaustionMessage, enrichGatewayFallbackExhaustedError, logResolvedModelRouting, mapGatewayFallbackAttemptsToRouter } from './gateway-utils.js';
23
23
  export { getGatewayOperationalMode, isProdGatewayMode, parseModelProviderSpec } from './gateway-mode.js';
24
24
  export type { GatewayOperationalMode } from './gateway-mode.js';
25
25
  export { DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS, GATEWAY_DEFAULT_FREQUENCY_PENALTY, GATEWAY_DEFAULT_PRESENCE_PENALTY, GATEWAY_DEFAULT_RETRY, GATEWAY_DEFAULT_TEMPERATURE, GATEWAY_DEFAULT_TOP_P, resolveRetryConfig } from './gateway-defaults.js';