@x12i/ai-gateway 9.0.9 → 9.1.1

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 (38) hide show
  1. package/README.md +897 -998
  2. package/dist/activity-manager.js +46 -6
  3. package/dist/config/activity-tracking-config.d.ts +2 -1
  4. package/dist/config/activity-tracking-config.js +3 -2
  5. package/dist/gateway-memory.d.ts +1 -2
  6. package/dist/gateway-memory.js +1 -15
  7. package/dist/gateway-meta.js +3 -0
  8. package/dist/gateway-utils.d.ts +24 -2
  9. package/dist/gateway-utils.js +122 -18
  10. package/dist/gateway-validation.d.ts +3 -3
  11. package/dist/gateway-validation.js +10 -1
  12. package/dist/gateway.d.ts +2 -2
  13. package/dist/gateway.js +30 -24
  14. package/dist/index.d.ts +2 -2
  15. package/dist/instruction-optimizer.js +3 -0
  16. package/dist/runtime-objects.d.ts +2 -13
  17. package/dist/troubleshooting-helper.d.ts +0 -3
  18. package/dist/troubleshooting-helper.js +99 -20
  19. package/dist/types.d.ts +49 -91
  20. package/dist-cjs/activity-manager.cjs +45 -5
  21. package/dist-cjs/config/activity-tracking-config.cjs +3 -2
  22. package/dist-cjs/config/activity-tracking-config.d.ts +2 -1
  23. package/dist-cjs/gateway-memory.cjs +1 -15
  24. package/dist-cjs/gateway-memory.d.ts +1 -2
  25. package/dist-cjs/gateway-meta.cjs +3 -0
  26. package/dist-cjs/gateway-utils.cjs +126 -18
  27. package/dist-cjs/gateway-utils.d.ts +24 -2
  28. package/dist-cjs/gateway-validation.cjs +10 -1
  29. package/dist-cjs/gateway-validation.d.ts +3 -3
  30. package/dist-cjs/gateway.cjs +29 -23
  31. package/dist-cjs/gateway.d.ts +2 -2
  32. package/dist-cjs/index.d.ts +2 -2
  33. package/dist-cjs/instruction-optimizer.cjs +3 -0
  34. package/dist-cjs/runtime-objects.d.ts +2 -13
  35. package/dist-cjs/troubleshooting-helper.cjs +99 -20
  36. package/dist-cjs/troubleshooting-helper.d.ts +0 -3
  37. package/dist-cjs/types.d.ts +49 -91
  38. package/package.json +2 -2
@@ -37,12 +37,16 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  };
38
38
  })();
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = void 0;
40
41
  exports.generateMD5Hash = generateMD5Hash;
41
42
  exports.ensureTaskTypeId = ensureTaskTypeId;
42
43
  exports.mergeConfig = mergeConfig;
43
44
  exports.normalizeRouterUsageTokens = normalizeRouterUsageTokens;
44
45
  exports.extractTokenUsageFromRouterResponse = extractTokenUsageFromRouterResponse;
45
46
  exports.extractCostUsdFromRouterResponse = extractCostUsdFromRouterResponse;
47
+ exports.pickInvokeRoutingMetadataSlice = pickInvokeRoutingMetadataSlice;
48
+ exports.pickEffectiveModelConfigForMetadata = pickEffectiveModelConfigForMetadata;
49
+ exports.capActivityFullResponsePayload = capActivityFullResponsePayload;
46
50
  const crypto = __importStar(require("crypto"));
47
51
  const gateway_instructions_js_1 = require("./gateway-instructions.cjs");
48
52
  const flex_md_loader_js_1 = require("./flex-md-loader.cjs");
@@ -224,53 +228,90 @@ function firstFiniteNumber(...vals) {
224
228
  for (const v of vals) {
225
229
  if (typeof v === 'number' && Number.isFinite(v))
226
230
  return v;
231
+ if (typeof v === 'string' && v.trim() !== '') {
232
+ const n = Number(v);
233
+ if (Number.isFinite(n))
234
+ return n;
235
+ }
227
236
  }
228
237
  return undefined;
229
238
  }
239
+ function isNonZeroTokenCount(n) {
240
+ return !!(n.prompt || n.completion || n.total);
241
+ }
230
242
  /**
231
243
  * Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
232
- * Handles promptTokens/inputTokens, OpenAI-style snake_case, and missing total (sum prompt+completion).
244
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, Responses-style input/output tokens, and missing total (sum prompt+completion).
233
245
  */
