@axonflow/sdk 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +194 -9
  3. package/dist/cjs/client.d.ts +64 -40
  4. package/dist/cjs/client.d.ts.map +1 -1
  5. package/dist/cjs/client.js +279 -198
  6. package/dist/cjs/client.js.map +1 -1
  7. package/dist/cjs/errors.d.ts +51 -0
  8. package/dist/cjs/errors.d.ts.map +1 -0
  9. package/dist/cjs/errors.js +84 -0
  10. package/dist/cjs/errors.js.map +1 -0
  11. package/dist/cjs/index.d.ts +3 -2
  12. package/dist/cjs/index.d.ts.map +1 -1
  13. package/dist/cjs/index.js +10 -2
  14. package/dist/cjs/index.js.map +1 -1
  15. package/dist/cjs/interceptors/anthropic.d.ts +1 -1
  16. package/dist/cjs/interceptors/anthropic.d.ts.map +1 -1
  17. package/dist/cjs/interceptors/anthropic.js +6 -6
  18. package/dist/cjs/interceptors/anthropic.js.map +1 -1
  19. package/dist/cjs/interceptors/openai.d.ts +1 -1
  20. package/dist/cjs/interceptors/openai.d.ts.map +1 -1
  21. package/dist/cjs/interceptors/openai.js +5 -5
  22. package/dist/cjs/interceptors/openai.js.map +1 -1
  23. package/dist/cjs/types/config.d.ts +7 -1
  24. package/dist/cjs/types/config.d.ts.map +1 -1
  25. package/dist/cjs/types/gateway.d.ts +51 -114
  26. package/dist/cjs/types/gateway.d.ts.map +1 -1
  27. package/dist/cjs/types/gateway.js +2 -7
  28. package/dist/cjs/types/gateway.js.map +1 -1
  29. package/dist/cjs/utils/helpers.d.ts.map +1 -1
  30. package/dist/cjs/utils/helpers.js +3 -1
  31. package/dist/cjs/utils/helpers.js.map +1 -1
  32. package/dist/esm/client.d.ts +64 -40
  33. package/dist/esm/client.d.ts.map +1 -1
  34. package/dist/esm/client.js +279 -198
  35. package/dist/esm/client.js.map +1 -1
  36. package/dist/esm/errors.d.ts +51 -0
  37. package/dist/esm/errors.d.ts.map +1 -0
  38. package/dist/esm/errors.js +75 -0
  39. package/dist/esm/errors.js.map +1 -0
  40. package/dist/esm/index.d.ts +3 -2
  41. package/dist/esm/index.d.ts.map +1 -1
  42. package/dist/esm/index.js +3 -1
  43. package/dist/esm/index.js.map +1 -1
  44. package/dist/esm/interceptors/anthropic.d.ts +1 -1
  45. package/dist/esm/interceptors/anthropic.d.ts.map +1 -1
  46. package/dist/esm/interceptors/anthropic.js +6 -6
  47. package/dist/esm/interceptors/anthropic.js.map +1 -1
  48. package/dist/esm/interceptors/openai.d.ts +1 -1
  49. package/dist/esm/interceptors/openai.d.ts.map +1 -1
  50. package/dist/esm/interceptors/openai.js +5 -5
  51. package/dist/esm/interceptors/openai.js.map +1 -1
  52. package/dist/esm/types/config.d.ts +7 -1
  53. package/dist/esm/types/config.d.ts.map +1 -1
  54. package/dist/esm/types/gateway.d.ts +51 -114
  55. package/dist/esm/types/gateway.d.ts.map +1 -1
  56. package/dist/esm/types/gateway.js +2 -7
  57. package/dist/esm/types/gateway.js.map +1 -1
  58. package/dist/esm/utils/helpers.d.ts.map +1 -1
  59. package/dist/esm/utils/helpers.js +3 -1
  60. package/dist/esm/utils/helpers.js.map +1 -1
  61. package/package.json +21 -7
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AxonFlow = void 0;
4
+ const errors_1 = require("./errors");
4
5
  const openai_1 = require("./interceptors/openai");
5
6
  const anthropic_1 = require("./interceptors/anthropic");
6
7
  const helpers_1 = require("./utils/helpers");
