@slates/provider-handler 1.0.0-rc.1 → 1.0.0-rc.11

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,46 @@ 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
+ callbackParams: params.callbackParams || {},
543
+ callbackState: params.callbackState || {}
544
+ })
545
+ )
546
+ );
258
547
 
259
- return {
548
+ return withRequestTraces(context, {
260
549
  output: callbackRes.output,
261
- input: callbackRes.input
262
- };
550
+ input: callbackRes.input,
551
+ scopes: callbackRes.scopes
552
+ });
263
553
  }
264
554
 
265
555
  throw new ServiceError(
@@ -274,19 +564,42 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
274
564
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
275
565
 
276
566
  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
- });
567
+ let context = getEmptyContext();
568
+ let urlRes = await traceProviderCall(
569
+ {
570
+ component: 'auth',
571
+ functionName: 'getAuthorizationUrl',
572
+ message: 'Getting authentication authorization URL',
573
+ successMessage: 'Authentication authorization URL retrieved',
574
+ metadata: {
575
+ authenticationMethodId: params.authenticationMethodId,
576
+ authenticationMethodName: authMethod.name,
577
+ scopeCount: params.scopes.length,
578
+ inputKeyCount: getObjectKeyCount(params.input)
579
+ },
580
+ onSuccess: result => ({
581
+ returnedInput: !!result.input,
582
+ hasCallbackState: !!result.callbackState
583
+ })
584
+ },
585
+ () =>
586
+ runWithContext(context, () =>
587
+ authMethod.getAuthorizationUrl({
588
+ redirectUri: params.redirectUri,
589
+ state: params.state,
590
+ input: params.input,
591
+ clientId: params.clientId,
592
+ clientSecret: params.clientSecret,
593
+ scopes: params.scopes
594
+ })
595
+ )
596
+ );
285
597
 
286
- return {
598
+ return withRequestTraces(context, {
287
599
  authorizationUrl: urlRes.url,
288
- input: urlRes.input
289
- };
600
+ input: urlRes.input,
601
+ callbackState: urlRes.callbackState
602
+ });
290
603
  }
291
604
 
292
605
  throw new ServiceError(
@@ -301,15 +614,39 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
301
614
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
302
615
 
303
616
  if (authMethod.getProfile) {
304
- let profileRes = await authMethod.getProfile({
305
- output: params.output as any,
306
- input: params.input,
307
- scopes: params.scopes
308
- });
617
+ let context = getEmptyContext();
618
+ let profileRes = await traceProviderCall(
619
+ {
620
+ component: 'auth',
621
+ functionName: 'getProfile',
622
+ message: 'Getting authentication profile',
623
+ successMessage: 'Authentication profile retrieved',
624
+ metadata: {
625
+ authenticationMethodId: params.authenticationMethodId,
626
+ authenticationMethodName: authMethod.name,
627
+ scopeCount: params.scopes.length,
628
+ inputKeyCount: getObjectKeyCount(params.input),
629
+ outputKeyCount: getObjectKeyCount(params.output)
630
+ },
631
+ onSuccess: result => ({
632
+ profileKeyCount: getObjectKeyCount(result.profile)
633
+ })
634
+ },
635
+ () =>
636
+ runWithContext(
637
+ context,
638
+ () =>
639
+ authMethod.getProfile!({
640
+ output: params.output as any,
641
+ input: params.input,
642
+ scopes: params.scopes
643
+ })!
644
+ )
645
+ );
309
646
 
310
- return {
647
+ return withRequestTraces(context, {
311
648
  profile: profileRes.profile
312
- };
649
+ });
313
650
  }
314
651
 
315
652
  throw new ServiceError(
@@ -324,18 +661,41 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
324
661
  let authMethod = getAuthMethod(slate, params.authenticationMethodId);
325
662
 
326
663
  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
- });
664
+ let context = getEmptyContext();
665
+ let refreshRes = await traceProviderCall(
666
+ {
667
+ component: 'auth',
668
+ functionName: 'handleTokenRefresh',
669
+ message: 'Refreshing authentication token',
670
+ successMessage: 'Authentication token refreshed',
671
+ metadata: {
672
+ authenticationMethodId: params.authenticationMethodId,
673
+ authenticationMethodName: authMethod.name,
674
+ scopeCount: params.scopes.length,
675
+ inputKeyCount: getObjectKeyCount(params.input),
676
+ outputKeyCount: getObjectKeyCount(params.output)
677
+ },
678
+ onSuccess: result => ({
679
+ refreshedOutputKeyCount: getObjectKeyCount(result.output),
680
+ returnedInput: !!result.input
681
+ })
682
+ },
683
+ () =>
684
+ runWithContext(context, () =>
685
+ authMethod.handleTokenRefresh!({
686
+ output: params.output as any,
687
+ input: params.input,
688
+ clientId: params.clientId,
689
+ clientSecret: params.clientSecret,
690
+ scopes: params.scopes
691
+ })
692
+ )
693
+ );
334
694
 
