@x12i/ai-gateway 7.9.1 → 9.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.
Files changed (66) hide show
  1. package/README.md +107 -94
  2. package/dist/activity-manager.d.ts +3 -1
  3. package/dist/activity-manager.js +48 -17
  4. package/dist/content-normalizer/content-normalizer.d.ts +2 -1
  5. package/dist/content-normalizer/content-normalizer.js +5 -5
  6. package/dist/flex-md-loader.d.ts +2 -1
  7. package/dist/flex-md-loader.js +18 -15
  8. package/dist/gateway-config.d.ts +4 -4
  9. package/dist/gateway-config.js +21 -25
  10. package/dist/gateway-conversion.js +2 -2
  11. package/dist/gateway-log-meta.d.ts +15 -0
  12. package/dist/gateway-log-meta.js +36 -0
  13. package/dist/gateway-memory.js +6 -6
  14. package/dist/gateway-messages.js +4 -4
  15. package/dist/gateway-meta.d.ts +3 -1
  16. package/dist/gateway-meta.js +9 -2
  17. package/dist/gateway-utils.js +9 -9
  18. package/dist/gateway-validation.js +9 -0
  19. package/dist/gateway.js +32 -33
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.js +2 -1
  22. package/dist/instruction-optimizer.d.ts +6 -1
  23. package/dist/instruction-optimizer.js +10 -3
  24. package/dist/instructions-parser.d.ts +2 -1
  25. package/dist/instructions-parser.js +2 -2
  26. package/dist/logger-factory.js +12 -1
  27. package/dist/message-builder.js +19 -19
  28. package/dist/request-report-generator.js +1 -1
  29. package/dist/template-parser.d.ts +3 -1
  30. package/dist/template-parser.js +3 -2
  31. package/dist/troubleshooting-helper.d.ts +1 -1
  32. package/dist/troubleshooting-helper.js +2 -2
  33. package/dist/types.d.ts +16 -10
  34. package/dist-cjs/activity-manager.cjs +48 -17
  35. package/dist-cjs/activity-manager.d.ts +3 -1
  36. package/dist-cjs/content-normalizer/content-normalizer.cjs +5 -5
  37. package/dist-cjs/content-normalizer/content-normalizer.d.ts +2 -1
  38. package/dist-cjs/flex-md-loader.cjs +18 -15
  39. package/dist-cjs/flex-md-loader.d.ts +2 -1
  40. package/dist-cjs/gateway-config.cjs +21 -25
  41. package/dist-cjs/gateway-config.d.ts +4 -4
  42. package/dist-cjs/gateway-conversion.cjs +2 -2
  43. package/dist-cjs/gateway-log-meta.cjs +41 -0
  44. package/dist-cjs/gateway-log-meta.d.ts +15 -0
  45. package/dist-cjs/gateway-memory.cjs +6 -6
  46. package/dist-cjs/gateway-messages.cjs +4 -4
  47. package/dist-cjs/gateway-meta.cjs +9 -2
  48. package/dist-cjs/gateway-meta.d.ts +3 -1
  49. package/dist-cjs/gateway-utils.cjs +9 -9
  50. package/dist-cjs/gateway-validation.cjs +9 -0
  51. package/dist-cjs/gateway.cjs +31 -32
  52. package/dist-cjs/index.cjs +6 -1
  53. package/dist-cjs/index.d.ts +3 -2
  54. package/dist-cjs/instruction-optimizer.cjs +10 -3
  55. package/dist-cjs/instruction-optimizer.d.ts +6 -1
  56. package/dist-cjs/instructions-parser.cjs +2 -2
  57. package/dist-cjs/instructions-parser.d.ts +2 -1
  58. package/dist-cjs/logger-factory.cjs +12 -1
  59. package/dist-cjs/message-builder.cjs +19 -19
  60. package/dist-cjs/request-report-generator.cjs +1 -1
  61. package/dist-cjs/template-parser.cjs +3 -2
  62. package/dist-cjs/template-parser.d.ts +3 -1
  63. package/dist-cjs/troubleshooting-helper.cjs +2 -2
  64. package/dist-cjs/troubleshooting-helper.d.ts +1 -1
  65. package/dist-cjs/types.d.ts +16 -10
  66. package/package.json +3 -3
@@ -10,6 +10,7 @@ exports.ActivityManager = void 0;
10
10
  exports.ensureGatewayRequestIdentity = ensureGatewayRequestIdentity;
11
11
  const activix_1 = require("@x12i/activix");
12
12
  const activity_tracking_config_js_1 = require("./config/activity-tracking-config.cjs");
