@optimizely-opal/opal-tool-ocp-sdk 1.0.0-beta.1 → 1.0.0-beta.10

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 (74) hide show
  1. package/README.md +169 -3
  2. package/dist/auth/AuthUtils.d.ts +12 -5
  3. package/dist/auth/AuthUtils.d.ts.map +1 -1
  4. package/dist/auth/AuthUtils.js +80 -25
  5. package/dist/auth/AuthUtils.js.map +1 -1
  6. package/dist/auth/AuthUtils.test.js +161 -117
  7. package/dist/auth/AuthUtils.test.js.map +1 -1
  8. package/dist/function/GlobalToolFunction.d.ts +5 -3
  9. package/dist/function/GlobalToolFunction.d.ts.map +1 -1
  10. package/dist/function/GlobalToolFunction.js +32 -8
  11. package/dist/function/GlobalToolFunction.js.map +1 -1
  12. package/dist/function/GlobalToolFunction.test.js +73 -12
  13. package/dist/function/GlobalToolFunction.test.js.map +1 -1
  14. package/dist/function/ToolFunction.d.ts +11 -4
  15. package/dist/function/ToolFunction.d.ts.map +1 -1
  16. package/dist/function/ToolFunction.js +45 -9
  17. package/dist/function/ToolFunction.js.map +1 -1
  18. package/dist/function/ToolFunction.test.js +278 -11
  19. package/dist/function/ToolFunction.test.js.map +1 -1
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +3 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/logging/ToolLogger.d.ts +42 -0
  25. package/dist/logging/ToolLogger.d.ts.map +1 -0
  26. package/dist/logging/ToolLogger.js +255 -0
  27. package/dist/logging/ToolLogger.js.map +1 -0
  28. package/dist/logging/ToolLogger.test.d.ts +2 -0
  29. package/dist/logging/ToolLogger.test.d.ts.map +1 -0
  30. package/dist/logging/ToolLogger.test.js +864 -0
  31. package/dist/logging/ToolLogger.test.js.map +1 -0
  32. package/dist/service/Service.d.ts +88 -2
  33. package/dist/service/Service.d.ts.map +1 -1
  34. package/dist/service/Service.js +228 -39
  35. package/dist/service/Service.js.map +1 -1
  36. package/dist/service/Service.test.js +558 -22
  37. package/dist/service/Service.test.js.map +1 -1
  38. package/dist/types/Models.d.ts +7 -1
  39. package/dist/types/Models.d.ts.map +1 -1
  40. package/dist/types/Models.js +5 -1
  41. package/dist/types/Models.js.map +1 -1
  42. package/dist/types/ToolError.d.ts +72 -0
  43. package/dist/types/ToolError.d.ts.map +1 -0
  44. package/dist/types/ToolError.js +107 -0
  45. package/dist/types/ToolError.js.map +1 -0
  46. package/dist/types/ToolError.test.d.ts +2 -0
  47. package/dist/types/ToolError.test.d.ts.map +1 -0
  48. package/dist/types/ToolError.test.js +185 -0
  49. package/dist/types/ToolError.test.js.map +1 -0
  50. package/dist/validation/ParameterValidator.d.ts +31 -0
  51. package/dist/validation/ParameterValidator.d.ts.map +1 -0
  52. package/dist/validation/ParameterValidator.js +129 -0
  53. package/dist/validation/ParameterValidator.js.map +1 -0
  54. package/dist/validation/ParameterValidator.test.d.ts +2 -0
  55. package/dist/validation/ParameterValidator.test.d.ts.map +1 -0
  56. package/dist/validation/ParameterValidator.test.js +323 -0
  57. package/dist/validation/ParameterValidator.test.js.map +1 -0
  58. package/package.json +3 -3
  59. package/src/auth/AuthUtils.test.ts +176 -157
  60. package/src/auth/AuthUtils.ts +96 -33
  61. package/src/function/GlobalToolFunction.test.ts +78 -14
  62. package/src/function/GlobalToolFunction.ts +46 -11
  63. package/src/function/ToolFunction.test.ts +298 -13
  64. package/src/function/ToolFunction.ts +61 -13
  65. package/src/index.ts +2 -1
  66. package/src/logging/ToolLogger.test.ts +1020 -0
  67. package/src/logging/ToolLogger.ts +292 -0
  68. package/src/service/Service.test.ts +712 -28
  69. package/src/service/Service.ts +288 -38
  70. package/src/types/Models.ts +8 -1
  71. package/src/types/ToolError.test.ts +222 -0
  72. package/src/types/ToolError.ts +125 -0
  73. package/src/validation/ParameterValidator.test.ts +371 -0
  74. package/src/validation/ParameterValidator.ts +150 -0
