@slates/provider-handler 1.0.0-rc.1 → 1.0.0-rc.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.
package/src/index.ts CHANGED
@@ -1,9 +1,110 @@
1
1
  import { badRequestError, preconditionFailedError, ServiceError } from '@lowerdeck/error';
2
- import { createSlatesProviderProtoHandler, SlatesParticipant } from '@slates/proto';
3
- import { Slate, SlateContext, SlateLogger, SlateLogListener } from '@slates/provider';
2
+ import {
3
+ createSlatesProviderProtoHandler,
4
+ SLATES_PROTOCOL_VERSION,
5
+ type SlatesParticipant
6
+ } from '@slates/proto';
7
+ import {
8
+ runWithContext,
9
+ type Slate,
10
+ type SlateAttachment,
11
+ SlateContext,
12
+ SlateLogger,
13
+ type SlateLogListener
14
+ } from '@slates/provider';
4
15
  import { getAction, getActionWithType, getAuthMethod, mapAction, mapAuthMethod } from './spec';
5
16
  import { State } from './state';
6
- import { validate } from './validation';
17
+ import { toJsonSchema, validate } from './validation';
18
+
19
+ let isRecord = (value: unknown): value is Record<string, unknown> =>
20
+ typeof value === 'object' && value !== null && !Array.isArray(value);
21
+
22
+ let getObjectKeyCount = (value: unknown) =>
23
+ isRecord(value) ? Object.keys(value).length : undefined;
24
+
25
+ let DOWNLOAD_ATTACHMENT_URL_KEYS = new Set([
26
+ 'downloadUrl',
27
+ 'fileUrl',
28
+ 'temporaryDownloadUrl',
29
+ 'webContentLink'
30
+ ]);
31
+
32
+ let isAttachmentUrl = (value: string) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value);
33
+
34
+ let collectOutputUrlAttachments = (
35
+ value: unknown,
36
+ seen = new Set<string>()
37
+ ): SlateAttachment[] => {
38
+ if (Array.isArray(value)) {
39
+ return value.flatMap(item => collectOutputUrlAttachments(item, seen));
40
+ }
41
+
42
+ if (!isRecord(value)) {
43
+ return [];
44
+ }
45
+
46
+ let attachments: SlateAttachment[] = [];
47
+
48
+ for (let [key, nestedValue] of Object.entries(value)) {
49
+ if (
50
+ DOWNLOAD_ATTACHMENT_URL_KEYS.has(key) &&
51
+ typeof nestedValue === 'string' &&
52
+ nestedValue.length > 0 &&
53
+ isAttachmentUrl(nestedValue) &&
54
+ !seen.has(nestedValue)
55
+ ) {
56
+ seen.add(nestedValue);
57
+ attachments.push({
58
+ content: {
59
+ type: 'url',
60
+ url: nestedValue
61
+ }
62
+ });
63
+ continue;
64
+ }
65
+
66
+ attachments.push(...collectOutputUrlAttachments(nestedValue, seen));
67
+ }
68
+
69
+ return attachments;
70
+ };
71
+
72
+ let mergeAttachments = (
73
+ explicitAttachments: SlateAttachment[] | undefined,
74
+ output: unknown
75
+ ): SlateAttachment[] | undefined => {
76
+ let attachments = [...(explicitAttachments ?? [])];
77
+ let seen = new Set(attachments.map(attachment => JSON.stringify(attachment)));
78
+
79
+ for (let attachment of collectOutputUrlAttachments(output)) {
80
+ let key = JSON.stringify(attachment);
81
+ if (seen.has(key)) continue;
82
+ seen.add(key);
83
+ attachments.push(attachment);
84
+ }
85
+
86
+ return attachments.length > 0 ? attachments : undefined;
87
+ };
88
+
89
+ let toErrorMetadata = (error: unknown) => {
90
+ if (error instanceof Error) {
91
+ return {
92
+ errorName: error.name,
93
+ errorMessage: error.message,
94
+ errorStack: error.stack
95
+ };
96
+ }
97
+
98
+ return {
99
+ errorValue: String(error)
100
+ };
101
+ };
102
+
103
+ let formatEntityLabel = (name: string, key: string) => `"${name}" (${key})`;
104
+ let resolveTraceMessage = <ResultType>(
105
+ message: string | ((result: ResultType) => string),
106
+ result: ResultType
107
+ ) => (typeof message === 'function' ? message(result) : message);
7
108
 