335
- return {
695
+ return withRequestTraces(context, {
336
696
  output: refreshRes.output,
337
697
  input: refreshRes.input
338
- };
698
+ });
339
699
  }
340
700
 
341
701
  throw new ServiceError(
@@ -345,7 +705,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
345
705
  );
346
706
  });
347
707
 
348
- manager.onRequest('slates/actions.list', async ({ params }) => {
708
+ manager.onRequest('slates/actions.list', async () => {
349
709
  getContextBasic();
350
710
 
351
711
  return {
@@ -373,11 +733,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
373
733
  `Invalid input for tool ID: ${params.actionId}`
374
734
  );
375
735
 
376
- let res = await action.handleInvocation(
377
- new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
736
+ let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
737
+ let res = await traceProviderCall(
738
+ {
739
+ component: 'action',
740
+ functionName: 'handleInvocation',
741
+ message: `Starting tool ${formatEntityLabel(action.name, action.key)}`,
742
+ successMessage: `Completed tool ${formatEntityLabel(action.name, action.key)}`,
743
+ errorMessage: `Tool ${formatEntityLabel(action.name, action.key)} failed`,
744
+ metadata: {
745
+ actionId: action.key,
746
+ actionName: action.name,
747
+ actionType: action.type,
748
+ inputKeyCount: getObjectKeyCount(input)
749
+ },
750
+ onSuccess: result => ({
751
+ hasMessage: !!result.message,
752
+ actionResultMessage: result.message,
753
+ outputKeyCount: getObjectKeyCount(result.output),
754
+ attachmentCount: result.attachments?.length
755
+ })
756
+ },
757
+ () => runWithContext(context, () => action.handleInvocation(context))
378
758
  );
379
759
 
380
- return { output: res.output, message: res.message };
760
+ return withRequestTraces(context, {
761
+ output: res.output,
762
+ message: res.message,
763
+ attachments: mergeAttachments(res.attachments, res.output)
764
+ });
381
765
  });
382
766
 
383
767
  manager.onRequest('slates/action.trigger.map_event', async ({ params }) => {
@@ -391,11 +775,31 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
391
775
  `Invalid event for trigger ID: ${params.actionId}`
392
776
  );
393
777
 
394
- let res = await action.handleEvent(
395
- new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
778
+ let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
779
+ let res = await traceProviderCall(
780
+ {
781
+ component: 'action',
782
+ functionName: 'handleEvent',
783
+ message: `Mapping event for trigger ${formatEntityLabel(action.name, action.key)}`,
784
+ successMessage: result =>
785
+ `Mapped trigger event "${result.type}" for ${formatEntityLabel(action.name, action.key)}`,
786
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while mapping an event`,
787
+ metadata: {
788
+ actionId: action.key,
789
+ actionName: action.name,
790
+ actionType: action.type,
791
+ inputKeyCount: getObjectKeyCount(input)
792
+ },
793
+ onSuccess: result => ({
794
+ eventType: result.type,
795
+ hasEventId: !!result.id,
796
+ outputKeyCount: getObjectKeyCount(result.output)
797
+ })
798
+ },
799
+ () => runWithContext(context, () => action.handleEvent(context))
396
800
  );
397
801
 
398
- return { id: res.id, type: res.type, output: res.output };
802
+ return withRequestTraces(context, { id: res.id, type: res.type, output: res.output });
399
803
  });
400
804
 
401
805
  manager.onRequest('slates/action.trigger.poll_events', async ({ params }) => {
@@ -410,17 +814,39 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
410
814
  );
411
815
  }
412
816
 
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
- )
817
+ let context = new SlateContext(
818
+ ctx.config,
819
+ { state: params.state },
820
+ ctx.auth?.output!,
821
+ slate.spec,
822
+ logger
823
+ );
824
+ let res = await traceProviderCall(
825
+ {
826
+ component: 'action',
827
+ functionName: 'pollEvents',
828
+ message: `Polling events for trigger ${formatEntityLabel(action.name, action.key)}`,
829
+ successMessage: result =>
830
+ `Polled ${result.inputs.length} event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
831
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while polling events`,
832
+ metadata: {
833
+ actionId: action.key,
834
+ actionName: action.name,
835
+ actionType: action.type,
836
+ hasPreviousState: params.state !== null
837
+ },
838
+ onSuccess: result => ({
839
+ inputCount: result.inputs.length,
840
+ hasUpdatedState: result.updatedState !== undefined
841
+ })
842
+ },
843
+ () => runWithContext(context, () => action.pollEvents!(context))
421
844
  );
422
845
 
423
- return { inputs: res.inputs, updatedState: res.updatedState };
846
+ return withRequestTraces(context, {
847
+ inputs: res.inputs,
848
+ updatedState: res.updatedState
849
+ });
424
850
  });
425
851
 
426
852
  manager.onRequest('slates/action.trigger.webhook_handle', async ({ params }) => {
@@ -443,17 +869,41 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
443
869
  : null
444
870
  });
445
871
 
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
- )
872
+ let context = new SlateContext(
873
+ ctx.config,
874
+ { request: req, state: params.state },
875
+ ctx.auth?.output!,
876
+ slate.spec,
877
+ logger
878
+ );
879
+ let res = await traceProviderCall(
880
+ {
881
+ component: 'action',
882
+ functionName: 'handleRequest',
883
+ message: `Handling webhook request for trigger ${formatEntityLabel(action.name, action.key)}`,
884
+ successMessage: result =>
885
+ `Received ${result.inputs.length} webhook event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
886
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while handling a webhook request`,
887
+ metadata: {
888
+ actionId: action.key,
889
+ actionName: action.name,
890
+ actionType: action.type,
891
+ requestMethod: params.method,
892
+ hasRequestBody: !!params.body,
893
+ hasPreviousState: params.state !== null
894
+ },
895
+ onSuccess: result => ({
896
+ inputCount: result.inputs.length,
897
+ hasUpdatedState: result.updatedState !== undefined
898
+ })
899
+ },
900
+ () => runWithContext(context, () => action.handleRequest!(context))
454
901
  );
455
902
 
456
- return { inputs: res.inputs, updatedState: res.updatedState };
903
+ return withRequestTraces(context, {
904
+ inputs: res.inputs,
905
+ updatedState: res.updatedState
906
+ });
457
907
  });
458
908
 
459
909
  manager.onRequest('slates/action.trigger.webhook_register', async ({ params }) => {
@@ -468,17 +918,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
468
918
  );
469
919
  }
470
920
 
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
- )
921
+ let context = new SlateContext(
922
+ ctx.config,
923
+ { webhookBaseUrl: params.webhookBaseUrl },
924
+ ctx.auth?.output!,
925
+ slate.spec,
926
+ logger
927
+ );
928
+ let res = await traceProviderCall(
929
+ {
930
+ component: 'action',
931
+ functionName: 'autoRegisterWebhook',
932
+ message: `Registering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
933
+ successMessage: `Registered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
934
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while registering a webhook`,
935
+ metadata: {
936
+ actionId: action.key,
937
+ actionName: action.name,
938
+ actionType: action.type
939
+ },
940
+ onSuccess: result => ({
941
+ hasRegistrationDetails: result.registrationDetails !== undefined,
942
+ hasState: result.state !== undefined
943
+ })
944
+ },
945
+ () => runWithContext(context, () => action.autoRegisterWebhook!(context))
479
946
  );
480
947
 
481
- return { registrationDetails: res.registrationDetails, state: res.state };
948
+ return withRequestTraces(context, {
949
+ registrationDetails: res.registrationDetails,
950
+ state: res.state
951
+ });
482
952
  });
483
953
 
484
954
  manager.onRequest('slates/action.trigger.webhook_unregister', async ({ params }) => {
@@ -493,20 +963,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
493
963
  );
494
964
  }
495
965
 
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
- )
966
+ let context = new SlateContext(
967
+ ctx.config,
968
+ {
969
+ webhookBaseUrl: params.webhookBaseUrl,
970
+ registrationDetails: params.registrationDetails,
971
+ state: params.state
972
+ },
973
+ ctx.auth?.output!,
974
+ slate.spec,
975
+ logger
976
+ );
977
+ await traceProviderCall(
978
+ {
979
+ component: 'action',
980
+ functionName: 'autoUnregisterWebhook',
981
+ message: `Unregistering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
982
+ successMessage: `Unregistered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
983
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while unregistering a webhook`,
984
+ metadata: {
985
+ actionId: action.key,
986
+ actionName: action.name,
987
+ actionType: action.type,
988
+ hasRegistrationDetails: params.registrationDetails !== null,
989
+ hasPreviousState: params.state !== null
990
+ }
991
+ },
992
+ () => runWithContext(context, () => action.autoUnregisterWebhook!(context))
508
993
  );
509
994
 
510
- return {};
995
+ return withRequestTraces(context, {});
511
996
  });
512
997
  });