13
+ const gateway_log_meta_js_1 = require("./gateway-log-meta.cjs");
13
14
  function readAiRequestIdFromRequest(request) {
14
15
  const aiRequestId = request.aiRequestId;
15
16
  if (typeof aiRequestId === 'string' && aiRequestId.trim().length > 0) {
@@ -106,40 +107,64 @@ function buildInstanceEnvelope(request) {
106
107
  function resolveSessionIdForRequest(request, incomingIdentity, aiRequestId) {
107
108
  const fromIdentity = trimIdentityString(incomingIdentity?.sessionId);
108
109
  const topLevel = trimIdentityString(request.sessionId);
109
- const jobId = trimIdentityString(request.jobId);
110
+ const identityJobId = trimIdentityString(incomingIdentity?.jobId);
110
111
  if (fromIdentity !== undefined && topLevel !== undefined && fromIdentity !== topLevel) {
111
112
  throw new Error(`identity.sessionId (${fromIdentity}) and top-level sessionId (${topLevel}) must agree`);
112
113
  }
113
- return fromIdentity ?? topLevel ?? jobId ?? aiRequestId;
114
+ return fromIdentity ?? topLevel ?? identityJobId ?? aiRequestId;
115
+ }
116
+ function logUpstreamIdentityWarnings(logger, incomingIdentity, merged) {
117
+ const aiRequestId = merged.aiRequestId;
118
+ if (!incomingIdentity || typeof incomingIdentity !== 'object') {
119
+ logger?.warn('missingRuntimeIdentityObject', (0, gateway_log_meta_js_1.withActivityIdentity)(merged, {
120
+ message: 'request.identity is missing; upstream must send the mandatory runtime identity object',
121
+ aiRequestId,
122
+ debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
123
+ }));
124
+ return;
125
+ }
126
+ const missing = [];
127
+ if (!trimIdentityString(incomingIdentity.jobId))
128
+ missing.push('jobId');
129
+ if (!trimIdentityString(incomingIdentity.taskId))
130
+ missing.push('taskId');
131
+ if (missing.length > 0) {
132
+ logger?.warn('missingUpstreamIdentityFields', (0, gateway_log_meta_js_1.withActivityIdentity)(merged, {
133
+ message: `Upstream identity is missing required field(s): ${missing.join(', ')}. jobId and taskId must be set on request.identity by the client; the gateway does not generate them.`,
134
+ missingFields: missing,
135
+ aiRequestId,
136
+ debugKind: gateway_log_meta_js_1.gatewayLogDebug.anomaly
137
+ }));
138
+ }
114
139
  }
115
140
  function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
116
141
  const incomingIdentity = request.identity;
117
142
  const sessionId = resolveSessionIdForRequest(request, incomingIdentity, aiRequestId);
118
- const legacyJobId = trimIdentityString(incomingIdentity?.jobId) ?? trimIdentityString(request.jobId);
143
+ const upstreamJobId = trimIdentityString(incomingIdentity?.jobId) ?? '';
144
+ const upstreamTaskId = trimIdentityString(incomingIdentity?.taskId) ?? '';
119
145
  const combinedEnrichment = pickGatewayEnrichmentFields({
120
146
  ...graphIdentityExtrasFromRequest(request),
121
147
  ...(extras || {})
122
148
  });
123
149
  const safeEnrichment = mergeActivixEnrichment(incomingIdentity, combinedEnrichment);
124
- // Request fields are defaults for this hop; incoming `identity` from upstream wins on conflict.
125
- // Gateway enrichment (graph/skill, etc.) fills only keys the outer envelope did not already set.
150
+ // jobId / taskId: upstream `request.identity` only (never top-level request.jobId / request.taskId).
126
151
  const merged = {
127
152
  jobTypeId: request.jobTypeId,
128
153
  agentId: request.agentId,
129
- taskId: request.taskId,
130
154
  taskTypeId: request.taskTypeId,
131
155
  ...safeEnrichment,
132
156
  ...(incomingIdentity || {}),
133
157
  sessionId,
134
158
  instance: buildInstanceEnvelope(request),
135
- aiRequestId
159
+ aiRequestId,
160
+ jobId: upstreamJobId,
161
+ taskId: upstreamTaskId
136
162
  };
137
163
  merged.sessionId = sessionId;
138
164
  merged.instance = buildInstanceEnvelope(request);
139
165
  merged.aiRequestId = aiRequestId;
140
- if (legacyJobId !== undefined) {
141
- merged.jobId = legacyJobId;
142
- }
166
+ merged.jobId = upstreamJobId;
167
+ merged.taskId = upstreamTaskId;
143
168
  return merged;
144
169
  }
145
170
  /**
@@ -149,13 +174,16 @@ function mergeGatewayActivityIdentity(request, aiRequestId, extras) {
149
174
  * **Execution context:** treats `request.identity` as received from upstream. Root fields (`sessionId`,
150
175
  * `instance`, and any keys already set on that object) are not replaced by gateway defaults; the gateway
151
176
  * only fills missing required pieces and adds local context (e.g. graph linkage) where absent.
177
+ *
178
+ * @param logger - Optional Logxer used to WARN when mandatory upstream `identity.jobId` / `identity.taskId` are absent.
152
179
  */
153
- function ensureGatewayRequestIdentity(request, extras) {
180
+ function ensureGatewayRequestIdentity(request, extras, logger) {
154
181
  const aiRequestId = readAiRequestIdFromRequest(request);
155
182
  const identity = mergeGatewayActivityIdentity(request, aiRequestId, {
156
183
  ...graphIdentityExtrasFromRequest(request),
157
184
  ...(extras || {})
158
185
  });
186
+ logUpstreamIdentityWarnings(logger, request.identity, identity);
159
187
  request.identity = identity;
160
188
  return identity;
161
189
  }
@@ -291,7 +319,7 @@ class ActivityManager {
291
319
  * @returns Activity metadata (contains unique _id/recordId) or undefined if tracking is disabled
292
320
  */
293
321
  async startActivity(request, startTime) {
294
- const identity = ensureGatewayRequestIdentity(request);
322
+ const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
295
323
  const aiRequestId = identity.aiRequestId;
296
324
  // Capture ONLY request data - NO response data at this stage
297
325
  // Note: v2.3.2+ excludes flat request/config fields from root level
@@ -311,9 +339,10 @@ class ActivityManager {
311
339
  }
312
340
  const activityMetadata = {
313
341
  aiRequestId,
342
+ jobId: identity.jobId,
314
343
  jobTypeId: request.jobTypeId,
315
344
  agentId: request.agentId,
316
- taskId: request.taskId,
345
+ taskId: identity.taskId,
317
346
  taskTypeId: request.taskTypeId,
318
347
  startTime,
319
348
  status: 'started',
@@ -507,7 +536,7 @@ class ActivityManager {
507
536
  }
508
537
  // Determine inference type (will be extracted in gateway.ts and passed via request metadata)
509
538
  const inferenceType = aiRequest.inferenceType || 'question-answer';
510
- const identity = ensureGatewayRequestIdentity(request);
539
+ const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
511
540
  const aiRequestId = identity.aiRequestId;
512
541
  if (!this.activix) {
513
542
  return undefined;
@@ -525,9 +554,10 @@ class ActivityManager {
525
554
  skillId: skillId ?? skillKey,
526
555
  inferenceType, // ✅ Recommended: type of inference
527
556
  aiRequestId,
557
+ jobId: identity.jobId,
528
558
  jobTypeId: request.jobTypeId,
529
559
  agentId: request.agentId,
530
- taskId: request.taskId,
560
+ taskId: identity.taskId,
531
561
  taskTypeId: request.taskTypeId,
532
562
  startTime,
533
563
  status: 'started',
@@ -901,7 +931,7 @@ class ActivityManager {
901
931
  * @param startTime - When the request started
902
932
  */
903
933
  async logBadRequest(request, error, details, startTime) {
904
- const identity = ensureGatewayRequestIdentity(request);
934
+ const identity = ensureGatewayRequestIdentity(request, undefined, this.logger);
905
935
  const aiRequestId = identity.aiRequestId;
906
936
  const endTime = details.endTime;
907
937
  const duration = details.duration;
@@ -926,9 +956,10 @@ class ActivityManager {
926
956
  const badRequestMetadata = {
927
957
  activityType: 'bad-request',
928
958
  aiRequestId,
959
+ jobId: identity.jobId,
929
960
  jobTypeId: request.jobTypeId,
930
961
  agentId: request.agentId,
931
- taskId: request.taskId,
962
+ taskId: identity.taskId,
932
963
  taskTypeId: request.taskTypeId,
933
964
  startTime,
934
965
  status: 'started',
@@ -15,8 +15,10 @@ type Request = ChatRequest | AIRequest;
15
15
  * **Execution context:** treats `request.identity` as received from upstream. Root fields (`sessionId`,
16
16
  * `instance`, and any keys already set on that object) are not replaced by gateway defaults; the gateway
17
17
  * only fills missing required pieces and adds local context (e.g. graph linkage) where absent.
18
+ *
19
+ * @param logger - Optional Logxer used to WARN when mandatory upstream `identity.jobId` / `identity.taskId` are absent.
18
20
  */
19
- export declare function ensureGatewayRequestIdentity(request: ChatRequest | AIRequest, extras?: Partial<ActivityIdentity>): ActivityIdentity;
21
+ export declare function ensureGatewayRequestIdentity(request: ChatRequest | AIRequest, extras?: Partial<ActivityIdentity>, logger?: Logxer): ActivityIdentity;
20
22
  type ActivityMetadata = Record<string, any> & {
21
23
  activityId?: string;
22
24
  recordId?: string;
@@ -187,14 +187,14 @@ function determineContentType(value, parsedContent) {
187
187
  * @param response - Router response object
188
188
  * @returns Normalized content with metadata
189
189
  */
190
- function normalizeContent(response) {
190
+ function normalizeContent(response, logger) {
191
191
  // Extract content value from response
192
192
  const contentValue = extractContentValue(response);
193
193
  // CRITICAL: Check if router already converted object to "[object Object]" string
194
194
  // This happens when router uses String() on objects before returning
195
195
  if (typeof contentValue === 'string' && contentValue === '[object Object]') {
196
196
  // Router already broke it - try to recover from other fields
197
- console.error('[content-normalizer] Router returned "[object Object]" string - attempting recovery', {
197
+ logger?.error('[content-normalizer] Router returned "[object Object]" string - attempting recovery', {
198
198
  responseKeys: Object.keys(response),
199
199
  hasOutput: !!response.output,
200
200
  hasRawText: !!response.rawText,
@@ -235,7 +235,7 @@ function normalizeContent(response) {
235
235
  };
236
236
  }
237
237
  // If we can't recover, log error and return empty
238
- console.error('[content-normalizer] Cannot recover from "[object Object]" - no valid object found in response');
238
+ logger?.error('[content-normalizer] Cannot recover from "[object Object]" - no valid object found in response');
239
239
  return {
240
240
  content: '',
241
241
  rawText: undefined,
@@ -324,7 +324,7 @@ function normalizeContent(response) {
324
324
  // Last resort: use String() but this should never happen
325
325
  normalizedString = String(contentValue);
326
326
  // This will be "[object Object]" but we've exhausted all options
327
- console.error('[content-normalizer] Failed to stringify object and no rawText available', {
327
+ logger?.error('[content-normalizer] Failed to stringify object and no rawText available', {
328
328
  error,
329
329
  contentValueType: typeof contentValue,
330
330
  isArray: Array.isArray(contentValue)
@@ -347,7 +347,7 @@ function normalizeContent(response) {
347
347
  }
348
348
  catch {
349
349
  // If this also fails, we're stuck with "[object Object]"
350
- console.error('[content-normalizer] Cannot recover from "[object Object]" - all recovery attempts failed');
350
+ logger?.error('[content-normalizer] Cannot recover from "[object Object]" - all recovery attempts failed');
351
351
  }
352
352
  }
353
353
  }
@@ -6,6 +6,7 @@
6
6
  * Preserves both rawText and parsedContent for downstream consumers.
7
7
  */
8
8
  import type { LLMResponse } from '../types.js';
9
+ import type { Logxer } from '@x12i/logxer';
9
10
  import type { NormalizedContent } from './types.js';
10
11
  /**
11
12
  * Normalizes content from router response
@@ -20,7 +21,7 @@ import type { NormalizedContent } from './types.js';
20
21
  * @param response - Router response object
21
22
  * @returns Normalized content with metadata
22
23
  */
23
- export declare function normalizeContent(response: LLMResponse): NormalizedContent;
24
+ export declare function normalizeContent(response: LLMResponse, logger?: Logxer): NormalizedContent;
24
25
  /**
25
26
  * Checks if content is empty
26
27
  *
@@ -61,6 +61,7 @@ exports.hasFlexMdContract = hasFlexMdContract;
61
61
  exports.detectComplianceLevel = detectComplianceLevel;
62
62
  const path = __importStar(require("path"));
63
63
  const url_1 = require("url");
64
+ const logxer_1 = require("@x12i/logxer");
64
65
  let flexMdModule = null;
65
66
  let flexMdLoadError = null;
66
67
  let flexMdLoading = null;
@@ -374,7 +375,7 @@ async function extractFlexMdOutputFormatFromInstructions(instructions, complianc
374
375
  * - Other flex-md options
375
376
  * @returns Parsed JSON object and method used, or null if parsing fails
376
377
  */
377
- async function extractJsonFromFlexMd(content) {
378
+ async function extractJsonFromFlexMd(content, logger) {
378
379
  // ARCHITECTURE: ai-gateway layer only handles unstructured parsing
379
380
  // Schema-driven parsing is handled at skills layer with nx-md-parser
380
381
  // No schema parameter - this layer doesn't know about schemas
@@ -392,8 +393,9 @@ async function extractJsonFromFlexMd(content) {
392
393
  }
393
394
  }
394
395
  catch (error) {
395
- // markdownToJson failed, continue to other methods
396
- console.warn('markdownToJson failed:', error instanceof Error ? error.message : String(error));
396
+ logger?.warn('markdownToJson failed', {
397
+ error: error instanceof Error ? error.message : String(error)
398
+ });
397
399
  }
398
400
  }
399
401
  // Secondary approach: Use extractFromMarkdown for low-level section extraction
@@ -403,15 +405,16 @@ async function extractJsonFromFlexMd(content) {
403
405
  if (result && typeof result === 'object' && result !== null) {
404
406
  const json = result.json || result.data || result.result || result;
405
407
  if (json && typeof json === 'object' && json !== null) {
406
- console.debug('extractJsonFromFlexMd: using extractFromMarkdown');
407
- console.debug('extractJsonFromFlexMd: json:', json);
408
+ logger?.debug('extractJsonFromFlexMd: using extractFromMarkdown', { debugKind: logxer_1.DebugLogAbstract.TRACE });
409
+ logger?.debug('extractJsonFromFlexMd: parsed json', { json, debugKind: logxer_1.DebugLogAbstract.STATE });
408
410
  return { json, method: 'extractFromMarkdown' };
409
411
  }
410
412
  }
411
413
  }
412
414
  catch (error) {
413
- // extractFromMarkdown failed, continue
414
- console.warn('extractFromMarkdown failed:', error instanceof Error ? error.message : String(error));
415
+ logger?.warn('extractFromMarkdown failed', {
416
+ error: error instanceof Error ? error.message : String(error)
417
+ });
415
418
  }
416
419
  }
417
420
  }
@@ -419,13 +422,14 @@ async function extractJsonFromFlexMd(content) {
419
422
  // flex-md loading failed, log once and continue to fallback.
420
423
  if (!hasLoggedFlexMdLoadWarning) {
421
424
  hasLoggedFlexMdLoadWarning = true;
422
- console.warn('extractJsonFromFlexMd: flex-md library failed to load, using markdown parser fallback:', error instanceof Error ? error.message : String(error));
425
+ logger?.warn('extractJsonFromFlexMd: flex-md library failed to load, using markdown parser fallback', {
426
+ error: error instanceof Error ? error.message : String(error)
427
+ });
423
428
  }
424
429
  }
425
- console.debug('extractJsonFromFlexMd: using markdown parser fallback');
426
- console.debug('extractJsonFromFlexMd: content:', content);
430
+ logger?.debug('extractJsonFromFlexMd: using markdown parser fallback', { contentLength: content.length });
427
431
  // Final fallback: Parse markdown sections
428
- return fallbackMarkdownParser(content);
432
+ return fallbackMarkdownParser(content, logger);
429
433
  }
430
434
  /**
431
435
  * Helper function to detect and parse markdown lists into arrays
@@ -483,7 +487,7 @@ function parseMarkdownList(content) {
483
487
  /**
484
488
  * Simple markdown parser for unstructured content (fallback when flex-md unavailable)
485
489
  */
486
- function fallbackMarkdownParser(content) {
490
+ function fallbackMarkdownParser(content, logger) {
487
491
  const result = {};
488
492
  const detectedHeaders = [];
489
493
  // Simple section extraction based on markdown headers
@@ -526,12 +530,11 @@ function fallbackMarkdownParser(content) {
526
530
  result[key] = parseMarkdownList(currentContent);
527
531
  detectedHeaders.push(key);
528
532
  }
529
- // Debug logging for incomplete parsing
530
- console.log('Fallback parser detected headers:', detectedHeaders);
533
+ logger?.debug('Fallback parser detected headers', { detectedHeaders, debugKind: logxer_1.DebugLogAbstract.STATE });
531
534
  const expectedHeaders = ['shortAnswer', 'fullAnswer', 'assumptions', 'unknowns', 'evidence'];
532
535
  const missingHeaders = expectedHeaders.filter(h => !detectedHeaders.includes(h));
533
536
  if (missingHeaders.length > 0) {
534
- console.log('Fallback parser missing expected headers:', missingHeaders);
537
+ logger?.debug('Fallback parser missing expected headers', { missingHeaders, debugKind: logxer_1.DebugLogAbstract.STATE });
535
538
  }
536
539
  // If we extracted some structure, return it
537
540
  if (Object.keys(result).length > 0) {
@@ -13,6 +13,7 @@
13
13
  * - Native output format support: Extracts and uses flex-md native output formats from instructions
14
14
  * based on compliance level when no explicit format is provided
15
15
  */
16
+ import { type Logxer } from '@x12i/logxer';
16
17
  /**
17
18
  * Load flex-md module (ES Module) asynchronously
18
19
  * Caches the result to avoid multiple imports
@@ -38,7 +39,7 @@ export declare function loadFlexMd(): Promise<any>;
38
39
  * - Other flex-md options
39
40
  * @returns Parsed JSON object and method used, or null if parsing fails
40
41
  */
41
- export declare function extractJsonFromFlexMd(content: string): Promise<{
42
+ export declare function extractJsonFromFlexMd(content: string, logger?: Logxer): Promise<{
42
43
  json: any;
43
44
  method: string;
44
45
  } | null>;
@@ -92,10 +92,10 @@ const template_render_merge_js_1 = require("./template-render-merge.cjs");
92
92
  const gateway_rate_limiter_js_1 = require("./gateway-rate-limiter.cjs");
93
93
  const gateway_rate_limiter_constants_js_1 = require("./gateway-rate-limiter-constants.cjs");
94
94
  /**
95
- * Loads configuration from JSON files (model config and instructionsBlocks)
96
- * Note: Called before logger is initialized, so no logging here
95
+ * Loads configuration from JSON files (model config and instructionsBlocks).
96
+ * Pass a {@link Logxer} instance so load diagnostics go through logxer (not console).
97
97
  */
98
- function loadConfig() {
98
+ function loadConfig(logger) {
99
99
  const defaultModelConfig = {};
100
100
  const defaultInstructionsBlocks = {};
101
101
  let defaultTemplateRendering;
@@ -120,26 +120,22 @@ function loadConfig() {
120
120
  const parsed = JSON.parse(content);
121
121
  // Use Object.assign to merge, preserving nested structure
122
122
  Object.assign(defaultInstructionsBlocks, parsed);
123
- // Debug: Log what was loaded (only in development)
124
- if (process.env.NODE_ENV !== 'production') {
125
- console.log('Loaded instructions blocks:', {
126
- topLevelKeys: Object.keys(defaultInstructionsBlocks),
127
- hasOutput: 'output' in defaultInstructionsBlocks,
128
- outputKeys: 'output' in defaultInstructionsBlocks ? Object.keys(defaultInstructionsBlocks.output) : []
129
- });
130
- }
123
+ logger.debug('Loaded instructions blocks from defaults', {
124
+ topLevelKeys: Object.keys(defaultInstructionsBlocks),
125
+ hasOutput: 'output' in defaultInstructionsBlocks,
126
+ outputKeys: 'output' in defaultInstructionsBlocks ? Object.keys(defaultInstructionsBlocks.output) : []
127
+ });
131
128
  }
132
129
  else {
133
- // Optional file: fallback defaults below still keep the gateway functional.
134
- if (process.env.NODE_ENV !== 'production') {
135
- console.debug('Optional instructions blocks file not found at:', instructionsBlocksPath);
136
- }
130
+ logger.verbose('Optional instructions blocks file not found; using packaged fallbacks', {
131
+ instructionsBlocksPath
132
+ });
137
133
  }
138
134
  }
139
135
  catch (error) {
140
- // Can't log here as logger isn't initialized yet
141
- // Error will be logged after logger initialization in constructor
142
- console.warn('Failed to load defaults from JSON files:', error);
136
+ logger.warn('Failed to load defaults from JSON files', {
137
+ error: error instanceof Error ? error.message : String(error)
138
+ });
143
139
  }
144
140
  // Ensure critical blocks exist even if file loading failed
145
141
  if (!defaultInstructionsBlocks['outputObjectPrefix']) {
@@ -167,10 +163,10 @@ function getFlexMdMinComplianceLevel() {
167
163
  function setupRequestInterceptor(router, logger) {
168
164
  logger.debug('Setting up request interceptor for jobId propagation and config cleanup');
169
165
  router.addRequestInterceptor(async (request, provider) => {
170
- // Propagate jobId to request metadata
171
- if (request.jobId) {
172
- logger.verbose('Propagating jobId to request metadata', {
173
- jobId: request.jobId,
166
+ const identityJobId = request?.identity?.jobId;
167
+ if (typeof identityJobId === 'string' && identityJobId.trim().length > 0) {
168
+ logger.verbose('Propagating identity.jobId to request metadata', {
169
+ jobId: identityJobId,
174
170
  provider: provider?.getProviderName?.() || 'unknown'
175
171
  });
176
172
  if (!request.config) {
@@ -179,7 +175,7 @@ function setupRequestInterceptor(router, logger) {
179
175
  if (!request.config.metadata) {
180
176
  request.config.metadata = {};
181
177
  }
182
- request.config.metadata.jobId = request.jobId;
178
+ request.config.metadata.jobId = identityJobId;
183
179
  }
184
180
  // Remove 'provider' from config - router uses it for routing but providers don't accept it
185
181
  // Router reads config.provider to determine which provider to call, but then passes
@@ -198,14 +194,14 @@ function setupRequestInterceptor(router, logger) {
198
194
  /**
199
195
  * Initializes gateway components
200
196
  */
201
- function initializeGatewayComponents(config, defaultModelConfig, defaultInstructionsBlocks, defaultTemplateRendering) {
197
+ function initializeGatewayComponents(config) {
202
198
  // Initialize logger FIRST (before other components that might need it)
203
199
  const logger = (0, logger_factory_js_1.createGatewayLogger)({
204
200
  enableLogging: config.enableLogging ?? true,
205
201
  packageName: config.packageName,
206
202
  customLogger: config.logger
207
203
  });
208
- // Now that logger is initialized, log the defaults loading
204
+ const { defaultModelConfig, defaultInstructionsBlocks, defaultTemplateRendering } = loadConfig(logger);
209
205
  logger.verbose('Gateway initializing', {
210
206
  defaultEngine: config.defaultEngine,
211
207
  hasDefaultInstructionsBlocks: Object.keys(defaultInstructionsBlocks).length > 0
@@ -20,10 +20,10 @@ export interface GatewayConfigContext {
20
20
  messageBuilderConfig: MessageBuilderConfig;
21
21
  }
22
22
  /**
23
- * Loads configuration from JSON files (model config and instructionsBlocks)
24
- * Note: Called before logger is initialized, so no logging here
23
+ * Loads configuration from JSON files (model config and instructionsBlocks).
24
+ * Pass a {@link Logxer} instance so load diagnostics go through logxer (not console).
25
25
  */
26
- export declare function loadConfig(): {
26
+ export declare function loadConfig(logger: Logxer): {
27
27
  defaultModelConfig: Record<string, unknown>;
28
28
  defaultInstructionsBlocks: Record<string, any>;
29
29
  defaultTemplateRendering?: TemplateRenderOptions;
@@ -40,7 +40,7 @@ export declare function setupRequestInterceptor(router: LLMProviderRouter, logge
40
40
  /**
41
41
  * Initializes gateway components
42
42
  */
43
- export declare function initializeGatewayComponents(config: GatewayConfig, defaultModelConfig: Record<string, unknown>, defaultInstructionsBlocks: Record<string, any>, defaultTemplateRendering?: TemplateRenderOptions): {
43
+ export declare function initializeGatewayComponents(config: GatewayConfig): {
44
44
  logger: Logxer;
45
45
  router: LLMProviderRouter;
46
46
  activityManager: ActivityManager;
@@ -77,7 +77,7 @@ async function invokeStructuredText(request, gateway) {
77
77
  // Create a new request with structured-text mode
78
78
  const structuredRequest = {
79
79
  ...request,
80
- jobId: `${request.jobId}-step1`
80
+ identity: { ...request.identity }
81
81
  };
82
82
  // Make the call (this will recursively call invoke, but with structured-text mode)
83
83
  return await gateway.invoke(structuredRequest);
@@ -119,7 +119,7 @@ async function convertStructuredToJson(structuredText, originalRequest, config,
119
119
  });
120
120
  try {
121
121
  // Use flex-md 3.0.0 to extract and parse JSON
122
- const flexMdResult = await (0, flex_md_loader_js_1.extractJsonFromFlexMd)(structuredText);
122
+ const flexMdResult = await (0, flex_md_loader_js_1.extractJsonFromFlexMd)(structuredText, logger);
123
123
  if (!flexMdResult || !flexMdResult.json) {
124
124
  throw new Error('flex-md SDK could not extract JSON from structured text');
125
125
  }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gatewayLogDebug = void 0;
4
+ exports.activityIdentityToLogMeta = activityIdentityToLogMeta;
5
+ exports.withActivityIdentity = withActivityIdentity;
6
+ const logxer_1 = require("@x12i/logxer");
7
+ /** Subset of identity forwarded on every structured log line (plus full envelope under `identity`). */
8
+ function activityIdentityToLogMeta(identity) {
9
+ if (!identity) {
10
+ return {};
11
+ }
12
+ const meta = {
13
+ identity: JSON.stringify({
14
+ sessionId: identity.sessionId,
15
+ aiRequestId: identity.aiRequestId,
16
+ jobId: identity.jobId,
17
+ taskId: identity.taskId,
18
+ agentId: identity.agentId
19
+ }),
20
+ jobId: identity.jobId,
21
+ sessionId: identity.sessionId,
22
+ correlationId: identity.aiRequestId,
23
+ taskId: identity.taskId,
24
+ activityIdentity: identity
25
+ };
26
+ return meta;
27
+ }
28
+ function withActivityIdentity(identity, data, debugKind) {
29
+ return {
30
+ ...activityIdentityToLogMeta(identity),
31
+ ...(data || {}),
32
+ ...(debugKind !== undefined ? { debugKind } : {})
33
+ };
34
+ }
35
+ exports.gatewayLogDebug = {
36
+ anomaly: logxer_1.DebugLogAbstract.ANOMALY,
37
+ trace: logxer_1.DebugLogAbstract.TRACE,
38
+ intent: logxer_1.DebugLogAbstract.INTENT,
39
+ event: logxer_1.DebugLogAbstract.EVENT,
40
+ state: logxer_1.DebugLogAbstract.STATE
41
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Maps gateway {@link ActivityIdentity} into Logxer {@link LogMeta} fields for correlation and querying.
3
+ */
4
+ import type { LogMeta } from '@x12i/logxer';
5
+ import type { ActivityIdentity } from './types.js';
6
+ /** Subset of identity forwarded on every structured log line (plus full envelope under `identity`). */
7
+ export declare function activityIdentityToLogMeta(identity: Partial<ActivityIdentity> | undefined): LogMeta;
8
+ export declare function withActivityIdentity(identity: Partial<ActivityIdentity> | undefined, data?: LogMeta, debugKind?: LogMeta['debugKind']): LogMeta;
9
+ export declare const gatewayLogDebug: {
10
+ readonly anomaly: "ANOMALY";
11
+ readonly trace: "TRACE";
12
+ readonly intent: "INTENT";
13
+ readonly event: "EVENT";
14
+ readonly state: "STATE";
15
+ };
@@ -117,8 +117,8 @@ function buildWorkingMemory(request, existingWorkingMemory, otherMemories) {
117
117
  if (!workingMemory.job.narrative && request.prompt) {
118
118
  workingMemory.job.narrative = request.prompt;
119
119
  }
120
- if (!workingMemory.job.id && request.jobId) {
121
- workingMemory.job.id = request.jobId;
120
+ if (!workingMemory.job.id && request.identity.jobId) {
121
+ workingMemory.job.id = request.identity.jobId;
122
122
  }
123
123
  // Populate task fields from request if not already present in existing memory
124
124
  if (!workingMemory.task.objective && request.instructions) {
@@ -128,8 +128,8 @@ function buildWorkingMemory(request, existingWorkingMemory, otherMemories) {
128
128
  workingMemory.task.context = request.context;
129
129
  }
130
130
  // Input field has been removed - data should come from workingMemory.input
131
- if (!workingMemory.task.id && request.taskId) {
132
- workingMemory.task.id = request.taskId;
131
+ if (!workingMemory.task.id && request.identity.taskId) {
132
+ workingMemory.task.id = request.identity.taskId;
133
133
  }
134
134
  // Add entity information if not present
135
135
  if (!workingMemory.entity && request.agentId) {
@@ -186,7 +186,7 @@ async function resolveTemplateParams(request, config, logger) {
186
186
  // If memory component fails, fall back to request-only mode
187
187
  logger?.warn('Memory component resolution failed, using request-only mode', {
188
188
  error: error instanceof Error ? error.message : String(error),
189
- jobId: request.jobId
189
+ jobId: request.identity.jobId
190
190
  });
191
191
  }
192
192
  }
@@ -198,7 +198,7 @@ async function resolveTemplateParams(request, config, logger) {
198
198
  // Rendrix priority: shortTermMemory > workingMemory > experienceMemory > knowledgeMemory
199
199
  if (request.templateTokens && Object.keys(request.templateTokens).length > 0) {
200
200
  logger?.debug('Merged templateTokens into shortTermMemory (tier 1 - highest priority)', {
201
- jobId: request.jobId,
201
+ jobId: request.identity.jobId,
202
202
  tokenKeys: Object.keys(request.templateTokens)
203
203
  });
204
204
  }
@@ -27,7 +27,7 @@ function isAIRequest(request) {
27
27
  */
28
28
  async function constructMessages(request, config, logger, parsedSnapshot) {
29
29
  logger.verbose('Constructing messages from request', {
30
- jobId: request.jobId,
30
+ jobId: request.identity.jobId,
31
31
  agentId: request.agentId,
32
32
  hasInstructions: !!request.instructions,
33
33
  hasContext: !!request.context,
@@ -35,11 +35,11 @@ async function constructMessages(request, config, logger, parsedSnapshot) {
35
35
  hasWorkingMemory: !!request.workingMemory
36
36
  });
37
37
  // LOGGING: Construct messages phase (instructions should already be resolved)
38
- const traceId = request.jobId || `trace-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
38
+ const traceId = request.identity.jobId || request.aiRequestId;
39
39
  const agentId = request.agentId;
40
40
  logger.info('instructions.constructMessages.entry', {
41
41
  traceId,
42
- jobId: request.jobId,
42
+ jobId: request.identity.jobId,
43
43
  agentId,
44
44
  'instructionsType': typeof request.instructions,
45
45
  'instructionsLength': typeof request.instructions === 'string' ? request.instructions.length : 'N/A',
@@ -62,7 +62,7 @@ async function constructMessages(request, config, logger, parsedSnapshot) {
62
62
  if (typeof finalInstructions === 'string') {
63
63
  finalInstructions = `${finalInstructions}\n\nExamples:\n${fewShotExamples}`;
64
64
  logger.debug('Added few-shot examples to instructions', {
65
- jobId: request.jobId,
65
+ jobId: request.identity.jobId,
66
66
  exampleCount: Math.min(examples.length, 3),
67
67
  totalExamples: examples.length
68
68
  });
@@ -20,12 +20,19 @@ async function testInstructions(instructions, testInput, expectedSchema, options
20
20
  if (!model) {
21
21
  throw new Error('Model must be provided in options.model or configured as default');
22
22
  }
23
+ const aiRequestId = `test-instructions-${Date.now()}`;
24
+ const runtimeIdentity = {
25
+ ...options.identity,
26
+ aiRequestId,
27
+ agentId
28
+ };
23
29
  // Create test request with internal system action config
24
30
  const testRequest = {
25
- jobId: `test-instructions-${Date.now()}`,
31
+ aiRequestId,
26
32
  agentId,
27
33
  instructions,
28
- input: testInput,
34
+ identity: runtimeIdentity,
35
+ workingMemory: { input: testInput },
29
36
  config: {
30
37
  model,
31
38
  provider,