@optimizely-opal/opal-tool-ocp-sdk 1.0.0-beta.6 → 1.0.0-beta.7

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 (55) hide show
  1. package/README.md +13 -3
  2. package/dist/auth/AuthUtils.d.ts +0 -7
  3. package/dist/auth/AuthUtils.d.ts.map +1 -1
  4. package/dist/auth/AuthUtils.js +0 -27
  5. package/dist/auth/AuthUtils.js.map +1 -1
  6. package/dist/auth/AuthUtils.test.js +0 -99
  7. package/dist/auth/AuthUtils.test.js.map +1 -1
  8. package/dist/function/GlobalToolFunction.d.ts +3 -2
  9. package/dist/function/GlobalToolFunction.d.ts.map +1 -1
  10. package/dist/function/GlobalToolFunction.js +4 -4
  11. package/dist/function/GlobalToolFunction.js.map +1 -1
  12. package/dist/function/GlobalToolFunction.test.js +16 -4
  13. package/dist/function/GlobalToolFunction.test.js.map +1 -1
  14. package/dist/function/ToolFunction.d.ts +3 -2
  15. package/dist/function/ToolFunction.d.ts.map +1 -1
  16. package/dist/function/ToolFunction.js +5 -12
  17. package/dist/function/ToolFunction.js.map +1 -1
  18. package/dist/function/ToolFunction.test.js +15 -209
  19. package/dist/function/ToolFunction.test.js.map +1 -1
  20. package/dist/logging/ToolLogger.d.ts.map +1 -1
  21. package/dist/logging/ToolLogger.js +1 -2
  22. package/dist/logging/ToolLogger.js.map +1 -1
  23. package/dist/logging/ToolLogger.test.js +2 -114
  24. package/dist/logging/ToolLogger.test.js.map +1 -1
  25. package/dist/service/Service.d.ts +0 -73
  26. package/dist/service/Service.d.ts.map +1 -1
  27. package/dist/service/Service.js +42 -188
  28. package/dist/service/Service.js.map +1 -1
  29. package/dist/service/Service.test.js +34 -380
  30. package/dist/service/Service.test.js.map +1 -1
  31. package/dist/types/Models.d.ts +4 -0
  32. package/dist/types/Models.d.ts.map +1 -1
  33. package/dist/types/ToolError.d.ts +0 -13
  34. package/dist/types/ToolError.d.ts.map +1 -1
  35. package/dist/types/ToolError.js +2 -30
  36. package/dist/types/ToolError.js.map +1 -1
  37. package/dist/types/ToolError.test.js +3 -27
  38. package/dist/types/ToolError.test.js.map +1 -1
  39. package/dist/validation/ParameterValidator.test.js +1 -2
  40. package/dist/validation/ParameterValidator.test.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/auth/AuthUtils.test.ts +1 -115
  43. package/src/auth/AuthUtils.ts +1 -30
  44. package/src/function/GlobalToolFunction.test.ts +21 -6
  45. package/src/function/GlobalToolFunction.ts +6 -5
  46. package/src/function/ToolFunction.test.ts +21 -225
  47. package/src/function/ToolFunction.ts +8 -13
  48. package/src/logging/ToolLogger.test.ts +2 -118
  49. package/src/logging/ToolLogger.ts +1 -2
  50. package/src/service/Service.test.ts +32 -474
  51. package/src/service/Service.ts +41 -245
  52. package/src/types/Models.ts +5 -0
  53. package/src/types/ToolError.test.ts +3 -33
  54. package/src/types/ToolError.ts +2 -32
  55. package/src/validation/ParameterValidator.test.ts +1 -4
