@x12i/ai-gateway 9.0.9 → 9.1.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 (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 +9 -1
  9. package/dist/gateway-utils.js +79 -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 +20 -3
  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 +39 -89
  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 +81 -18
  27. package/dist-cjs/gateway-utils.d.ts +9 -1
  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 +19 -2
  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 +39 -89
  38. package/package.json +2 -2
@@ -37,12 +37,14 @@ 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.capActivityFullResponsePayload = capActivityFullResponsePayload;
46
48
  const crypto = __importStar(require("crypto"));
47
49
  const gateway_instructions_js_1 = require("./gateway-instructions.cjs");
48
50
  const flex_md_loader_js_1 = require("./flex-md-loader.cjs");
@@ -224,53 +226,90 @@ function firstFiniteNumber(...vals) {
224
226
  for (const v of vals) {
225
227
  if (typeof v === 'number' && Number.isFinite(v))
226
228
  return v;
229
+ if (typeof v === 'string' && v.trim() !== '') {
230
+ const n = Number(v);
231
+ if (Number.isFinite(n))
232
+ return n;
233
+ }
227
234
  }
228
235
  return undefined;
229
236
  }
237
+ function isNonZeroTokenCount(n) {
238
+ return !!(n.prompt || n.completion || n.total);
239
+ }
230
240
  /**
231
241
  * 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).
242
+ * Handles promptTokens/inputTokens, OpenAI-style snake_case, Responses-style input/output tokens, and missing total (sum prompt+completion).
233
243
  */
234
244
  function normalizeRouterUsageTokens(usage) {
235
245
  if (usage == null || typeof usage !== 'object')
236
246
  return undefined;
237
247
  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;
248
+ const prompt = firstFiniteNumber(u.promptTokens, u.inputTokens, u.input_tokens, u.prompt, u.prompt_tokens) ?? 0;
249
+ const completion = firstFiniteNumber(u.completionTokens, u.outputTokens, u.output_tokens, u.completion, u.completion_tokens) ?? 0;
250
+ let total = firstFiniteNumber(u.totalTokens, u.total_tokens, u.total) ?? 0;
241
251
  if (!total && (prompt || completion))
242
252
  total = prompt + completion;
243
253
  return { prompt, completion, total };
244
254
  }
245
255
  /**
246
- * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
256
+ * Collect usage from one router/provider envelope (single object).
257
+ * When followRaw is true, also reads `(rawResponse ?? raw).usage` on that envelope.
247
258
  */
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
259
+ function collectUsageBucketsFromRoot(root, followRaw) {
260
+ const meta = root.metadata != null && typeof root.metadata === 'object'
261
+ ? root.metadata
255
262
  : undefined;
256
- const buckets = [r.usage];
263
+ const buckets = [root.usage];
257
264
  if (meta) {
258
265
  buckets.push(meta.usage);
266
+ buckets.push(meta.tokens);
259
267
  const nested = meta['ai-activities-response'];
260
268
  if (nested != null && typeof nested === 'object') {
261
269
  buckets.push(nested.usage);
262
270
  }
263
271
  }
264
- const raw = r.rawResponse ?? r.raw;
265
- if (raw != null && typeof raw === 'object') {
266
- buckets.push(raw.usage);
272
+ if (followRaw) {
273
+ const raw = root.rawResponse ?? root.raw;
274
+ if (raw != null && typeof raw === 'object') {
275
+ buckets.push(raw.usage);
276
+ }
267
277
  }
278
+ return buckets;
279
+ }
280
+ function firstNonZeroUsageFromBuckets(buckets) {
268
281
  for (const b of buckets) {
269
282
  const n = normalizeRouterUsageTokens(b);
270
- if (n && (n.prompt || n.completion || n.total))
283
+ if (n && isNonZeroTokenCount(n))
271
284
  return n;
272
285
  }
273
- return { prompt: 0, completion: 0, total: 0 };
286
+ return undefined;
287
+ }
288
+ /**
289
+ * Reads token usage from every stable location the router may populate (see docs/PROVIDERS_ROUTER_DIAGNOSTICS_TRACE_REQUIREMENTS.md).
290
+ * Prefers the raw/provider body (`rawResponse` / `raw`) when it carries non-zero usage before re-reading the outer envelope.
291
+ */
292
+ function extractTokenUsageFromRouterResponse(routerResponse) {
293
+ const zeros = { prompt: 0, completion: 0, total: 0 };
294
+ if (routerResponse == null || typeof routerResponse !== 'object') {
295
+ return zeros;
296
+ }
297
+ const r = routerResponse;
298
+ const raw = r.rawResponse ?? r.raw;
299
+ const inner = raw != null && typeof raw === 'object' ? raw : undefined;
300
+ const roots = inner != null && inner !== r
301
+ ? [
302
+ { root: inner, followRaw: false },
303
+ { root: r, followRaw: true }
304
+ ]
305
+ : [{ root: r, followRaw: true }];
306
+ for (const { root, followRaw } of roots) {
307
+ const buckets = collectUsageBucketsFromRoot(root, followRaw);
308
+ const found = firstNonZeroUsageFromBuckets(buckets);
309
+ if (found)
310
+ return found;
311
+ }
312
+ return zeros;
274
313
  }
275
314
  /**
276
315
  * Best-effort USD cost from router/sync AIResponse shape: metadata.costUsd (preferred),
@@ -319,3 +358,27 @@ function extractCostUsdFromRouterResponse(routerResponse) {
319
358
  }
320
359
  return undefined;
321
360
  }
361
+ /** Default JSON string length cap for Activix `content.fullResponse` when diagnostics allow storing it. */
362
+ exports.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = 512_000;
363
+ /**
364
+ * Size-cap a provider/router payload before storing on an activity record.
365
+ * Non-serializable values become a small marker object instead of throwing.
366
+ */
367
+ function capActivityFullResponsePayload(payload, maxChars = exports.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS) {
368
+ if (payload == null)
369
+ return payload;
370
+ let serialized;
371
+ try {
372
+ serialized = typeof payload === 'string' ? payload : JSON.stringify(payload);
373
+ }
374
+ catch {
375
+ return { _truncated: true, _reason: 'not_serializable' };
376
+ }
377
+ if (serialized.length <= maxChars)
378
+ return payload;
379
+ return {
380
+ _truncated: true,
381
+ _originalCharLength: serialized.length,
382
+ _preview: serialized.slice(0, maxChars)
383
+ };
384
+ }
@@ -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,10 @@ 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
+ /** Default JSON string length cap for Activix `content.fullResponse` when diagnostics allow storing it. */
47
+ export declare const DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS = 512000;
48
+ /**
49
+ * Size-cap a provider/router payload before storing on an activity record.
50
+ * Non-serializable values become a small marker object instead of throwing.
51
+ */
52
+ 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,7 +516,15 @@ 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 || {};
522
530
  const enhancedResponse = {
@@ -567,11 +575,20 @@ class AIGateway {
567
575
  // Track activity success if activity was started
568
576
  if (activity) {
569
577
  try {
578
+ const diag = request.diagnostics;
579
+ const includeFullProviderBlob = diag?.includeFullProviderResponseInActivity !== false;
580
+ const maxFullChars = typeof diag?.activityFullResponseMaxChars === 'number' && diag.activityFullResponseMaxChars > 0
581
+ ? diag.activityFullResponseMaxChars
582
+ : gateway_utils_js_1.DEFAULT_ACTIVITY_FULL_RESPONSE_MAX_CHARS;
583
+ const rawFull = routerResponse.rawResponse || routerResponse;
584
+ const fullResponseForActivity = includeFullProviderBlob
585
+ ? (0, gateway_utils_js_1.capActivityFullResponsePayload)(rawFull, maxFullChars)
586
+ : undefined;
570
587
  // Create activity response with proper structure for ActivityTracker
571
588
  const activityResponse = {
572
589
  content: {
573
590
  rawContent: content, // Store the actual response content as rawContent
574
- fullResponse: routerResponse.rawResponse || routerResponse // Include full router response
591
+ ...(fullResponseForActivity !== undefined ? { fullResponse: fullResponseForActivity } : {})
575
592
  },
576
593
  parsed: parsedContent, // Include parsed content in activity record
577
594
  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