@axonflow/sdk 5.5.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.
- package/dist/cjs/adapters/governed-tool.js +4 -4
- package/dist/cjs/adapters/langgraph.js +9 -9
- package/dist/cjs/client.d.ts +43 -8
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +304 -176
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/errors.js +1 -1
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/telemetry.d.ts.map +1 -1
- package/dist/cjs/telemetry.js +57 -8
- package/dist/cjs/telemetry.js.map +1 -1
- package/dist/cjs/types/connector.d.ts +33 -5
- package/dist/cjs/types/connector.d.ts.map +1 -1
- package/dist/cjs/types/connector.js.map +1 -1
- package/dist/cjs/types/cost-controls.d.ts +29 -0
- package/dist/cjs/types/cost-controls.d.ts.map +1 -1
- package/dist/cjs/types/execution-replay.d.ts +2 -0
- package/dist/cjs/types/execution-replay.d.ts.map +1 -1
- package/dist/cjs/types/masfeat.d.ts +2 -0
- package/dist/cjs/types/masfeat.d.ts.map +1 -1
- package/dist/cjs/types/planning.d.ts +78 -8
- package/dist/cjs/types/planning.d.ts.map +1 -1
- package/dist/cjs/types/policies.d.ts +92 -14
- package/dist/cjs/types/policies.d.ts.map +1 -1
- package/dist/cjs/types/policy.d.ts +58 -4
- package/dist/cjs/types/policy.d.ts.map +1 -1
- package/dist/cjs/types/proxy.d.ts +22 -4
- package/dist/cjs/types/proxy.d.ts.map +1 -1
- package/dist/cjs/types/workflows.d.ts +142 -17
- package/dist/cjs/types/workflows.d.ts.map +1 -1
- package/dist/cjs/types/workflows.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/adapters/governed-tool.js +4 -4
- package/dist/esm/adapters/langgraph.js +9 -9
- package/dist/esm/client.d.ts +43 -8
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +304 -176
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/errors.js +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/telemetry.d.ts.map +1 -1
- package/dist/esm/telemetry.js +57 -8
- package/dist/esm/telemetry.js.map +1 -1
- package/dist/esm/types/connector.d.ts +33 -5
- package/dist/esm/types/connector.d.ts.map +1 -1
- package/dist/esm/types/connector.js.map +1 -1
- package/dist/esm/types/cost-controls.d.ts +29 -0
- package/dist/esm/types/cost-controls.d.ts.map +1 -1
- package/dist/esm/types/execution-replay.d.ts +2 -0
- package/dist/esm/types/execution-replay.d.ts.map +1 -1
- package/dist/esm/types/masfeat.d.ts +2 -0
- package/dist/esm/types/masfeat.d.ts.map +1 -1
- package/dist/esm/types/planning.d.ts +78 -8
- package/dist/esm/types/planning.d.ts.map +1 -1
- package/dist/esm/types/policies.d.ts +92 -14
- package/dist/esm/types/policies.d.ts.map +1 -1
- package/dist/esm/types/policy.d.ts +58 -4
- package/dist/esm/types/policy.d.ts.map +1 -1
- package/dist/esm/types/proxy.d.ts +22 -4
- package/dist/esm/types/proxy.d.ts.map +1 -1
- package/dist/esm/types/workflows.d.ts +142 -17
- package/dist/esm/types/workflows.d.ts.map +1 -1
- package/dist/esm/types/workflows.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +3 -2
package/dist/cjs/client.js
CHANGED
|
@@ -33,12 +33,12 @@ function mapIdempotencyKeyMismatch(err) {
|
|
|
33
33
|
* Returns -1 if a < b, 0 if equal, 1 if a > b.
|
|
34
34
|
*/
|
|
35
35
|
function compareSemver(a, b) {
|
|
36
|
-
const parseVersion = (v) => v.split('.').map(p => parseInt(p.split('-')[0], 10)
|
|
36
|
+
const parseVersion = (v) => v.split('.').map(p => parseInt(p.split('-')[0], 10) ?? 0);
|
|
37
37
|
const aParts = parseVersion(a);
|
|
38
38
|
const bParts = parseVersion(b);
|
|
39
39
|
const len = Math.max(aParts.length, bParts.length);
|
|
40
40
|
for (let i = 0; i < len; i++) {
|
|
41
|
-
const diff = (aParts[i]
|
|
41
|
+
const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);
|
|
42
42
|
if (diff !== 0)
|
|
43
43
|
return diff < 0 ? -1 : 1;
|
|
44
44
|
}
|
|
@@ -67,7 +67,7 @@ class AxonFlow {
|
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
69
|
// Set defaults first to determine endpoint
|
|
70
|
-
const endpoint = config.endpoint
|
|
70
|
+
const endpoint = config.endpoint ?? 'https://staging-eu.getaxonflow.com';
|
|
71
71
|
// Credentials check: OAuth2-style (clientId/clientSecret)
|
|
72
72
|
const hasCredentials = !!(config.clientId && config.clientSecret);
|
|
73
73
|
// Set configuration
|
|
@@ -75,19 +75,19 @@ class AxonFlow {
|
|
|
75
75
|
clientId: config.clientId,
|
|
76
76
|
clientSecret: config.clientSecret,
|
|
77
77
|
endpoint,
|
|
78
|
-
mode: config.mode
|
|
79
|
-
tenant: config.tenant
|
|
80
|
-
debug: config.debug
|
|
81
|
-
timeout: config.timeout
|
|
82
|
-
mapTimeout: config.mapTimeout
|
|
78
|
+
mode: config.mode ?? 'production',
|
|
79
|
+
tenant: config.tenant ?? '',
|
|
80
|
+
debug: config.debug ?? false,
|
|
81
|
+
timeout: config.timeout ?? 30000,
|
|
82
|
+
mapTimeout: config.mapTimeout ?? 120000, // 2 minutes for MAP operations
|
|
83
83
|
retry: {
|
|
84
84
|
enabled: config.retry?.enabled !== false,
|
|
85
|
-
maxAttempts: config.retry?.maxAttempts
|
|
86
|
-
delay: config.retry?.delay
|
|
85
|
+
maxAttempts: config.retry?.maxAttempts ?? 3,
|
|
86
|
+
delay: config.retry?.delay ?? 1000,
|
|
87
87
|
},
|
|
88
88
|
cache: {
|
|
89
89
|
enabled: config.cache?.enabled !== false,
|
|
90
|
-
ttl: config.cache?.ttl
|
|
90
|
+
ttl: config.cache?.ttl ?? 60000,
|
|
91
91
|
},
|
|
92
92
|
};
|
|
93
93
|
// Interceptors removed in v3.0.0 (deprecated wrapOpenAIClient/wrapAnthropicClient)
|
|
@@ -124,7 +124,7 @@ class AxonFlow {
|
|
|
124
124
|
// clientSecret defaults to empty string for community/no-secret mode.
|
|
125
125
|
const effectiveClientId = this.getEffectiveClientId();
|
|
126
126
|
if (effectiveClientId) {
|
|
127
|
-
const credentials = Buffer.from(`${effectiveClientId}:${this.config.clientSecret
|
|
127
|
+
const credentials = Buffer.from(`${effectiveClientId}:${this.config.clientSecret ?? ''}`).toString('base64');
|
|
128
128
|
headers['Authorization'] = `Basic ${credentials}`;
|
|
129
129
|
}
|
|
130
130
|
// Include SDK version for version discovery and compatibility checks
|
|
@@ -141,6 +141,9 @@ class AxonFlow {
|
|
|
141
141
|
* @returns The clientId to use in requests
|
|
142
142
|
*/
|
|
143
143
|
getEffectiveClientId() {
|
|
144
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
145
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
144
147
|
return this.config.clientId || this.config.tenant || 'community';
|
|
145
148
|
}
|
|
146
149
|
/**
|
|
@@ -212,7 +215,7 @@ class AxonFlow {
|
|
|
212
215
|
// If denied, throw error
|
|
213
216
|
if (!governanceResponse.allowed) {
|
|
214
217
|
const violation = governanceResponse.violations?.[0];
|
|
215
|
-
throw new Error(`Request blocked by AxonFlow: ${violation?.description
|
|
218
|
+
throw new Error(`Request blocked by AxonFlow: ${violation?.description ?? 'Policy violation'}`);
|
|
216
219
|
}
|
|
217
220
|
// Execute the AI call (possibly with modifications)
|
|
218
221
|
const modifiedCall = governanceResponse.modifiedRequest
|
|
@@ -262,6 +265,9 @@ class AxonFlow {
|
|
|
262
265
|
const agentRequest = {
|
|
263
266
|
query: request.aiRequest.prompt,
|
|
264
267
|
user_token: '',
|
|
268
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
269
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
270
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
265
271
|
client_id: this.config.clientId || this.config.tenant,
|
|
266
272
|
request_type: 'llm_chat',
|
|
267
273
|
context: {
|
|
@@ -289,7 +295,7 @@ class AxonFlow {
|
|
|
289
295
|
const agentResponse = await response.json();
|
|
290
296
|
// Transform Agent API response to SDK format
|
|
291
297
|
// Extract policy name from policy_info if available
|
|
292
|
-
const policyName = agentResponse.policy_info?.policies_evaluated?.[0]
|
|
298
|
+
const policyName = agentResponse.policy_info?.policies_evaluated?.[0] ?? 'agent-policy';
|
|
293
299
|
return {
|
|
294
300
|
requestId: request.requestId,
|
|
295
301
|
allowed: !agentResponse.blocked,
|
|
@@ -298,17 +304,17 @@ class AxonFlow {
|
|
|
298
304
|
{
|
|
299
305
|
type: 'security',
|
|
300
306
|
severity: 'high',
|
|
301
|
-
description: agentResponse.block_reason
|
|
307
|
+
description: agentResponse.block_reason ?? 'Request blocked by policy',
|
|
302
308
|
policy: policyName,
|
|
303
309
|
action: 'blocked',
|
|
304
310
|
},
|
|
305
311
|
]
|
|
306
312
|
: [],
|
|
307
313
|
modifiedRequest: agentResponse.data,
|
|
308
|
-
policies: agentResponse.policy_info?.policies_evaluated
|
|
314
|
+
policies: agentResponse.policy_info?.policies_evaluated ?? [],
|
|
309
315
|
audit: {
|
|
310
316
|
timestamp: Date.now(),
|
|
311
|
-
duration: parseInt(agentResponse.policy_info?.processing_time?.replace('ms', '')
|
|
317
|
+
duration: parseInt(agentResponse.policy_info?.processing_time?.replace('ms', '') ?? '0'),
|
|
312
318
|
tenant: this.config.tenant,
|
|
313
319
|
},
|
|
314
320
|
};
|
|
@@ -322,7 +328,7 @@ class AxonFlow {
|
|
|
322
328
|
if (this.config.debug) {
|
|
323
329
|
(0, helpers_1.debugLog)('Request processed', {
|
|
324
330
|
allowed: response.allowed,
|
|
325
|
-
violations: response.violations?.length
|
|
331
|
+
violations: response.violations?.length ?? 0,
|
|
326
332
|
duration: response.audit.duration,
|
|
327
333
|
});
|
|
328
334
|
}
|
|
@@ -331,9 +337,10 @@ class AxonFlow {
|
|
|
331
337
|
* Check if an error is from AxonFlow (vs the AI provider)
|
|
332
338
|
*/
|
|
333
339
|
isAxonFlowError(error) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
340
|
+
const msg = error?.message;
|
|
341
|
+
if (typeof msg !== 'string')
|
|
342
|
+
return false;
|
|
343
|
+
return msg.includes('AxonFlow') || msg.includes('governance') || msg.includes('fetch');
|
|
337
344
|
}
|
|
338
345
|
/**
|
|
339
346
|
* Create a sandbox client for testing
|
|
@@ -529,13 +536,16 @@ class AxonFlow {
|
|
|
529
536
|
*/
|
|
530
537
|
async proxyLLMCall(options) {
|
|
531
538
|
// Default to "anonymous" if userToken is empty/undefined (community mode)
|
|
532
|
-
const effectiveUserToken = options.userToken
|
|
539
|
+
const effectiveUserToken = options.userToken ?? 'anonymous';
|
|
533
540
|
const agentRequest = {
|
|
534
541
|
query: options.query,
|
|
535
542
|
user_token: effectiveUserToken,
|
|
543
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
544
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
545
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
536
546
|
client_id: this.config.clientId || this.config.tenant,
|
|
537
547
|
request_type: options.requestType,
|
|
538
|
-
context: options.context
|
|
548
|
+
context: options.context ?? {},
|
|
539
549
|
};
|
|
540
550
|
if (options.media && options.media.length > 0) {
|
|
541
551
|
agentRequest.media = options.media.map(m => ({
|
|
@@ -588,7 +598,7 @@ class AxonFlow {
|
|
|
588
598
|
try {
|
|
589
599
|
const errorJson = JSON.parse(errorText);
|
|
590
600
|
if (errorJson.blocked || errorJson.block_reason) {
|
|
591
|
-
throw new errors_1.PolicyViolationError(errorJson.block_reason
|
|
601
|
+
throw new errors_1.PolicyViolationError(errorJson.block_reason ?? 'Request blocked by policy', errorJson.policy_info?.policies_evaluated);
|
|
592
602
|
}
|
|
593
603
|
}
|
|
594
604
|
catch (e) {
|
|
@@ -602,13 +612,11 @@ class AxonFlow {
|
|
|
602
612
|
}
|
|
603
613
|
}
|
|
604
614
|
// Parse response if not already parsed (from 402 handling)
|
|
605
|
-
|
|
606
|
-
data = await response.json();
|
|
607
|
-
}
|
|
615
|
+
data ?? (data = await response.json());
|
|
608
616
|
// Check for policy violation in successful response (some blocked responses return 200)
|
|
609
617
|
// Note: Don't throw for budget blocks (402 responses) - return with budgetInfo instead
|
|
610
618
|
if (data.blocked && !data.budget_info) {
|
|
611
|
-
throw new errors_1.PolicyViolationError(data.block_reason
|
|
619
|
+
throw new errors_1.PolicyViolationError(data.block_reason ?? 'Request blocked by policy', data.policy_info?.policies_evaluated);
|
|
612
620
|
}
|
|
613
621
|
// Transform snake_case response to camelCase
|
|
614
622
|
const result = {
|
|
@@ -617,18 +625,18 @@ class AxonFlow {
|
|
|
617
625
|
result: data.result,
|
|
618
626
|
planId: data.plan_id,
|
|
619
627
|
requestId: data.request_id,
|
|
620
|
-
metadata: data.metadata
|
|
628
|
+
metadata: data.metadata ?? {},
|
|
621
629
|
error: data.error,
|
|
622
|
-
blocked: data.blocked
|
|
630
|
+
blocked: data.blocked ?? false,
|
|
623
631
|
blockReason: data.block_reason,
|
|
624
632
|
};
|
|
625
633
|
// Parse policy info if present
|
|
626
634
|
if (data.policy_info) {
|
|
627
635
|
result.policyInfo = {
|
|
628
|
-
policiesEvaluated: data.policy_info.policies_evaluated
|
|
629
|
-
staticChecks: data.policy_info.static_checks
|
|
630
|
-
processingTime: data.policy_info.processing_time
|
|
631
|
-
tenantId: data.policy_info.tenant_id
|
|
636
|
+
policiesEvaluated: data.policy_info.policies_evaluated ?? [],
|
|
637
|
+
staticChecks: data.policy_info.static_checks ?? [],
|
|
638
|
+
processingTime: data.policy_info.processing_time ?? '',
|
|
639
|
+
tenantId: data.policy_info.tenant_id ?? '',
|
|
632
640
|
codeArtifact: data.policy_info.code_artifact,
|
|
633
641
|
};
|
|
634
642
|
}
|
|
@@ -637,10 +645,10 @@ class AxonFlow {
|
|
|
637
645
|
result.budgetInfo = {
|
|
638
646
|
budgetId: data.budget_info.budget_id,
|
|
639
647
|
budgetName: data.budget_info.budget_name,
|
|
640
|
-
usedUsd: data.budget_info.used_usd
|
|
641
|
-
limitUsd: data.budget_info.limit_usd
|
|
642
|
-
percentage: data.budget_info.percentage
|
|
643
|
-
exceeded: data.budget_info.exceeded
|
|
648
|
+
usedUsd: data.budget_info.used_usd ?? 0,
|
|
649
|
+
limitUsd: data.budget_info.limit_usd ?? 0,
|
|
650
|
+
percentage: data.budget_info.percentage ?? 0,
|
|
651
|
+
exceeded: data.budget_info.exceeded ?? false,
|
|
644
652
|
action: data.budget_info.action,
|
|
645
653
|
};
|
|
646
654
|
}
|
|
@@ -684,7 +692,7 @@ class AxonFlow {
|
|
|
684
692
|
async listConnectors() {
|
|
685
693
|
const response = await this.orchestratorRequest('GET', '/api/v1/connectors');
|
|
686
694
|
// Handle wrapped response
|
|
687
|
-
const connectors = Array.isArray(response) ? response : response.connectors
|
|
695
|
+
const connectors = Array.isArray(response) ? response : (response.connectors ?? []);
|
|
688
696
|
if (this.config.debug) {
|
|
689
697
|
(0, helpers_1.debugLog)('Listed connectors', { count: connectors.length });
|
|
690
698
|
}
|
|
@@ -737,11 +745,14 @@ class AxonFlow {
|
|
|
737
745
|
const agentRequest = {
|
|
738
746
|
query,
|
|
739
747
|
user_token: '',
|
|
748
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
749
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
750
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
740
751
|
client_id: this.config.clientId || this.config.tenant,
|
|
741
752
|
request_type: 'mcp-query',
|
|
742
753
|
context: {
|
|
743
754
|
connector: connectorName,
|
|
744
|
-
params: params
|
|
755
|
+
params: params ?? {},
|
|
745
756
|
},
|
|
746
757
|
};
|
|
747
758
|
const url = `${this.config.endpoint}/api/request`;
|
|
@@ -810,7 +821,7 @@ class AxonFlow {
|
|
|
810
821
|
const body = {
|
|
811
822
|
connector: options.connector,
|
|
812
823
|
statement: options.statement,
|
|
813
|
-
options: options.options
|
|
824
|
+
options: options.options ?? {},
|
|
814
825
|
};
|
|
815
826
|
if (this.config.debug) {
|
|
816
827
|
(0, helpers_1.debugLog)('MCP Query', {
|
|
@@ -827,7 +838,7 @@ class AxonFlow {
|
|
|
827
838
|
const responseData = await response.json();
|
|
828
839
|
// Handle policy blocks (403 responses)
|
|
829
840
|
if (!response.ok) {
|
|
830
|
-
throw new errors_1.ConnectorError(responseData.error
|
|
841
|
+
throw new errors_1.ConnectorError(responseData.error ?? `MCP query failed: ${response.status} ${response.statusText}`, options.connector, 'mcpQuery');
|
|
831
842
|
}
|
|
832
843
|
if (this.config.debug) {
|
|
833
844
|
(0, helpers_1.debugLog)('MCP Query result', {
|
|
@@ -893,7 +904,7 @@ class AxonFlow {
|
|
|
893
904
|
if (options.parameters) {
|
|
894
905
|
body.parameters = options.parameters;
|
|
895
906
|
}
|
|
896
|
-
body.operation = options.operation
|
|
907
|
+
body.operation = options.operation ?? 'execute';
|
|
897
908
|
if (this.config.debug) {
|
|
898
909
|
(0, helpers_1.debugLog)('MCP Check Input', {
|
|
899
910
|
connectorType: options.connectorType,
|
|
@@ -909,7 +920,7 @@ class AxonFlow {
|
|
|
909
920
|
const responseData = await response.json();
|
|
910
921
|
// 403 means policy blocked — this is a valid check response, not an error
|
|
911
922
|
if (!response.ok && response.status !== 403) {
|
|
912
|
-
throw new errors_1.ConnectorError(responseData.error
|
|
923
|
+
throw new errors_1.ConnectorError(responseData.error ?? 'MCP check-input failed', options.connectorType, 'check-input');
|
|
913
924
|
}
|
|
914
925
|
if (this.config.debug) {
|
|
915
926
|
(0, helpers_1.debugLog)('MCP Check Input result', {
|
|
@@ -981,7 +992,7 @@ class AxonFlow {
|
|
|
981
992
|
const responseData = await response.json();
|
|
982
993
|
// 403 means policy blocked — this is a valid check response, not an error
|
|
983
994
|
if (!response.ok && response.status !== 403) {
|
|
984
|
-
throw new errors_1.ConnectorError(responseData.error
|
|
995
|
+
throw new errors_1.ConnectorError(responseData.error ?? 'MCP check-output failed', options.connectorType, 'check-output');
|
|
985
996
|
}
|
|
986
997
|
if (this.config.debug) {
|
|
987
998
|
(0, helpers_1.debugLog)('MCP Check Output result', {
|
|
@@ -1027,7 +1038,13 @@ class AxonFlow {
|
|
|
1027
1038
|
}
|
|
1028
1039
|
const agentRequest = {
|
|
1029
1040
|
query,
|
|
1041
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
1042
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
1043
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
1030
1044
|
user_token: userToken || this.config.clientId || this.config.tenant,
|
|
1045
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
1046
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
1047
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
1031
1048
|
client_id: this.config.clientId || this.config.tenant,
|
|
1032
1049
|
request_type: 'multi-agent-plan',
|
|
1033
1050
|
context,
|
|
@@ -1053,18 +1070,24 @@ class AxonFlow {
|
|
|
1053
1070
|
throw new errors_1.PlanExecutionError(`Plan generation failed: ${agentResponse.error}`, undefined, 'generation');
|
|
1054
1071
|
}
|
|
1055
1072
|
// plan_id can be at top level or inside data
|
|
1056
|
-
const planId = agentResponse.plan_id
|
|
1073
|
+
const planId = agentResponse.plan_id ?? agentResponse.data?.plan_id;
|
|
1057
1074
|
if (this.config.debug) {
|
|
1058
1075
|
(0, helpers_1.debugLog)('Plan generated', { planId });
|
|
1059
1076
|
}
|
|
1060
1077
|
return {
|
|
1061
1078
|
planId,
|
|
1062
|
-
status: agentResponse.data?.status
|
|
1063
|
-
steps: agentResponse.data?.steps
|
|
1064
|
-
domain: agentResponse.data?.domain
|
|
1065
|
-
complexity: agentResponse.data?.complexity
|
|
1066
|
-
parallel: agentResponse.data?.parallel
|
|
1067
|
-
metadata: agentResponse.metadata
|
|
1079
|
+
status: agentResponse.data?.status ?? 'pending',
|
|
1080
|
+
steps: agentResponse.data?.steps ?? [],
|
|
1081
|
+
domain: agentResponse.data?.domain ?? domain ?? 'generic',
|
|
1082
|
+
complexity: agentResponse.data?.complexity ?? 0,
|
|
1083
|
+
parallel: agentResponse.data?.parallel ?? false,
|
|
1084
|
+
metadata: agentResponse.metadata ?? {},
|
|
1085
|
+
success: agentResponse.success,
|
|
1086
|
+
version: agentResponse.version ?? agentResponse.data?.version,
|
|
1087
|
+
result: agentResponse.result ?? agentResponse.data?.result,
|
|
1088
|
+
error: agentResponse.error,
|
|
1089
|
+
workflow_execution_id: agentResponse.workflow_execution_id ?? agentResponse.data?.workflow_execution_id,
|
|
1090
|
+
policy_info: agentResponse.policy_info ?? agentResponse.data?.policy_info,
|
|
1068
1091
|
};
|
|
1069
1092
|
}
|
|
1070
1093
|
/**
|
|
@@ -1075,7 +1098,13 @@ class AxonFlow {
|
|
|
1075
1098
|
async executePlan(planId, userToken) {
|
|
1076
1099
|
const agentRequest = {
|
|
1077
1100
|
query: '',
|
|
1101
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
1102
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
1103
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
1078
1104
|
user_token: userToken || this.config.clientId || this.config.tenant,
|
|
1105
|
+
// Intentional || (not ??): an empty-string clientId/tenant is treated as
|
|
1106
|
+
// "missing" by the SDK contract — see tests/smart-defaults.test.ts.
|
|
1107
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
1079
1108
|
client_id: this.config.clientId || this.config.tenant,
|
|
1080
1109
|
request_type: 'execute-plan',
|
|
1081
1110
|
context: { plan_id: planId },
|
|
@@ -1107,7 +1136,7 @@ class AxonFlow {
|
|
|
1107
1136
|
if (data.error && !error)
|
|
1108
1137
|
error = data.error;
|
|
1109
1138
|
// Throw on nested failure (e.g., cancelled plan execution)
|
|
1110
|
-
throw new errors_1.PlanExecutionError(error
|
|
1139
|
+
throw new errors_1.PlanExecutionError(error ?? 'Plan execution failed', planId, 'execution');
|
|
1111
1140
|
}
|
|
1112
1141
|
if (!result && data?.result)
|
|
1113
1142
|
result = data.result;
|
|
@@ -1189,8 +1218,13 @@ class AxonFlow {
|
|
|
1189
1218
|
(0, helpers_1.debugLog)('Plan cancelled', { planId, status: data.status });
|
|
1190
1219
|
}
|
|
1191
1220
|
return {
|
|
1192
|
-
planId: data.plan_id
|
|
1221
|
+
planId: data.plan_id ?? planId,
|
|
1193
1222
|
status: data.status,
|
|
1223
|
+
// `success` is the canonical wire boolean. The deprecated
|
|
1224
|
+
// `message` slot is also kept populated for back-compat
|
|
1225
|
+
// readers; on a current server it will always be undefined
|
|
1226
|
+
// because the wire never emits it.
|
|
1227
|
+
success: data.success,
|
|
1194
1228
|
message: data.message,
|
|
1195
1229
|
};
|
|
1196
1230
|
}
|
|
@@ -1215,6 +1249,9 @@ class AxonFlow {
|
|
|
1215
1249
|
if (request.domain) {
|
|
1216
1250
|
body.domain = request.domain;
|
|
1217
1251
|
}
|
|
1252
|
+
if (request.metadata !== undefined) {
|
|
1253
|
+
body.metadata = request.metadata;
|
|
1254
|
+
}
|
|
1218
1255
|
const response = await fetch(url, {
|
|
1219
1256
|
method: 'PUT',
|
|
1220
1257
|
headers,
|
|
@@ -1234,7 +1271,7 @@ class AxonFlow {
|
|
|
1234
1271
|
(0, helpers_1.debugLog)('Plan updated', { planId, version: data.version });
|
|
1235
1272
|
}
|
|
1236
1273
|
return {
|
|
1237
|
-
planId: data.plan_id
|
|
1274
|
+
planId: data.plan_id ?? planId,
|
|
1238
1275
|
version: data.version,
|
|
1239
1276
|
status: data.status,
|
|
1240
1277
|
success: data.success ?? true,
|
|
@@ -1259,7 +1296,7 @@ class AxonFlow {
|
|
|
1259
1296
|
throw new errors_1.PlanExecutionError(`Get plan versions failed: ${response.status} ${response.statusText} - ${errorText}`, planId, 'versions');
|
|
1260
1297
|
}
|
|
1261
1298
|
const data = await response.json();
|
|
1262
|
-
const versions = (data.versions
|
|
1299
|
+
const versions = (data.versions ?? []).map((v) => ({
|
|
1263
1300
|
version: v.version,
|
|
1264
1301
|
changedAt: v.changed_at,
|
|
1265
1302
|
changedBy: v.changed_by,
|
|
@@ -1267,7 +1304,7 @@ class AxonFlow {
|
|
|
1267
1304
|
changeSummary: v.change_summary,
|
|
1268
1305
|
}));
|
|
1269
1306
|
return {
|
|
1270
|
-
planId: data.plan_id
|
|
1307
|
+
planId: data.plan_id ?? planId,
|
|
1271
1308
|
versions,
|
|
1272
1309
|
};
|
|
1273
1310
|
}
|
|
@@ -1297,9 +1334,13 @@ class AxonFlow {
|
|
|
1297
1334
|
(0, helpers_1.debugLog)('Plan resumed', { planId, approved: data.approved });
|
|
1298
1335
|
}
|
|
1299
1336
|
return {
|
|
1300
|
-
planId: data.plan_id
|
|
1337
|
+
planId: data.plan_id ?? planId,
|
|
1301
1338
|
status: data.status,
|
|
1339
|
+
// `result` is the canonical wire-aggregated outcome on resume.
|
|
1340
|
+
result: data.result,
|
|
1302
1341
|
approved: data.approved,
|
|
1342
|
+
// `message` kept populated for the back-compat alias; legacy
|
|
1343
|
+
// path read this slot historically.
|
|
1303
1344
|
message: data.message,
|
|
1304
1345
|
};
|
|
1305
1346
|
}
|
|
@@ -1362,8 +1403,8 @@ class AxonFlow {
|
|
|
1362
1403
|
user_token: options.userToken,
|
|
1363
1404
|
client_id: clientId,
|
|
1364
1405
|
query: options.query,
|
|
1365
|
-
data_sources: options.dataSources
|
|
1366
|
-
context: options.context
|
|
1406
|
+
data_sources: options.dataSources ?? [],
|
|
1407
|
+
context: options.context ?? {},
|
|
1367
1408
|
};
|
|
1368
1409
|
const headers = {
|
|
1369
1410
|
'Content-Type': 'application/json',
|
|
@@ -1394,9 +1435,9 @@ class AxonFlow {
|
|
|
1394
1435
|
const result = {
|
|
1395
1436
|
contextId: data.context_id,
|
|
1396
1437
|
approved: data.approved,
|
|
1397
|
-
requiresRedaction: data.requires_redaction
|
|
1398
|
-
approvedData: data.approved_data
|
|
1399
|
-
policies: data.policies
|
|
1438
|
+
requiresRedaction: data.requires_redaction ?? false,
|
|
1439
|
+
approvedData: data.approved_data ?? {},
|
|
1440
|
+
policies: data.policies ?? [],
|
|
1400
1441
|
expiresAt,
|
|
1401
1442
|
blockReason: data.block_reason,
|
|
1402
1443
|
};
|
|
@@ -1455,7 +1496,7 @@ class AxonFlow {
|
|
|
1455
1496
|
total_tokens: options.tokenUsage.totalTokens,
|
|
1456
1497
|
},
|
|
1457
1498
|
latency_ms: options.latencyMs,
|
|
1458
|
-
metadata: options.metadata
|
|
1499
|
+
metadata: options.metadata ?? {},
|
|
1459
1500
|
};
|
|
1460
1501
|
const headers = {
|
|
1461
1502
|
'Content-Type': 'application/json',
|
|
@@ -1574,7 +1615,7 @@ class AxonFlow {
|
|
|
1574
1615
|
const response = await this.orchestratorRequest('GET', '/api/v1/circuit-breaker/status');
|
|
1575
1616
|
const data = response.data;
|
|
1576
1617
|
return {
|
|
1577
|
-
activeCircuits: (data.active_circuits
|
|
1618
|
+
activeCircuits: (data.active_circuits ?? []).map(c => ({
|
|
1578
1619
|
id: c.id,
|
|
1579
1620
|
scope: c.scope,
|
|
1580
1621
|
scopeId: c.scope_id,
|
|
@@ -1617,7 +1658,7 @@ class AxonFlow {
|
|
|
1617
1658
|
const response = await this.orchestratorRequest('GET', path);
|
|
1618
1659
|
const data = response.data;
|
|
1619
1660
|
return {
|
|
1620
|
-
history: (data.history
|
|
1661
|
+
history: (data.history ?? []).map(h => ({
|
|
1621
1662
|
id: h.id,
|
|
1622
1663
|
orgId: h.org_id,
|
|
1623
1664
|
scope: h.scope,
|
|
@@ -1873,10 +1914,10 @@ class AxonFlow {
|
|
|
1873
1914
|
* forward compatibility per ADR-043.
|
|
1874
1915
|
*/
|
|
1875
1916
|
parseDecisionExplanation(raw) {
|
|
1876
|
-
const r = raw
|
|
1877
|
-
const rawMatches = r.policy_matches
|
|
1917
|
+
const r = raw ?? {};
|
|
1918
|
+
const rawMatches = r.policy_matches ?? [];
|
|
1878
1919
|
const policyMatches = rawMatches.map(m => ({
|
|
1879
|
-
policyId: m.policy_id
|
|
1920
|
+
policyId: m.policy_id ?? '',
|
|
1880
1921
|
policyName: m.policy_name,
|
|
1881
1922
|
action: m.action,
|
|
1882
1923
|
riskLevel: m.risk_level,
|
|
@@ -1885,18 +1926,18 @@ class AxonFlow {
|
|
|
1885
1926
|
}));
|
|
1886
1927
|
const rawRules = r.matched_rules;
|
|
1887
1928
|
const matchedRules = rawRules?.map(m => ({
|
|
1888
|
-
policyId: m.policy_id
|
|
1929
|
+
policyId: m.policy_id ?? '',
|
|
1889
1930
|
ruleId: m.rule_id,
|
|
1890
1931
|
ruleText: m.rule_text,
|
|
1891
1932
|
matchedOn: m.matched_on,
|
|
1892
1933
|
}));
|
|
1893
1934
|
return {
|
|
1894
|
-
decisionId: r.decision_id
|
|
1935
|
+
decisionId: r.decision_id ?? '',
|
|
1895
1936
|
timestamp: r.timestamp ? new Date(r.timestamp) : new Date(),
|
|
1896
1937
|
policyMatches,
|
|
1897
1938
|
matchedRules,
|
|
1898
|
-
decision: r.decision
|
|
1899
|
-
reason: r.reason
|
|
1939
|
+
decision: r.decision ?? '',
|
|
1940
|
+
reason: r.reason ?? '',
|
|
1900
1941
|
riskLevel: r.risk_level,
|
|
1901
1942
|
overrideAvailable: r.override_available ?? false,
|
|
1902
1943
|
overrideExistingId: r.override_existing_id,
|
|
@@ -1943,7 +1984,7 @@ class AxonFlow {
|
|
|
1943
1984
|
};
|
|
1944
1985
|
}
|
|
1945
1986
|
const data = response;
|
|
1946
|
-
const entries = (data.entries
|
|
1987
|
+
const entries = (data.entries ?? []).map(e => this.parseAuditLogEntry(e));
|
|
1947
1988
|
return {
|
|
1948
1989
|
entries,
|
|
1949
1990
|
total: data.total ?? entries.length,
|
|
@@ -1997,7 +2038,7 @@ class AxonFlow {
|
|
|
1997
2038
|
};
|
|
1998
2039
|
}
|
|
1999
2040
|
const data = response;
|
|
2000
|
-
const entries = (data.entries
|
|
2041
|
+
const entries = (data.entries ?? []).map(e => this.parseAuditLogEntry(e));
|
|
2001
2042
|
return {
|
|
2002
2043
|
entries,
|
|
2003
2044
|
total: data.total ?? entries.length,
|
|
@@ -2117,7 +2158,7 @@ class AxonFlow {
|
|
|
2117
2158
|
}
|
|
2118
2159
|
// Backend returns { policies: [], pagination: {} }, extract the array
|
|
2119
2160
|
const response = await this.policyRequest('GET', path);
|
|
2120
|
-
return response.policies
|
|
2161
|
+
return response.policies ?? [];
|
|
2121
2162
|
}
|
|
2122
2163
|
/**
|
|
2123
2164
|
* Get a specific static policy by ID.
|
|
@@ -2168,12 +2209,19 @@ class AxonFlow {
|
|
|
2168
2209
|
severity: policy.severity,
|
|
2169
2210
|
enabled: policy.enabled,
|
|
2170
2211
|
action: policy.action,
|
|
2171
|
-
tier: policy.tier
|
|
2212
|
+
tier: policy.tier ?? 'tenant',
|
|
2172
2213
|
};
|
|
2173
2214
|
// Add organization_id for organization tier policies
|
|
2174
2215
|
if (policy.organizationId) {
|
|
2175
2216
|
requestBody.organization_id = policy.organizationId;
|
|
2176
2217
|
}
|
|
2218
|
+
// Wire-canonical fields surfaced in the v6 alignment sweep.
|
|
2219
|
+
if (policy.priority !== undefined) {
|
|
2220
|
+
requestBody.priority = policy.priority;
|
|
2221
|
+
}
|
|
2222
|
+
if (policy.tags !== undefined) {
|
|
2223
|
+
requestBody.tags = policy.tags;
|
|
2224
|
+
}
|
|
2177
2225
|
return this.policyRequest('POST', '/api/v1/static-policies', requestBody);
|
|
2178
2226
|
}
|
|
2179
2227
|
/**
|
|
@@ -2262,7 +2310,7 @@ class AxonFlow {
|
|
|
2262
2310
|
}
|
|
2263
2311
|
// Backend returns { static: [], dynamic: [], ... }, extract the static array
|
|
2264
2312
|
const response = await this.policyRequest('GET', path);
|
|
2265
|
-
return response.static
|
|
2313
|
+
return response.static ?? [];
|
|
2266
2314
|
}
|
|
2267
2315
|
/**
|
|
2268
2316
|
* Test a regex pattern against sample inputs.
|
|
@@ -2307,12 +2355,21 @@ class AxonFlow {
|
|
|
2307
2355
|
(0, helpers_1.debugLog)('Getting static policy versions', { id });
|
|
2308
2356
|
}
|
|
2309
2357
|
const response = await this.policyRequest('GET', `/api/v1/static-policies/${id}/versions`);
|
|
2310
|
-
// Transform snake_case API response to camelCase
|
|
2358
|
+
// Transform snake_case API response to camelCase, populating
|
|
2359
|
+
// both the wire-canonical fields (`id`, `policy_id`,
|
|
2360
|
+
// `change_summary`, `snapshot`) and the legacy aliases (kept for
|
|
2361
|
+
// back-compat; will read undefined on a current server).
|
|
2311
2362
|
return response.versions.map(v => ({
|
|
2363
|
+
id: v.id,
|
|
2364
|
+
policy_id: v.policy_id,
|
|
2312
2365
|
version: v.version,
|
|
2313
2366
|
changedBy: v.changed_by,
|
|
2314
2367
|
changedAt: v.changed_at,
|
|
2315
2368
|
changeType: v.change_type,
|
|
2369
|
+
change_summary: v.change_summary,
|
|
2370
|
+
snapshot: v.snapshot,
|
|
2371
|
+
// @deprecated aliases — only populated if a legacy server
|
|
2372
|
+
// still emits them; current servers don't.
|
|
2316
2373
|
changeDescription: v.change_description,
|
|
2317
2374
|
previousValues: v.previous_values,
|
|
2318
2375
|
newValues: v.new_values,
|
|
@@ -2381,7 +2438,7 @@ class AxonFlow {
|
|
|
2381
2438
|
(0, helpers_1.debugLog)('Listing policy overrides');
|
|
2382
2439
|
}
|
|
2383
2440
|
const response = await this.policyRequest('GET', '/api/v1/static-policies/overrides');
|
|
2384
|
-
return response.overrides
|
|
2441
|
+
return response.overrides ?? [];
|
|
2385
2442
|
}
|
|
2386
2443
|
// ============================================================================
|
|
2387
2444
|
// Dynamic Policy Methods
|
|
@@ -2428,7 +2485,7 @@ class AxonFlow {
|
|
|
2428
2485
|
// API returns {"policies": [...]} wrapper via Agent proxy
|
|
2429
2486
|
const response = await this.orchestratorRequest('GET', path);
|
|
2430
2487
|
// Handle both wrapped and unwrapped responses for compatibility
|
|
2431
|
-
return Array.isArray(response) ? response : response.policies
|
|
2488
|
+
return Array.isArray(response) ? response : (response.policies ?? []);
|
|
2432
2489
|
}
|
|
2433
2490
|
/**
|
|
2434
2491
|
* Get a specific dynamic policy by ID.
|
|
@@ -2483,7 +2540,7 @@ class AxonFlow {
|
|
|
2483
2540
|
requestBody.priority = policy.priority;
|
|
2484
2541
|
if (policy.enabled !== undefined)
|
|
2485
2542
|
requestBody.enabled = policy.enabled;
|
|
2486
|
-
requestBody.tier = policy.tier
|
|
2543
|
+
requestBody.tier = policy.tier ?? 'tenant';
|
|
2487
2544
|
if (policy.organizationId) {
|
|
2488
2545
|
requestBody.organization_id = policy.organizationId;
|
|
2489
2546
|
}
|
|
@@ -2577,7 +2634,7 @@ class AxonFlow {
|
|
|
2577
2634
|
// API returns {"policies": [...]} wrapper via Agent proxy
|
|
2578
2635
|
const response = await this.orchestratorRequest('GET', path);
|
|
2579
2636
|
// Handle both wrapped and unwrapped responses for compatibility
|
|
2580
|
-
return Array.isArray(response) ? response : response.policies
|
|
2637
|
+
return Array.isArray(response) ? response : (response.policies ?? []);
|
|
2581
2638
|
}
|
|
2582
2639
|
// ============================================================================
|
|
2583
2640
|
// Portal Authentication Methods (Enterprise)
|
|
@@ -2906,7 +2963,7 @@ class AxonFlow {
|
|
|
2906
2963
|
const response = await this.portalRequest('GET', path);
|
|
2907
2964
|
// Transform snake_case response to camelCase
|
|
2908
2965
|
return {
|
|
2909
|
-
prs: (response.prs
|
|
2966
|
+
prs: (response.prs ?? []).map(pr => ({
|
|
2910
2967
|
id: pr.id,
|
|
2911
2968
|
prNumber: pr.pr_number,
|
|
2912
2969
|
prUrl: pr.pr_url,
|
|
@@ -3112,7 +3169,7 @@ class AxonFlow {
|
|
|
3112
3169
|
}
|
|
3113
3170
|
const response = await this.portalRequest('GET', path);
|
|
3114
3171
|
return {
|
|
3115
|
-
records: (response.records
|
|
3172
|
+
records: (response.records ?? []).map(r => ({
|
|
3116
3173
|
id: r.id,
|
|
3117
3174
|
prNumber: r.pr_number,
|
|
3118
3175
|
prUrl: r.pr_url,
|
|
@@ -3288,7 +3345,7 @@ class AxonFlow {
|
|
|
3288
3345
|
}
|
|
3289
3346
|
const response = await this.orchestratorRequest('GET', path);
|
|
3290
3347
|
return {
|
|
3291
|
-
executions: (response.executions
|
|
3348
|
+
executions: (response.executions ?? []).map(e => ({
|
|
3292
3349
|
requestId: e.request_id,
|
|
3293
3350
|
workflowName: e.workflow_name,
|
|
3294
3351
|
status: e.status,
|
|
@@ -3372,6 +3429,7 @@ class AxonFlow {
|
|
|
3372
3429
|
approvalRequired: s.approval_required,
|
|
3373
3430
|
approvedBy: s.approved_by,
|
|
3374
3431
|
approvedAt: s.approved_at,
|
|
3432
|
+
retryCount: s.retry_count,
|
|
3375
3433
|
})),
|
|
3376
3434
|
};
|
|
3377
3435
|
}
|
|
@@ -3415,6 +3473,7 @@ class AxonFlow {
|
|
|
3415
3473
|
approvalRequired: s.approval_required,
|
|
3416
3474
|
approvedBy: s.approved_by,
|
|
3417
3475
|
approvedAt: s.approved_at,
|
|
3476
|
+
retryCount: s.retry_count,
|
|
3418
3477
|
}));
|
|
3419
3478
|
}
|
|
3420
3479
|
/**
|
|
@@ -3553,8 +3612,8 @@ class AxonFlow {
|
|
|
3553
3612
|
const path = `/api/v1/budgets${queryString ? `?${queryString}` : ''}`;
|
|
3554
3613
|
const response = await this.orchestratorRequest('GET', path);
|
|
3555
3614
|
return {
|
|
3556
|
-
budgets: (response.budgets
|
|
3557
|
-
total: response.total
|
|
3615
|
+
budgets: (response.budgets ?? []).map(b => this.mapBudgetResponse(b)),
|
|
3616
|
+
total: response.total ?? 0,
|
|
3558
3617
|
};
|
|
3559
3618
|
}
|
|
3560
3619
|
/**
|
|
@@ -3598,13 +3657,13 @@ class AxonFlow {
|
|
|
3598
3657
|
const response = await this.orchestratorRequest('GET', `/api/v1/budgets/${budgetId}/status`);
|
|
3599
3658
|
return {
|
|
3600
3659
|
budget: this.mapBudgetResponse(response.budget),
|
|
3601
|
-
usedUsd: response.used_usd
|
|
3602
|
-
remainingUsd: response.remaining_usd
|
|
3603
|
-
percentage: response.percentage
|
|
3604
|
-
isExceeded: response.is_exceeded
|
|
3605
|
-
isBlocked: response.is_blocked
|
|
3606
|
-
periodStart: response.period_start
|
|
3607
|
-
periodEnd: response.period_end
|
|
3660
|
+
usedUsd: response.used_usd ?? 0,
|
|
3661
|
+
remainingUsd: response.remaining_usd ?? 0,
|
|
3662
|
+
percentage: response.percentage ?? 0,
|
|
3663
|
+
isExceeded: response.is_exceeded ?? false,
|
|
3664
|
+
isBlocked: response.is_blocked ?? false,
|
|
3665
|
+
periodStart: response.period_start ?? '',
|
|
3666
|
+
periodEnd: response.period_end ?? '',
|
|
3608
3667
|
};
|
|
3609
3668
|
}
|
|
3610
3669
|
/**
|
|
@@ -3615,19 +3674,20 @@ class AxonFlow {
|
|
|
3615
3674
|
*/
|
|
3616
3675
|
async getBudgetAlerts(budgetId) {
|
|
3617
3676
|
const response = await this.orchestratorRequest('GET', `/api/v1/budgets/${budgetId}/alerts`);
|
|
3618
|
-
const alerts = (response.alerts
|
|
3619
|
-
id: a.id
|
|
3620
|
-
budgetId: a.budget_id
|
|
3621
|
-
alertType: a.alert_type
|
|
3622
|
-
threshold: a.threshold
|
|
3623
|
-
percentageReached: a.percentage_reached
|
|
3624
|
-
amountUsd: a.amount_usd
|
|
3625
|
-
message: a.message
|
|
3626
|
-
createdAt: a.created_at
|
|
3677
|
+
const alerts = (response.alerts ?? []).map((a) => ({
|
|
3678
|
+
id: a.id ?? '',
|
|
3679
|
+
budgetId: a.budget_id ?? '',
|
|
3680
|
+
alertType: a.alert_type ?? '',
|
|
3681
|
+
threshold: a.threshold ?? 0,
|
|
3682
|
+
percentageReached: a.percentage_reached ?? 0,
|
|
3683
|
+
amountUsd: a.amount_usd ?? 0,
|
|
3684
|
+
message: a.message ?? '',
|
|
3685
|
+
createdAt: a.created_at ?? '',
|
|
3686
|
+
acknowledged: a.acknowledged,
|
|
3627
3687
|
}));
|
|
3628
3688
|
return {
|
|
3629
3689
|
alerts,
|
|
3630
|
-
count: response.count
|
|
3690
|
+
count: response.count ?? 0,
|
|
3631
3691
|
};
|
|
3632
3692
|
}
|
|
3633
3693
|
/**
|
|
@@ -3650,11 +3710,11 @@ class AxonFlow {
|
|
|
3650
3710
|
body.user_id = request.userId;
|
|
3651
3711
|
const response = await this.orchestratorRequest('POST', '/api/v1/budgets/check', body);
|
|
3652
3712
|
return {
|
|
3653
|
-
allowed: response.allowed
|
|
3713
|
+
allowed: response.allowed ?? false,
|
|
3654
3714
|
action: response.action,
|
|
3655
3715
|
message: response.message,
|
|
3656
3716
|
budgets: response.budgets
|
|
3657
|
-
? (response.budgets
|
|
3717
|
+
? (response.budgets ?? []).map(b => this.mapBudgetResponse(b))
|
|
3658
3718
|
: undefined,
|
|
3659
3719
|
};
|
|
3660
3720
|
}
|
|
@@ -3671,14 +3731,14 @@ class AxonFlow {
|
|
|
3671
3731
|
const path = period ? `/api/v1/usage?period=${period}` : '/api/v1/usage';
|
|
3672
3732
|
const response = await this.orchestratorRequest('GET', path);
|
|
3673
3733
|
return {
|
|
3674
|
-
totalCostUsd: response.total_cost_usd
|
|
3675
|
-
totalRequests: response.total_requests
|
|
3676
|
-
totalTokensIn: response.total_tokens_in
|
|
3677
|
-
totalTokensOut: response.total_tokens_out
|
|
3678
|
-
averageCostPerRequest: response.average_cost_per_request
|
|
3679
|
-
period: response.period
|
|
3680
|
-
periodStart: response.period_start
|
|
3681
|
-
periodEnd: response.period_end
|
|
3734
|
+
totalCostUsd: response.total_cost_usd ?? 0,
|
|
3735
|
+
totalRequests: response.total_requests ?? 0,
|
|
3736
|
+
totalTokensIn: response.total_tokens_in ?? 0,
|
|
3737
|
+
totalTokensOut: response.total_tokens_out ?? 0,
|
|
3738
|
+
averageCostPerRequest: response.average_cost_per_request ?? 0,
|
|
3739
|
+
period: response.period ?? '',
|
|
3740
|
+
periodStart: response.period_start ?? '',
|
|
3741
|
+
periodEnd: response.period_end ?? '',
|
|
3682
3742
|
};
|
|
3683
3743
|
}
|
|
3684
3744
|
/**
|
|
@@ -3694,21 +3754,22 @@ class AxonFlow {
|
|
|
3694
3754
|
if (period)
|
|
3695
3755
|
params.set('period', period);
|
|
3696
3756
|
const response = await this.orchestratorRequest('GET', `/api/v1/usage/breakdown?${params.toString()}`);
|
|
3697
|
-
const items = (response.items
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3757
|
+
const items = (response.items ?? []).map((i) => ({
|
|
3758
|
+
groupBy: i.group_by,
|
|
3759
|
+
groupValue: i.group_value ?? '',
|
|
3760
|
+
costUsd: i.cost_usd ?? 0,
|
|
3761
|
+
percentage: i.percentage ?? 0,
|
|
3762
|
+
requestCount: i.request_count ?? 0,
|
|
3763
|
+
tokensIn: i.tokens_in ?? 0,
|
|
3764
|
+
tokensOut: i.tokens_out ?? 0,
|
|
3704
3765
|
}));
|
|
3705
3766
|
return {
|
|
3706
|
-
groupBy: response.group_by
|
|
3707
|
-
totalCostUsd: response.total_cost_usd
|
|
3767
|
+
groupBy: response.group_by ?? '',
|
|
3768
|
+
totalCostUsd: response.total_cost_usd ?? 0,
|
|
3708
3769
|
items,
|
|
3709
|
-
period: response.period
|
|
3710
|
-
periodStart: response.period_start
|
|
3711
|
-
periodEnd: response.period_end
|
|
3770
|
+
period: response.period ?? '',
|
|
3771
|
+
periodStart: response.period_start ?? '',
|
|
3772
|
+
periodEnd: response.period_end ?? '',
|
|
3712
3773
|
};
|
|
3713
3774
|
}
|
|
3714
3775
|
/**
|
|
@@ -3730,21 +3791,33 @@ class AxonFlow {
|
|
|
3730
3791
|
const queryString = params.toString();
|
|
3731
3792
|
const path = `/api/v1/usage/records${queryString ? `?${queryString}` : ''}`;
|
|
3732
3793
|
const response = await this.orchestratorRequest('GET', path);
|
|
3733
|
-
const records = (response.records
|
|
3734
|
-
id: r.id
|
|
3735
|
-
provider: r.provider
|
|
3736
|
-
model: r.model
|
|
3737
|
-
tokensIn: r.tokens_in
|
|
3738
|
-
tokensOut: r.tokens_out
|
|
3739
|
-
costUsd: r.cost_usd
|
|
3794
|
+
const records = (response.records ?? []).map((r) => ({
|
|
3795
|
+
id: r.id ?? '',
|
|
3796
|
+
provider: r.provider ?? '',
|
|
3797
|
+
model: r.model ?? '',
|
|
3798
|
+
tokensIn: r.tokens_in ?? 0,
|
|
3799
|
+
tokensOut: r.tokens_out ?? 0,
|
|
3800
|
+
costUsd: r.cost_usd ?? 0,
|
|
3740
3801
|
requestId: r.request_id,
|
|
3741
3802
|
orgId: r.org_id,
|
|
3742
3803
|
agentId: r.agent_id,
|
|
3804
|
+
// Wire-canonical fields surfaced in the v6 alignment sweep.
|
|
3805
|
+
// The `timestamp` legacy field below has always read undefined
|
|
3806
|
+
// because the server emits `created_at`; both kept populated
|
|
3807
|
+
// so existing readers of `timestamp` see no behavior change.
|
|
3808
|
+
created_at: r.created_at,
|
|
3809
|
+
success: r.success,
|
|
3810
|
+
error_message: r.error_message,
|
|
3811
|
+
latency_ms: r.latency_ms,
|
|
3812
|
+
team_id: r.team_id,
|
|
3813
|
+
tenant_id: r.tenant_id,
|
|
3814
|
+
user_id: r.user_id,
|
|
3815
|
+
workflow_id: r.workflow_id,
|
|
3743
3816
|
timestamp: r.timestamp,
|
|
3744
3817
|
}));
|
|
3745
3818
|
return {
|
|
3746
3819
|
records,
|
|
3747
|
-
total: response.total
|
|
3820
|
+
total: response.total ?? 0,
|
|
3748
3821
|
};
|
|
3749
3822
|
}
|
|
3750
3823
|
// ========================================
|
|
@@ -3772,7 +3845,7 @@ class AxonFlow {
|
|
|
3772
3845
|
const pricing = this.mapPricingResponse(response);
|
|
3773
3846
|
return { pricing: [pricing] };
|
|
3774
3847
|
}
|
|
3775
|
-
const pricingList = (response.pricing
|
|
3848
|
+
const pricingList = (response.pricing ?? []).map(p => this.mapPricingResponse(p));
|
|
3776
3849
|
return { pricing: pricingList };
|
|
3777
3850
|
}
|
|
3778
3851
|
// ========================================
|
|
@@ -3780,15 +3853,17 @@ class AxonFlow {
|
|
|
3780
3853
|
// ========================================
|
|
3781
3854
|
mapBudgetResponse(response) {
|
|
3782
3855
|
return {
|
|
3783
|
-
id: response.id
|
|
3784
|
-
name: response.name
|
|
3785
|
-
scope: response.scope
|
|
3786
|
-
limitUsd: response.limit_usd
|
|
3787
|
-
period: response.period
|
|
3788
|
-
onExceed: response.on_exceed
|
|
3789
|
-
alertThresholds: response.alert_thresholds
|
|
3856
|
+
id: response.id ?? '',
|
|
3857
|
+
name: response.name ?? '',
|
|
3858
|
+
scope: response.scope ?? '',
|
|
3859
|
+
limitUsd: response.limit_usd ?? 0,
|
|
3860
|
+
period: response.period ?? '',
|
|
3861
|
+
onExceed: response.on_exceed ?? '',
|
|
3862
|
+
alertThresholds: response.alert_thresholds ?? [],
|
|
3790
3863
|
enabled: response.enabled ?? true,
|
|
3791
3864
|
scopeId: response.scope_id,
|
|
3865
|
+
tenant_id: response.tenant_id,
|
|
3866
|
+
org_id: response.org_id,
|
|
3792
3867
|
createdAt: response.created_at,
|
|
3793
3868
|
updatedAt: response.updated_at,
|
|
3794
3869
|
};
|
|
@@ -3796,11 +3871,11 @@ class AxonFlow {
|
|
|
3796
3871
|
mapPricingResponse(response) {
|
|
3797
3872
|
const pricingData = response.pricing;
|
|
3798
3873
|
return {
|
|
3799
|
-
provider: response.provider
|
|
3800
|
-
model: response.model
|
|
3874
|
+
provider: response.provider ?? '',
|
|
3875
|
+
model: response.model ?? '',
|
|
3801
3876
|
pricing: {
|
|
3802
|
-
inputPer1k: pricingData?.input_per_1k
|
|
3803
|
-
outputPer1k: pricingData?.output_per_1k
|
|
3877
|
+
inputPer1k: pricingData?.input_per_1k ?? 0,
|
|
3878
|
+
outputPer1k: pricingData?.output_per_1k ?? 0,
|
|
3804
3879
|
},
|
|
3805
3880
|
};
|
|
3806
3881
|
}
|
|
@@ -3997,7 +4072,7 @@ class AxonFlow {
|
|
|
3997
4072
|
throw new errors_1.ConfigurationError('Step ID is required');
|
|
3998
4073
|
}
|
|
3999
4074
|
try {
|
|
4000
|
-
await this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/complete`, request
|
|
4075
|
+
await this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/complete`, request ?? {});
|
|
4001
4076
|
}
|
|
4002
4077
|
catch (err) {
|
|
4003
4078
|
const idem = mapIdempotencyKeyMismatch(err);
|
|
@@ -4116,26 +4191,37 @@ class AxonFlow {
|
|
|
4116
4191
|
/**
|
|
4117
4192
|
* Approve a workflow step that requires human approval.
|
|
4118
4193
|
*
|
|
4119
|
-
*
|
|
4194
|
+
* The server requires `comment` with a minimum of 10 characters — it's the
|
|
4195
|
+
* audit-trail justification that every approval carries into the workflow
|
|
4196
|
+
* history. Callers should always supply a meaningful comment.
|
|
4120
4197
|
*
|
|
4121
4198
|
* @param workflowId - ID of the workflow
|
|
4122
4199
|
* @param stepId - ID of the step to approve
|
|
4200
|
+
* @param comment - Audit justification for the approval (min 10 chars)
|
|
4123
4201
|
* @returns Approval response with status
|
|
4124
4202
|
*
|
|
4125
4203
|
* @example
|
|
4126
4204
|
* ```typescript
|
|
4127
|
-
* const result = await client.approveStep(
|
|
4205
|
+
* const result = await client.approveStep(
|
|
4206
|
+
* 'wf_123',
|
|
4207
|
+
* 'step_456',
|
|
4208
|
+
* 'Approved after full audit review'
|
|
4209
|
+
* );
|
|
4128
4210
|
* console.log(`Step ${result.step_id} status: ${result.status}`);
|
|
4129
4211
|
* ```
|
|
4130
4212
|
*/
|
|
4131
|
-
async approveStep(workflowId, stepId) {
|
|
4213
|
+
async approveStep(workflowId, stepId, comment) {
|
|
4132
4214
|
if (!workflowId) {
|
|
4133
4215
|
throw new errors_1.ConfigurationError('Workflow ID is required');
|
|
4134
4216
|
}
|
|
4135
4217
|
if (!stepId) {
|
|
4136
4218
|
throw new errors_1.ConfigurationError('Step ID is required');
|
|
4137
4219
|
}
|
|
4138
|
-
|
|
4220
|
+
const body = {};
|
|
4221
|
+
if (comment) {
|
|
4222
|
+
body.comment = comment;
|
|
4223
|
+
}
|
|
4224
|
+
return this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/approve`, body);
|
|
4139
4225
|
}
|
|
4140
4226
|
/**
|
|
4141
4227
|
* Reject a workflow step that requires human approval.
|
|
@@ -4164,21 +4250,26 @@ class AxonFlow {
|
|
|
4164
4250
|
if (reason) {
|
|
4165
4251
|
body.reason = reason;
|
|
4166
4252
|
}
|
|
4167
|
-
return this.orchestratorRequest('POST', `/api/v1/
|
|
4253
|
+
return this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/reject`, body);
|
|
4168
4254
|
}
|
|
4169
4255
|
/**
|
|
4170
|
-
* Get pending approvals for workflow steps.
|
|
4256
|
+
* Get pending approvals for workflow steps — the WCP-plane listing.
|
|
4257
|
+
*
|
|
4258
|
+
* Lists steps that are waiting for human approval across all planes for
|
|
4259
|
+
* the caller's tenant. Use {@link getPendingPlanApprovals} for the
|
|
4260
|
+
* MAP-plane listing (scopes to MAP-backed workflows and populates
|
|
4261
|
+
* `plan_id` on every entry).
|
|
4171
4262
|
*
|
|
4172
|
-
*
|
|
4263
|
+
* Available on Evaluation+ licenses.
|
|
4173
4264
|
*
|
|
4174
4265
|
* @param options - Optional filtering options
|
|
4175
|
-
* @returns List of pending approvals with
|
|
4266
|
+
* @returns List of pending approvals with count
|
|
4176
4267
|
*
|
|
4177
4268
|
* @example
|
|
4178
4269
|
* ```typescript
|
|
4179
4270
|
* const pending = await client.getPendingApprovals({ limit: 10 });
|
|
4180
|
-
* console.log(`${pending.
|
|
4181
|
-
* for (const approval of pending.
|
|
4271
|
+
* console.log(`${pending.count} approvals pending`);
|
|
4272
|
+
* for (const approval of pending.pending_approvals) {
|
|
4182
4273
|
* console.log(`${approval.workflow_name} / ${approval.step_name}`);
|
|
4183
4274
|
* }
|
|
4184
4275
|
* ```
|
|
@@ -4190,8 +4281,44 @@ class AxonFlow {
|
|
|
4190
4281
|
}
|
|
4191
4282
|
const queryString = params.toString();
|
|
4192
4283
|
const path = queryString
|
|
4193
|
-
? `/api/v1/
|
|
4194
|
-
: '/api/v1/
|
|
4284
|
+
? `/api/v1/workflows/approvals/pending?${queryString}`
|
|
4285
|
+
: '/api/v1/workflows/approvals/pending';
|
|
4286
|
+
return this.orchestratorRequest('GET', path);
|
|
4287
|
+
}
|
|
4288
|
+
/**
|
|
4289
|
+
* List pending approvals for MAP-backed workflows — the MAP-plane
|
|
4290
|
+
* counterpart of {@link getPendingApprovals}. Every entry has `plan_id`
|
|
4291
|
+
* populated. Pass `options.plan_id` to scope the listing to a single plan.
|
|
4292
|
+
*
|
|
4293
|
+
* Requires an Evaluation or Enterprise license (same tier gate as the
|
|
4294
|
+
* MAP step approve/reject endpoints).
|
|
4295
|
+
*
|
|
4296
|
+
* @param options - Optional filtering options (`limit`, `plan_id`)
|
|
4297
|
+
* @returns List of MAP-plane pending approvals with count
|
|
4298
|
+
*
|
|
4299
|
+
* @example
|
|
4300
|
+
* ```typescript
|
|
4301
|
+
* const pending = await client.getPendingPlanApprovals({
|
|
4302
|
+
* plan_id: 'plan-abc123',
|
|
4303
|
+
* limit: 10,
|
|
4304
|
+
* });
|
|
4305
|
+
* for (const approval of pending.pending_approvals) {
|
|
4306
|
+
* console.log(`Plan ${approval.plan_id} step ${approval.step_id} awaiting approval`);
|
|
4307
|
+
* }
|
|
4308
|
+
* ```
|
|
4309
|
+
*/
|
|
4310
|
+
async getPendingPlanApprovals(options) {
|
|
4311
|
+
const params = new URLSearchParams();
|
|
4312
|
+
if (options?.limit !== undefined) {
|
|
4313
|
+
params.set('limit', options.limit.toString());
|
|
4314
|
+
}
|
|
4315
|
+
if (options?.plan_id) {
|
|
4316
|
+
params.set('plan_id', options.plan_id);
|
|
4317
|
+
}
|
|
4318
|
+
const queryString = params.toString();
|
|
4319
|
+
const path = queryString
|
|
4320
|
+
? `/api/v1/plans/approvals/pending?${queryString}`
|
|
4321
|
+
: '/api/v1/plans/approvals/pending';
|
|
4195
4322
|
return this.orchestratorRequest('GET', path);
|
|
4196
4323
|
}
|
|
4197
4324
|
// =============================================================================
|
|
@@ -4231,7 +4358,7 @@ class AxonFlow {
|
|
|
4231
4358
|
(0, helpers_1.debugLog)('Plan rolled back', { planId, version: data.version });
|
|
4232
4359
|
}
|
|
4233
4360
|
return {
|
|
4234
|
-
planId: data.plan_id
|
|
4361
|
+
planId: data.plan_id ?? planId,
|
|
4235
4362
|
version: data.version,
|
|
4236
4363
|
previousVersion: data.previous_version,
|
|
4237
4364
|
status: data.status,
|
|
@@ -4497,7 +4624,7 @@ class AxonFlow {
|
|
|
4497
4624
|
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
4498
4625
|
}
|
|
4499
4626
|
const data = await response.json();
|
|
4500
|
-
return (data
|
|
4627
|
+
return (data ?? []).map((s) => this.mapSystemResponse(s));
|
|
4501
4628
|
}
|
|
4502
4629
|
async masfeatActivateSystem(systemId) {
|
|
4503
4630
|
// Use PUT to update status - the /activate endpoint doesn't exist
|
|
@@ -4552,8 +4679,8 @@ class AxonFlow {
|
|
|
4552
4679
|
highMaterialityCount: data.high_materiality_count ?? data.high_materiality ?? 0,
|
|
4553
4680
|
mediumMaterialityCount: data.medium_materiality_count ?? data.medium_materiality ?? 0,
|
|
4554
4681
|
lowMaterialityCount: data.low_materiality_count ?? data.low_materiality ?? 0,
|
|
4555
|
-
byUseCase: data.by_use_case
|
|
4556
|
-
byStatus: data.by_status
|
|
4682
|
+
byUseCase: data.by_use_case ?? {},
|
|
4683
|
+
byStatus: data.by_status ?? {},
|
|
4557
4684
|
};
|
|
4558
4685
|
}
|
|
4559
4686
|
// Assessment Methods
|
|
@@ -4561,7 +4688,7 @@ class AxonFlow {
|
|
|
4561
4688
|
const url = `${this.config.endpoint}/api/v1/masfeat/assessments`;
|
|
4562
4689
|
const body = {
|
|
4563
4690
|
system_id: request.systemId,
|
|
4564
|
-
assessment_type: request.assessmentType
|
|
4691
|
+
assessment_type: request.assessmentType ?? 'periodic',
|
|
4565
4692
|
assessors: request.assessors,
|
|
4566
4693
|
};
|
|
4567
4694
|
if (request.assessmentDate)
|
|
@@ -4700,7 +4827,7 @@ class AxonFlow {
|
|
|
4700
4827
|
throw new errors_1.APIError(response.status, response.statusText, errorText);
|
|
4701
4828
|
}
|
|
4702
4829
|
const data = await response.json();
|
|
4703
|
-
return (data
|
|
4830
|
+
return (data ?? []).map((a) => this.mapAssessmentResponse(a));
|
|
4704
4831
|
}
|
|
4705
4832
|
async masfeatSubmitAssessment(assessmentId) {
|
|
4706
4833
|
const url = `${this.config.endpoint}/api/v1/masfeat/assessments/${assessmentId}/submit`;
|
|
@@ -4916,20 +5043,20 @@ class AxonFlow {
|
|
|
4916
5043
|
if (data && typeof data === 'object' && 'history' in data) {
|
|
4917
5044
|
data = data.history;
|
|
4918
5045
|
}
|
|
4919
|
-
return (data
|
|
5046
|
+
return (data ?? []).map((e) => ({
|
|
4920
5047
|
id: e.id,
|
|
4921
5048
|
killSwitchId: e.kill_switch_id,
|
|
4922
5049
|
// Handle both API formats: event_type (SDK expected) vs action (API actual)
|
|
4923
|
-
eventType: e.event_type
|
|
5050
|
+
eventType: e.event_type ?? e.action,
|
|
4924
5051
|
// Build eventData from additional fields if not present
|
|
4925
|
-
eventData: e.event_data
|
|
4926
|
-
(e.previous_status
|
|
5052
|
+
eventData: e.event_data ??
|
|
5053
|
+
((e.previous_status ?? e.new_status ?? e.reason)
|
|
4927
5054
|
? { previousStatus: e.previous_status, newStatus: e.new_status, reason: e.reason }
|
|
4928
5055
|
: undefined),
|
|
4929
5056
|
// Handle both API formats: created_by vs performed_by
|
|
4930
|
-
createdBy: e.created_by
|
|
5057
|
+
createdBy: e.created_by ?? e.performed_by,
|
|
4931
5058
|
// Handle both API formats: created_at vs performed_at
|
|
4932
|
-
createdAt: new Date(e.created_at
|
|
5059
|
+
createdAt: new Date(e.created_at ?? e.performed_at),
|
|
4933
5060
|
}));
|
|
4934
5061
|
}
|
|
4935
5062
|
// Helper methods for MAS FEAT
|
|
@@ -4943,7 +5070,7 @@ class AxonFlow {
|
|
|
4943
5070
|
useCase: data.use_case,
|
|
4944
5071
|
ownerTeam: data.owner_team,
|
|
4945
5072
|
technicalOwner: data.technical_owner,
|
|
4946
|
-
businessOwner: data.business_owner
|
|
5073
|
+
businessOwner: data.business_owner ?? data.owner_email,
|
|
4947
5074
|
customerImpact: data.customer_impact ?? data.risk_rating_impact,
|
|
4948
5075
|
modelComplexity: data.model_complexity ?? data.risk_rating_complexity,
|
|
4949
5076
|
humanReliance: data.human_reliance ?? data.risk_rating_reliance,
|
|
@@ -4964,6 +5091,7 @@ class AxonFlow {
|
|
|
4964
5091
|
description: data.description,
|
|
4965
5092
|
status: data.status,
|
|
4966
5093
|
remediation: data.remediation,
|
|
5094
|
+
article: data.article,
|
|
4967
5095
|
dueDate: data.due_date ? new Date(data.due_date) : undefined,
|
|
4968
5096
|
};
|
|
4969
5097
|
}
|
|
@@ -5011,7 +5139,7 @@ class AxonFlow {
|
|
|
5011
5139
|
autoTriggerEnabled: data.auto_trigger_enabled,
|
|
5012
5140
|
triggeredAt: data.triggered_at ? new Date(data.triggered_at) : undefined,
|
|
5013
5141
|
triggeredBy: data.triggered_by,
|
|
5014
|
-
triggeredReason: data.triggered_reason
|
|
5142
|
+
triggeredReason: data.triggered_reason ?? data.trigger_reason,
|
|
5015
5143
|
restoredAt: data.restored_at ? new Date(data.restored_at) : undefined,
|
|
5016
5144
|
restoredBy: data.restored_by,
|
|
5017
5145
|
createdAt: new Date(data.created_at),
|
|
@@ -5184,7 +5312,7 @@ class AxonFlow {
|
|
|
5184
5312
|
}
|
|
5185
5313
|
const response = await this.orchestratorRequest('GET', path);
|
|
5186
5314
|
return {
|
|
5187
|
-
items: response.data
|
|
5315
|
+
items: response.data ?? [],
|
|
5188
5316
|
total: response.meta?.total ?? 0,
|
|
5189
5317
|
has_more: (response.meta?.offset ?? 0) + (response.data?.length ?? 0) < (response.meta?.total ?? 0),
|
|
5190
5318
|
};
|
|
@@ -5489,7 +5617,7 @@ class AxonFlow {
|
|
|
5489
5617
|
// Process complete SSE events (separated by double newline)
|
|
5490
5618
|
const events = buffer.split('\n\n');
|
|
5491
5619
|
// Keep the last (potentially incomplete) chunk in the buffer
|
|
5492
|
-
buffer = events.pop()
|
|
5620
|
+
buffer = events.pop() ?? '';
|
|
5493
5621
|
for (const event of events) {
|
|
5494
5622
|
const trimmed = event.trim();
|
|
5495
5623
|
if (!trimmed) {
|