@@ -22,12 +22,22 @@ jest.mock('@zaiusinc/app-sdk', () => ({
22
22
  }
23
23
  },
24
24
  Request: jest.fn().mockImplementation(() => ({})),
25
- Response: jest.fn().mockImplementation((status, data) => ({
25
+ Response: jest.fn().mockImplementation((status, data, headers) => ({
26
26
  status,
27
27
  data,
28
28
  bodyJSON: data,
29
- bodyAsU8Array: new Uint8Array()
29
+ bodyAsU8Array: new Uint8Array(),
30
+ headers
30
31
  })),
32
+ Headers: jest.fn().mockImplementation((entries) => {
33
+ const headers = new Map();
34
+ if (entries) {
35
+ for (const [key, value] of entries) {
36
+ headers.set(key, value);
37
+ }
38
+ }
39
+ return headers;
40
+ }),
31
41
  amendLogContext: jest.fn(),
32
42
  getAppContext: jest.fn(),
33
43
  logger: {
@@ -36,20 +46,23 @@ jest.mock('@zaiusinc/app-sdk', () => ({
36
46
  warn: jest.fn(),
37
47
  debug: jest.fn(),
38
48
  },
49
+ LogVisibility: {
50
+ Zaius: 'zaius'
51
+ },
39
52
  }));
40
53
 
41
54
  // Create a concrete implementation for testing
42
55
  class TestToolFunction extends ToolFunction {
43
- private mockReady: jest.MockedFunction<() => Promise<boolean>>;
56
+ private mockReady: jest.MockedFunction<() => Promise<{ ready: boolean; reason?: string }>>;
44
57
 
45
58
  public constructor(request?: any) {
46
59
  super(request || {});
47
60
  (this as any).request = request;
48
- this.mockReady = jest.fn().mockResolvedValue(true);
61
+ this.mockReady = jest.fn().mockResolvedValue({ ready: true });
49
62
  }
50
63
 
51
64
  // Override the ready method with mock implementation for testing
52
- protected ready(): Promise<boolean> {
65
+ protected ready(): Promise<{ ready: boolean; reason?: string }> {
53
66
  return this.mockReady();
54
67
  }
55
68
 
@@ -161,7 +174,7 @@ describe('ToolFunction', () => {
161
174
  const readyRequest = createReadyRequestWithAuth();
162
175
 
163
176
  toolFunction = new TestToolFunction(readyRequest);
164
- toolFunction.getMockReady().mockResolvedValue(true);
177
+ toolFunction.getMockReady().mockResolvedValue({ ready: true });
165
178
 
166
179
  // Act
167
180
  const result = await toolFunction.perform();
@@ -177,7 +190,7 @@ describe('ToolFunction', () => {
177
190
  const readyRequest = createReadyRequestWithAuth();
178
191
 
179
192
  toolFunction = new TestToolFunction(readyRequest);
180
- toolFunction.getMockReady().mockResolvedValue(false);
193
+ toolFunction.getMockReady().mockResolvedValue({ ready: false });
181
194
 
182
195
  // Act
183
196
  const result = await toolFunction.perform();
@@ -188,6 +201,22 @@ describe('ToolFunction', () => {
188
201
  expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
189
202
  });
190
203
 
204
+ it('should return ready: false with reason when ready method returns false with reason', async () => {
205
+ // Arrange
206
+ const readyRequest = createReadyRequestWithAuth();
207
+
208
+ toolFunction = new TestToolFunction(readyRequest);
209
+ toolFunction.getMockReady().mockResolvedValue({ ready: false, reason: 'Database connection failed' });
210
+
211
+ // Act
212
+ const result = await toolFunction.perform();
213
+
214
+ // Assert
215
+ expect(toolFunction.getMockReady()).toHaveBeenCalledTimes(1);
216
+ expect(result).toEqual(new Response(200, { ready: false, reason: 'Database connection failed' }));
217
+ expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
218
+ });
219
+
191
220
  it('should handle ready method throwing an error', async () => {
192
221
  // Arrange
193
222
  const readyRequest = createReadyRequestWithAuth();
@@ -248,7 +277,13 @@ describe('ToolFunction', () => {
248
277
 
249
278
  const result = await toolFunction.perform();
250
279
 
251
- expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
280
+ expect(result.status).toBe(403);
281
+ expect(result.bodyJSON).toEqual({
282
+ title: 'Forbidden',
283
+ status: 403,
284
+ detail: 'Invalid OptiID access token',
285
+ instance: '/test'
286
+ });
252
287
  expect(mockGetTokenVerifier).toHaveBeenCalled();
253
288
  expect(mockTokenVerifier.verify).toHaveBeenCalledWith('valid-access-token');
254
289
  expect(mockProcessRequest).not.toHaveBeenCalled();
@@ -274,7 +309,13 @@ describe('ToolFunction', () => {
274
309
 
275
310
  const result = await toolFunctionWithDifferentOrgId.perform();
276
311
 
277
- expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
312
+ expect(result.status).toBe(403);
313
+ expect(result.bodyJSON).toEqual({
314
+ title: 'Forbidden',
315
+ status: 403,
316
+ detail: 'Organization ID does not match',
317
+ instance: '/test'
318
+ });
278
319
  expect(mockGetAppContext).toHaveBeenCalled();
279
320
  expect(mockProcessRequest).not.toHaveBeenCalled();
280
321
  });
@@ -299,7 +340,13 @@ describe('ToolFunction', () => {
299
340
 
300
341
  const result = await toolFunctionWithoutToken.perform();
301
342
 
302
- expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
343
+ expect(result.status).toBe(403);
344
+ expect(result.bodyJSON).toEqual({
345
+ title: 'Forbidden',
346
+ status: 403,
347
+ detail: 'OptiID access token is required',
348
+ instance: '/test'
349
+ });
303
350
  expect(mockGetTokenVerifier).not.toHaveBeenCalled();
304
351
  expect(mockProcessRequest).not.toHaveBeenCalled();
305
352
  });
@@ -324,7 +371,13 @@ describe('ToolFunction', () => {
324
371
 
325
372
  const result = await toolFunctionWithoutCustomerId.perform();
326
373
 
327
- expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
374
+ expect(result.status).toBe(403);
375
+ expect(result.bodyJSON).toEqual({
376
+ title: 'Forbidden',
377
+ status: 403,
378
+ detail: 'Organization ID is required',
379
+ instance: '/test'
380
+ });
328
381
  expect(mockGetTokenVerifier).not.toHaveBeenCalled();
329
382
  expect(mockProcessRequest).not.toHaveBeenCalled();
330
383
  });
@@ -343,7 +396,13 @@ describe('ToolFunction', () => {
343
396
 
344
397
  const result = await toolFunctionWithoutAuth.perform();
345
398
 
346
- expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
399
+ expect(result.status).toBe(403);
400
+ expect(result.bodyJSON).toEqual({
401
+ title: 'Forbidden',
402
+ status: 403,
403
+ detail: 'Authentication data is required',
404
+ instance: '/test'
405
+ });
347
406
  expect(mockGetTokenVerifier).not.toHaveBeenCalled();
348
407
  expect(mockProcessRequest).not.toHaveBeenCalled();
349
408
  });
@@ -354,7 +413,13 @@ describe('ToolFunction', () => {
354
413
 
355
414
  const result = await toolFunction.perform();
356
415
 
357
- expect(result).toEqual(new Response(403, { error: 'Forbidden' }));
416
+ expect(result.status).toBe(403);
417
+ expect(result.bodyJSON).toEqual({
418
+ title: 'Forbidden',
419
+ status: 403,
420
+ detail: 'Token verification failed',
421
+ instance: '/test'
422
+ });
358
423
  expect(mockGetTokenVerifier).toHaveBeenCalled();
359
424
  expect(mockProcessRequest).not.toHaveBeenCalled();
360
425
  });
@@ -371,4 +436,224 @@ describe('ToolFunction', () => {
371
436
  expect(toolFunction.getRequest()).toBe(mockRequest);
372
437
  });
373
438
  });
439
+
440
+ describe('internal request authentication', () => {
441
+ beforeEach(() => {
442
+ // Reset mocks before each test
443
+ jest.clearAllMocks();
444
+ setupAuthMocks();
445
+ });
446
+
447
+ it('should use internal authentication for /overrides endpoint', async () => {
448
+ const overridesRequest = {
449
+ path: '/overrides',
450
+ method: 'DELETE',
451
+ bodyJSON: {},
452
+ headers: {
453
+ get: jest.fn().mockImplementation((name: string) => {
454
+ if (name === 'Authorization' || name === 'authorization') return 'internal-token';
455
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
456
+ return null;
457
+ })
458
+ }
459
+ };
460
+
461
+ mockProcessRequest.mockResolvedValue(mockResponse);
462
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
463
+ const result = await toolFunctionOverrides.perform();
464
+
465
+ expect(result).toBe(mockResponse);
466
+ expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
467
+ });
468
+
469
+ it('should throw ToolError when internal authentication fails for /overrides endpoint', async () => {
470
+ const overridesRequest = {
471
+ path: '/overrides',
472
+ method: 'DELETE',
473
+ bodyJSON: {},
474
+ headers: {
475
+ get: jest.fn().mockImplementation((name: string) => {
476
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
477
+ return null; // No Authorization header
478
+ })
479
+ }
480
+ };
481
+
482
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
483
+ const result = await toolFunctionOverrides.perform();
484
+
485
+ expect(result.status).toBe(401);
486
+ expect(result.bodyJSON).toEqual({
487
+ title: 'Unauthorized',
488
+ status: 401,
489
+ detail: 'Internal request authentication failed',
490
+ instance: '/overrides'
491
+ });
492
+ expect(mockProcessRequest).not.toHaveBeenCalled();
493
+ });
494
+
495
+ it('should use regular authentication for non-overrides endpoints', async () => {
496
+ const regularRequest = {
497
+ ...mockRequest,
498
+ path: '/regular-tool',
499
+ headers: {
500
+ get: jest.fn().mockImplementation((name: string) => {
501
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
502
+ return null;
503
+ })
504
+ }
505
+ };
506
+
507
+ mockProcessRequest.mockResolvedValue(mockResponse);
508
+ const toolFunctionRegular = new TestToolFunction(regularRequest);
509
+ const result = await toolFunctionRegular.perform();
510
+
511
+ expect(result).toBe(mockResponse);
512
+ expect(mockProcessRequest).toHaveBeenCalledWith(regularRequest, toolFunctionRegular);
513
+ });
514
+
515
+ it('should handle internal authentication with valid Authorization header', async () => {
516
+ const overridesRequest = {
517
+ path: '/overrides',
518
+ method: 'PATCH',
519
+ bodyJSON: {
520
+ functions: [
521
+ {
522
+ name: 'test_tool',
523
+ description: 'Updated description'
524
+ }
525
+ ]
526
+ },
527
+ headers: {
528
+ get: jest.fn().mockImplementation((name: string) => {
529
+ if (name === 'Authorization') return 'valid-internal-token';
530
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
531
+ return null;
532
+ })
533
+ }
534
+ };
535
+
536
+ mockProcessRequest.mockResolvedValue(mockResponse);
537
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
538
+ const result = await toolFunctionOverrides.perform();
539
+
540
+ expect(result).toBe(mockResponse);
541
+ expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
542
+ });
543
+
544
+ it('should handle internal authentication with lowercase authorization header', async () => {
545
+ const overridesRequest = {
546
+ path: '/overrides',
547
+ method: 'PATCH',
548
+ bodyJSON: {
549
+ functions: [
550
+ {
551
+ name: 'test_tool',
552
+ description: 'Updated description'
553
+ }
554
+ ]
555
+ },
556
+ headers: {
557
+ get: jest.fn().mockImplementation((name: string) => {
558
+ if (name === 'authorization') return 'valid-internal-token';
559
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
560
+ return null;
561
+ })
562
+ }
563
+ };
564
+
565
+ mockProcessRequest.mockResolvedValue(mockResponse);
566
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
567
+ const result = await toolFunctionOverrides.perform();
568
+
569
+ expect(result).toBe(mockResponse);
570
+ expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
571
+ });
572
+
573
+ it('should fail internal authentication when token verification fails', async () => {
574
+ // Mock token verifier to return false for invalid token
575
+ mockTokenVerifier.verify.mockResolvedValue(false);
576
+
577
+ const overridesRequest = {
578
+ path: '/overrides',
579
+ method: 'DELETE',
580
+ bodyJSON: {},
581
+ headers: {
582
+ get: jest.fn().mockImplementation((name: string) => {
583
+ if (name === 'Authorization') return 'invalid-token';
584
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
585
+ return null;
586
+ })
587
+ }
588
+ };
589
+
590
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
591
+ const result = await toolFunctionOverrides.perform();
592
+
593
+ expect(result.status).toBe(401);
594
+ expect(result.bodyJSON).toEqual({
595
+ title: 'Unauthorized',
596
+ status: 401,
597
+ detail: 'Internal request authentication failed',
598
+ instance: '/overrides'
599
+ });
600
+ expect(mockProcessRequest).not.toHaveBeenCalled();
601
+ });
602
+
603
+ it('should fail internal authentication when token verifier throws error', async () => {
604
+ // Mock token verifier to throw an error
605
+ mockTokenVerifier.verify.mockRejectedValue(new Error('Token service unavailable'));
606
+
607
+ const overridesRequest = {
608
+ path: '/overrides',
609
+ method: 'DELETE',
610
+ bodyJSON: {},
611
+ headers: {
612
+ get: jest.fn().mockImplementation((name: string) => {
613
+ if (name === 'Authorization') return 'some-token';
614
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
615
+ return null;
616
+ })
617
+ }
618
+ };
619
+
620
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
621
+ const result = await toolFunctionOverrides.perform();
622
+
623
+ expect(result.status).toBe(401);
624
+ expect(result.bodyJSON).toEqual({
625
+ title: 'Unauthorized',
626
+ status: 401,
627
+ detail: 'Internal request authentication failed',
628
+ instance: '/overrides'
629
+ });
630
+ expect(mockProcessRequest).not.toHaveBeenCalled();
631
+ });
632
+
633
+ it('should fail internal authentication when headers object is missing', async () => {
634
+ const overridesRequest = {
635
+ path: '/overrides',
636
+ method: 'DELETE',
637
+ bodyJSON: {},
638
+ headers: {
639
+ get: jest.fn().mockImplementation((name: string) => {
640
+ if (name === 'x-opal-thread-id') return 'test-thread-id';
641
+ return null;
642
+ })
643
+ }
644
+ };
645
+
646
+ const toolFunctionOverrides = new TestToolFunction(overridesRequest);
647
+ const result = await toolFunctionOverrides.perform();
648
+
649
+ expect(result.status).toBe(401);
650
+ expect(result.bodyJSON).toEqual({
651
+ title: 'Unauthorized',
652
+ status: 401,
653
+ detail: 'Internal request authentication failed',
654
+ instance: '/overrides'
655
+ });
656
+ expect(mockProcessRequest).not.toHaveBeenCalled();
657
+ });
658
+ });
374
659
  });
@@ -1,6 +1,9 @@
1
- import { Function, Response, amendLogContext } from '@zaiusinc/app-sdk';
2
- import { authenticateRegularRequest } from '../auth/AuthUtils';
1
+ import { Function, Response, Headers, amendLogContext } from '@zaiusinc/app-sdk';
2
+ import { authenticateRegularRequest, authenticateInternalRequest } from '../auth/AuthUtils';
3
3
  import { toolsService } from '../service/Service';
4
+ import { ToolLogger } from '../logging/ToolLogger';
5
+ import { ToolError } from '../types/ToolError';
6
+ import { ReadyResponse } from '../types/Models';
4
7
 
5
8
  /**
6
9
  * Abstract base class for tool-based function execution
@@ -12,27 +15,66 @@ export abstract class ToolFunction extends Function {
12
15
  * Override this method to implement any required credentials and/or other configuration
13
16
  * exist and are valid. Reasonable caching should be utilized to prevent excessive requests to external resources.
14
17
  * @async
15
- * @returns true if the opal function is ready to use
18
+ * @returns ReadyResponse containing ready status and optional reason when not ready
16
19
  */
17
- protected ready(): Promise<boolean> {
18
- return Promise.resolve(true);
20
+ protected ready(): Promise<ReadyResponse | boolean> {
21
+ return Promise.resolve({ ready: true });
19
22
  }
20
23
 
21
24
  /**
22
- * Process the incoming request using the tools service
25
+ * Process the incoming request with logging
23
26
  *
24
27
  * @returns Response as the HTTP response
25
28
  */
26
29
  public async perform(): Promise<Response> {
30
+ const startTime = Date.now();
27
31
  amendLogContext({ opalThreadId: this.request.headers.get('x-opal-thread-id') || '' });
28
- if (!(await this.authorizeRequest())) {
29
- return new Response(403, { error: 'Forbidden' });
32
+
33
+ ToolLogger.logRequest(this.request);
34
+
35
+ const response = await this.handleRequest();
36
+
37
+ ToolLogger.logResponse(this.request, response, Date.now() - startTime);
38
+ return response;
39
+ }
40
+
41
+ /**
42
+ * Handle the core request processing logic
43
+ *
44
+ * @returns Response as the HTTP response
45
+ */
46
+ private async handleRequest(): Promise<Response> {
47
+ try {
48
+ await this.authorizeRequest();
49
+ } catch (error) {
50
+ if (error instanceof ToolError) {
51
+ return new Response(
52
+ error.status,
53
+ error.toProblemDetails(this.request.path),
54
+ new Headers([['content-type', 'application/problem+json']])
55
+ );
56
+ }
57
+ // Fallback for unexpected errors
58
+ return new Response(
59
+ 500,
60
+ {
61
+ title: 'Internal Server Error',
62
+ status: 500,
63
+ detail: 'An unexpected error occurred during authentication',
64
+ instance: this.request.path
65
+ },
66
+ new Headers([['content-type', 'application/problem+json']])
67
+ );
30
68
  }
31
69
 
32
70
  if (this.request.path === '/ready') {
33
- const isReady = await this.ready();
34
- return new Response(200, { ready: isReady });
71
+ const readyResult = await this.ready();
72
+ const readyResponse = typeof readyResult === 'boolean'
73
+ ? { ready: readyResult }
74
+ : readyResult;
75
+ return new Response(200, readyResponse);
35
76
  }
77
+
36
78
  // Pass 'this' as context so decorated methods can use the existing instance
37
79
  return toolsService.processRequest(this.request, this);
38
80
  }
@@ -40,9 +82,15 @@ export abstract class ToolFunction extends Function {
40
82
  /**
41
83
  * Authenticate the incoming request by validating the OptiID token and organization ID
42
84
  *
43
- * @returns true if authentication succeeds
85
+ * @throws {ToolError} If authentication fails
44
86
  */
45
- private async authorizeRequest(): Promise<boolean> {
46
- return await authenticateRegularRequest(this.request);
87
+ private async authorizeRequest(): Promise<void> {
88
+ // Use internal authentication for overrides endpoint (header-based token)
89
+ if (this.request.path === '/overrides') {
90
+ await authenticateInternalRequest(this.request);
91
+ } else {
92
+ // Use regular authentication for other endpoints (body-based token with org validation)
93
+ await authenticateRegularRequest(this.request);
94
+ }
47
95
  }
48
96
  }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from './function/ToolFunction';
2
2
  export * from './function/GlobalToolFunction';
3
3
  export * from './types/Models';
4
+ export * from './types/ToolError';
4
5
  export * from './decorator/Decorator';
5
6
  export * from './auth/TokenVerifier';
6
- export { Tool, Interaction, InteractionResult } from './service/Service';
7
+ export { Tool, Interaction, InteractionResult, NestedInteractions } from './service/Service';