234
246
  function normalizeRouterUsageTokens(usage) {
235
247
  if (usage == null || typeof usage !== 'object')
236
248
  return undefined;
237
249
  const u = usage;
238
- const prompt = firstFiniteNumber(u.promptTokens, u.inputTokens, u.prompt, u.prompt_tokens) ?? 0;
239
- const completion = firstFiniteNumber(u.completionTokens, u.outputTokens, u.completion, u.completion_tokens) ?? 0;
240
- let total = firstFiniteNumber(u.totalTokens, u.total_tokens) ?? 0;
250
+ const prompt = firstFiniteNumber(u.promptTokens, u.inputTokens, u.input_tokens, u.prompt, u.prompt_tokens) ?? 0;
251
+ const completion = firstFiniteNumber(u.completionTokens, u.outputTokens, u.output_tokens, u.completion, u.completion_tokens) ?? 0;
252
+ let total = firstFiniteNumber(u.totalTokens, u.total_tokens, u.total) ?? 0;
241
253
  if (!total && (prompt || completion))
242
254
  total = prompt + completion;
243
255
  return { prompt, completion, total };
244
256
  }
245
257
  /**
246
- * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
258
+ * Collect usage from one router/provider envelope (single object).
259
+ * When followRaw is true, also reads `(rawResponse ?? raw).usage` on that envelope.
247
260
  */
