@axonflow/sdk 5.6.0 → 6.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 (69) hide show
  1. package/dist/cjs/adapters/governed-tool.js +4 -4
  2. package/dist/cjs/adapters/langgraph.js +9 -9
  3. package/dist/cjs/client.d.ts.map +1 -1
  4. package/dist/cjs/client.js +240 -164
  5. package/dist/cjs/client.js.map +1 -1
  6. package/dist/cjs/errors.js +1 -1
  7. package/dist/cjs/index.d.ts +2 -2
  8. package/dist/cjs/index.d.ts.map +1 -1
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/telemetry.d.ts.map +1 -1
  11. package/dist/cjs/telemetry.js +42 -6
  12. package/dist/cjs/telemetry.js.map +1 -1
  13. package/dist/cjs/types/connector.d.ts +33 -5
  14. package/dist/cjs/types/connector.d.ts.map +1 -1
  15. package/dist/cjs/types/connector.js.map +1 -1
  16. package/dist/cjs/types/cost-controls.d.ts +29 -0
  17. package/dist/cjs/types/cost-controls.d.ts.map +1 -1
  18. package/dist/cjs/types/execution-replay.d.ts +2 -0
  19. package/dist/cjs/types/execution-replay.d.ts.map +1 -1
  20. package/dist/cjs/types/masfeat.d.ts +2 -0
  21. package/dist/cjs/types/masfeat.d.ts.map +1 -1
  22. package/dist/cjs/types/planning.d.ts +78 -8
  23. package/dist/cjs/types/planning.d.ts.map +1 -1
  24. package/dist/cjs/types/policies.d.ts +92 -14
  25. package/dist/cjs/types/policies.d.ts.map +1 -1
  26. package/dist/cjs/types/policy.d.ts +58 -4
  27. package/dist/cjs/types/policy.d.ts.map +1 -1
  28. package/dist/cjs/types/proxy.d.ts +22 -4
  29. package/dist/cjs/types/proxy.d.ts.map +1 -1
  30. package/dist/cjs/types/workflows.d.ts +54 -6
  31. package/dist/cjs/types/workflows.d.ts.map +1 -1
  32. package/dist/cjs/types/workflows.js.map +1 -1
  33. package/dist/cjs/version.d.ts +1 -1
  34. package/dist/cjs/version.js +1 -1
  35. package/dist/esm/adapters/governed-tool.js +4 -4
  36. package/dist/esm/adapters/langgraph.js +9 -9
  37. package/dist/esm/client.d.ts.map +1 -1
  38. package/dist/esm/client.js +240 -164
  39. package/dist/esm/client.js.map +1 -1
  40. package/dist/esm/errors.js +1 -1
  41. package/dist/esm/index.d.ts +2 -2
  42. package/dist/esm/index.d.ts.map +1 -1
  43. package/dist/esm/index.js.map +1 -1
  44. package/dist/esm/telemetry.d.ts.map +1 -1
  45. package/dist/esm/telemetry.js +42 -6
  46. package/dist/esm/telemetry.js.map +1 -1
  47. package/dist/esm/types/connector.d.ts +33 -5
  48. package/dist/esm/types/connector.d.ts.map +1 -1
  49. package/dist/esm/types/connector.js.map +1 -1
  50. package/dist/esm/types/cost-controls.d.ts +29 -0
  51. package/dist/esm/types/cost-controls.d.ts.map +1 -1
  52. package/dist/esm/types/execution-replay.d.ts +2 -0
  53. package/dist/esm/types/execution-replay.d.ts.map +1 -1
  54. package/dist/esm/types/masfeat.d.ts +2 -0
  55. package/dist/esm/types/masfeat.d.ts.map +1 -1
  56. package/dist/esm/types/planning.d.ts +78 -8
  57. package/dist/esm/types/planning.d.ts.map +1 -1
  58. package/dist/esm/types/policies.d.ts +92 -14
  59. package/dist/esm/types/policies.d.ts.map +1 -1
  60. package/dist/esm/types/policy.d.ts +58 -4
  61. package/dist/esm/types/policy.d.ts.map +1 -1
  62. package/dist/esm/types/proxy.d.ts +22 -4
  63. package/dist/esm/types/proxy.d.ts.map +1 -1
  64. package/dist/esm/types/workflows.d.ts +54 -6
  65. package/dist/esm/types/workflows.d.ts.map +1 -1
  66. package/dist/esm/types/workflows.js.map +1 -1
  67. package/dist/esm/version.d.ts +1 -1
  68. package/dist/esm/version.js +1 -1
  69. package/package.json +3 -2
@@ -30,12 +30,12 @@ function mapIdempotencyKeyMismatch(err) {
30
30
  * Returns -1 if a < b, 0 if equal, 1 if a > b.
31
31
  */
32
32
  function compareSemver(a, b) {
33
- const parseVersion = (v) => v.split('.').map(p => parseInt(p.split('-')[0], 10) || 0);
33
+ const parseVersion = (v) => v.split('.').map(p => parseInt(p.split('-')[0], 10) ?? 0);
34
34
  const aParts = parseVersion(a);
35
35
  const bParts = parseVersion(b);
36
36
  const len = Math.max(aParts.length, bParts.length);
37
37
  for (let i = 0; i < len; i++) {
38
- const diff = (aParts[i] || 0) - (bParts[i] || 0);
38
+ const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);
39
39
  if (diff !== 0)
40
40
  return diff < 0 ? -1 : 1;
41
41
  }
@@ -64,7 +64,7 @@ export class AxonFlow {
64
64
  };
65
65
  }
66
66
  // Set defaults first to determine endpoint
67
- const endpoint = config.endpoint || 'https://staging-eu.getaxonflow.com';
67
+ const endpoint = config.endpoint ?? 'https://staging-eu.getaxonflow.com';
68
68
  // Credentials check: OAuth2-style (clientId/clientSecret)
69
69
  const hasCredentials = !!(config.clientId && config.clientSecret);
70
70
  // Set configuration
@@ -72,19 +72,19 @@ export class AxonFlow {
72
72
  clientId: config.clientId,
73
73
  clientSecret: config.clientSecret,
74
74
  endpoint,
75
- mode: config.mode || 'production',
76
- tenant: config.tenant || '',
77
- debug: config.debug || false,
78
- timeout: config.timeout || 30000,
79
- mapTimeout: config.mapTimeout || 120000, // 2 minutes for MAP operations
75
+ mode: config.mode ?? 'production',
76
+ tenant: config.tenant ?? '',
77
+ debug: config.debug ?? false,
78
+ timeout: config.timeout ?? 30000,
79
+ mapTimeout: config.mapTimeout ?? 120000, // 2 minutes for MAP operations
80
80
  retry: {
81
81
  enabled: config.retry?.enabled !== false,
82
- maxAttempts: config.retry?.maxAttempts || 3,
83
- delay: config.retry?.delay || 1000,
82
+ maxAttempts: config.retry?.maxAttempts ?? 3,
83
+ delay: config.retry?.delay ?? 1000,
84
84
  },
85
85
  cache: {
86
86
  enabled: config.cache?.enabled !== false,
87
- ttl: config.cache?.ttl || 60000,
87
+ ttl: config.cache?.ttl ?? 60000,
88
88
  },
89
89
  };