@@ -53,16 +53,16 @@ jest.mock('@zaiusinc/app-sdk', () => ({
53
53
 
54
54
  // Create a concrete implementation for testing
55
55
  class TestToolFunction extends ToolFunction {
56
- private mockReady: jest.MockedFunction<() => Promise<boolean>>;
56
+ private mockReady: jest.MockedFunction<() => Promise<{ ready: boolean; reason?: string }>>;
57
57
 
58
58
  public constructor(request?: any) {
59
59
  super(request || {});
60
60
  (this as any).request = request;
61
- this.mockReady = jest.fn().mockResolvedValue(true);
61
+ this.mockReady = jest.fn().mockResolvedValue({ ready: true });
62
62
  }
63
63
 
64
64
  // Override the ready method with mock implementation for testing
65
- protected ready(): Promise<boolean> {
65
+ protected ready(): Promise<{ ready: boolean; reason?: string }> {
66
66
  return this.mockReady();
67
67
  }
68
68
 
@@ -174,7 +174,7 @@ describe('ToolFunction', () => {
174
174
  const readyRequest = createReadyRequestWithAuth();
175
175
 
176
176
  toolFunction = new TestToolFunction(readyRequest);
177
- toolFunction.getMockReady().mockResolvedValue(true);
177
+ toolFunction.getMockReady().mockResolvedValue({ ready: true });
178
178
 
179
179
  // Act
180
180
  const result = await toolFunction.perform();
@@ -190,7 +190,7 @@ describe('ToolFunction', () => {
190
190
  const readyRequest = createReadyRequestWithAuth();
191
191
 
192
192
  toolFunction = new TestToolFunction(readyRequest);
193
- toolFunction.getMockReady().mockResolvedValue(false);
193
+ toolFunction.getMockReady().mockResolvedValue({ ready: false });
194
194
 
195
195
  // Act
196
196
  const result = await toolFunction.perform();
@@ -201,6 +201,22 @@ describe('ToolFunction', () => {
201
201
  expect(mockProcessRequest).not.toHaveBeenCalled(); // Should not call service
202
202
  });
203
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
+
204
220
  it('should handle ready method throwing an error', async () => {
205
221
  // Arrange
206
222
  const readyRequest = createReadyRequestWithAuth();
@@ -420,224 +436,4 @@ describe('ToolFunction', () => {
420
436
  expect(toolFunction.getRequest()).toBe(mockRequest);
421
437
  });
422
438
  });
423
-
424
- describe('internal request authentication', () => {
425
- beforeEach(() => {
426
- // Reset mocks before each test
427
- jest.clearAllMocks();
428
- setupAuthMocks();
429
- });
430
-
431
- it('should use internal authentication for /overrides endpoint', async () => {
432
- const overridesRequest = {
433
- path: '/overrides',
434
- method: 'DELETE',
435
- bodyJSON: {},
436
- headers: {
437
- get: jest.fn().mockImplementation((name: string) => {
438
- if (name === 'Authorization' || name === 'authorization') return 'internal-token';
439
- if (name === 'x-opal-thread-id') return 'test-thread-id';
440
- return null;
441
- })
442
- }
443
- };
444
-
445
- mockProcessRequest.mockResolvedValue(mockResponse);
446
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
447
- const result = await toolFunctionOverrides.perform();
448
-
449
- expect(result).toBe(mockResponse);
450
- expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
451
- });
452
-
453
- it('should throw ToolError when internal authentication fails for /overrides endpoint', async () => {
454
- const overridesRequest = {
455
- path: '/overrides',
456
- method: 'DELETE',
457
- bodyJSON: {},
458
- headers: {
459
- get: jest.fn().mockImplementation((name: string) => {
460
- if (name === 'x-opal-thread-id') return 'test-thread-id';
461
- return null; // No Authorization header
462
- })
463
- }
464
- };
465
-
466
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
467
- const result = await toolFunctionOverrides.perform();
468
-
469
- expect(result.status).toBe(401);
470
- expect(result.bodyJSON).toEqual({
471
- title: 'Unauthorized',
472
- status: 401,
473
- detail: 'Internal request authentication failed',
474
- instance: '/overrides'
475
- });
476
- expect(mockProcessRequest).not.toHaveBeenCalled();
477
- });
478
-
479
- it('should use regular authentication for non-overrides endpoints', async () => {
480
- const regularRequest = {
481
- ...mockRequest,
482
- path: '/regular-tool',
483
- headers: {
484
- get: jest.fn().mockImplementation((name: string) => {
485
- if (name === 'x-opal-thread-id') return 'test-thread-id';
486
- return null;
487
- })
488
- }
489
- };
490
-
491
- mockProcessRequest.mockResolvedValue(mockResponse);
492
- const toolFunctionRegular = new TestToolFunction(regularRequest);
493
- const result = await toolFunctionRegular.perform();
494
-
495
- expect(result).toBe(mockResponse);
496
- expect(mockProcessRequest).toHaveBeenCalledWith(regularRequest, toolFunctionRegular);
497
- });
498
-
499
- it('should handle internal authentication with valid Authorization header', async () => {
500
- const overridesRequest = {
501
- path: '/overrides',
502
- method: 'PATCH',
503
- bodyJSON: {
504
- functions: [
505
- {
506
- name: 'test_tool',
507
- description: 'Updated description'
508
- }
509
- ]
510
- },
511
- headers: {
512
- get: jest.fn().mockImplementation((name: string) => {
513
- if (name === 'Authorization') return 'valid-internal-token';
514
- if (name === 'x-opal-thread-id') return 'test-thread-id';
515
- return null;
516
- })
517
- }
518
- };
519
-
520
- mockProcessRequest.mockResolvedValue(mockResponse);
521
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
522
- const result = await toolFunctionOverrides.perform();
523
-
524
- expect(result).toBe(mockResponse);
525
- expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
526
- });
527
-
528
- it('should handle internal authentication with lowercase authorization header', async () => {
529
- const overridesRequest = {
530
- path: '/overrides',
531
- method: 'PATCH',
532
- bodyJSON: {
533
- functions: [
534
- {
535
- name: 'test_tool',
536
- description: 'Updated description'
537
- }
538
- ]
539
- },
540
- headers: {
541
- get: jest.fn().mockImplementation((name: string) => {
542
- if (name === 'authorization') return 'valid-internal-token';
543
- if (name === 'x-opal-thread-id') return 'test-thread-id';
544
- return null;
545
- })
546
- }
547
- };
548
-
549
- mockProcessRequest.mockResolvedValue(mockResponse);
550
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
551
- const result = await toolFunctionOverrides.perform();
552
-
553
- expect(result).toBe(mockResponse);
554
- expect(mockProcessRequest).toHaveBeenCalledWith(overridesRequest, toolFunctionOverrides);
555
- });
556
-
557
- it('should fail internal authentication when token verification fails', async () => {
558
- // Mock token verifier to return false for invalid token
559
- mockTokenVerifier.verify.mockResolvedValue(false);
560
-
561
- const overridesRequest = {
562
- path: '/overrides',
563
- method: 'DELETE',
564
- bodyJSON: {},
565
- headers: {
566
- get: jest.fn().mockImplementation((name: string) => {
567
- if (name === 'Authorization') return 'invalid-token';
568
- if (name === 'x-opal-thread-id') return 'test-thread-id';
569
- return null;
570
- })
571
- }
572
- };
573
-
574
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
575
- const result = await toolFunctionOverrides.perform();
576
-
577
- expect(result.status).toBe(401);
578
- expect(result.bodyJSON).toEqual({
579
- title: 'Unauthorized',
580
- status: 401,
581
- detail: 'Internal request authentication failed',
582
- instance: '/overrides'
583
- });
584
- expect(mockProcessRequest).not.toHaveBeenCalled();
585
- });
586
-
587
- it('should fail internal authentication when token verifier throws error', async () => {
588
- // Mock token verifier to throw an error
589
- mockTokenVerifier.verify.mockRejectedValue(new Error('Token service unavailable'));
590
-
591
- const overridesRequest = {
592
- path: '/overrides',
593
- method: 'DELETE',
594
- bodyJSON: {},
595
- headers: {
596
- get: jest.fn().mockImplementation((name: string) => {
597
- if (name === 'Authorization') return 'some-token';
598
- if (name === 'x-opal-thread-id') return 'test-thread-id';
599
- return null;
600
- })
601
- }
602
- };
603
-
604
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
605
- const result = await toolFunctionOverrides.perform();
606
-
607
- expect(result.status).toBe(401);
608
- expect(result.bodyJSON).toEqual({
609
- title: 'Unauthorized',
610
- status: 401,
611
- detail: 'Internal request authentication failed',
612
- instance: '/overrides'
613
- });
614
- expect(mockProcessRequest).not.toHaveBeenCalled();
615
- });
616
-
617
- it('should fail internal authentication when headers object is missing', async () => {
618
- const overridesRequest = {
619
- path: '/overrides',
620
- method: 'DELETE',
621
- bodyJSON: {},
622
- headers: {
623
- get: jest.fn().mockImplementation((name: string) => {
624
- if (name === 'x-opal-thread-id') return 'test-thread-id';
625
- return null;
626
- })
627
- }
628
- };
629
-
630
- const toolFunctionOverrides = new TestToolFunction(overridesRequest);
631
- const result = await toolFunctionOverrides.perform();
632
-
633
- expect(result.status).toBe(401);
634
- expect(result.bodyJSON).toEqual({
635
- title: 'Unauthorized',
636
- status: 401,
637
- detail: 'Internal request authentication failed',
638
- instance: '/overrides'
639
- });
640
- expect(mockProcessRequest).not.toHaveBeenCalled();
641
- });
642
- });
643
439
  });
@@ -1,8 +1,9 @@
1
1
  import { Function, Response, Headers, amendLogContext } from '@zaiusinc/app-sdk';
2
- import { authenticateRegularRequest, authenticateInternalRequest } from '../auth/AuthUtils';
2
+ import { authenticateRegularRequest } from '../auth/AuthUtils';
3
3
  import { toolsService } from '../service/Service';
4
4
  import { ToolLogger } from '../logging/ToolLogger';
5
5
  import { ToolError } from '../types/ToolError';
6
+ import { ReadyResponse } from '../types/Models';
6
7
 
7
8
  /**
8
9
  * Abstract base class for tool-based function execution
@@ -14,10 +15,10 @@ export abstract class ToolFunction extends Function {
14
15
  * Override this method to implement any required credentials and/or other configuration
15
16
  * exist and are valid. Reasonable caching should be utilized to prevent excessive requests to external resources.
16
17
  * @async
17
- * @returns true if the opal function is ready to use
18
+ * @returns ReadyResponse containing ready status and optional reason when not ready
18
19
  */
19
- protected ready(): Promise<boolean> {
20
- return Promise.resolve(true);
20
+ protected ready(): Promise<ReadyResponse> {
21
+ return Promise.resolve({ ready: true });
21
22
  }
22
23
 
23
24
  /**
@@ -67,8 +68,8 @@ export abstract class ToolFunction extends Function {
67
68
  }
68
69
 
69
70
  if (this.request.path === '/ready') {
70
- const isReady = await this.ready();
71
- return new Response(200, { ready: isReady });
71
+ const readyResponse = await this.ready();
72
+ return new Response(200, readyResponse);
72
73
  }
73
74
 
74
75
  // Pass 'this' as context so decorated methods can use the existing instance
@@ -81,12 +82,6 @@ export abstract class ToolFunction extends Function {
81
82
  * @throws {ToolError} If authentication fails
82
83
  */
83
84
  private async authorizeRequest(): Promise<void> {
84
- // Use internal authentication for overrides endpoint (header-based token)
85
- if (this.request.path === '/overrides') {
86
- await authenticateInternalRequest(this.request);
87
- } else {
88
- // Use regular authentication for other endpoints (body-based token with org validation)
89
- await authenticateRegularRequest(this.request);
90
- }
85
+ await authenticateRegularRequest(this.request);
91
86
  }
92
87
  }
@@ -32,7 +32,6 @@ describe('ToolLogger', () => {
32
32
  const createMockRequest = (overrides: any = {}): App.Request => {
33
33
  const defaultRequest = {
34
34
  path: '/test-tool',
35
- method: 'POST',
36
35
  bodyJSON: {
37
36
  parameters: {
38
37
  name: 'test',
@@ -97,7 +96,6 @@ describe('ToolLogger', () => {
97
96
  const expectedLog = {
98
97
  event: 'opal_tool_request',
99
98
  path: '/test-tool',
100
- method: 'POST',
101
99
  parameters: {
102
100
  name: 'test',
103
101
  value: 'data'
@@ -120,7 +118,6 @@ describe('ToolLogger', () => {
120
118
  expectJsonLog({
121
119
  event: 'opal_tool_request',
122
120
  path: '/test-tool',
123
- method: 'POST',
124
121
  parameters: null
125
122
  });
126
123
  });
@@ -138,7 +135,6 @@ describe('ToolLogger', () => {
138
135
  expectJsonLog({
139
136
  event: 'opal_tool_request',
140
137
  path: '/test-tool',
141
- method: 'POST',
142
138
  parameters: {
143
139
  name: 'direct',
144
140
  action: 'test'
@@ -171,7 +167,6 @@ describe('ToolLogger', () => {
171
167
  expectJsonLog({
172
168
  event: 'opal_tool_request',
173
169
  path: '/test-tool',
174
- method: 'POST',
175
170
  parameters: {
176
171
  username: 'john',
177
172
  password: '[REDACTED]',
@@ -209,7 +204,6 @@ describe('ToolLogger', () => {
209
204
  expectJsonLog({
210
205
  event: 'opal_tool_request',
211
206
  path: '/test-tool',
212
- method: 'POST',
213
207
  parameters: {
214
208
  PASSWORD: '[REDACTED]',
215
209
  API_KEY: '[REDACTED]',
@@ -238,7 +232,6 @@ describe('ToolLogger', () => {
238
232
  expectJsonLog({
239
233
  event: 'opal_tool_request',
240
234
  path: '/test-tool',
241
- method: 'POST',
242
235
  parameters: {
243
236
  description: `${'a'.repeat(100)}... (truncated, 150 chars total)`,
244
237
  short_field: 'normal'
@@ -262,7 +255,6 @@ describe('ToolLogger', () => {
262
255
  expectJsonLog({
263
256
  event: 'opal_tool_request',
264
257
  path: '/test-tool',
265
- method: 'POST',
266
258
  parameters: {
267
259
  items: [
268
260
  ...largeArray.slice(0, 10),
@@ -299,7 +291,6 @@ describe('ToolLogger', () => {
299
291
  expectJsonLog({
300
292
  event: 'opal_tool_request',
301
293
  path: '/test-tool',
302
- method: 'POST',
303
294
  parameters: {
304
295
  user: {
305
296
  name: 'John',
@@ -337,7 +328,6 @@ describe('ToolLogger', () => {
337
328
  expectJsonLog({
338
329
  event: 'opal_tool_request',
339
330
  path: '/test-tool',
340
- method: 'POST',
341
331
  parameters: {
342
332
  nullValue: null,
343
333
  emptyString: '',
@@ -363,7 +353,6 @@ describe('ToolLogger', () => {
363
353
  expectJsonLog({
364
354
  event: 'opal_tool_request',
365
355
  path: '/test-tool',
366
- method: 'POST',
367
356
  parameters: {
368
357
  credentials: '[REDACTED]',
369
358
  public_list: ['item1', 'item2']
@@ -392,7 +381,6 @@ describe('ToolLogger', () => {
392
381
  expectJsonLog({
393
382
  event: 'opal_tool_request',
394
383
  path: '/test-tool',
395
- method: 'POST',
396
384
  parameters: {
397
385
  auth: '[REDACTED]',
398
386
  public_config: {
@@ -508,9 +496,7 @@ describe('ToolLogger', () => {
508
496
  // First item: deeply nested object with inner parts replaced by placeholder
509
497
  expect(items[0]).toEqual({
510
498
  level2: {
511
- level3: {
512
- level4: '[MAX_DEPTH_EXCEEDED]'
513
- }
499
+ level3: '[MAX_DEPTH_EXCEEDED]'
514
500
  }
515
501
  });
516
502
 
@@ -523,9 +509,7 @@ describe('ToolLogger', () => {
523
509
  // Fourth item: another deeply nested object with inner parts replaced by placeholder
524
510
  expect(items[3]).toEqual({
525
511
  level2: {
526
- level3: {
527
- level4: '[MAX_DEPTH_EXCEEDED]'
528
- }
512
+ level3: '[MAX_DEPTH_EXCEEDED]'
529
513
  }
530
514
  });
531
515
  });
@@ -710,7 +694,6 @@ describe('ToolLogger', () => {
710
694
  expectJsonLog({
711
695
  event: 'opal_tool_request',
712
696
  path: '/test-tool',
713
- method: 'POST',
714
697
  parameters: {}
715
698
  });
716
699
  });
@@ -729,7 +712,6 @@ describe('ToolLogger', () => {
729
712
  expectJsonLog({
730
713
  event: 'opal_tool_request',
731
714
  path: '/test-tool',
732
- method: 'POST',
733
715
  parameters: {
734
716
  field: 'value'
735
717
  }
@@ -756,7 +738,6 @@ describe('ToolLogger', () => {
756
738
  expectJsonLog({
757
739
  event: 'opal_tool_request',
758
740
  path: '/test-tool',
759
- method: 'POST',
760
741
  parameters: {
761
742
  string: 'text',
762
743
  number: 42,
@@ -768,102 +749,5 @@ describe('ToolLogger', () => {
768
749
  }
769
750
  });
770
751
  });
771
-
772
- it('should handle tool override request with enhanced parameter descriptions', () => {
773
- const overrideRequest = {
774
- tools: [
775
- {
776
- name: 'calculate_experiment_runtime',
777
- description: 'OVERRIDDEN: Enhanced experiment runtime calculator with advanced features',
778
- parameters: [
779
- {
780
- name: 'BCR',
781
- type: 'number',
782
- description: 'OVERRIDDEN: Enhanced baseline conversion rate with validation',
783
- required: true
784
- },
785
- {
786
- name: 'MDE',
787
- type: 'number',
788
- description: 'OVERRIDDEN: Enhanced minimum detectable effect calculation',
789
- required: true
790
- },
791
- {
792
- name: 'sigLevel',
793
- type: 'number',
794
- description: 'OVERRIDDEN: Enhanced statistical significance level',
795
- required: true
796
- },
797
- {
798
- name: 'numVariations',
799
- type: 'number',
800
- description: 'OVERRIDDEN: Enhanced number of variations handling',
801
- required: true
802
- },
803
- {
804
- name: 'dailyVisitors',
805
- type: 'number',
806
- description: 'OVERRIDDEN: Enhanced daily visitor count with forecasting',
807
- required: true
808
- }
809
- ]
810
- }
811
- // Note: NOT including calculate_sample_size in override
812
- ]
813
- };
814
-
815
- const req = createMockRequest({
816
- path: '/overrides',
817
- bodyJSON: overrideRequest
818
- });
819
-
820
- ToolLogger.logRequest(req);
821
-
822
- expectJsonLog({
823
- event: 'opal_tool_request',
824
- path: '/overrides',
825
- method: 'POST',
826
- parameters: {
827
- tools: [
828
- {
829
- name: 'calculate_experiment_runtime',
830
- description: 'OVERRIDDEN: Enhanced experiment runtime calculator with advanced features',
831
- parameters: [
832
- {
833
- name: 'BCR',
834
- type: 'number',
835
- description: 'OVERRIDDEN: Enhanced baseline conversion rate with validation',
836
- required: true
837
- },
838
- {
839
- name: 'MDE',
840
- type: 'number',
841
- description: 'OVERRIDDEN: Enhanced minimum detectable effect calculation',
842
- required: true
843
- },
844
- {
845
- name: 'sigLevel',
846
- type: 'number',
847
- description: 'OVERRIDDEN: Enhanced statistical significance level',
848
- required: true
849
- },
850
- {
851
- name: 'numVariations',
852
- type: 'number',
853
- description: 'OVERRIDDEN: Enhanced number of variations handling',
854
- required: true
855
- },
856
- {
857
- name: 'dailyVisitors',
858
- type: 'number',
859
- description: 'OVERRIDDEN: Enhanced daily visitor count with forecasting',
860
- required: true
861
- }
862
- ]
863
- }
864
- ]
865
- }
866
- });
867
- });
868
752
  });
869
753
  });
@@ -75,7 +75,7 @@ export class ToolLogger {
75
75
 
76
76
  if (Array.isArray(data)) {
77
77
  const truncated = data.slice(0, this.MAX_ARRAY_ITEMS);
78
- const result = truncated.map((item) => this.redactSensitiveData(item, maxDepth));
78
+ const result = truncated.map((item) => this.redactSensitiveData(item, maxDepth - 1));
79
79
  if (data.length > this.MAX_ARRAY_ITEMS) {
80
80
  result.push(`... (${data.length - this.MAX_ARRAY_ITEMS} more items truncated)`);
81
81
  }
@@ -146,7 +146,6 @@ export class ToolLogger {
146
146
  const requestLog = {
147
147
  event: 'opal_tool_request',
148
148
  path: req.path,
149
- method: req.method,
150
149
  parameters: this.createParameterSummary(params)
151
150
  };
152
151