248
- function extractTokenUsageFromRouterResponse(routerResponse) {
249
- if (routerResponse == null || typeof routerResponse !== 'object') {
250
- return { prompt: 0, completion: 0, total: 0 };
251
- }
252
- const r = routerResponse;
253
- const meta = r.metadata != null && typeof r.metadata === 'object'
254
- ? r.metadata
261
+ function collectUsageBucketsFromRoot(root, followRaw) {
262
+ const meta = root.metadata != null && typeof root.metadata === 'object'
263
+ ? root.metadata
255
264
  : undefined;
256
- const buckets = [r.usage];
265
+ const buckets = [root.usage];
257
266
  if (meta) {
258
267
  buckets.push(meta.usage);
268
+ buckets.push(meta.tokens);
259
269
  const nested = meta['ai-activities-response'];
260
270
  if (nested != null && typeof nested === 'object') {
261
271
  buckets.push(nested.usage);
262
272
  }
263
273
  }
264
- const raw = r.rawResponse ?? r.raw;
265
- if (raw != null && typeof raw === 'object') {
266
- buckets.push(raw.usage);
274
+ if (followRaw) {
275
+ const raw = root.rawResponse ?? root.raw;
276
+ if (raw != null && typeof raw === 'object') {
277
+ buckets.push(raw.usage);
278
+ }
267
279
  }
280
+ return buckets;
281
+ }
282
+ function firstNonZeroUsageFromBuckets(buckets) {
268
283
  for (const b of buckets) {
269
284
  const n = normalizeRouterUsageTokens(b);
270
- if (n && (n.prompt || n.completion || n.total))
285
+ if (n && isNonZeroTokenCount(n))
271
286
  return n;
272
287
  }
273
- return { prompt: 0, completion: 0, total: 0 };
288
+ return undefined;
289
+ }
290
+ /**
291
+ * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
292
+ * Prefers the raw/provider body (`rawResponse` / `raw`) when it carries non-zero usage before re-reading the outer envelope.
293
+ */
294
+ function extractTokenUsageFromRouterResponse(routerResponse) {
295
+ const zeros = { prompt: 0, completion: 0, total: 0 };
296
+ if (routerResponse == null || typeof routerResponse !== 'object') {
297
+ return zeros;
298
+ }
299
+ const r = routerResponse;
300
+ const raw = r.rawResponse ?? r.raw;
301
+ const inner = raw != null && typeof raw === 'object' ? raw : undefined;
302
+ const roots = inner != null && inner !== r
303
+ ? [
304
+ { root: inner, followRaw: false },
305
+ { root: r, followRaw: true }
306
+ ]
307
+ : [{ root: r, followRaw: true }];
308
+ for (const { root, followRaw } of roots) {
309
+ const buckets = collectUsageBucketsFromRoot(root, followRaw);
310
+ const found = firstNonZeroUsageFromBuckets(buckets);
311
+ if (found)
312
+ return found;
313
+ }
314
+ return zeros;
274
315
  }
275
316
  /**
276
317
  * Best-effort USD cost from router/sync AIResponse shape: metadata.costUsd (preferred),
@@ -319,3 +360,70 @@ function extractCostUsdFromRouterResponse(routerResponse) {
319
360
  }
320
361
  return undefined;
321
362
  }
363
+ /**
364
+ * Stable routing facts for gateway response metadata (router metadata + merged config fallbacks).
365
+ * Matches trace-mode resolution; intended for every successful invoke(), not only diagnostics.trace.
366
+ */
367
+ function pickInvokeRoutingMetadataSlice(routerResponse, mergedConfig) {
368
+ const rr = routerResponse != null && typeof routerResponse === 'object' ? routerResponse : {};
369
+ const meta = rr.metadata != null && typeof rr.metadata === 'object' ? rr.metadata : {};
370
+ const cfg = mergedConfig != null && typeof mergedConfig === 'object' ? mergedConfig : {};
371
+ const provider = meta.provider || rr.provider || cfg.provider;
372
+ const modelUsed = meta.modelUsed || meta.model || rr.model || cfg.model;
373
+ const maxTokensRequested = typeof meta.maxTokensRequested === 'number'
374
+ ? meta.maxTokensRequested
375
+ : typeof cfg.maxTokens === 'number'
376
+ ? cfg.maxTokens
377
+ : undefined;
378
+ const region = typeof meta.region === 'string' ? meta.region : undefined;
379
+ const out = {};
380
+ if (provider !== undefined && provider !== null)
381
+ out.provider = provider;
382
+ if (modelUsed !== undefined && modelUsed !== null)
383
+ out.modelUsed = modelUsed;
384
+ if (maxTokensRequested !== undefined)
385
+ out.maxTokensRequested = maxTokensRequested;
386
+ if (region !== undefined)
387
+ out.region = region;
388
+ return out;
389
+ }
390
+ /**
391
+ * Allowlisted generation profile from merged config for client introspection (no secrets, no arbitrary extras).
392
+ */
393
+ function pickEffectiveModelConfigForMetadata(mergedConfig) {
394
+ if (mergedConfig == null || typeof mergedConfig !== 'object')
395
+ return undefined;
396
+ const c = mergedConfig;
397
+ const keys = ['model', 'modelId', 'provider', 'temperature', 'maxTokens', 'topP'];
398
+ const out = {};
399
+ for (const k of keys) {
400
+ const v = c[k];
401
+ if (v !== undefined)
402
+ out[k] = v;
403
+ }
404
+ return Object.keys(out).length ? out : undefined;
405
+ }
406
+ /** Default JSON string length cap for Activix `content.fullResponse` when diagnostics allow storing it. */
407
+ exports.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = 512_000;
408
+ /**
409
+ * Size-cap a provider/router payload before storing on an activity record.
410
+ * Non-serializable values become a small marker object instead of throwing.
411
+ */
412
+ function capActivityFullResponsePayload(payload, maxChars = exports.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS) {
413
+ if (payload == null)
414
+ return payload;
415
+ let serialized;
416
+ try {
417
+ serialized = typeof payload === 'string' ? payload : JSON.stringify(payload);
418
+ }
419
+ catch {
420
+ return { _truncated: true, _reason: 'not_serializable' };
421
+ }
422
+ if (serialized.length <= maxChars)
423
+ return payload;
424
+ return {
425
+ _truncated: true,
426
+ _originalCharLength: serialized.length,
427
+ _preview: serialized.slice(0, maxChars)
428
+ };
429
+ }
@@ -2,7 +2,7 @@
2
2
  * Gateway Utilities Module
3
3
  * Handles utility functions
4
4
  */
5
- import type { ChatRequest, GatewayConfig } from './types.js';
5
+ import type { ChatRequest, GatewayConfig, ModelConfig } from './types.js';
6
6
  import type { Logxer } from '@x12i/logxer';
7
7
  /**
8
8
  * Generates MD5 hash of a string
@@ -21,7 +21,7 @@ export declare function mergeConfig(request: ChatRequest & {
21
21
  }, config: GatewayConfig, logger: Logxer): Promise<ChatRequest['config']>;
22
22
  /**
23
23
  * Maps provider/router usage objects to gateway token counts (`metadata.tokens`, Activix, trace attempts).
24
- * Handles promptTokens/inputTokens, OpenAI-style snake_case, and missing total (sum prompt+completion).
24
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, Responses-style input/output tokens, and missing total (sum prompt+completion).
25
25
  */
26
26
  export declare function normalizeRouterUsageTokens(usage: unknown): {
27
27
  prompt: number;
@@ -30,6 +30,7 @@ export declare function normalizeRouterUsageTokens(usage: unknown): {
30
30
  } | undefined;
31
31
  /**
32
32
  * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
33
+ * Prefers the raw/provider body (`rawResponse` / `raw`) when it carries non-zero usage before re-reading the outer envelope.
33
34
  */
34
35
  export declare function extractTokenUsageFromRouterResponse(routerResponse: unknown): {
35
36
  prompt: number;
@@ -42,3 +43,24 @@ export declare function extractTokenUsageFromRouterResponse(routerResponse: unkn
42
43
  * Does not compute cost from tokens — adapters must populate normalized fields or raw usage.cost-style keys.
43
44
  */
44
45
  export declare function extractCostUsdFromRouterResponse(routerResponse: unknown): number | undefined;
46
+ /**
47
+ * Stable routing facts for gateway response metadata (router metadata + merged config fallbacks).
48
+ * Matches trace-mode resolution; intended for every successful invoke(), not only diagnostics.trace.
49
+ */
50
+ export declare function pickInvokeRoutingMetadataSlice(routerResponse: unknown, mergedConfig: unknown): Partial<{
51
+ provider: string;
52
+ modelUsed: string;
53
+ maxTokensRequested: number;
54
+ region: string;
55
+ }>;
56
+ /**
57
+ * Allowlisted generation profile from merged config for client introspection (no secrets, no arbitrary extras).
58
+ */
59
+ export declare function pickEffectiveModelConfigForMetadata(mergedConfig: unknown): Partial<Pick<ModelConfig, 'model' | 'modelId' | 'provider' | 'temperature' | 'maxTokens' | 'topP'>> | undefined;
60
+ /** Default JSON string length cap for Activix `content.fullResponse` when diagnostics allow storing it. */
61
+ export declare const DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = 512000;
62
+ /**
63
+ * Size-cap a provider/router payload before storing on an activity record.
64
+ * Non-serializable values become a small marker object instead of throwing.
65
+ */
66
+ export declare function capActivityFullResponsePayload(payload: unknown, maxChars?: number): unknown;
@@ -36,8 +36,9 @@ function validateChatRequest(request) {
36
36
  throw err;
37
37
  }
38
38
  }
39
+ const GATEWAY_ACTION_TYPES = ['skill', 'preSkill', 'postSkill'];
39
40
  /**
40
- * Validates AIRequest has required fields
41
+ * Validates AIInvokeRequest has required fields
41
42
  */
42
43
  function validateAIRequest(request) {
43
44
  if (!request.aiRequestId) {
@@ -47,6 +48,14 @@ function validateAIRequest(request) {
47
48
  throw new Error('agentId is required for AI requests');
48
49
  }
49
50
  validateMandatoryRuntimeIdentity(request);
51
+ if (!request.actionType ||
52
+ !GATEWAY_ACTION_TYPES.includes(request.actionType)) {
53
+ throw new Error(`actionType is required and must be one of: ${GATEWAY_ACTION_TYPES.join(', ')}`);
54
+ }
55
+ const ref = typeof request.actionRef === 'string' ? request.actionRef.trim() : '';
56
+ if (!ref) {
57
+ throw new Error('actionRef is required and must be a non-empty string');
58
+ }
50
59
  // Reject input field - it has been removed
51
60
  if ('input' in request && request.input !== undefined) {
52
61
  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.`);
@@ -2,12 +2,12 @@
2
2
  * Gateway Validation Module
3
3
  * Basic validation for clean proxy implementation
4
4
  */
5
- import type { ChatRequest, AIRequest } from './types.js';
5
+ import type { ChatRequest, AIInvokeRequest } from './types.js';
6
6
  /**
7
7
  * Validates ChatRequest has required fields
8
8
  */
9
9
  export declare function validateChatRequest(request: ChatRequest): void;
10
10
  /**
11
- * Validates AIRequest has required fields
11
+ * Validates AIInvokeRequest has required fields
12
12
  */
13
- export declare function validateAIRequest(request: AIRequest): void;
13
+ export declare function validateAIRequest(request: AIInvokeRequest): void;
@@ -516,9 +516,19 @@ class AIGateway {
516
516
  }
517
517
  contentType = 'structured';
518
518
  parsingMethod = 'flex-md';
519
- const tokens = (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(routerResponse);
519
+ let tokens = (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(routerResponse);
520
+ if (!(tokens.prompt || tokens.completion || tokens.total)) {
521
+ const alt = routerResponse?.rawResponse ?? routerResponse?.raw;
522
+ if (alt != null && typeof alt === 'object' && alt !== routerResponse) {
523
+ const second = (0, gateway_utils_js_1.extractTokenUsageFromRouterResponse)(alt);
524
+ if (second.prompt || second.completion || second.total)
525
+ tokens = second;
526
+ }
527
+ }
520
528
  const resolvedCostUsd = (0, gateway_utils_js_1.extractCostUsdFromRouterResponse)(routerResponse);
521
529
  const routerMetaForCost = routerResponse?.metadata || {};
530
+ const routingMetadataSlice = (0, gateway_utils_js_1.pickInvokeRoutingMetadataSlice)(routerResponse, mergedConfig);
531
+ const effectiveModelConfig = (0, gateway_utils_js_1.pickEffectiveModelConfigForMetadata)(mergedConfig);
522
532
  const enhancedResponse = {
523
533
  content: content,
524
534
  parsedContent: parsedContent,
@@ -531,6 +541,8 @@ class AIGateway {
531
541
  agentType: 'ai',
532
542
  contentType,
533
543
  parsingMethod,
544
+ ...routingMetadataSlice,
545
+ ...(effectiveModelConfig !== undefined ? { effectiveModelConfig } : {}),
534
546
  ...(typeof resolvedCostUsd === 'number'
535
547
  ? {
536
548
  costUsd: resolvedCostUsd,
@@ -540,38 +552,32 @@ class AIGateway {
540
552
  }
541
553
  : {}),
542
554
  ...(traceEnabled
543
- ? (() => {
544
- const meta = routerResponse?.metadata || {};
545
- const provider = meta.provider || routerResponse?.provider || mergedConfig?.provider;
546
- const region = typeof meta.region === 'string' ? meta.region : undefined;
547
- const modelUsed = meta.modelUsed || meta.model || routerResponse?.model || mergedConfig?.model;
548
- const maxTokensRequested = typeof meta.maxTokensRequested === 'number'
549
- ? meta.maxTokensRequested
550
- : typeof mergedConfig?.maxTokens === 'number'
551
- ? mergedConfig.maxTokens
552
- : undefined;
553
- return {
554
- provider,
555
- region,
556
- modelUsed,
557
- maxTokensRequested,
558
- requestIds: traceRequestIds,
559
- retryCount: traceRetryCount,
560
- fallbackCount: traceFallbackCount,
561
- attempts: traceAttempts
562
- };
563
- })()
555
+ ? {
556
+ requestIds: traceRequestIds,
557
+ retryCount: traceRetryCount,
558
+ fallbackCount: traceFallbackCount,
559
+ attempts: traceAttempts
560
+ }
564
561
  : {})
565
562
  }
566
563
  };
567
564
  // Track activity success if activity was started
568
565
  if (activity) {
569
566
  try {
567
+ const diag = request.diagnostics;
568
+ const includeFullProviderBlob = diag?.includeFullProviderResponseInActivity !== false;
569
+ const maxFullChars = typeof diag?.activityFullResponseMaxChars === 'number' && diag.activityFullResponseMaxChars > 0
570
+ ? diag.activityFullResponseMaxChars
571
+ : gateway_utils_js_1.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS;
572
+ const rawFull = routerResponse.rawResponse || routerResponse;
573
+ const fullResponseForActivity = includeFullProviderBlob
574
+ ? (0, gateway_utils_js_1.capActivityFullResponsePayload)(rawFull, maxFullChars)
575
+ : undefined;
570
576
  // Create activity response with proper structure for ActivityTracker
571
577
  const activityResponse = {
572
578
  content: {
573
579
  rawContent: content, // Store the actual response content as rawContent
574
- fullResponse: routerResponse.rawResponse || routerResponse // Include full router response
580
+ ...(fullResponseForActivity !== undefined ? { fullResponse: fullResponseForActivity } : {})
575
581
  },
576
582
  parsed: parsedContent, // Include parsed content in activity record
577
583
  metadata: enhancedResponse.metadata,
@@ -4,7 +4,7 @@
4
4
  * Simplified AI Gateway - Clean proxy implementation
5
5
  */
6
6
  import { LLMProviderRouter } from '@x12i/ai-providers-router';
7
- import type { GatewayConfig, ChatRequest, AIRequest, EnhancedLLMResponse } from './types.js';
7
+ import type { GatewayConfig, ChatRequest, AIInvokeRequest, EnhancedLLMResponse } from './types.js';
8
8
  import type { Logxer } from '@x12i/logxer';
9
9
  import { ActivityManager } from './activity-manager.js';
10
10
  /**
@@ -25,7 +25,7 @@ export declare class AIGateway {
25
25
  /**
26
26
  * Invoke AI request (with structured output support)
27
27
  */
28
- invoke<TContent = unknown>(request: AIRequest): Promise<EnhancedLLMResponse<TContent>>;
28
+ invoke<TContent = unknown>(request: AIInvokeRequest): Promise<EnhancedLLMResponse<TContent>>;
29
29
  /**
30
30
  * Build simple messages from request (instructions and prompt as literal template text; no registry).
31
31
  */
@@ -16,11 +16,11 @@ export * from '@x12i/ai-providers-router';
16
16
  export { AIGateway } from './gateway.js';
17
17
  export { InstructionNotFoundError, InstructionBackendError } from './instruction-errors.js';
18
18
  export { autoRegisterProviders } from './gateway-provider-auto-register.js';
19
- export type { GatewayConfig, ProviderModelRef, ModelConfig, RetryConfig, ChatRequest, AIRequest, EnhancedLLMResponse, InstructionMetadata, ValidationRule, ResponseTransformationConfig, TemplateRenderOptions } from './types.js';
19
+ export type { GatewayConfig, ProviderModelRef, ModelConfig, RetryConfig, ChatRequest, AIInvokeRequest, AIRequest, GatewayActionType, EnhancedLLMResponse, InstructionMetadata, ValidationRule, TemplateRenderOptions } from './types.js';
20
20
  export { mergeTemplateRenderOptions } from './template-render-merge.js';
21
21
  export type { UsageTier } from './types.js';
22
22
  export { Activix } from '@x12i/activix';
23
- export type { ActivixRunContext, FindByRunContextCriteria } from '@x12i/activix';
23
+ export type { ActivixRunContext, FindByRunContextCriteria, GetJobActivitiesInput, GetJobActivitiesResult } from '@x12i/activix';
24
24
  export { ActivityManager, ensureGatewayRequestIdentity } from './activity-manager.js';
25
25
  export type { ActivityIdentity } from './types.js';
26
26
  export { activityIdentityToLogMeta, withActivityIdentity, gatewayLogDebug } from './gateway-log-meta.js';
@@ -145,8 +145,11 @@ async function optimizeInstructions(gateway, originalInstructions, options) {
145
145
  const optimizationRequest = {
146
146
  aiRequestId,
147
147
  agentId,
148
+ actionType: 'skill',
149
+ actionRef: 'internal/instruction-optimizer',
148
150
  instructions: INSTRUCTION_OPTIMIZER_INSTRUCTIONS + additionalContext,
149
151
  identity,
152
+ prompt: '{{input}}',
150
153
  workingMemory: { input: originalInstructions },
151
154
  config: {
152
155
  model,
@@ -1,17 +1,6 @@
1
1
  import type { Logxer } from '@x12i/logxer';
2
- import type { Activix } from '@x12i/activix';
3
- export type ActivixQueryableClient = {
4
- getJobActivities(input: {
5
- jobId: string;
6
- graphId?: string;
7
- nodeId?: string;
8
- limit?: number;
9
- }): Promise<{
10
- jobId: string;
11
- graphRun?: unknown;
12
- activities: unknown[];
13
- }>;
14
- };
2
+ import type { Activix, ActivixQueryableClient } from '@x12i/activix';
3
+ export type { ActivixQueryableClient } from '@x12i/activix';
15
4
  export type LogxerQueryableClient = {
16
5
  getJobLogs(input: {
17
6
  jobId: string;
@@ -187,18 +187,27 @@ function validateSingleObjectType(objType, context, index) {
187
187
  /**
188
188
  * Validates AIRequest structure
189
189
  */
190
+ const GATEWAY_ACTION_TYPES_DIAG = ['skill', 'preSkill', 'postSkill'];
190
191
  function validateAIRequest(request) {
191
192
  const errors = [];
192
193
  const warnings = [];
193
- // Validate base fields
194
- if (!request.jobId) {
195
- errors.push('jobId is required');
194
+ const identityJobId = request.identity && typeof request.identity === 'object' ? request.identity.jobId : undefined;
195
+ if (!identityJobId && !request.jobId) {
196
+ errors.push('identity.jobId is required (legacy top-level jobId is deprecated)');
196
197
  }
197
198
  if (!request.agentId) {
198
199
  errors.push('agentId is required');
199
200
  }
201
+ if (!request.actionType ||
202
+ !GATEWAY_ACTION_TYPES_DIAG.includes(request.actionType)) {
203
+ errors.push('actionType is required and must be skill, preSkill, or postSkill');
204
+ }
205
+ const actionRefTrim = typeof request.actionRef === 'string' ? request.actionRef.trim() : '';
206
+ if (!actionRefTrim) {
207
+ errors.push('actionRef is required (non-empty string, e.g. skill id)');
208
+ }
200
209
  if (!request.instructions) {
201
- warnings.push('instructions is optional (system-context will be used as fallback)');
210
+ errors.push('instructions is required');
202
211
  }
203
212
  if (!request.prompt) {
204
213
  errors.push('Prompt is required (input field has been removed - use workingMemory.input instead)');
@@ -449,11 +458,23 @@ function supportsJSONMode(provider, model) {
449
458
  * Creates a test AIRequest with minimal valid structure
450
459
  */
451
460
  function createTestAIRequest(overrides) {
452
- return {
453
- jobId: 'test-job-' + Date.now(),
461
+ const aiRequestId = 'test-ai-req-' + Date.now();
462
+ const base = {
463
+ aiRequestId,
454
464
  agentId: 'test-agent',
465
+ actionType: 'skill',
466
+ actionRef: 'tests/createTestAIRequest',
455
467
  instructions: 'Test instructions',
456
- input: 'Test input',
468
+ prompt: '{{input}}',
469
+ workingMemory: { input: 'Test input' },
470
+ identity: {
471
+ sessionId: `session-${aiRequestId}`,
472
+ instance: { instanceId: 'test-agent', type: 'test' },
473
+ aiRequestId,
474
+ jobId: `job-${aiRequestId}`,
475
+ agentId: 'test-agent',
476
+ taskId: `task-${aiRequestId}`
477
+ },
457
478
  config: {
458
479
  model: 'gpt-4o',
459
480
  provider: 'openai'
@@ -470,20 +491,33 @@ function createTestAIRequest(overrides) {
470
491
  },
471
492
  ...overrides
472
493
  };
494
+ return base;
473
495
  }
474
496
  /**
475
497
  * Creates test cases for validation
476
498
  */
477
499
  function createValidationTestCases() {
500
+ const sampleIdentity = (jobId, agentId) => ({
501
+ sessionId: `session-${jobId}`,
502
+ instance: { instanceId: agentId, type: 'test' },
503
+ aiRequestId: `ai-${jobId}`,
504
+ jobId,
505
+ agentId,
506
+ taskId: `task-${jobId}`
507
+ });
478
508
  return [
479
509
  {
480
510
  name: 'Valid minimal request',
481
511
  request: {
482
- jobId: 'test',
512
+ aiRequestId: 'ai-valid-min',
483
513
  agentId: 'agent-1',
514
+ actionType: 'skill',
515
+ actionRef: 'validation/minimal',
516
+ identity: sampleIdentity('test', 'agent-1'),
484
517
  instructions: 'Test',
485
518
  prompt: 'test.prompt',
486
- workingMemory: { input: 'Test' }
519
+ workingMemory: { input: 'Test' },
520
+ config: { model: 'gpt-4o', provider: 'openai' }
487
521
  },
488
522
  shouldFail: false,
489
523
  expectedErrors: []
@@ -491,8 +525,11 @@ function createValidationTestCases() {
491
525
  {
492
526
  name: 'Valid request with config',
493
527
  request: {
494
- jobId: 'test',
528
+ aiRequestId: 'ai-valid-cfg',
495
529
  agentId: 'agent-1',
530
+ actionType: 'skill',
531
+ actionRef: 'validation/config',
532
+ identity: sampleIdentity('test', 'agent-1'),
496
533
  instructions: 'Test',
497
534
  prompt: 'test.prompt',
498
535
  workingMemory: { input: 'Test' },
@@ -505,23 +542,40 @@ function createValidationTestCases() {
505
542
  expectedErrors: []
506
543
  },
507
544
  {
508
- name: 'Missing jobId',
545
+ name: 'Missing identity.jobId',
509
546
  request: {
547
+ aiRequestId: 'ai-no-job',
510
548
  agentId: 'agent-1',
549
+ actionType: 'skill',
550
+ actionRef: 'validation/x',
551
+ identity: {
552
+ sessionId: 's',
553
+ instance: { instanceId: 'agent-1', type: 'test' },
554
+ aiRequestId: 'ai-no-job',
555
+ jobId: '',
556
+ agentId: 'agent-1',
557
+ taskId: 't'
558
+ },
511
559
  instructions: 'Test',
512
560
  prompt: 'test.prompt',
513
- workingMemory: { input: 'Test' }
561
+ workingMemory: { input: 'Test' },
562
+ config: { model: 'gpt-4o', provider: 'openai' }
514
563
  },
515
564
  shouldFail: true,
516
- expectedErrors: ['jobId is required']
565
+ expectedErrors: ['identity.jobId is required (legacy top-level jobId is deprecated)']
517
566
  },
518
567
  {
519
568
  name: 'Missing agentId',
520
569
  request: {
521
- jobId: 'test',
570
+ aiRequestId: 'ai-no-agent',
571
+ actionType: 'skill',
572
+ actionRef: 'validation/x',
573
+ identity: sampleIdentity('test', 'agent-1'),
522
574
  instructions: 'Test',
523
575
  prompt: 'test.prompt',
524
- workingMemory: { input: 'Test' }
576
+ workingMemory: { input: 'Test' },
577
+ config: { model: 'gpt-4o', provider: 'openai' }
578
+ // intentional: no top-level agentId
525
579
  },
526
580
  shouldFail: true,
527
581
  expectedErrors: ['agentId is required']
@@ -529,10 +583,14 @@ function createValidationTestCases() {
529
583
  {
530
584
  name: 'Missing instructions',
531
585
  request: {
532
- jobId: 'test',
586
+ aiRequestId: 'ai-no-inst',
533
587
  agentId: 'agent-1',
588
+ actionType: 'skill',
589
+ actionRef: 'validation/x',
590
+ identity: sampleIdentity('test', 'agent-1'),
534
591
  prompt: 'test.prompt',
535
- workingMemory: { input: 'Test' }
592
+ workingMemory: { input: 'Test' },
593
+ config: { model: 'gpt-4o', provider: 'openai' }
536
594
  },
537
595
  shouldFail: true,
538
596
  expectedErrors: ['instructions is required']
@@ -540,12 +598,33 @@ function createValidationTestCases() {
540
598
  {
541
599
  name: 'Missing prompt',
542
600
  request: {
543
- jobId: 'test',
601
+ aiRequestId: 'ai-no-prompt',
544
602
  agentId: 'agent-1',
545
- instructions: 'Test'
603
+ actionType: 'skill',
604
+ actionRef: 'validation/x',
605
+ identity: sampleIdentity('test', 'agent-1'),
606
+ instructions: 'Test',
607
+ workingMemory: { input: 'Test' },
608
+ config: { model: 'gpt-4o', provider: 'openai' }
609
+ },
610
+ shouldFail: true,
611
+ expectedErrors: ['Prompt is required (input field has been removed - use workingMemory.input instead)']
612
+ },
613
+ {
614
+ name: 'Missing actionRef',
615
+ request: {
616
+ aiRequestId: 'ai-no-ref',
617
+ agentId: 'agent-1',
618
+ actionType: 'skill',
619
+ actionRef: ' ',
620
+ identity: sampleIdentity('test', 'agent-1'),
621
+ instructions: 'Test',
622
+ prompt: 'p',
623
+ workingMemory: { input: 'Test' },
624
+ config: { model: 'gpt-4o', provider: 'openai' }
546
625
  },
547
626
  shouldFail: true,
548
- expectedErrors: ['input is required']
627
+ expectedErrors: ['actionRef is required (non-empty string, e.g. skill id)']
549
628
  }
550
629
  ];
551
630
  }
@@ -57,9 +57,6 @@ export interface DiagnosticInfo {
57
57
  validationErrors?: string[];
58
58
  };
59
59
  }
60
- /**
61
- * Validates AIRequest structure
62
- */
63
60
  export declare function validateAIRequest(request: Partial<AIRequest>): ValidationResult;
64
61
  /**
65
62
  * Validates JSON string