8
109
  export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
9
110
  slate: Slate<ConfigType, AuthType>,
@@ -15,10 +116,69 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
15
116
 
16
117
  let auth = new State<{ authenticationMethodId: string; output: AuthType } | null>(null);
17
118
  let config = new State<{ value: ConfigType } | null>(null);
18
-
19
119
  let session = new State<{ id: string; state: any } | null>(null);
20
120
 
21
121
  let logger = new SlateLogger(listeners);
122
+ let providerTrace = {
123
+ providerId: slate.spec.key,
124
+ providerName: slate.spec.name
125
+ };
126
+
127
+ let traceProviderCall = async <ResultType>(
128
+ trace: {
129
+ component: 'config' | 'auth' | 'action';
130
+ functionName: string;
131
+ message: string;
132
+ successMessage: string | ((result: ResultType) => string);
133
+ errorMessage?: string;
134
+ metadata?: Record<string, unknown>;
135
+ onSuccess?: (result: ResultType) => Record<string, unknown> | undefined;
136
+ },
137
+ handler: () => Promise<ResultType>
138
+ ): Promise<ResultType> => {
139
+ let startedAt = Date.now();
140
+
141
+ logger.info({
142
+ ...providerTrace,
143
+ ...trace.metadata,
144
+ component: trace.component,
145
+ functionName: trace.functionName,
146
+ phase: 'start',
147
+ message: trace.message
148
+ });
149
+
150
+ try {
151
+ let result = await handler();
152
+ let successMessage = resolveTraceMessage(trace.successMessage, result);
153
+
154
+ logger.info({
155
+ ...providerTrace,
156
+ ...trace.metadata,
157
+ ...(trace.onSuccess?.(result) ?? {}),
158
+ component: trace.component,
159
+ functionName: trace.functionName,
160
+ phase: 'success',
161
+ durationMs: Date.now() - startedAt,
162
+ message: successMessage
163
+ });
164
+
165
+ return result;
166
+ } catch (error) {
167
+ logger.error({
168
+ ...providerTrace,
169
+ ...trace.metadata,
170
+ ...toErrorMetadata(error),
171
+ component: trace.component,
172
+ functionName: trace.functionName,
173
+ phase: 'error',
174
+ durationMs: Date.now() - startedAt,
175
+ message:
176
+ trace.errorMessage ??
177
+ `${typeof trace.successMessage === 'string' ? trace.successMessage : trace.message} failed`
178
+ });
179
+ throw error;
180
+ }
181
+ };
22
182
 
23
183
  let getContextBasic = () => {
24
184
  let currentProtocol = protocol.get();
@@ -65,6 +225,15 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
65
225
  };
66
226
  };
67
227
 
228
+ let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec as any, logger);
229
+ let withRequestTraces = <Result extends Record<string, any>>(
230
+ context: SlateContext<any, any, any>,
231
+ result: Result
232
+ ) => {
233
+ let requestTraces = context.getHttpTraces();
234
+ return requestTraces.length > 0 ? { ...result, requestTraces } : result;
235
+ };
236
+
68
237
  manager.onNotification('slates/hello', async ({ params }) => {
69
238
  protocol.set(params.protocol);
70
239
  });
@@ -133,15 +302,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
133
302
  return { success: true, config: newConfig };
134
303
  }
135
304
 
