@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/esm/client.js
CHANGED
|
@@ -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)
|
|
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]
|
|
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
|
|
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
|
|
76
|
-
tenant: config.tenant
|
|
77
|
-
debug: config.debug
|
|
78
|
-
timeout: config.timeout
|
|
79
|
-
mapTimeout: config.mapTimeout
|
|
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
|
|
83
|
-
delay: config.retry?.delay
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
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
|
|
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', '')
|
|
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
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
638
|
-
limitUsd: data.budget_info.limit_usd
|
|
639
|
-
percentage: data.budget_info.percentage
|
|
640
|
-
exceeded: data.budget_info.exceeded
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1060
|
-
steps: agentResponse.data?.steps
|
|
1061
|
-
domain: agentResponse.data?.domain
|
|
1062
|
-
complexity: agentResponse.data?.complexity
|
|
1063
|
-
parallel: agentResponse.data?.parallel
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3554
|
-
total: response.total
|
|
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
|
|
3599
|
-
remainingUsd: response.remaining_usd
|
|
3600
|
-
percentage: response.percentage
|
|
3601
|
-
isExceeded: response.is_exceeded
|
|
3602
|
-
isBlocked: response.is_blocked
|
|
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
|
|
3616
|
-
id: a.id
|
|
3617
|
-
budgetId: a.budget_id
|
|
3618
|
-
alertType: a.alert_type
|
|
3619
|
-
threshold: a.threshold
|
|
3620
|
-
percentageReached: a.percentage_reached
|
|
3621
|
-
amountUsd: a.amount_usd
|
|
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
|
|
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
|
|
3710
|
+
allowed: response.allowed ?? false,
|
|
3651
3711
|
action: response.action,
|
|
3652
3712
|
message: response.message,
|
|
3653
3713
|
budgets: response.budgets
|
|
3654
|
-
? (response.budgets
|
|
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
|
|
3672
|
-
totalRequests: response.total_requests
|
|
3673
|
-
totalTokensIn: response.total_tokens_in
|
|
3674
|
-
totalTokensOut: response.total_tokens_out
|
|
3675
|
-
averageCostPerRequest: response.average_cost_per_request
|
|
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
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
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
|
|
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
|
|
3731
|
-
id: r.id
|
|
3732
|
-
provider: r.provider
|
|
3733
|
-
model: r.model
|
|
3734
|
-
tokensIn: r.tokens_in
|
|
3735
|
-
tokensOut: r.tokens_out
|
|
3736
|
-
costUsd: r.cost_usd
|
|
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
|
|
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
|
|
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
|
|
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
|
|
3800
|
-
outputPer1k: pricingData?.output_per_1k
|
|
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);
|
|
@@ -4113,26 +4188,37 @@ export class AxonFlow {
|
|
|
4113
4188
|
/**
|
|
4114
4189
|
* Approve a workflow step that requires human approval.
|
|
4115
4190
|
*
|
|
4116
|
-
*
|
|
4191
|
+
* The server requires `comment` with a minimum of 10 characters — it's the
|
|
4192
|
+
* audit-trail justification that every approval carries into the workflow
|
|
4193
|
+
* history. Callers should always supply a meaningful comment.
|
|
4117
4194
|
*
|
|
4118
4195
|
* @param workflowId - ID of the workflow
|
|
4119
4196
|
* @param stepId - ID of the step to approve
|
|
4197
|
+
* @param comment - Audit justification for the approval (min 10 chars)
|
|
4120
4198
|
* @returns Approval response with status
|
|
4121
4199
|
*
|
|
4122
4200
|
* @example
|
|
4123
4201
|
* ```typescript
|
|
4124
|
-
* const result = await client.approveStep(
|
|
4202
|
+
* const result = await client.approveStep(
|
|
4203
|
+
* 'wf_123',
|
|
4204
|
+
* 'step_456',
|
|
4205
|
+
* 'Approved after full audit review'
|
|
4206
|
+
* );
|
|
4125
4207
|
* console.log(`Step ${result.step_id} status: ${result.status}`);
|
|
4126
4208
|
* ```
|
|
4127
4209
|
*/
|
|
4128
|
-
async approveStep(workflowId, stepId) {
|
|
4210
|
+
async approveStep(workflowId, stepId, comment) {
|
|
4129
4211
|
if (!workflowId) {
|
|
4130
4212
|
throw new ConfigurationError('Workflow ID is required');
|
|
4131
4213
|
}
|
|
4132
4214
|
if (!stepId) {
|
|
4133
4215
|
throw new ConfigurationError('Step ID is required');
|
|
4134
4216
|
}
|
|
4135
|
-
|
|
4217
|
+
const body = {};
|
|
4218
|
+
if (comment) {
|
|
4219
|
+
body.comment = comment;
|
|
4220
|
+
}
|
|
4221
|
+
return this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/approve`, body);
|
|
4136
4222
|
}
|
|
4137
4223
|
/**
|
|
4138
4224
|
* Reject a workflow step that requires human approval.
|
|
@@ -4161,21 +4247,26 @@ export class AxonFlow {
|
|
|
4161
4247
|
if (reason) {
|
|
4162
4248
|
body.reason = reason;
|
|
4163
4249
|
}
|
|
4164
|
-
return this.orchestratorRequest('POST', `/api/v1/
|
|
4250
|
+
return this.orchestratorRequest('POST', `/api/v1/workflows/${workflowId}/steps/${stepId}/reject`, body);
|
|
4165
4251
|
}
|
|
4166
4252
|
/**
|
|
4167
|
-
* Get pending approvals for workflow steps.
|
|
4253
|
+
* Get pending approvals for workflow steps — the WCP-plane listing.
|
|
4254
|
+
*
|
|
4255
|
+
* Lists steps that are waiting for human approval across all planes for
|
|
4256
|
+
* the caller's tenant. Use {@link getPendingPlanApprovals} for the
|
|
4257
|
+
* MAP-plane listing (scopes to MAP-backed workflows and populates
|
|
4258
|
+
* `plan_id` on every entry).
|
|
4168
4259
|
*
|
|
4169
|
-
*
|
|
4260
|
+
* Available on Evaluation+ licenses.
|
|
4170
4261
|
*
|
|
4171
4262
|
* @param options - Optional filtering options
|
|
4172
|
-
* @returns List of pending approvals with
|
|
4263
|
+
* @returns List of pending approvals with count
|
|
4173
4264
|
*
|
|
4174
4265
|
* @example
|
|
4175
4266
|
* ```typescript
|
|
4176
4267
|
* const pending = await client.getPendingApprovals({ limit: 10 });
|
|
4177
|
-
* console.log(`${pending.
|
|
4178
|
-
* for (const approval of pending.
|
|
4268
|
+
* console.log(`${pending.count} approvals pending`);
|
|
4269
|
+
* for (const approval of pending.pending_approvals) {
|
|
4179
4270
|
* console.log(`${approval.workflow_name} / ${approval.step_name}`);
|
|
4180
4271
|
* }
|
|
4181
4272
|
* ```
|
|
@@ -4187,8 +4278,44 @@ export class AxonFlow {
|
|
|
4187
4278
|
}
|
|
4188
4279
|
const queryString = params.toString();
|
|
4189
4280
|
const path = queryString
|
|
4190
|
-
? `/api/v1/
|
|
4191
|
-
: '/api/v1/
|
|
4281
|
+
? `/api/v1/workflows/approvals/pending?${queryString}`
|
|
4282
|
+
: '/api/v1/workflows/approvals/pending';
|
|
4283
|
+
return this.orchestratorRequest('GET', path);
|
|
4284
|
+
}
|
|
4285
|
+
/**
|
|
4286
|
+
* List pending approvals for MAP-backed workflows — the MAP-plane
|
|
4287
|
+
* counterpart of {@link getPendingApprovals}. Every entry has `plan_id`
|
|
4288
|
+
* populated. Pass `options.plan_id` to scope the listing to a single plan.
|
|
4289
|
+
*
|
|
4290
|
+
* Requires an Evaluation or Enterprise license (same tier gate as the
|
|
4291
|
+
* MAP step approve/reject endpoints).
|
|
4292
|
+
*
|
|
4293
|
+
* @param options - Optional filtering options (`limit`, `plan_id`)
|
|
4294
|
+
* @returns List of MAP-plane pending approvals with count
|
|
4295
|
+
*
|
|
4296
|
+
* @example
|
|
4297
|
+
* ```typescript
|
|
4298
|
+
* const pending = await client.getPendingPlanApprovals({
|
|
4299
|
+
* plan_id: 'plan-abc123',
|
|
4300
|
+
* limit: 10,
|
|
4301
|
+
* });
|
|
4302
|
+
* for (const approval of pending.pending_approvals) {
|
|
4303
|
+
* console.log(`Plan ${approval.plan_id} step ${approval.step_id} awaiting approval`);
|
|
4304
|
+
* }
|
|
4305
|
+
* ```
|
|
4306
|
+
*/
|
|
4307
|
+
async getPendingPlanApprovals(options) {
|
|
4308
|
+
const params = new URLSearchParams();
|
|
4309
|
+
if (options?.limit !== undefined) {
|
|
4310
|
+
params.set('limit', options.limit.toString());
|
|
4311
|
+
}
|
|
4312
|
+
if (options?.plan_id) {
|
|
4313
|
+
params.set('plan_id', options.plan_id);
|
|
4314
|
+
}
|
|
4315
|
+
const queryString = params.toString();
|
|
4316
|
+
const path = queryString
|
|
4317
|
+
? `/api/v1/plans/approvals/pending?${queryString}`
|
|
4318
|
+
: '/api/v1/plans/approvals/pending';
|
|
4192
4319
|
return this.orchestratorRequest('GET', path);
|
|
4193
4320
|
}
|
|
4194
4321
|
// =============================================================================
|
|
@@ -4228,7 +4355,7 @@ export class AxonFlow {
|
|
|
4228
4355
|
debugLog('Plan rolled back', { planId, version: data.version });
|
|
4229
4356
|
}
|
|
4230
4357
|
return {
|
|
4231
|
-
planId: data.plan_id
|
|
4358
|
+
planId: data.plan_id ?? planId,
|
|
4232
4359
|
version: data.version,
|
|
4233
4360
|
previousVersion: data.previous_version,
|
|
4234
4361
|
status: data.status,
|
|
@@ -4494,7 +4621,7 @@ export class AxonFlow {
|
|
|
4494
4621
|
throw new APIError(response.status, response.statusText, errorText);
|
|
4495
4622
|
}
|
|
4496
4623
|
const data = await response.json();
|
|
4497
|
-
return (data
|
|
4624
|
+
return (data ?? []).map((s) => this.mapSystemResponse(s));
|
|
4498
4625
|
}
|
|
4499
4626
|
async masfeatActivateSystem(systemId) {
|
|
4500
4627
|
// Use PUT to update status - the /activate endpoint doesn't exist
|
|
@@ -4549,8 +4676,8 @@ export class AxonFlow {
|
|
|
4549
4676
|
highMaterialityCount: data.high_materiality_count ?? data.high_materiality ?? 0,
|
|
4550
4677
|
mediumMaterialityCount: data.medium_materiality_count ?? data.medium_materiality ?? 0,
|
|
4551
4678
|
lowMaterialityCount: data.low_materiality_count ?? data.low_materiality ?? 0,
|
|
4552
|
-
byUseCase: data.by_use_case
|
|
4553
|
-
byStatus: data.by_status
|
|
4679
|
+
byUseCase: data.by_use_case ?? {},
|
|
4680
|
+
byStatus: data.by_status ?? {},
|
|
4554
4681
|
};
|
|
4555
4682
|
}
|
|
4556
4683
|
// Assessment Methods
|
|
@@ -4558,7 +4685,7 @@ export class AxonFlow {
|
|
|
4558
4685
|
const url = `${this.config.endpoint}/api/v1/masfeat/assessments`;
|
|
4559
4686
|
const body = {
|
|
4560
4687
|
system_id: request.systemId,
|
|
4561
|
-
assessment_type: request.assessmentType
|
|
4688
|
+
assessment_type: request.assessmentType ?? 'periodic',
|
|
4562
4689
|
assessors: request.assessors,
|
|
4563
4690
|
};
|
|
4564
4691
|
if (request.assessmentDate)
|
|
@@ -4697,7 +4824,7 @@ export class AxonFlow {
|
|
|
4697
4824
|
throw new APIError(response.status, response.statusText, errorText);
|
|
4698
4825
|
}
|
|
4699
4826
|
const data = await response.json();
|
|
4700
|
-
return (data
|
|
4827
|
+
return (data ?? []).map((a) => this.mapAssessmentResponse(a));
|
|
4701
4828
|
}
|
|
4702
4829
|
async masfeatSubmitAssessment(assessmentId) {
|
|
4703
4830
|
const url = `${this.config.endpoint}/api/v1/masfeat/assessments/${assessmentId}/submit`;
|
|
@@ -4913,20 +5040,20 @@ export class AxonFlow {
|
|
|
4913
5040
|
if (data && typeof data === 'object' && 'history' in data) {
|
|
4914
5041
|
data = data.history;
|
|
4915
5042
|
}
|
|
4916
|
-
return (data
|
|
5043
|
+
return (data ?? []).map((e) => ({
|
|
4917
5044
|
id: e.id,
|
|
4918
5045
|
killSwitchId: e.kill_switch_id,
|
|
4919
5046
|
// Handle both API formats: event_type (SDK expected) vs action (API actual)
|
|
4920
|
-
eventType: e.event_type
|
|
5047
|
+
eventType: e.event_type ?? e.action,
|
|
4921
5048
|
// Build eventData from additional fields if not present
|
|
4922
|
-
eventData: e.event_data
|
|
4923
|
-
(e.previous_status
|
|
5049
|
+
eventData: e.event_data ??
|
|
5050
|
+
((e.previous_status ?? e.new_status ?? e.reason)
|
|
4924
5051
|
? { previousStatus: e.previous_status, newStatus: e.new_status, reason: e.reason }
|
|
4925
5052
|
: undefined),
|
|
4926
5053
|
// Handle both API formats: created_by vs performed_by
|
|
4927
|
-
createdBy: e.created_by
|
|
5054
|
+
createdBy: e.created_by ?? e.performed_by,
|
|
4928
5055
|
// Handle both API formats: created_at vs performed_at
|
|
4929
|
-
createdAt: new Date(e.created_at
|
|
5056
|
+
createdAt: new Date(e.created_at ?? e.performed_at),
|
|
4930
5057
|
}));
|
|
4931
5058
|
}
|
|
4932
5059
|
// Helper methods for MAS FEAT
|
|
@@ -4940,7 +5067,7 @@ export class AxonFlow {
|
|
|
4940
5067
|
useCase: data.use_case,
|
|
4941
5068
|
ownerTeam: data.owner_team,
|
|
4942
5069
|
technicalOwner: data.technical_owner,
|
|
4943
|
-
businessOwner: data.business_owner
|
|
5070
|
+
businessOwner: data.business_owner ?? data.owner_email,
|
|
4944
5071
|
customerImpact: data.customer_impact ?? data.risk_rating_impact,
|
|
4945
5072
|
modelComplexity: data.model_complexity ?? data.risk_rating_complexity,
|
|
4946
5073
|
humanReliance: data.human_reliance ?? data.risk_rating_reliance,
|
|
@@ -4961,6 +5088,7 @@ export class AxonFlow {
|
|
|
4961
5088
|
description: data.description,
|
|
4962
5089
|
status: data.status,
|
|
4963
5090
|
remediation: data.remediation,
|
|
5091
|
+
article: data.article,
|
|
4964
5092
|
dueDate: data.due_date ? new Date(data.due_date) : undefined,
|
|
4965
5093
|
};
|
|
4966
5094
|
}
|
|
@@ -5008,7 +5136,7 @@ export class AxonFlow {
|
|
|
5008
5136
|
autoTriggerEnabled: data.auto_trigger_enabled,
|
|
5009
5137
|
triggeredAt: data.triggered_at ? new Date(data.triggered_at) : undefined,
|
|
5010
5138
|
triggeredBy: data.triggered_by,
|
|
5011
|
-
triggeredReason: data.triggered_reason
|
|
5139
|
+
triggeredReason: data.triggered_reason ?? data.trigger_reason,
|
|
5012
5140
|
restoredAt: data.restored_at ? new Date(data.restored_at) : undefined,
|
|
5013
5141
|
restoredBy: data.restored_by,
|
|
5014
5142
|
createdAt: new Date(data.created_at),
|
|
@@ -5181,7 +5309,7 @@ export class AxonFlow {
|
|
|
5181
5309
|
}
|
|
5182
5310
|
const response = await this.orchestratorRequest('GET', path);
|
|
5183
5311
|
return {
|
|
5184
|
-
items: response.data
|
|
5312
|
+
items: response.data ?? [],
|
|
5185
5313
|
total: response.meta?.total ?? 0,
|
|
5186
5314
|
has_more: (response.meta?.offset ?? 0) + (response.data?.length ?? 0) < (response.meta?.total ?? 0),
|
|
5187
5315
|
};
|
|
@@ -5486,7 +5614,7 @@ export class AxonFlow {
|
|
|
5486
5614
|
// Process complete SSE events (separated by double newline)
|
|
5487
5615
|
const events = buffer.split('\n\n');
|
|
5488
5616
|
// Keep the last (potentially incomplete) chunk in the buffer
|
|
5489
|
-
buffer = events.pop()
|
|
5617
|
+
buffer = events.pop() ?? '';
|
|
5490
5618
|
for (const event of events) {
|
|
5491
5619
|
const trimmed = event.trim();
|
|
5492
5620
|
if (!trimmed) {
|