@@ -10,24 +11,49 @@ const helpers_1 = require("./utils/helpers");
10
11
  class AxonFlow {
11
12
  constructor(config) {
12
13
  this.interceptors = [];
13
- // Set defaults
14
+ // Set defaults first to determine endpoint
15
+ const endpoint = config.endpoint || 'https://staging-eu.getaxonflow.com';
16
+ // Check if running in self-hosted mode (localhost)
17
+ const isLocalhost = endpoint.includes('localhost') || endpoint.includes('127.0.0.1');
18
+ // License key is optional for self-hosted deployments
19
+ // When not provided, agent must have SELF_HOSTED_MODE=true
20
+ if (!isLocalhost && !config.licenseKey && !config.apiKey) {
21
+ throw new Error('Either licenseKey or apiKey must be provided for non-localhost endpoints');
22
+ }
23
+ if (isLocalhost && !config.licenseKey && !config.apiKey && config.debug) {
24
+ console.warn('[AxonFlow] No license key provided - ensure agent has SELF_HOSTED_MODE=true');
25
+ }
26
+ // Set configuration
14
27
  this.config = {
15
28
  apiKey: config.apiKey,
16
- endpoint: config.endpoint || 'https://staging-eu.getaxonflow.com',
17
- mode: config.mode || 'production',
29
+ licenseKey: config.licenseKey,
30
+ endpoint,
31
+ mode: config.mode || (isLocalhost ? 'sandbox' : 'production'),
18
32
  tenant: config.tenant || 'default',
19
33
  debug: config.debug || false,
20
34
  timeout: config.timeout || 30000,
21
- retry: config.retry || { enabled: true, maxAttempts: 3, delay: 1000 },
22
- cache: config.cache || { enabled: true, ttl: 60000 }
35
+ retry: {
36
+ enabled: config.retry?.enabled !== false,
37
+ maxAttempts: config.retry?.maxAttempts || 3,
38
+ delay: config.retry?.delay || 1000,
39
+ },
40
+ cache: {
41
+ enabled: config.cache?.enabled !== false,
42
+ ttl: config.cache?.ttl || 60000,
43
+ },
23
44
  };
24
45
  // Initialize interceptors
25
- this.interceptors = [
26
- new openai_1.OpenAIInterceptor(),
27
- new anthropic_1.AnthropicInterceptor()
28
- ];
46
+ this.interceptors = [new openai_1.OpenAIInterceptor(), new anthropic_1.AnthropicInterceptor()];
29
47
  if (this.config.debug) {
30
- (0, helpers_1.debugLog)('AxonFlow initialized', { mode: this.config.mode, endpoint: this.config.endpoint });
48
+ (0, helpers_1.debugLog)('AxonFlow initialized', {
49
+ mode: this.config.mode,
50
+ endpoint: this.config.endpoint,
51
+ authMethod: isLocalhost
52
+ ? 'self-hosted (no auth)'
53
+ : this.config.licenseKey
54
+ ? 'license-key'
55
+ : 'api-key',
56
+ });
31
57
  }
32
58
  }
33
59
  /**
@@ -48,7 +74,7 @@ class AxonFlow {
48
74
  timestamp: Date.now(),
49
75
  aiRequest,
50
76
  mode: this.config.mode,
51
- tenant: this.config.tenant
77
+ tenant: this.config.tenant,
52
78
  };
53
79
  // Check policies with AxonFlow Agent
54
80
  const governanceResponse = await this.checkPolicies(governanceRequest);
@@ -93,7 +119,7 @@ class AxonFlow {
93
119
  provider: 'unknown',
94
120
  model: 'unknown',
95
121
  prompt: aiCall.toString(),
96
- parameters: {}
122
+ parameters: {},
97
123
  };
98
124
  }
99
125
  /**
@@ -104,7 +130,7 @@ class AxonFlow {
104
130
  // Transform SDK request to Agent API format
105
131
  const agentRequest = {
106
132
  query: request.aiRequest.prompt,
107
- user_token: this.config.apiKey,
133
+ user_token: this.config.apiKey || '',
108
134
  client_id: this.config.tenant,
109
135
  request_type: 'llm_chat',
110
136
  context: {
@@ -112,16 +138,23 @@ class AxonFlow {
112
138
  model: request.aiRequest.model,
113
139
  parameters: request.aiRequest.parameters,
114
140
  requestId: request.requestId,
115
- mode: this.config.mode
116
- }
141
+ mode: this.config.mode,
142
+ },
117
143
  };
144
+ const headers = {
145
+ 'Content-Type': 'application/json',
146
+ };
147
+ // Add license key header if available (preferred auth method)
148
+ // Skip auth headers for localhost (self-hosted mode)
149
+ const isLocalhost = this.config.endpoint.includes('localhost') || this.config.endpoint.includes('127.0.0.1');
150
+ if (!isLocalhost && this.config.licenseKey) {
151
+ headers['X-License-Key'] = this.config.licenseKey;
152
+ }
118
153
  const response = await fetch(url, {
119
154
  method: 'POST',
120
- headers: {
121
- 'Content-Type': 'application/json'
122
- },
155
+ headers,
123
156
  body: JSON.stringify(agentRequest),
124
- signal: AbortSignal.timeout(this.config.timeout)
157
+ signal: AbortSignal.timeout(this.config.timeout),
125
158
  });
126
159
  if (!response.ok) {
127
160
  const errorText = await response.text();
@@ -129,23 +162,29 @@ class AxonFlow {
129
162
  }
130
163
  const agentResponse = await response.json();
131
164
  // Transform Agent API response to SDK format
165
+ // Extract policy name from policy_info if available
166
+ const policyName = agentResponse.policy_info?.policies_evaluated?.[0] || 'agent-policy';
132
167
  return {
133
168
  requestId: request.requestId,
134
169
  allowed: !agentResponse.blocked,
135
- violations: agentResponse.blocked ? [{
136
- type: 'security',
137
- severity: 'high',
138
- description: agentResponse.block_reason || 'Request blocked by policy',
139
- policy: 'agent-policy',
140
- action: 'blocked'
141
- }] : [],
170
+ violations: agentResponse.blocked
171
+ ? [
172
+ {
173
+ type: 'security',
174
+ severity: 'high',
175
+ description: agentResponse.block_reason || 'Request blocked by policy',
176
+ policy: policyName,
177
+ action: 'blocked',
178
+ },
179
+ ]
180
+ : [],
142
181
  modifiedRequest: agentResponse.data,
143
- policies: [],
182
+ policies: agentResponse.policy_info?.policies_evaluated || [],
144
183
  audit: {
145
184
  timestamp: Date.now(),
146
185
  duration: parseInt(agentResponse.policy_info?.processing_time?.replace('ms', '') || '0'),
147
- tenant: this.config.tenant
148
- }
186
+ tenant: this.config.tenant,
187
+ },
149
188
  };
150
189
  }
151
190
  /**
@@ -158,7 +197,7 @@ class AxonFlow {
158
197
  (0, helpers_1.debugLog)('Request processed', {
159
198
  allowed: response.allowed,
160
199
  violations: response.violations?.length || 0,
161
- duration: response.audit.duration
200
+ duration: response.audit.duration,
162
201
  });
163
202
  }
164
203
  }
@@ -166,9 +205,9 @@ class AxonFlow {
166
205
  * Check if an error is from AxonFlow (vs the AI provider)
167
206
  */
168
207
  isAxonFlowError(error) {
169
- return error?.message?.includes('AxonFlow') ||
208
+ return (error?.message?.includes('AxonFlow') ||
170
209
  error?.message?.includes('governance') ||
171
- error?.message?.includes('fetch');
210
+ error?.message?.includes('fetch'));
172
211
  }
173
212
  /**
174
213
  * Create a sandbox client for testing
@@ -178,7 +217,7 @@ class AxonFlow {
178
217
  apiKey,
179
218
  mode: 'sandbox',
180
219
  endpoint: 'https://staging-eu.getaxonflow.com',
181
- debug: true
220
+ debug: true,
182
221
  });
183
222
  }
184
223
  /**
@@ -188,7 +227,7 @@ class AxonFlow {
188
227
  const url = `${this.config.endpoint}/api/connectors`;
189
228
  const response = await fetch(url, {
190
229
  method: 'GET',
191
- signal: AbortSignal.timeout(this.config.timeout)
230
+ signal: AbortSignal.timeout(this.config.timeout),
192
231
  });
193
232
  if (!response.ok) {
194
233
  throw new Error(`Failed to list connectors: ${response.status} ${response.statusText}`);
@@ -204,14 +243,21 @@ class AxonFlow {
204
243
  */
205
244
  async installConnector(request) {
206
245
  const url = `${this.config.endpoint}/api/connectors/install`;
246
+ const headers = {
247
+ 'Content-Type': 'application/json',
248
+ };
249
+ // Add authentication headers
250
+ if (this.config.licenseKey) {
251
+ headers['X-License-Key'] = this.config.licenseKey;
252
+ }
253
+ else if (this.config.apiKey) {
254
+ headers['X-Client-Secret'] = this.config.apiKey;
255
+ }
207
256
  const response = await fetch(url, {
208
257
  method: 'POST',
209
- headers: {
210
- 'Content-Type': 'application/json',
211
- 'X-Client-Secret': this.config.apiKey
212
- },
258
+ headers,
213
259
  body: JSON.stringify(request),
214
- signal: AbortSignal.timeout(this.config.timeout)
260
+ signal: AbortSignal.timeout(this.config.timeout),
215
261
  });
216
262
  if (!response.ok) {
217
263
  const errorText = await response.text();
@@ -227,22 +273,26 @@ class AxonFlow {
227
273
  async queryConnector(connectorName, query, params) {
228
274
  const agentRequest = {
229
275
  query,
230
- user_token: this.config.apiKey,
276
+ user_token: this.config.apiKey || '',
231
277
  client_id: this.config.tenant,
232
278
  request_type: 'mcp-query',
233
279
  context: {
234
280
  connector: connectorName,
235
- params: params || {}
236
- }
281
+ params: params || {},
282
+ },
237
283
  };
238
284
  const url = `${this.config.endpoint}/api/request`;
285
+ const headers = {
286
+ 'Content-Type': 'application/json',
287
+ };
288
+ if (this.config.licenseKey) {
289
+ headers['X-License-Key'] = this.config.licenseKey;
290
+ }
239
291
  const response = await fetch(url, {
240
292
  method: 'POST',
241
- headers: {
242
- 'Content-Type': 'application/json'
243
- },
293
+ headers,
244
294
  body: JSON.stringify(agentRequest),
245
- signal: AbortSignal.timeout(this.config.timeout)
295
+ signal: AbortSignal.timeout(this.config.timeout),
246
296
  });
247
297
  if (!response.ok) {
248
298
  const errorText = await response.text();
@@ -256,28 +306,35 @@ class AxonFlow {
256
306
  success: agentResponse.success,
257
307
  data: agentResponse.data,
258
308
  error: agentResponse.error,
259
- meta: agentResponse.metadata
309
+ meta: agentResponse.metadata,
260
310
  };
261
311
  }
262
312
  /**
263
313
  * Generate a multi-agent execution plan from a natural language query
314
+ * @param query - Natural language query describing the task
315
+ * @param domain - Optional domain hint (travel, healthcare, etc.)
316
+ * @param userToken - Optional user token for authentication (defaults to tenant/client_id)
264
317
  */
265
- async generatePlan(query, domain) {
318
+ async generatePlan(query, domain, userToken) {
266
319
  const agentRequest = {
267
320
  query,
268
- user_token: this.config.apiKey,
321
+ user_token: userToken || this.config.tenant,
269
322
  client_id: this.config.tenant,
270
323
  request_type: 'multi-agent-plan',
271
- context: domain ? { domain } : {}
324
+ context: domain ? { domain } : {},
272
325
  };
273
326
  const url = `${this.config.endpoint}/api/request`;
327
+ const headers = {
328
+ 'Content-Type': 'application/json',
329
+ };
330
+ if (this.config.licenseKey) {
331
+ headers['X-License-Key'] = this.config.licenseKey;
332
+ }
274
333
  const response = await fetch(url, {
275
334
  method: 'POST',
276
- headers: {
277
- 'Content-Type': 'application/json'
278
- },
335
+ headers,
279
336
  body: JSON.stringify(agentRequest),
280
- signal: AbortSignal.timeout(this.config.timeout)
337
+ signal: AbortSignal.timeout(this.config.timeout),
281
338
  });
282
339
  if (!response.ok) {
283
340
  const errorText = await response.text();
@@ -296,28 +353,34 @@ class AxonFlow {
296
353
  domain: agentResponse.data?.domain || domain || 'generic',
297
354
  complexity: agentResponse.data?.complexity || 0,
298
355
  parallel: agentResponse.data?.parallel || false,
299
- metadata: agentResponse.metadata || {}
356
+ metadata: agentResponse.metadata || {},
300
357
  };
301
358
  }
302
359
  /**
303
360
  * Execute a previously generated multi-agent plan
361
+ * @param planId - ID of the plan to execute
362
+ * @param userToken - Optional user token for authentication (defaults to tenant/client_id)
304
363
  */
305
- async executePlan(planId) {
364
+ async executePlan(planId, userToken) {
306
365
  const agentRequest = {
307
366
  query: '',
308
- user_token: this.config.apiKey,
367
+ user_token: userToken || this.config.tenant,
309
368
  client_id: this.config.tenant,
310
369
  request_type: 'execute-plan',
311
- context: { plan_id: planId }
370
+ context: { plan_id: planId },
312
371
  };
313
372
  const url = `${this.config.endpoint}/api/request`;
373
+ const headers = {
374
+ 'Content-Type': 'application/json',
375
+ };
376
+ if (this.config.licenseKey) {
377
+ headers['X-License-Key'] = this.config.licenseKey;
378
+ }
314
379
  const response = await fetch(url, {
315
380
  method: 'POST',
316
- headers: {
317
- 'Content-Type': 'application/json'
318
- },
381
+ headers,
319
382
  body: JSON.stringify(agentRequest),
320
- signal: AbortSignal.timeout(this.config.timeout)
383
+ signal: AbortSignal.timeout(this.config.timeout),
321
384
  });
322
385
  if (!response.ok) {
323
386
  const errorText = await response.text();
@@ -333,7 +396,7 @@ class AxonFlow {
333
396
  result: agentResponse.result,
334
397
  stepResults: agentResponse.metadata?.step_results,
335
398
  error: agentResponse.error,
336
- duration: agentResponse.metadata?.duration
399
+ duration: agentResponse.metadata?.duration,
337
400
  };
338
401
  }
339
402
  /**
@@ -343,7 +406,7 @@ class AxonFlow {
343
406
  const url = `${this.config.endpoint}/api/plans/${planId}`;
344
407
  const response = await fetch(url, {
345
408
  method: 'GET',
346
- signal: AbortSignal.timeout(this.config.timeout)
409
+ signal: AbortSignal.timeout(this.config.timeout),
347
410
  });
348
411
  if (!response.ok) {
349
412
  const errorText = await response.text();
@@ -356,191 +419,209 @@ class AxonFlow {
356
419
  result: status.result,
357
420
  stepResults: status.step_results,
358
421
  error: status.error,
359
- duration: status.duration
422
+ duration: status.duration,
360
423
  };
361
424
  }
362
- // ===========================================================================
363
- // Gateway Mode SDK Methods
364
- // ===========================================================================
365
- // Gateway Mode allows clients to make LLM calls directly while still using
366
- // AxonFlow for policy enforcement and audit logging.
367
- //
368
- // Usage:
369
- // 1. Call getPolicyApprovedContext() before making LLM call
370
- // 2. Make LLM call directly to your provider (using returned approved data)
371
- // 3. Call auditLLMCall() after to record the call for compliance
372
- //
373
- // Example:
374
- // const ctx = await axonflow.getPolicyApprovedContext({
375
- // userToken: 'user-jwt',
376
- // dataSources: ['postgres'],
377
- // query: 'Find patients with diabetes'
378
- // });
379
- // if (!ctx.approved) throw new Error(ctx.blockReason);
380
- //
381
- // const llmResp = await openai.chat.completions.create({...}); // Your LLM call
382
- //
383
- // await axonflow.auditLLMCall({
384
- // contextId: ctx.contextId,
385
- // responseSummary: 'Found 5 patients',
386
- // provider: 'openai',
387
- // model: 'gpt-4',
388
- // tokenUsage: { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
389
- // latencyMs: 250
390
- // });
425
+ // ============================================================================
426
+ // Gateway Mode Methods
427
+ // ============================================================================
391
428
  /**
392
- * Perform policy pre-check before making LLM call
393
- *
394
- * This is the first step in Gateway Mode. Call this before making your
395
- * LLM call to ensure policy compliance.
429
+ * Gateway Mode: Pre-check policy approval before making a direct LLM call.
430
+ * Alias for getPolicyApprovedContext() for simpler API.
431
+ */
432
+ async preCheck(options) {
433
+ return this.getPolicyApprovedContext(options);
434
+ }
435
+ /**
436
+ * Gateway Mode: Get policy-approved context before making a direct LLM call.
396
437
  *
397
- * @param request Pre-check request containing user token, query, and optional data sources
398
- * @returns PolicyApprovalResult with context ID and approved data (if any)
438
+ * Use this when you want to:
439
+ * - Make direct LLM calls (not through AxonFlow proxy)
440
+ * - Have full control over your LLM provider/model selection
441
+ * - Minimize latency by calling LLM directly
399
442
  *
400
443
  * @example
401
- * const result = await axonflow.getPolicyApprovedContext({
402
- * userToken: 'user-jwt-token',
403
- * dataSources: ['postgres', 'salesforce'],
404
- * query: 'Find all patients with recent lab results'
444
+ * ```typescript
445
+ * const ctx = await axonflow.getPolicyApprovedContext({
446
+ * userToken: 'user-jwt',
447
+ * query: 'Analyze this customer data',
448
+ * dataSources: ['postgres']
405
449
  * });
406
450
  *
407
- * if (!result.approved) {
408
- * throw new Error(`Request blocked: ${result.blockReason}`);
451
+ * if (!ctx.approved) {
452
+ * throw new Error(`Blocked: ${ctx.blockReason}`);
409
453
  * }
410
454
  *
411
- * // Use result.approvedData to build your LLM prompt
412
- * const prompt = buildPrompt(result.approvedData);
455
+ * // Make direct LLM call with approved data
456
+ * const response = await openai.chat.completions.create({
457
+ * model: 'gpt-4',
458
+ * messages: [{ role: 'user', content: JSON.stringify(ctx.approvedData) }]
459
+ * });
460
+ *
461
+ * // Audit the call
462
+ * await axonflow.auditLLMCall({
463
+ * contextId: ctx.contextId,
464
+ * responseSummary: response.choices[0].message.content.substring(0, 100),
465
+ * provider: 'openai',
466
+ * model: 'gpt-4',
467
+ * tokenUsage: {
468
+ * promptTokens: response.usage.prompt_tokens,
469
+ * completionTokens: response.usage.completion_tokens,
470
+ * totalTokens: response.usage.total_tokens
471
+ * },
472
+ * latencyMs: 250
473
+ * });
474
+ * ```
413
475
  */
414
- async getPolicyApprovedContext(request) {
476
+ async getPolicyApprovedContext(options) {
415
477
  const url = `${this.config.endpoint}/api/policy/pre-check`;
416
- const body = {
417
- user_token: request.userToken,
478
+ const requestBody = {
479
+ user_token: options.userToken,
418
480
  client_id: this.config.tenant,
419
- query: request.query,
420
- data_sources: request.dataSources || [],
421
- context: request.context || {}
481
+ query: options.query,
482
+ data_sources: options.dataSources || [],
483
+ context: options.context || {},
484
+ };
485
+ const headers = {
486
+ 'Content-Type': 'application/json',
422
487
  };
488
+ // Add authentication headers
489
+ const isLocalhost = this.config.endpoint.includes('localhost') || this.config.endpoint.includes('127.0.0.1');
490
+ if (!isLocalhost) {
491
+ if (this.config.licenseKey) {
492
+ headers['X-License-Key'] = this.config.licenseKey;
493
+ }
494
+ else if (this.config.apiKey) {
495
+ headers['X-Client-Secret'] = this.config.apiKey;
496
+ }
497
+ }
423
498
  if (this.config.debug) {
424
- (0, helpers_1.debugLog)('Gateway pre-check request', {
425
- query: request.query.substring(0, 50),
426
- dataSources: request.dataSources
427
- });
499
+ (0, helpers_1.debugLog)('Gateway Mode: Pre-check', { query: options.query.substring(0, 50) });
428
500
  }
429
- const startTime = Date.now();
430
501
  const response = await fetch(url, {
431
502
  method: 'POST',
432
- headers: {
433
- 'Content-Type': 'application/json',
434
- 'X-Client-Secret': this.config.apiKey,
435
- 'X-License-Key': this.config.apiKey
436
- },
437
- body: JSON.stringify(body),
438
- signal: AbortSignal.timeout(this.config.timeout)
503
+ headers,
504
+ body: JSON.stringify(requestBody),
505
+ signal: AbortSignal.timeout(this.config.timeout),
439
506
  });
440
- const duration = Date.now() - startTime;
441
507
  if (!response.ok) {
442
508
  const errorText = await response.text();
443
- throw new Error(`Pre-check failed: ${response.status} ${response.statusText} - ${errorText}`);
509
+ if (response.status === 401 || response.status === 403) {
510
+ throw new errors_1.AuthenticationError(`Policy pre-check authentication failed: ${errorText}`);
511
+ }
512
+ throw new errors_1.APIError(response.status, response.statusText, errorText);
444
513
  }
445
514
  const data = await response.json();
446
- if (this.config.debug) {
447
- (0, helpers_1.debugLog)('Gateway pre-check complete', {
448
- contextId: data.context_id,
449
- approved: data.approved,
450
- duration
451
- });
452
- }
453
- return {
515
+ // Transform snake_case response to camelCase
516
+ // Default expiration to 5 minutes from now if not provided
517
+ const expiresAt = data.expires_at
518
+ ? new Date(data.expires_at)
519
+ : new Date(Date.now() + 5 * 60 * 1000);
520
+ const result = {
454
521
  contextId: data.context_id,
455
522
  approved: data.approved,
456
- approvedData: data.approved_data,
523
+ approvedData: data.approved_data || {},
457
524
  policies: data.policies || [],
458
- rateLimitInfo: data.rate_limit ? {
525
+ expiresAt,
526
+ blockReason: data.block_reason,
527
+ };
528
+ // Parse rate limit info if present
529
+ if (data.rate_limit) {
530
+ result.rateLimitInfo = {
459
531
  limit: data.rate_limit.limit,
460
532
  remaining: data.rate_limit.remaining,
461
- resetAt: new Date(data.rate_limit.reset_at)
462
- } : undefined,
463
- expiresAt: new Date(data.expires_at),
464
- blockReason: data.block_reason
465
- };
533
+ resetAt: new Date(data.rate_limit.reset_at),
534
+ };
535
+ }
536
+ if (this.config.debug) {
537
+ (0, helpers_1.debugLog)('Gateway Mode: Pre-check result', {
538
+ approved: result.approved,
539
+ contextId: result.contextId,
540
+ policies: result.policies.length,
541
+ });
542
+ }
543
+ return result;
466
544
  }
467
545
  /**
468
- * Report LLM call details for audit logging
546
+ * Gateway Mode: Audit an LLM call after completion.
469
547
  *
470
- * This is the second step in Gateway Mode. Call this after making your
471
- * LLM call to record it in the audit trail.
472
- *
473
- * @param contextId Context ID from getPolicyApprovedContext()
474
- * @param responseSummary Brief summary of the LLM response (not full response)
475
- * @param provider LLM provider name
476
- * @param model Model name
477
- * @param tokenUsage Token counts from LLM response
478
- * @param latencyMs Time taken for LLM call in milliseconds
479
- * @param metadata Optional additional metadata
480
- * @returns AuditResult confirming the audit was recorded
548
+ * Call this after making a direct LLM call to log the audit trail.
549
+ * This is required for compliance and monitoring.
481
550
  *
482
551
  * @example
483
- * const result = await axonflow.auditLLMCall(
484
- * ctx.contextId,
485
- * 'Found 5 patients with recent lab results',
486
- * 'openai',
487
- * 'gpt-4',
488
- * { promptTokens: 100, completionTokens: 50, totalTokens: 150 },
489
- * 250,
490
- * { sessionId: 'session-123' }
491
- * );
552
+ * ```typescript
553
+ * await axonflow.auditLLMCall({
554
+ * contextId: ctx.contextId,
555
+ * responseSummary: 'Generated report with 5 items',
556
+ * provider: 'openai',
557
+ * model: 'gpt-4',
558
+ * tokenUsage: {
559
+ * promptTokens: 100,
560
+ * completionTokens: 50,
561
+ * totalTokens: 150
562
+ * },
563
+ * latencyMs: 250
564
+ * });
565
+ * ```
492
566
  */
493
- async auditLLMCall(contextId, responseSummary, provider, model, tokenUsage, latencyMs, metadata) {
567
+ async auditLLMCall(options) {
494
568
  const url = `${this.config.endpoint}/api/audit/llm-call`;
495
- const body = {
496
- context_id: contextId,
569
+ const requestBody = {
570
+ context_id: options.contextId,
497
571
  client_id: this.config.tenant,
498
- response_summary: responseSummary,
499
- provider,
500
- model,
572
+ response_summary: options.responseSummary,
573
+ provider: options.provider,
574
+ model: options.model,
501
575
  token_usage: {
502
- prompt_tokens: tokenUsage.promptTokens,
503
- completion_tokens: tokenUsage.completionTokens,
504
- total_tokens: tokenUsage.totalTokens
576
+ prompt_tokens: options.tokenUsage.promptTokens,
577
+ completion_tokens: options.tokenUsage.completionTokens,
578
+ total_tokens: options.tokenUsage.totalTokens,
505
579
  },
506
- latency_ms: latencyMs,
507
- metadata
580
+ latency_ms: options.latencyMs,
581
+ metadata: options.metadata || {},
508
582
  };
583
+ const headers = {
584
+ 'Content-Type': 'application/json',
585
+ };
586
+ // Add authentication headers
587
+ const isLocalhost = this.config.endpoint.includes('localhost') || this.config.endpoint.includes('127.0.0.1');
588
+ if (!isLocalhost) {
589
+ if (this.config.licenseKey) {
590
+ headers['X-License-Key'] = this.config.licenseKey;
591
+ }
592
+ else if (this.config.apiKey) {
593
+ headers['X-Client-Secret'] = this.config.apiKey;
594
+ }
595
+ }
509
596
  if (this.config.debug) {
510
- (0, helpers_1.debugLog)('Gateway audit request', {
511
- contextId,
512
- provider,
513
- model,
514
- tokens: tokenUsage.totalTokens
597
+ (0, helpers_1.debugLog)('Gateway Mode: Audit', {
598
+ contextId: options.contextId,
599
+ provider: options.provider,
600
+ model: options.model,
515
601
  });
516
602
  }
517
- const startTime = Date.now();
518
603
  const response = await fetch(url, {
519
604
  method: 'POST',
520
- headers: {
521
- 'Content-Type': 'application/json',
522
- 'X-Client-Secret': this.config.apiKey,
523
- 'X-License-Key': this.config.apiKey
524
- },
525
- body: JSON.stringify(body),
526
- signal: AbortSignal.timeout(this.config.timeout)
605
+ headers,
606
+ body: JSON.stringify(requestBody),
607
+ signal: AbortSignal.timeout(this.config.timeout),
527
608
  });
528
- const duration = Date.now() - startTime;
529
609
  if (!response.ok) {
530
610
  const errorText = await response.text();
531
- throw new Error(`Audit failed: ${response.status} ${response.statusText} - ${errorText}`);
611
+ if (response.status === 401 || response.status === 403) {
612
+ throw new errors_1.AuthenticationError(`Audit logging authentication failed: ${errorText}`);
613
+ }
614
+ throw new errors_1.APIError(response.status, response.statusText, errorText);
532
615
  }
533
616
  const data = await response.json();
534
- if (this.config.debug) {
535
- (0, helpers_1.debugLog)('Gateway audit complete', {
536
- auditId: data.audit_id,
537
- duration
538
- });
539
- }
540
- return {
617
+ const result = {
541
618
  success: data.success,
542
- auditId: data.audit_id
619
+ auditId: data.audit_id,
543
620
  };
621
+ if (this.config.debug) {
622
+ (0, helpers_1.debugLog)('Gateway Mode: Audit logged', { auditId: result.auditId });
623
+ }
624
+ return result;
544
625
  }
545
626
  }
546
627
  exports.AxonFlow = AxonFlow;