136
- let updatedConfig = await configChanged({
137
- previousConfig: params.previousConfig as ConfigType | null,
138
- newConfig
139
- });
305
+ let context = getEmptyContext();
306
+ let updatedConfig = await traceProviderCall<{ config?: ConfigType } | undefined>(
307
+ {
308
+ component: 'config',
309
+ functionName: 'configChanged',
310
+ message: 'Running config change handler',
311
+ successMessage: 'Config change handler completed',
312
+ metadata: {
313
+ hasPreviousConfig: params.previousConfig !== null,
314
+ newConfigKeyCount: getObjectKeyCount(newConfig)
315
+ },
316
+ onSuccess: result => ({
317
+ returnedConfig: !!result?.config
318
+ })
319
+ },
320
+ () =>
321
+ runWithContext(context, async () =>
322
+ configChanged({
323
+ previousConfig: params.previousConfig as ConfigType | null,
324
+ newConfig
325
+ })
326
+ )
327
+ );
140
328
 
141
- return { success: true, config: updatedConfig?.config ?? newConfig };
329
+ return withRequestTraces(context, {
330
+ success: true,
331
+ config: (updatedConfig?.config ?? newConfig) as Record<string, any>
332
+ });
142
333
  });
143
334
 
144
- manager.onRequest('slates/config.get_default', async ({ params }) => {
335
+ manager.onRequest('slates/config.get_default', async () => {
145
336
  getContextBasic();
146
337
 
147
338
  let getDefaultConfig = slate.spec.config.handlers.getDefaultConfig;
@@ -149,21 +340,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
149
340
  return { config: null };
150
341
  }
151
342
 
152
- let defaultConfig = await getDefaultConfig();
153
- return { config: defaultConfig };
343
+ let context = getEmptyContext();
344
+ let defaultConfig = await traceProviderCall<ConfigType>(
345
+ {
346
+ component: 'config',
347
+ functionName: 'getDefaultConfig',
348
+ message: 'Getting default config',
349
+ successMessage: 'Default config retrieved',
350
+ onSuccess: result => ({
351
+ configKeyCount: getObjectKeyCount(result)
352
+ })
353
+ },
354
+ () => runWithContext(context, async () => getDefaultConfig())
355
+ );
356
+ return withRequestTraces(context, {
357
+ config: (defaultConfig ?? null) as Record<string, any> | null
358
+ });
154
359
  });
155
360
 
156
- manager.onRequest('slates/config.schema.get', async ({ params }) => {
361
+ manager.onRequest('slates/config.schema.get', async () => {
157
362
  getContextBasic();
158
363
 
159
- return { schema: slate.spec.configSchema.toJSONSchema() };
364
+ return { schema: toJsonSchema(slate.spec.configSchema) };
160
365
  });
161
366
 
162
- manager.onRequest('slates/provider.identify', async ({ params }) => {
367
+ manager.onRequest('slates/provider.identify', async () => {
163
368
  getContextBasic();
164
369
 
165
370
  return {
166
- protocol: 'slates@2026-01-01',
371
+ protocol: SLATES_PROTOCOL_VERSION,
167
372
  provider: {
168
373
  type: 'provider',
169
374
  id: slate.spec.key,
@@ -174,7 +379,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
174
379
  };
175
380
  });
176
381
 
177
- manager.onRequest('slates/auth.methods.list', async ({ params }) => {
382
+ manager.onRequest('slates/auth.methods.list', async () => {
178
383
  getContextBasic();
179
384
 
180
385
  return {
@@ -199,7 +404,25 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
199
404
  return { input: null };
200
405
  }
201
406
 
202
- return { input: await authMethod.getDefaultInput() };
407
+ let context = getEmptyContext();
408
+ let input = await traceProviderCall(
409
+ {
410
+ component: 'auth',
411
+ functionName: 'getDefaultInput',
412
+ message: 'Getting default authentication input',
413
+ successMessage: 'Default authentication input retrieved',
414
+ metadata: {
415
+ authenticationMethodId: params.authenticationMethodId,
416
+ authenticationMethodName: authMethod.name
417
+ },
418
+ onSuccess: result => ({
419
+ inputKeyCount: getObjectKeyCount(result)
420
+ })
421
+ },
422
+ () => runWithContext(context, () => authMethod.getDefaultInput!())
423
+ );
424
+
425
+ return withRequestTraces(context, { input });
203
426
  });
204
427
 
205
428
  manager.onRequest('slates/auth.input.changed', async ({ params }) => {
@@ -210,12 +433,36 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
210
433
  return { success: true, input: params.newInput };
211
434
  }
212
435
 
213
- let updatedInput = await authMethod.onInputChanged({
214
- previousInput: params.previousInput as any | null,
215
- newInput: params.newInput
216
- });
436
+ let context = getEmptyContext();
437
+ let updatedInput = await traceProviderCall(
438
+ {
439
+ component: 'auth',
440
+ functionName: 'onInputChanged',
441
+ message: 'Running authentication input change handler',
442
+ successMessage: 'Authentication input change handler completed',
443
+ metadata: {
444
+ authenticationMethodId: params.authenticationMethodId,
445
+ authenticationMethodName: authMethod.name,
446
+ hasPreviousInput: params.previousInput !== null,
447
+ newInputKeyCount: getObjectKeyCount(params.newInput)
448
+ },
449
+ onSuccess: result => ({
450
+ returnedInput: !!result?.input
451
+ })
452
+ },
453
+ () =>
454
+ runWithContext(context, () =>
455
+ authMethod.onInputChanged!({
456
+ previousInput: params.previousInput as any | null,
457
+ newInput: params.newInput
458
+ })
459
+ )
460
+ );
217
461
 
218
- return { success: true, input: updatedInput?.input ?? params.newInput };
462
+ return withRequestTraces(context, {
463
+ success: true,
464
+ input: updatedInput?.input ?? params.newInput
465
+ });
219
466
  });
220
467
 
221
468
  manager.onRequest('slates/auth.output.get', async ({ params }) => {
@@ -234,8 +481,25 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
234
481
  }
235
482
 
236
483
  if ('getOutput' in authMethod) {
237
- let outputRes = await authMethod.getOutput({ input });
238
- return { output: outputRes.output };
484
+ let context = getEmptyContext();
485
+ let outputRes = await traceProviderCall(
486
+ {
487
+ component: 'auth',
488
+ functionName: 'getOutput',
489
+ message: 'Getting authentication output',
490
+ successMessage: 'Authentication output retrieved',
491
+ metadata: {
492
+ authenticationMethodId: params.authenticationMethodId,
493
+ authenticationMethodName: authMethod.name,
494
+ inputKeyCount: getObjectKeyCount(input)
495
+ },
496
+ onSuccess: result => ({
497
+ outputKeyCount: getObjectKeyCount(result.output)
498
+ })
499
+ },
500
+ () => runWithContext(context, () => authMethod.getOutput({ input }))
501
+ );
502
+ return withRequestTraces(context, { output: outputRes.output });
239
503
  }
240
504
 
241
505
  return { output: input as any };
@@ -246,20 +510,45 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
246
510
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
247
511
 
248
512
  if ('handleCallback' in authMethod) {
249
- let callbackRes = await authMethod.handleCallback({
250
- code: params.code,
251
- state: params.state,
252
- redirectUri: params.redirectUri,
253
- input: params.input,
254
- clientId: params.clientId,
255
- clientSecret: params.clientSecret,
256
- scopes: params.scopes
257
- });
513
+ let context = getEmptyContext();
514
+ let callbackRes = await traceProviderCall(
515
+ {
516
+ component: 'auth',
517
+ functionName: 'handleCallback',
518
+ message: 'Handling authentication callback',
519
+ successMessage: 'Authentication callback handled',
520
+ metadata: {
521
+ authenticationMethodId: params.authenticationMethodId,
522
+ authenticationMethodName: authMethod.name,
523
+ scopeCount: params.scopes.length,
524
+ hasCallbackState: !!params.callbackState
525
+ },
526
+ onSuccess: result => ({
527
+ outputKeyCount: getObjectKeyCount(result.output),
528
+ returnedInput: !!result.input,
529
+ returnedScopeCount: result.scopes?.length
530
+ })
531
+ },
532
+ () =>
533
+ runWithContext(context, () =>
534
+ authMethod.handleCallback({
535
+ code: params.code,
536
+ state: params.state,
537
+ redirectUri: params.redirectUri,
538
+ input: params.input,
539
+ clientId: params.clientId,
540
+ clientSecret: params.clientSecret,
541
+ scopes: params.scopes,
542
+ callbackState: params.callbackState || {}
543
+ })
544
+ )
545
+ );
258
546
 
259
- return {
547
+ return withRequestTraces(context, {
260
548
  output: callbackRes.output,
261
- input: callbackRes.input
262
- };
549
+ input: callbackRes.input,
550
+ scopes: callbackRes.scopes
551
+ });
263
552
  }
264
553
 
265
554
  throw new ServiceError(
@@ -274,19 +563,42 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
274
563
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
275
564
 
276
565
  if ('getAuthorizationUrl' in authMethod) {
277
- let urlRes = await authMethod.getAuthorizationUrl({
278
- redirectUri: params.redirectUri,
279
- state: params.state,
280
- input: params.input,
281
- clientId: params.clientId,
282
- clientSecret: params.clientSecret,
283
- scopes: params.scopes
284
- });
566
+ let context = getEmptyContext();
567
+ let urlRes = await traceProviderCall(
568
+ {
569
+ component: 'auth',
570
+ functionName: 'getAuthorizationUrl',
571
+ message: 'Getting authentication authorization URL',
572
+ successMessage: 'Authentication authorization URL retrieved',
573
+ metadata: {
574
+ authenticationMethodId: params.authenticationMethodId,
575
+ authenticationMethodName: authMethod.name,
576
+ scopeCount: params.scopes.length,
577
+ inputKeyCount: getObjectKeyCount(params.input)
578
+ },
579
+ onSuccess: result => ({
580
+ returnedInput: !!result.input,
581
+ hasCallbackState: !!result.callbackState
582
+ })
583
+ },
584
+ () =>
585
+ runWithContext(context, () =>
586
+ authMethod.getAuthorizationUrl({
587
+ redirectUri: params.redirectUri,
588
+ state: params.state,
589
+ input: params.input,
590
+ clientId: params.clientId,
591
+ clientSecret: params.clientSecret,
592
+ scopes: params.scopes
593
+ })
594
+ )
595
+ );
285
596
 
286
- return {
597
+ return withRequestTraces(context, {
287
598
  authorizationUrl: urlRes.url,
288
- input: urlRes.input
289
- };
599
+ input: urlRes.input,
600
+ callbackState: urlRes.callbackState
601
+ });
290
602
  }
291
603
 
292
604
  throw new ServiceError(
@@ -301,15 +613,39 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
301
613
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
302
614
 
303
615
  if (authMethod.getProfile) {
304
- let profileRes = await authMethod.getProfile({
305
- output: params.output as any,
306
- input: params.input,
307
- scopes: params.scopes
308
- });
616
+ let context = getEmptyContext();
617
+ let profileRes = await traceProviderCall(
618
+ {
619
+ component: 'auth',
620
+ functionName: 'getProfile',
621
+ message: 'Getting authentication profile',
622
+ successMessage: 'Authentication profile retrieved',
623
+ metadata: {
624
+ authenticationMethodId: params.authenticationMethodId,
625
+ authenticationMethodName: authMethod.name,
626
+ scopeCount: params.scopes.length,
627
+ inputKeyCount: getObjectKeyCount(params.input),
628
+ outputKeyCount: getObjectKeyCount(params.output)
629
+ },
630
+ onSuccess: result => ({
631
+ profileKeyCount: getObjectKeyCount(result.profile)
632
+ })
633
+ },
634
+ () =>
635
+ runWithContext(
636
+ context,
637
+ () =>
638
+ authMethod.getProfile!({
639
+ output: params.output as any,
640
+ input: params.input,
641
+ scopes: params.scopes
642
+ })!
643
+ )
644
+ );
309
645
 
310
- return {
646
+ return withRequestTraces(context, {
311
647
  profile: profileRes.profile
312
- };
648
+ });
313
649
  }
314
650
 
315
651
  throw new ServiceError(
@@ -324,18 +660,41 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
324
660
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
325
661
 
326
662
  if ('handleTokenRefresh' in authMethod && authMethod.handleTokenRefresh) {
327
- let refreshRes = await authMethod.handleTokenRefresh({
328
- output: params.output as any,
329
- input: params.input,
330
- clientId: params.clientId,
331
- clientSecret: params.clientSecret,
332
- scopes: params.scopes
333
- });
663
+ let context = getEmptyContext();
664
+ let refreshRes = await traceProviderCall(
665
+ {
666
+ component: 'auth',
667
+ functionName: 'handleTokenRefresh',
668
+ message: 'Refreshing authentication token',
669
+ successMessage: 'Authentication token refreshed',
670
+ metadata: {
671
+ authenticationMethodId: params.authenticationMethodId,
672
+ authenticationMethodName: authMethod.name,
673
+ scopeCount: params.scopes.length,
674
+ inputKeyCount: getObjectKeyCount(params.input),
675
+ outputKeyCount: getObjectKeyCount(params.output)
676
+ },
677
+ onSuccess: result => ({
678
+ refreshedOutputKeyCount: getObjectKeyCount(result.output),
679
+ returnedInput: !!result.input
680
+ })
681
+ },
682
+ () =>
683
+ runWithContext(context, () =>
684
+ authMethod.handleTokenRefresh!({
685
+ output: params.output as any,
686
+ input: params.input,
687
+ clientId: params.clientId,
688
+ clientSecret: params.clientSecret,
689
+ scopes: params.scopes
690
+ })
691
+ )
692
+ );
334
693
 
335
- return {
694
+ return withRequestTraces(context, {
336
695
  output: refreshRes.output,
337
696
  input: refreshRes.input
338
- };
697
+ });
339
698
  }
340
699
 
341
700
  throw new ServiceError(
@@ -345,7 +704,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
345
704
  );
346
705
  });
347
706
 
348
- manager.onRequest('slates/actions.list', async ({ params }) => {
707
+ manager.onRequest('slates/actions.list', async () => {
349
708
  getContextBasic();
350
709
 
351
710
  return {
@@ -373,11 +732,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
373
732
  `Invalid input for tool ID: ${params.actionId}`
374
733
  );
375
734
 
376
- let res = await action.handleInvocation(
377
- new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
735
+ let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
736
+ let res = await traceProviderCall(
737
+ {
738
+ component: 'action',
739
+ functionName: 'handleInvocation',
740
+ message: `Starting tool ${formatEntityLabel(action.name, action.key)}`,
741
+ successMessage: `Completed tool ${formatEntityLabel(action.name, action.key)}`,
742
+ errorMessage: `Tool ${formatEntityLabel(action.name, action.key)} failed`,
743
+ metadata: {
744
+ actionId: action.key,
745
+ actionName: action.name,
746
+ actionType: action.type,
747
+ inputKeyCount: getObjectKeyCount(input)
748
+ },
749
+ onSuccess: result => ({
750
+ hasMessage: !!result.message,
751
+ actionResultMessage: result.message,
752
+ outputKeyCount: getObjectKeyCount(result.output),
753
+ attachmentCount: result.attachments?.length
754
+ })
755
+ },
756
+ () => runWithContext(context, () => action.handleInvocation(context))
378
757
  );
379
758
 
380
- return { output: res.output, message: res.message };
759
+ return withRequestTraces(context, {
760
+ output: res.output,
761
+ message: res.message,
762
+ attachments: mergeAttachments(res.attachments, res.output)
763
+ });
381
764
  });
382
765
 
383
766
  manager.onRequest('slates/action.trigger.map_event', async ({ params }) => {
@@ -391,11 +774,31 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
391
774
  `Invalid event for trigger ID: ${params.actionId}`
392
775
  );
393
776
 
394
- let res = await action.handleEvent(
395
- new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
777
+ let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
778
+ let res = await traceProviderCall(
779
+ {
780
+ component: 'action',
781
+ functionName: 'handleEvent',
782
+ message: `Mapping event for trigger ${formatEntityLabel(action.name, action.key)}`,
783
+ successMessage: result =>
784
+ `Mapped trigger event "${result.type}" for ${formatEntityLabel(action.name, action.key)}`,
785
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while mapping an event`,
786
+ metadata: {
787
+ actionId: action.key,
788
+ actionName: action.name,
789
+ actionType: action.type,
790
+ inputKeyCount: getObjectKeyCount(input)
791
+ },
792
+ onSuccess: result => ({
793
+ eventType: result.type,
794
+ hasEventId: !!result.id,
795
+ outputKeyCount: getObjectKeyCount(result.output)
796
+ })
797
+ },
798
+ () => runWithContext(context, () => action.handleEvent(context))
396
799
  );
397
800
 
398
- return { id: res.id, type: res.type, output: res.output };
801
+ return withRequestTraces(context, { id: res.id, type: res.type, output: res.output });
399
802
  });
400
803
 
401
804
  manager.onRequest('slates/action.trigger.poll_events', async ({ params }) => {
@@ -410,17 +813,39 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
410
813
  );
411
814
  }
412
815
 
413
- let res = await action.pollEvents(
414
- new SlateContext(
415
- ctx.config,
416
- { state: params.state },
417
- ctx.auth?.output!,
418
- slate.spec,
419
- logger
420
- )
816
+ let context = new SlateContext(
817
+ ctx.config,
818
+ { state: params.state },
819
+ ctx.auth?.output!,
820
+ slate.spec,
821
+ logger
822
+ );
823
+ let res = await traceProviderCall(
824
+ {
825
+ component: 'action',
826
+ functionName: 'pollEvents',
827
+ message: `Polling events for trigger ${formatEntityLabel(action.name, action.key)}`,
828
+ successMessage: result =>
829
+ `Polled ${result.inputs.length} event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
830
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while polling events`,
831
+ metadata: {
832
+ actionId: action.key,
833
+ actionName: action.name,
834
+ actionType: action.type,
835
+ hasPreviousState: params.state !== null
836
+ },
837
+ onSuccess: result => ({
838
+ inputCount: result.inputs.length,
839
+ hasUpdatedState: result.updatedState !== undefined
840
+ })
841
+ },
842
+ () => runWithContext(context, () => action.pollEvents!(context))
421
843
  );
422
844
 
423
- return { inputs: res.inputs, updatedState: res.updatedState };
845
+ return withRequestTraces(context, {
846
+ inputs: res.inputs,
847
+ updatedState: res.updatedState
848
+ });
424
849
  });
425
850
 
426
851
  manager.onRequest('slates/action.trigger.webhook_handle', async ({ params }) => {
@@ -443,17 +868,41 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
443
868
  : null
444
869
  });
445
870
 
446
- let res = await action.handleRequest(
447
- new SlateContext(
448
- ctx.config,
449
- { request: req, state: params.state },
450
- ctx.auth?.output!,
451
- slate.spec,
452
- logger
453
- )
871
+ let context = new SlateContext(
872
+ ctx.config,
873
+ { request: req, state: params.state },
874
+ ctx.auth?.output!,
875
+ slate.spec,
876
+ logger
877
+ );
878
+ let res = await traceProviderCall(
879
+ {
880
+ component: 'action',
881
+ functionName: 'handleRequest',
882
+ message: `Handling webhook request for trigger ${formatEntityLabel(action.name, action.key)}`,
883
+ successMessage: result =>
884
+ `Received ${result.inputs.length} webhook event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
885
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while handling a webhook request`,
886
+ metadata: {
887
+ actionId: action.key,
888
+ actionName: action.name,
889
+ actionType: action.type,
890
+ requestMethod: params.method,
891
+ hasRequestBody: !!params.body,
892
+ hasPreviousState: params.state !== null
893
+ },
894
+ onSuccess: result => ({
895
+ inputCount: result.inputs.length,
896
+ hasUpdatedState: result.updatedState !== undefined
897
+ })
898
+ },
899
+ () => runWithContext(context, () => action.handleRequest!(context))
454
900
  );
455
901
 
456
- return { inputs: res.inputs, updatedState: res.updatedState };
902
+ return withRequestTraces(context, {
903
+ inputs: res.inputs,
904
+ updatedState: res.updatedState
905
+ });
457
906
  });
458
907
 
459
908
  manager.onRequest('slates/action.trigger.webhook_register', async ({ params }) => {
@@ -468,17 +917,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
468
917
  );
469
918
  }
470
919
 
471
- let res = await action.autoRegisterWebhook(
472
- new SlateContext(
473
- ctx.config,
474
- { webhookBaseUrl: params.webhookBaseUrl },
475
- ctx.auth?.output!,
476
- slate.spec,
477
- logger
478
- )
920
+ let context = new SlateContext(
921
+ ctx.config,
922
+ { webhookBaseUrl: params.webhookBaseUrl },
923
+ ctx.auth?.output!,
924
+ slate.spec,
925
+ logger
926
+ );
927
+ let res = await traceProviderCall(
928
+ {
929
+ component: 'action',
930
+ functionName: 'autoRegisterWebhook',
931
+ message: `Registering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
932
+ successMessage: `Registered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
933
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while registering a webhook`,
934
+ metadata: {
935
+ actionId: action.key,
936
+ actionName: action.name,
937
+ actionType: action.type
938
+ },
939
+ onSuccess: result => ({
940
+ hasRegistrationDetails: result.registrationDetails !== undefined,
941
+ hasState: result.state !== undefined
942
+ })
943
+ },
944
+ () => runWithContext(context, () => action.autoRegisterWebhook!(context))
479
945
  );
480
946
 
481
- return { registrationDetails: res.registrationDetails, state: res.state };
947
+ return withRequestTraces(context, {
948
+ registrationDetails: res.registrationDetails,
949
+ state: res.state
950
+ });
482
951
  });
483
952
 
484
953
  manager.onRequest('slates/action.trigger.webhook_unregister', async ({ params }) => {
@@ -493,20 +962,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
493
962
  );
494
963
  }
495
964
 
496
- await action.autoUnregisterWebhook(
497
- new SlateContext(
498
- ctx.config,
499
- {
500
- webhookBaseUrl: params.webhookBaseUrl,
501
- registrationDetails: params.registrationDetails,
502
- state: params.state
503
- },
504
- ctx.auth?.output!,
505
- slate.spec,
506
- logger
507
- )
965
+ let context = new SlateContext(
966
+ ctx.config,
967
+ {
968
+ webhookBaseUrl: params.webhookBaseUrl,
969
+ registrationDetails: params.registrationDetails,
970
+ state: params.state
971
+ },
972
+ ctx.auth?.output!,
973
+ slate.spec,
974
+ logger
975
+ );
976
+ await traceProviderCall(
977
+ {
978
+ component: 'action',
979
+ functionName: 'autoUnregisterWebhook',
980
+ message: `Unregistering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
981
+ successMessage: `Unregistered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
982
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while unregistering a webhook`,
983
+ metadata: {
984
+ actionId: action.key,
985
+ actionName: action.name,
986
+ actionType: action.type,
987
+ hasRegistrationDetails: params.registrationDetails !== null,
988
+ hasPreviousState: params.state !== null
989
+ }
990
+ },
991
+ () => runWithContext(context, () => action.autoUnregisterWebhook!(context))
508
992
  );
509
993
 
510
- return {};
994
+ return withRequestTraces(context, {});
511
995
  });
512
996
  });