90
90
  // Interceptors removed in v3.0.0 (deprecated wrapOpenAIClient/wrapAnthropicClient)
@@ -121,7 +121,7 @@ export class AxonFlow {
121
121
  // clientSecret defaults to empty string for community/no-secret mode.
122
122
  const effectiveClientId = this.getEffectiveClientId();
123
123
  if (effectiveClientId) {
124
- const credentials = Buffer.from(`${effectiveClientId}:${this.config.clientSecret || ''}`).toString('base64');
124
+ const credentials = Buffer.from(`${effectiveClientId}:${this.config.clientSecret ?? ''}`).toString('base64');
125
125
  headers['Authorization'] = `Basic ${credentials}`;
126
126
  }
127
127
  // Include SDK version for version discovery and compatibility checks
@@ -138,6 +138,9 @@ export class AxonFlow {
138
138
  * @returns The clientId to use in requests
139
139
  */
140
140
  getEffectiveClientId() {
141
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
142
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
143
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
141
144
  return this.config.clientId || this.config.tenant || 'community';
142
145
  }
143
146
  /**
@@ -209,7 +212,7 @@ export class AxonFlow {
209
212
  // If denied, throw error
210
213
  if (!governanceResponse.allowed) {
211
214
  const violation = governanceResponse.violations?.[0];
212
- throw new Error(`Request blocked by AxonFlow: ${violation?.description || 'Policy violation'}`);
215
+ throw new Error(`Request blocked by AxonFlow: ${violation?.description ?? 'Policy violation'}`);
213
216
  }
214
217
  // Execute the AI call (possibly with modifications)
215
218
  const modifiedCall = governanceResponse.modifiedRequest
@@ -259,6 +262,9 @@ export class AxonFlow {
259
262
  const agentRequest = {
260
263
  query: request.aiRequest.prompt,
261
264
  user_token: '',
265
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
266
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
267
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
262
268
  client_id: this.config.clientId || this.config.tenant,
263
269
  request_type: 'llm_chat',
264
270
  context: {
@@ -286,7 +292,7 @@ export class AxonFlow {
286
292
  const agentResponse = await response.json();
287
293
  // Transform Agent API response to SDK format
288
294
  // Extract policy name from policy_info if available
289
- const policyName = agentResponse.policy_info?.policies_evaluated?.[0] || 'agent-policy';
295
+ const policyName = agentResponse.policy_info?.policies_evaluated?.[0] ?? 'agent-policy';
290
296
  return {
291
297
  requestId: request.requestId,
292
298
  allowed: !agentResponse.blocked,
@@ -295,17 +301,17 @@ export class AxonFlow {
295
301
  {
296
302
  type: 'security',
297
303
  severity: 'high',
298
- description: agentResponse.block_reason || 'Request blocked by policy',
304
+ description: agentResponse.block_reason ?? 'Request blocked by policy',
299
305
  policy: policyName,
300
306
  action: 'blocked',
301
307
  },
302
308
  ]
303
309
  : [],
304
310
  modifiedRequest: agentResponse.data,
305
- policies: agentResponse.policy_info?.policies_evaluated || [],
311
+ policies: agentResponse.policy_info?.policies_evaluated ?? [],
306
312
  audit: {
307
313
  timestamp: Date.now(),
308
- duration: parseInt(agentResponse.policy_info?.processing_time?.replace('ms', '') || '0'),
314
+ duration: parseInt(agentResponse.policy_info?.processing_time?.replace('ms', '') ?? '0'),
309
315
  tenant: this.config.tenant,
310
316
  },
311
317
  };
@@ -319,7 +325,7 @@ export class AxonFlow {
319
325
  if (this.config.debug) {
320
326
  debugLog('Request processed', {
321
327
  allowed: response.allowed,
322
- violations: response.violations?.length || 0,
328
+ violations: response.violations?.length ?? 0,
323
329
  duration: response.audit.duration,
324
330
  });
325
331
  }
@@ -328,9 +334,10 @@ export class AxonFlow {
328
334
  * Check if an error is from AxonFlow (vs the AI provider)
329
335
  */
330
336
  isAxonFlowError(error) {
331
- return (error?.message?.includes('AxonFlow') ||
332
- error?.message?.includes('governance') ||
333
- error?.message?.includes('fetch'));
337
+ const msg = error?.message;
338
+ if (typeof msg !== 'string')
339
+ return false;
340
+ return msg.includes('AxonFlow') || msg.includes('governance') || msg.includes('fetch');
334
341
  }
335
342
  /**
336
343
  * Create a sandbox client for testing
@@ -526,13 +533,16 @@ export class AxonFlow {
526
533
  */
527
534
  async proxyLLMCall(options) {
528
535
  // Default to "anonymous" if userToken is empty/undefined (community mode)
529
- const effectiveUserToken = options.userToken || 'anonymous';
536
+ const effectiveUserToken = options.userToken ?? 'anonymous';
530
537
  const agentRequest = {
531
538
  query: options.query,
532
539
  user_token: effectiveUserToken,
540
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
541
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
542
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
533
543
  client_id: this.config.clientId || this.config.tenant,
534
544
  request_type: options.requestType,
535
- context: options.context || {},
545
+ context: options.context ?? {},
536
546
  };
537
547
  if (options.media && options.media.length > 0) {
538
548
  agentRequest.media = options.media.map(m => ({
@@ -585,7 +595,7 @@ export class AxonFlow {
585
595
  try {
586
596
  const errorJson = JSON.parse(errorText);
587
597
  if (errorJson.blocked || errorJson.block_reason) {
588
- throw new PolicyViolationError(errorJson.block_reason || 'Request blocked by policy', errorJson.policy_info?.policies_evaluated);
598
+ throw new PolicyViolationError(errorJson.block_reason ?? 'Request blocked by policy', errorJson.policy_info?.policies_evaluated);
589
599
  }
590
600
  }
591
601
  catch (e) {
@@ -599,13 +609,11 @@ export class AxonFlow {
599
609
  }
600
610
  }
601
611
  // Parse response if not already parsed (from 402 handling)
602
- if (!data) {
603
- data = await response.json();
604
- }
612
+ data ?? (data = await response.json());
605
613
  // Check for policy violation in successful response (some blocked responses return 200)
606
614
  // Note: Don't throw for budget blocks (402 responses) - return with budgetInfo instead
607
615
  if (data.blocked && !data.budget_info) {
608
- throw new PolicyViolationError(data.block_reason || 'Request blocked by policy', data.policy_info?.policies_evaluated);
616
+ throw new PolicyViolationError(data.block_reason ?? 'Request blocked by policy', data.policy_info?.policies_evaluated);
609
617
  }
610
618
  // Transform snake_case response to camelCase
611
619
  const result = {
@@ -614,18 +622,18 @@ export class AxonFlow {
614
622
  result: data.result,
615
623
  planId: data.plan_id,
616
624
  requestId: data.request_id,
617
- metadata: data.metadata || {},
625
+ metadata: data.metadata ?? {},
618
626
  error: data.error,
619
- blocked: data.blocked || false,
627
+ blocked: data.blocked ?? false,
620
628
  blockReason: data.block_reason,
621
629
  };
622
630
  // Parse policy info if present
623
631
  if (data.policy_info) {
624
632
  result.policyInfo = {
625
- policiesEvaluated: data.policy_info.policies_evaluated || [],
626
- staticChecks: data.policy_info.static_checks || [],
627
- processingTime: data.policy_info.processing_time || '',
628
- tenantId: data.policy_info.tenant_id || '',
633
+ policiesEvaluated: data.policy_info.policies_evaluated ?? [],
634
+ staticChecks: data.policy_info.static_checks ?? [],
635
+ processingTime: data.policy_info.processing_time ?? '',
636
+ tenantId: data.policy_info.tenant_id ?? '',
629
637
  codeArtifact: data.policy_info.code_artifact,
630
638
  };
631
639
  }
@@ -634,10 +642,10 @@ export class AxonFlow {
634
642
  result.budgetInfo = {
635
643
  budgetId: data.budget_info.budget_id,
636
644
  budgetName: data.budget_info.budget_name,
637
- usedUsd: data.budget_info.used_usd || 0,
638
- limitUsd: data.budget_info.limit_usd || 0,
639
- percentage: data.budget_info.percentage || 0,
640
- exceeded: data.budget_info.exceeded || false,
645
+ usedUsd: data.budget_info.used_usd ?? 0,
646
+ limitUsd: data.budget_info.limit_usd ?? 0,
647
+ percentage: data.budget_info.percentage ?? 0,
648
+ exceeded: data.budget_info.exceeded ?? false,
641
649
  action: data.budget_info.action,
642
650
  };
643
651
  }
@@ -681,7 +689,7 @@ export class AxonFlow {
681
689
  async listConnectors() {
682
690
  const response = await this.orchestratorRequest('GET', '/api/v1/connectors');
683
691
  // Handle wrapped response
684
- const connectors = Array.isArray(response) ? response : response.connectors || [];
692
+ const connectors = Array.isArray(response) ? response : (response.connectors ?? []);
685
693
  if (this.config.debug) {
686
694
  debugLog('Listed connectors', { count: connectors.length });
687
695
  }
@@ -734,11 +742,14 @@ export class AxonFlow {
734
742
  const agentRequest = {
735
743
  query,
736
744
  user_token: '',
745
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
746
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
747
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
737
748
  client_id: this.config.clientId || this.config.tenant,
738
749
  request_type: 'mcp-query',
739
750
  context: {
740
751
  connector: connectorName,
741
- params: params || {},
752
+ params: params ?? {},
742
753
  },
743
754
  };
744
755
  const url = `${this.config.endpoint}/api/request`;
@@ -807,7 +818,7 @@ export class AxonFlow {
807
818
  const body = {
808
819
  connector: options.connector,
809
820
  statement: options.statement,
810
- options: options.options || {},
821
+ options: options.options ?? {},
811
822
  };
812
823
  if (this.config.debug) {
813
824
  debugLog('MCP Query', {
@@ -824,7 +835,7 @@ export class AxonFlow {
824
835
  const responseData = await response.json();
825
836
  // Handle policy blocks (403 responses)
826
837
  if (!response.ok) {
827
- throw new ConnectorError(responseData.error || `MCP query failed: ${response.status} ${response.statusText}`, options.connector, 'mcpQuery');
838
+ throw new ConnectorError(responseData.error ?? `MCP query failed: ${response.status} ${response.statusText}`, options.connector, 'mcpQuery');
828
839
  }
829
840
  if (this.config.debug) {
830
841
  debugLog('MCP Query result', {
@@ -890,7 +901,7 @@ export class AxonFlow {
890
901
  if (options.parameters) {
891
902
  body.parameters = options.parameters;
892
903
  }
893
- body.operation = options.operation || 'execute';
904
+ body.operation = options.operation ?? 'execute';
894
905
  if (this.config.debug) {
895
906
  debugLog('MCP Check Input', {
896
907
  connectorType: options.connectorType,
@@ -906,7 +917,7 @@ export class AxonFlow {
906
917
  const responseData = await response.json();
907
918
  // 403 means policy blocked — this is a valid check response, not an error
908
919
  if (!response.ok && response.status !== 403) {
909
- throw new ConnectorError(responseData.error || 'MCP check-input failed', options.connectorType, 'check-input');
920
+ throw new ConnectorError(responseData.error ?? 'MCP check-input failed', options.connectorType, 'check-input');
910
921
  }
911
922
  if (this.config.debug) {
912
923
  debugLog('MCP Check Input result', {
@@ -978,7 +989,7 @@ export class AxonFlow {
978
989
  const responseData = await response.json();
979
990
  // 403 means policy blocked — this is a valid check response, not an error
980
991
  if (!response.ok && response.status !== 403) {
981
- throw new ConnectorError(responseData.error || 'MCP check-output failed', options.connectorType, 'check-output');
992
+ throw new ConnectorError(responseData.error ?? 'MCP check-output failed', options.connectorType, 'check-output');
982
993
  }
983
994
  if (this.config.debug) {
984
995
  debugLog('MCP Check Output result', {
@@ -1024,7 +1035,13 @@ export class AxonFlow {
1024
1035
  }
1025
1036
  const agentRequest = {
1026
1037
  query,
1038
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
1039
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
1040
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1027
1041
  user_token: userToken || this.config.clientId || this.config.tenant,
1042
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
1043
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
1044
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1028
1045
  client_id: this.config.clientId || this.config.tenant,
1029
1046
  request_type: 'multi-agent-plan',
1030
1047
  context,
@@ -1050,18 +1067,24 @@ export class AxonFlow {
1050
1067
  throw new PlanExecutionError(`Plan generation failed: ${agentResponse.error}`, undefined, 'generation');
1051
1068
  }
1052
1069
  // plan_id can be at top level or inside data
1053
- const planId = agentResponse.plan_id || agentResponse.data?.plan_id;
1070
+ const planId = agentResponse.plan_id ?? agentResponse.data?.plan_id;
1054
1071
  if (this.config.debug) {
1055
1072
  debugLog('Plan generated', { planId });
1056
1073
  }
1057
1074
  return {
1058
1075
  planId,
1059
- status: agentResponse.data?.status || 'pending',
1060
- steps: agentResponse.data?.steps || [],
1061
- domain: agentResponse.data?.domain || domain || 'generic',
1062
- complexity: agentResponse.data?.complexity || 0,
1063
- parallel: agentResponse.data?.parallel || false,
1064
- metadata: agentResponse.metadata || {},
1076
+ status: agentResponse.data?.status ?? 'pending',
1077
+ steps: agentResponse.data?.steps ?? [],
1078
+ domain: agentResponse.data?.domain ?? domain ?? 'generic',
1079
+ complexity: agentResponse.data?.complexity ?? 0,
1080
+ parallel: agentResponse.data?.parallel ?? false,
1081
+ metadata: agentResponse.metadata ?? {},
1082
+ success: agentResponse.success,
1083
+ version: agentResponse.version ?? agentResponse.data?.version,
1084
+ result: agentResponse.result ?? agentResponse.data?.result,
1085
+ error: agentResponse.error,
1086
+ workflow_execution_id: agentResponse.workflow_execution_id ?? agentResponse.data?.workflow_execution_id,
1087
+ policy_info: agentResponse.policy_info ?? agentResponse.data?.policy_info,
1065
1088
  };
1066
1089
  }
1067
1090
  /**
@@ -1072,7 +1095,13 @@ export class AxonFlow {
1072
1095
  async executePlan(planId, userToken) {
1073
1096
  const agentRequest = {
1074
1097
  query: '',
1098
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
1099
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
1100
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1075
1101
  user_token: userToken || this.config.clientId || this.config.tenant,
1102
+ // Intentional || (not ??): an empty-string clientId/tenant is treated as
1103
+ // "missing" by the SDK contract — see tests/smart-defaults.test.ts.
1104
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1076
1105
  client_id: this.config.clientId || this.config.tenant,
1077
1106
  request_type: 'execute-plan',
1078
1107
  context: { plan_id: planId },
@@ -1104,7 +1133,7 @@ export class AxonFlow {
1104
1133
  if (data.error && !error)
1105
1134
  error = data.error;
1106
1135
  // Throw on nested failure (e.g., cancelled plan execution)
1107
- throw new PlanExecutionError(error || 'Plan execution failed', planId, 'execution');
1136
+ throw new PlanExecutionError(error ?? 'Plan execution failed', planId, 'execution');
1108
1137
  }
1109
1138
  if (!result && data?.result)
1110
1139
  result = data.result;
@@ -1186,8 +1215,13 @@ export class AxonFlow {
1186
1215
  debugLog('Plan cancelled', { planId, status: data.status });
1187
1216
  }
1188
1217
  return {
1189
- planId: data.plan_id || planId,
1218
+ planId: data.plan_id ?? planId,
1190
1219
  status: data.status,
1220
+ // `success` is the canonical wire boolean. The deprecated
1221
+ // `message` slot is also kept populated for back-compat
1222
+ // readers; on a current server it will always be undefined
1223
+ // because the wire never emits it.
1224
+ success: data.success,
1191
1225
  message: data.message,
1192
1226
  };
1193
1227
  }
@@ -1212,6 +1246,9 @@ export class AxonFlow {
1212
1246
  if (request.domain) {
1213
1247
  body.domain = request.domain;
1214
1248
  }
1249
+ if (request.metadata !== undefined) {
1250
+ body.metadata = request.metadata;
1251
+ }
1215
1252
  const response = await fetch(url, {
1216
1253
  method: 'PUT',
1217
1254
  headers,
@@ -1231,7 +1268,7 @@ export class AxonFlow {
1231
1268
  debugLog('Plan updated', { planId, version: data.version });
1232
1269
  }
1233
1270
  return {
1234
- planId: data.plan_id || planId,
1271
+ planId: data.plan_id ?? planId,
1235
1272
  version: data.version,
1236
1273
  status: data.status,
1237
1274
  success: data.success ?? true,
@@ -1256,7 +1293,7 @@ export class AxonFlow {
1256
1293
  throw new PlanExecutionError(`Get plan versions failed: ${response.status} ${response.statusText} - ${errorText}`, planId, 'versions');
1257
1294
  }
1258
1295
  const data = await response.json();
1259
- const versions = (data.versions || []).map((v) => ({
1296
+ const versions = (data.versions ?? []).map((v) => ({
1260
1297
  version: v.version,
1261
1298
  changedAt: v.changed_at,
1262
1299
  changedBy: v.changed_by,
@@ -1264,7 +1301,7 @@ export class AxonFlow {
1264
1301
  changeSummary: v.change_summary,
1265
1302
  }));
1266
1303
  return {
1267
- planId: data.plan_id || planId,
1304
+ planId: data.plan_id ?? planId,
1268
1305
  versions,
1269
1306
  };
1270
1307
  }
@@ -1294,9 +1331,13 @@ export class AxonFlow {
1294
1331
  debugLog('Plan resumed', { planId, approved: data.approved });
1295
1332
  }
1296
1333
  return {
1297
- planId: data.plan_id || planId,
1334
+ planId: data.plan_id ?? planId,
1298
1335
  status: data.status,
1336
+ // `result` is the canonical wire-aggregated outcome on resume.
1337
+ result: data.result,
1299
1338
  approved: data.approved,
1339
+ // `message` kept populated for the back-compat alias; legacy
1340
+ // path read this slot historically.
1300
1341
  message: data.message,
1301
1342
  };
1302
1343
  }
@@ -1359,8 +1400,8 @@ export class AxonFlow {
1359
1400
  user_token: options.userToken,
1360
1401
  client_id: clientId,
1361
1402
  query: options.query,
1362
- data_sources: options.dataSources || [],
1363
- context: options.context || {},
1403
+ data_sources: options.dataSources ?? [],
1404
+ context: options.context ?? {},
1364
1405
  };
1365
1406
  const headers = {
1366
1407
  'Content-Type': 'application/json',
@@ -1391,9 +1432,9 @@ export class AxonFlow {
1391
1432
  const result = {
1392
1433
  contextId: data.context_id,
1393
1434
  approved: data.approved,
1394
- requiresRedaction: data.requires_redaction || false,
1395
- approvedData: data.approved_data || {},
1396
- policies: data.policies || [],
1435
+ requiresRedaction: data.requires_redaction ?? false,
1436
+ approvedData: data.approved_data ?? {},
1437
+ policies: data.policies ?? [],
1397
1438
  expiresAt,
1398
1439
  blockReason: data.block_reason,
1399
1440
  };
@@ -1452,7 +1493,7 @@ export class AxonFlow {
1452
1493
  total_tokens: options.tokenUsage.totalTokens,
1453
1494
  },
1454
1495
  latency_ms: options.latencyMs,
1455
- metadata: options.metadata || {},
1496
+ metadata: options.metadata ?? {},
1456
1497
  };
1457
1498
  const headers = {
1458
1499
  'Content-Type': 'application/json',
@@ -1571,7 +1612,7 @@ export class AxonFlow {
1571
1612
  const response = await this.orchestratorRequest('GET', '/api/v1/circuit-breaker/status');
1572
1613
  const data = response.data;
1573
1614
  return {
1574
- activeCircuits: (data.active_circuits || []).map(c => ({
1615
+ activeCircuits: (data.active_circuits ?? []).map(c => ({
1575
1616
  id: c.id,
1576
1617
  scope: c.scope,
1577
1618
  scopeId: c.scope_id,
@@ -1614,7 +1655,7 @@ export class AxonFlow {
1614
1655
  const response = await this.orchestratorRequest('GET', path);
1615
1656
  const data = response.data;
1616
1657
  return {
1617
- history: (data.history || []).map(h => ({
1658
+ history: (data.history ?? []).map(h => ({
1618
1659
  id: h.id,
1619
1660
  orgId: h.org_id,
1620
1661
  scope: h.scope,
@@ -1870,10 +1911,10 @@ export class AxonFlow {
1870
1911
  * forward compatibility per ADR-043.
1871
1912
  */
1872
1913
  parseDecisionExplanation(raw) {
1873
- const r = raw || {};
1874
- const rawMatches = r.policy_matches || [];
1914
+ const r = raw ?? {};
1915
+ const rawMatches = r.policy_matches ?? [];
1875
1916
  const policyMatches = rawMatches.map(m => ({
1876
- policyId: m.policy_id || '',
1917
+ policyId: m.policy_id ?? '',
1877
1918
  policyName: m.policy_name,
1878
1919
  action: m.action,
1879
1920
  riskLevel: m.risk_level,
@@ -1882,18 +1923,18 @@ export class AxonFlow {
1882
1923
  }));
1883
1924
  const rawRules = r.matched_rules;
1884
1925
  const matchedRules = rawRules?.map(m => ({
1885
- policyId: m.policy_id || '',
1926
+ policyId: m.policy_id ?? '',
1886
1927
  ruleId: m.rule_id,
1887
1928
  ruleText: m.rule_text,
1888
1929
  matchedOn: m.matched_on,
1889
1930
  }));
1890
1931
  return {
1891
- decisionId: r.decision_id || '',
1932
+ decisionId: r.decision_id ?? '',
1892
1933
  timestamp: r.timestamp ? new Date(r.timestamp) : new Date(),
1893
1934
  policyMatches,
1894
1935
  matchedRules,
1895
- decision: r.decision || '',
1896
- reason: r.reason || '',
1936
+ decision: r.decision ?? '',
1937
+ reason: r.reason ?? '',
1897
1938
  riskLevel: r.risk_level,
1898
1939
  overrideAvailable: r.override_available ?? false,
1899
1940
  overrideExistingId: r.override_existing_id,
@@ -1940,7 +1981,7 @@ export class AxonFlow {
1940
1981
  };
1941
1982
  }
1942
1983
  const data = response;
1943
- const entries = (data.entries || []).map(e => this.parseAuditLogEntry(e));
1984
+ const entries = (data.entries ?? []).map(e => this.parseAuditLogEntry(e));
1944
1985
  return {
1945
1986
  entries,
1946
1987
  total: data.total ?? entries.length,
@@ -1994,7 +2035,7 @@ export class AxonFlow {
1994
2035
  };
1995
2036
  }
1996
2037
  const data = response;
1997
- const entries = (data.entries || []).map(e => this.parseAuditLogEntry(e));
2038
+ const entries = (data.entries ?? []).map(e => this.parseAuditLogEntry(e));
1998
2039
  return {
1999
2040
  entries,
2000
2041
  total: data.total ?? entries.length,
@@ -2114,7 +2155,7 @@ export class AxonFlow {
2114
2155
  }
2115
2156
  // Backend returns { policies: [], pagination: {} }, extract the array
2116
2157
  const response = await this.policyRequest('GET', path);
2117
- return response.policies || [];
2158
+ return response.policies ?? [];
2118
2159
  }
2119
2160
  /**
2120
2161
  * Get a specific static policy by ID.
@@ -2165,12 +2206,19 @@ export class AxonFlow {
2165
2206
  severity: policy.severity,
2166
2207
  enabled: policy.enabled,
2167
2208
  action: policy.action,
2168
- tier: policy.tier || 'tenant',
2209
+ tier: policy.tier ?? 'tenant',
2169
2210
  };
2170
2211
  // Add organization_id for organization tier policies
2171
2212
  if (policy.organizationId) {
2172
2213
  requestBody.organization_id = policy.organizationId;
2173
2214
  }
2215
+ // Wire-canonical fields surfaced in the v6 alignment sweep.
2216
+ if (policy.priority !== undefined) {
2217
+ requestBody.priority = policy.priority;
2218
+ }
2219
+ if (policy.tags !== undefined) {
2220
+ requestBody.tags = policy.tags;
2221
+ }
2174
2222
  return this.policyRequest('POST', '/api/v1/static-policies', requestBody);
2175
2223
  }
2176
2224
  /**
@@ -2259,7 +2307,7 @@ export class AxonFlow {
2259
2307
  }
2260
2308
  // Backend returns { static: [], dynamic: [], ... }, extract the static array
2261
2309
  const response = await this.policyRequest('GET', path);
2262
- return response.static || [];
2310
+ return response.static ?? [];
2263
2311
  }
2264
2312
  /**
2265
2313
  * Test a regex pattern against sample inputs.
@@ -2304,12 +2352,21 @@ export class AxonFlow {
2304
2352
  debugLog('Getting static policy versions', { id });
2305
2353
  }
2306
2354
  const response = await this.policyRequest('GET', `/api/v1/static-policies/${id}/versions`);
2307
- // Transform snake_case API response to camelCase
2355
+ // Transform snake_case API response to camelCase, populating
2356
+ // both the wire-canonical fields (`id`, `policy_id`,
2357
+ // `change_summary`, `snapshot`) and the legacy aliases (kept for
2358
+ // back-compat; will read undefined on a current server).
2308
2359
  return response.versions.map(v => ({
2360
+ id: v.id,
2361
+ policy_id: v.policy_id,
2309
2362
  version: v.version,
2310
2363
  changedBy: v.changed_by,
2311
2364
  changedAt: v.changed_at,
2312
2365
  changeType: v.change_type,
2366
+ change_summary: v.change_summary,
2367
+ snapshot: v.snapshot,
2368
+ // @deprecated aliases — only populated if a legacy server
2369
+ // still emits them; current servers don't.
2313
2370
  changeDescription: v.change_description,
2314
2371
  previousValues: v.previous_values,
2315
2372
  newValues: v.new_values,
@@ -2378,7 +2435,7 @@ export class AxonFlow {
2378
2435
  debugLog('Listing policy overrides');
2379
2436
  }
2380
2437
  const response = await this.policyRequest('GET', '/api/v1/static-policies/overrides');
2381
- return response.overrides || [];
2438
+ return response.overrides ?? [];
2382
2439
  }
2383
2440
  // ============================================================================
2384
2441
  // Dynamic Policy Methods
@@ -2425,7 +2482,7 @@ export class AxonFlow {
2425
2482
  // API returns {"policies": [...]} wrapper via Agent proxy
2426
2483
  const response = await this.orchestratorRequest('GET', path);
2427
2484
  // Handle both wrapped and unwrapped responses for compatibility
2428
- return Array.isArray(response) ? response : response.policies || [];
2485
+ return Array.isArray(response) ? response : (response.policies ?? []);
2429
2486
  }
2430
2487
  /**
2431
2488
  * Get a specific dynamic policy by ID.
@@ -2480,7 +2537,7 @@ export class AxonFlow {
2480
2537
  requestBody.priority = policy.priority;
2481
2538
  if (policy.enabled !== undefined)
2482
2539
  requestBody.enabled = policy.enabled;
2483
- requestBody.tier = policy.tier || 'tenant';
2540
+ requestBody.tier = policy.tier ?? 'tenant';
2484
2541
  if (policy.organizationId) {
2485
2542
  requestBody.organization_id = policy.organizationId;
2486
2543
  }
@@ -2574,7 +2631,7 @@ export class AxonFlow {
2574
2631
  // API returns {"policies": [...]} wrapper via Agent proxy
2575
2632
  const response = await this.orchestratorRequest('GET', path);
2576
2633
  // Handle both wrapped and unwrapped responses for compatibility
2577
- return Array.isArray(response) ? response : response.policies || [];
2634
+ return Array.isArray(response) ? response : (response.policies ?? []);
2578
2635
  }
2579
2636
  // ============================================================================
2580
2637
  // Portal Authentication Methods (Enterprise)
@@ -2903,7 +2960,7 @@ export class AxonFlow {
2903
2960
  const response = await this.portalRequest('GET', path);
2904
2961
  // Transform snake_case response to camelCase
2905
2962
  return {
2906
- prs: (response.prs || []).map(pr => ({
2963
+ prs: (response.prs ?? []).map(pr => ({
2907
2964
  id: pr.id,
2908
2965
  prNumber: pr.pr_number,
2909
2966
  prUrl: pr.pr_url,
@@ -3109,7 +3166,7 @@ export class AxonFlow {
3109
3166
  }
3110
3167
  const response = await this.portalRequest('GET', path);
3111
3168
  return {
3112
- records: (response.records || []).map(r => ({
3169
+ records: (response.records ?? []).map(r => ({
3113
3170
  id: r.id,
3114
3171
  prNumber: r.pr_number,
3115
3172
  prUrl: r.pr_url,
@@ -3285,7 +3342,7 @@ export class AxonFlow {
3285
3342
  }
3286
3343
  const response = await this.orchestratorRequest('GET', path);
3287
3344
  return {
3288
- executions: (response.executions || []).map(e => ({
3345
+ executions: (response.executions ?? []).map(e => ({
3289
3346
  requestId: e.request_id,
3290
3347
  workflowName: e.workflow_name,
3291
3348
  status: e.status,
@@ -3369,6 +3426,7 @@ export class AxonFlow {
3369
3426
  approvalRequired: s.approval_required,
3370
3427
  approvedBy: s.approved_by,
3371
3428
  approvedAt: s.approved_at,
3429
+ retryCount: s.retry_count,
3372
3430
  })),
3373
3431
  };
3374
3432
  }
@@ -3412,6 +3470,7 @@ export class AxonFlow {
3412
3470
  approvalRequired: s.approval_required,
3413
3471
  approvedBy: s.approved_by,
3414
3472
  approvedAt: s.approved_at,
3473
+ retryCount: s.retry_count,
3415
3474
  }));
3416
3475
  }
3417
3476
  /**
@@ -3550,8 +3609,8 @@ export class AxonFlow {
3550
3609
  const path = `/api/v1/budgets${queryString ? `?${queryString}` : ''}`;
3551
3610
  const response = await this.orchestratorRequest('GET', path);
3552
3611
  return {
3553
- budgets: (response.budgets || []).map(b => this.mapBudgetResponse(b)),
3554
- total: response.total || 0,
3612
+ budgets: (response.budgets ?? []).map(b => this.mapBudgetResponse(b)),
3613
+ total: response.total ?? 0,
3555
3614
  };
3556
3615
  }
3557
3616
  /**
@@ -3595,13 +3654,13 @@ export class AxonFlow {
3595
3654
  const response = await this.orchestratorRequest('GET', `/api/v1/budgets/${budgetId}/status`);
3596
3655
  return {
3597
3656
  budget: this.mapBudgetResponse(response.budget),
3598
- usedUsd: response.used_usd || 0,
3599
- remainingUsd: response.remaining_usd || 0,
3600
- percentage: response.percentage || 0,
3601
- isExceeded: response.is_exceeded || false,
3602
- isBlocked: response.is_blocked || false,
3603
- periodStart: response.period_start || '',
3604
- periodEnd: response.period_end || '',
3657
+ usedUsd: response.used_usd ?? 0,
3658
+ remainingUsd: response.remaining_usd ?? 0,
3659
+ percentage: response.percentage ?? 0,
3660
+ isExceeded: response.is_exceeded ?? false,
3661
+ isBlocked: response.is_blocked ?? false,
3662
+ periodStart: response.period_start ?? '',
3663
+ periodEnd: response.period_end ?? '',
3605
3664
  };
3606
3665
  }
3607
3666
  /**
@@ -3612,19 +3671,20 @@ export class AxonFlow {
3612
3671
  */
3613
3672
  async getBudgetAlerts(budgetId) {
3614
3673
  const response = await this.orchestratorRequest('GET', `/api/v1/budgets/${budgetId}/alerts`);
3615
- const alerts = (response.alerts || []).map((a) => ({
3616
- id: a.id || '',
3617
- budgetId: a.budget_id || '',
3618
- alertType: a.alert_type || '',
3619
- threshold: a.threshold || 0,
3620
- percentageReached: a.percentage_reached || 0,
3621
- amountUsd: a.amount_usd || 0,
3622
- message: a.message || '',
3623
- createdAt: a.created_at || '',
3674
+ const alerts = (response.alerts ?? []).map((a) => ({
3675
+ id: a.id ?? '',
3676
+ budgetId: a.budget_id ?? '',
3677
+ alertType: a.alert_type ?? '',
3678
+ threshold: a.threshold ?? 0,
3679
+ percentageReached: a.percentage_reached ?? 0,
3680
+ amountUsd: a.amount_usd ?? 0,
3681
+ message: a.message ?? '',
3682
+ createdAt: a.created_at ?? '',
3683
+ acknowledged: a.acknowledged,
3624
3684
  }));
3625
3685
  return {
3626
3686
  alerts,
3627
- count: response.count || 0,
3687
+ count: response.count ?? 0,
3628
3688
  };
3629
3689
  }
3630
3690
  /**
@@ -3647,11 +3707,11 @@ export class AxonFlow {
3647
3707
  body.user_id = request.userId;
3648
3708
  const response = await this.orchestratorRequest('POST', '/api/v1/budgets/check', body);
3649
3709
  return {
3650
- allowed: response.allowed || false,
3710
+ allowed: response.allowed ?? false,
3651
3711
  action: response.action,
3652
3712
  message: response.message,
3653
3713
  budgets: response.budgets
3654
- ? (response.budgets || []).map(b => this.mapBudgetResponse(b))
3714
+ ? (response.budgets ?? []).map(b => this.mapBudgetResponse(b))
3655
3715
  : undefined,
3656
3716
  };
3657
3717
  }
@@ -3668,14 +3728,14 @@ export class AxonFlow {
3668
3728
  const path = period ? `/api/v1/usage?period=${period}` : '/api/v1/usage';
3669
3729
  const response = await this.orchestratorRequest('GET', path);
3670
3730
  return {
3671
- totalCostUsd: response.total_cost_usd || 0,
3672
- totalRequests: response.total_requests || 0,
3673
- totalTokensIn: response.total_tokens_in || 0,
3674
- totalTokensOut: response.total_tokens_out || 0,
3675
- averageCostPerRequest: response.average_cost_per_request || 0,
3676
- period: response.period || '',
3677
- periodStart: response.period_start || '',
3678
- periodEnd: response.period_end || '',
3731
+ totalCostUsd: response.total_cost_usd ?? 0,
3732
+ totalRequests: response.total_requests ?? 0,
3733
+ totalTokensIn: response.total_tokens_in ?? 0,
3734
+ totalTokensOut: response.total_tokens_out ?? 0,
3735
+ averageCostPerRequest: response.average_cost_per_request ?? 0,
3736
+ period: response.period ?? '',
3737
+ periodStart: response.period_start ?? '',
3738
+ periodEnd: response.period_end ?? '',
3679
3739
  };
3680
3740
  }
3681
3741
  /**
@@ -3691,21 +3751,22 @@ export class AxonFlow {
3691
3751
  if (period)
3692
3752
  params.set('period', period);
3693
3753
  const response = await this.orchestratorRequest('GET', `/api/v1/usage/breakdown?${params.toString()}`);
3694
- const items = (response.items || []).map((i) => ({
3695
- groupValue: i.group_value || '',
3696
- costUsd: i.cost_usd || 0,
3697
- percentage: i.percentage || 0,
3698
- requestCount: i.request_count || 0,
3699
- tokensIn: i.tokens_in || 0,
3700
- tokensOut: i.tokens_out || 0,
3754
+ const items = (response.items ?? []).map((i) => ({
3755
+ groupBy: i.group_by,
3756
+ groupValue: i.group_value ?? '',
3757
+ costUsd: i.cost_usd ?? 0,
3758
+ percentage: i.percentage ?? 0,
3759
+ requestCount: i.request_count ?? 0,
3760
+ tokensIn: i.tokens_in ?? 0,
3761
+ tokensOut: i.tokens_out ?? 0,
3701
3762
  }));
3702
3763
  return {
3703
- groupBy: response.group_by || '',
3704
- totalCostUsd: response.total_cost_usd || 0,
3764
+ groupBy: response.group_by ?? '',
3765
+ totalCostUsd: response.total_cost_usd ?? 0,
3705
3766
  items,
3706
- period: response.period || '',
3707
- periodStart: response.period_start || '',
3708
- periodEnd: response.period_end || '',
3767
+ period: response.period ?? '',
3768
+ periodStart: response.period_start ?? '',
3769
+ periodEnd: response.period_end ?? '',
3709
3770
  };
3710
3771
  }
3711
3772
  /**
@@ -3727,21 +3788,33 @@ export class AxonFlow {
3727
3788
  const queryString = params.toString();
3728
3789
  const path = `/api/v1/usage/records${queryString ? `?${queryString}` : ''}`;
3729
3790
  const response = await this.orchestratorRequest('GET', path);
3730
- const records = (response.records || []).map((r) => ({
3731
- id: r.id || '',
3732
- provider: r.provider || '',
3733
- model: r.model || '',
3734
- tokensIn: r.tokens_in || 0,
3735
- tokensOut: r.tokens_out || 0,
3736
- costUsd: r.cost_usd || 0,
3791
+ const records = (response.records ?? []).map((r) => ({
3792
+ id: r.id ?? '',
3793
+ provider: r.provider ?? '',
3794
+ model: r.model ?? '',
3795
+ tokensIn: r.tokens_in ?? 0,
3796
+ tokensOut: r.tokens_out ?? 0,
3797
+ costUsd: r.cost_usd ?? 0,
3737
3798
  requestId: r.request_id,
3738
3799
  orgId: r.org_id,
3739
3800
  agentId: r.agent_id,
3801
+ // Wire-canonical fields surfaced in the v6 alignment sweep.
3802
+ // The `timestamp` legacy field below has always read undefined
3803
+ // because the server emits `created_at`; both kept populated
3804
+ // so existing readers of `timestamp` see no behavior change.
3805
+ created_at: r.created_at,
3806
+ success: r.success,
3807
+ error_message: r.error_message,
3808
+ latency_ms: r.latency_ms,
3809
+ team_id: r.team_id,
3810
+ tenant_id: r.tenant_id,
3811
+ user_id: r.user_id,
3812
+ workflow_id: r.workflow_id,
3740
3813
  timestamp: r.timestamp,
3741
3814
  }));
3742
3815
  return {
3743
3816
  records,
3744
- total: response.total || 0,
3817
+ total: response.total ?? 0,
3745
3818
  };
3746
3819
  }
3747
3820
  // ========================================
@@ -3769,7 +3842,7 @@ export class AxonFlow {
3769
3842
  const pricing = this.mapPricingResponse(response);
3770
3843
  return { pricing: [pricing] };
3771
3844
  }
3772
- const pricingList = (response.pricing || []).map(p => this.mapPricingResponse(p));
3845
+ const pricingList = (response.pricing ?? []).map(p => this.mapPricingResponse(p));
3773
3846
  return { pricing: pricingList };
3774
3847
  }
3775
3848
  // ========================================
@@ -3777,15 +3850,17 @@ export class AxonFlow {
3777
3850
  // ========================================
3778
3851
  mapBudgetResponse(response) {
3779
3852
  return {
3780
- id: response.id || '',
3781
- name: response.name || '',
3782
- scope: response.scope || '',
3783
- limitUsd: response.limit_usd || 0,
3784
- period: response.period || '',
3785
- onExceed: response.on_exceed || '',
3786
- alertThresholds: response.alert_thresholds || [],
3853
+ id: response.id ?? '',
3854
+ name: response.name ?? '',
3855
+ scope: response.scope ?? '',
3856
+ limitUsd: response.limit_usd ?? 0,
3857
+ period: response.period ?? '',
3858
+ onExceed: response.on_exceed ?? '',
3859
+ alertThresholds: response.alert_thresholds ?? [],
3787
3860
  enabled: response.enabled ?? true,
3788
3861
  scopeId: response.scope_id,
3862
+ tenant_id: response.tenant_id,
3863
+ org_id: response.org_id,
3789
3864
  createdAt: response.created_at,
3790
3865
  updatedAt: response.updated_at,
3791
3866
  };
@@ -3793,11 +3868,11 @@ export class AxonFlow {
3793
3868
  mapPricingResponse(response) {
3794
3869
  const pricingData = response.pricing;
3795
3870
  return {
3796
- provider: response.provider || '',
3797
- model: response.model || '',
3871
+ provider: response.provider ?? '',
3872
+ model: response.model ?? '',
3798
3873
  pricing: {
3799
- inputPer1k: pricingData?.input_per_1k || 0,
3800
- outputPer1k: pricingData?.output_per_1k || 0,
3874
+ inputPer1k: pricingData?.input_per_1k ?? 0,
3875
+ outputPer1k: pricingData?.output_per_1k ?? 0,
3801
3876
  },
3802
3877
  };
3803
3878
  }
@@ -3994,7 +4069,7 @@ export class AxonFlow {
3994
4069
  throw new ConfigurationError('Step ID is required');
3995
4070
  }
3996
4071
  try {
3997
- await this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/complete`, request || {});
4072
+ await this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/complete`, request ?? {});
3998
4073
  }
3999
4074
  catch (err) {
4000
4075
  const idem = mapIdempotencyKeyMismatch(err);
@@ -4280,7 +4355,7 @@ export class AxonFlow {
4280
4355
  debugLog('Plan rolled back', { planId, version: data.version });
4281
4356
  }
4282
4357
  return {
4283
- planId: data.plan_id || planId,
4358
+ planId: data.plan_id ?? planId,
4284
4359
  version: data.version,
4285
4360
  previousVersion: data.previous_version,
4286
4361
  status: data.status,
@@ -4546,7 +4621,7 @@ export class AxonFlow {
4546
4621
  throw new APIError(response.status, response.statusText, errorText);
4547
4622
  }
4548
4623
  const data = await response.json();
4549
- return (data || []).map((s) => this.mapSystemResponse(s));
4624
+ return (data ?? []).map((s) => this.mapSystemResponse(s));
4550
4625
  }
4551
4626
  async masfeatActivateSystem(systemId) {
4552
4627
  // Use PUT to update status - the /activate endpoint doesn't exist
@@ -4601,8 +4676,8 @@ export class AxonFlow {
4601
4676
  highMaterialityCount: data.high_materiality_count ?? data.high_materiality ?? 0,
4602
4677
  mediumMaterialityCount: data.medium_materiality_count ?? data.medium_materiality ?? 0,
4603
4678
  lowMaterialityCount: data.low_materiality_count ?? data.low_materiality ?? 0,
4604
- byUseCase: data.by_use_case || {},
4605
- byStatus: data.by_status || {},
4679
+ byUseCase: data.by_use_case ?? {},
4680
+ byStatus: data.by_status ?? {},
4606
4681
  };
4607
4682
  }
4608
4683
  // Assessment Methods
@@ -4610,7 +4685,7 @@ export class AxonFlow {
4610
4685
  const url = `${this.config.endpoint}/api/v1/masfeat/assessments`;
4611
4686
  const body = {
4612
4687
  system_id: request.systemId,
4613
- assessment_type: request.assessmentType || 'periodic',
4688
+ assessment_type: request.assessmentType ?? 'periodic',
4614
4689
  assessors: request.assessors,
4615
4690
  };
4616
4691
  if (request.assessmentDate)
@@ -4749,7 +4824,7 @@ export class AxonFlow {
4749
4824
  throw new APIError(response.status, response.statusText, errorText);
4750
4825
  }
4751
4826
  const data = await response.json();
4752
- return (data || []).map((a) => this.mapAssessmentResponse(a));
4827
+ return (data ?? []).map((a) => this.mapAssessmentResponse(a));
4753
4828
  }
4754
4829
  async masfeatSubmitAssessment(assessmentId) {
4755
4830
  const url = `${this.config.endpoint}/api/v1/masfeat/assessments/${assessmentId}/submit`;
@@ -4965,20 +5040,20 @@ export class AxonFlow {
4965
5040
  if (data && typeof data === 'object' && 'history' in data) {
4966
5041
  data = data.history;
4967
5042
  }
4968
- return (data || []).map((e) => ({
5043
+ return (data ?? []).map((e) => ({
4969
5044
  id: e.id,
4970
5045
  killSwitchId: e.kill_switch_id,
4971
5046
  // Handle both API formats: event_type (SDK expected) vs action (API actual)
4972
- eventType: e.event_type || e.action,
5047
+ eventType: e.event_type ?? e.action,
4973
5048
  // Build eventData from additional fields if not present
4974
- eventData: e.event_data ||
4975
- (e.previous_status || e.new_status || e.reason
5049
+ eventData: e.event_data ??
5050
+ ((e.previous_status ?? e.new_status ?? e.reason)
4976
5051
  ? { previousStatus: e.previous_status, newStatus: e.new_status, reason: e.reason }
4977
5052
  : undefined),
4978
5053
  // Handle both API formats: created_by vs performed_by
4979
- createdBy: e.created_by || e.performed_by,
5054
+ createdBy: e.created_by ?? e.performed_by,
4980
5055
  // Handle both API formats: created_at vs performed_at
4981
- createdAt: new Date(e.created_at || e.performed_at),
5056
+ createdAt: new Date(e.created_at ?? e.performed_at),
4982
5057
  }));
4983
5058
  }
4984
5059
  // Helper methods for MAS FEAT
@@ -4992,7 +5067,7 @@ export class AxonFlow {
4992
5067
  useCase: data.use_case,
4993
5068
  ownerTeam: data.owner_team,
4994
5069
  technicalOwner: data.technical_owner,
4995
- businessOwner: data.business_owner || data.owner_email,
5070
+ businessOwner: data.business_owner ?? data.owner_email,
4996
5071
  customerImpact: data.customer_impact ?? data.risk_rating_impact,
4997
5072
  modelComplexity: data.model_complexity ?? data.risk_rating_complexity,
4998
5073
  humanReliance: data.human_reliance ?? data.risk_rating_reliance,
@@ -5013,6 +5088,7 @@ export class AxonFlow {
5013
5088
  description: data.description,
5014
5089
  status: data.status,
5015
5090
  remediation: data.remediation,
5091
+ article: data.article,
5016
5092
  dueDate: data.due_date ? new Date(data.due_date) : undefined,
5017
5093
  };
5018
5094
  }
@@ -5060,7 +5136,7 @@ export class AxonFlow {
5060
5136
  autoTriggerEnabled: data.auto_trigger_enabled,
5061
5137
  triggeredAt: data.triggered_at ? new Date(data.triggered_at) : undefined,
5062
5138
  triggeredBy: data.triggered_by,
5063
- triggeredReason: data.triggered_reason || data.trigger_reason,
5139
+ triggeredReason: data.triggered_reason ?? data.trigger_reason,
5064
5140
  restoredAt: data.restored_at ? new Date(data.restored_at) : undefined,
5065
5141
  restoredBy: data.restored_by,
5066
5142
  createdAt: new Date(data.created_at),
@@ -5233,7 +5309,7 @@ export class AxonFlow {
5233
5309
  }
5234
5310
  const response = await this.orchestratorRequest('GET', path);
5235
5311
  return {
5236
- items: response.data || [],
5312
+ items: response.data ?? [],
5237
5313
  total: response.meta?.total ?? 0,
5238
5314
  has_more: (response.meta?.offset ?? 0) + (response.data?.length ?? 0) < (response.meta?.total ?? 0),
5239
5315
  };
@@ -5538,7 +5614,7 @@ export class AxonFlow {
5538
5614
  // Process complete SSE events (separated by double newline)
5539
5615
  const events = buffer.split('\n\n');
5540
5616
  // Keep the last (potentially incomplete) chunk in the buffer
5541
- buffer = events.pop() || '';
5617
+ buffer = events.pop() ?? '';
5542
5618
  for (const event of events) {
5543
5619
  const trimmed = event.trim();
5544
5620
  if (!trimmed) {