@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.
@@ -0,0 +1,973 @@
1
+ // src/index.ts
2
+ import { badRequestError as badRequestError2, preconditionFailedError, ServiceError as ServiceError3 } from "@lowerdeck/error";
3
+ import {
4
+ createSlatesProviderProtoHandler,
5
+ SLATES_PROTOCOL_VERSION
6
+ } from "@slates/proto";
7
+ import {
8
+ runWithContext,
9
+ SlateContext,
10
+ SlateLogger
11
+ } from "@slates/provider";
12
+
13
+ // src/spec.ts
14
+ import { badRequestError, notFoundError, ServiceError as ServiceError2 } from "@lowerdeck/error";
15
+ import { SlateDefaultPollingIntervalSeconds } from "@slates/provider";
16
+ import z from "zod";
17
+
18
+ // src/validation.ts
19
+ import { ServiceError, validationError } from "@lowerdeck/error";
20
+ var zodToValidationError = (entity, message, e) => {
21
+ return validationError({
22
+ message,
23
+ entity,
24
+ errors: e.issues.map((i) => ({
25
+ ...i,
26
+ path: i.path.map((p) => String(p))
27
+ }))
28
+ });
29
+ };
30
+ var validate = (schema, data, entity, message) => {
31
+ let result = schema.safeParse(data);
32
+ if (!result.success) {
33
+ throw new ServiceError(zodToValidationError(entity, message, result.error));
34
+ }
35
+ return result.data;
36
+ };
37
+ var toJsonSchema = (schema) => schema.toJSONSchema({
38
+ unrepresentable: "any",
39
+ override: (ctx) => {
40
+ let def = ctx.zodSchema._zod.def;
41
+ if (def.type === "date") {
42
+ ctx.jsonSchema.type = "string";
43
+ ctx.jsonSchema.format = "date-time";
44
+ }
45
+ if (def.type === "bigint") {
46
+ ctx.jsonSchema.type = "number";
47
+ }
48
+ }
49
+ });
50
+
51
+ // src/spec.ts
52
+ var getAuthMethod = (slate, authenticationMethodId) => {
53
+ let authMethod = slate.spec.auth.authStack.find((m) => m.key === authenticationMethodId);
54
+ if (!authMethod) {
55
+ throw new ServiceError2(
56
+ badRequestError({
57
+ message: `Invalid authentication method ID: ${authenticationMethodId}`
58
+ })
59
+ );
60
+ }
61
+ return authMethod;
62
+ };
63
+ var mapAuthMethod = (slate, m) => ({
64
+ id: m.key,
65
+ name: m.name,
66
+ type: m.type,
67
+ scopes: "scopes" in m ? m.scopes.map((s) => ({
68
+ id: s.scope,
69
+ title: s.title,
70
+ description: s.description,
71
+ defaultChecked: s.defaultChecked
72
+ })) : void 0,
73
+ inputSchema: toJsonSchema(m.inputSchema ?? z.object({})),
74
+ outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
75
+ capabilities: {
76
+ getDefaultInput: { enabled: !!("getDefaultInput" in m && m.getDefaultInput) },
77
+ handleTokenRefresh: {
78
+ enabled: !!("handleTokenRefresh" in m && m.handleTokenRefresh)
79
+ },
80
+ handleChangedInput: {
81
+ enabled: !!m.onInputChanged
82
+ },
83
+ getProfile: { enabled: !!m.getProfile }
84
+ }
85
+ });
86
+ var getAction = (slate, actionId) => {
87
+ let action = slate.actions.find((m) => m.key === actionId);
88
+ if (!action) {
89
+ throw new ServiceError2(notFoundError(`action`, actionId));
90
+ }
91
+ return action;
92
+ };
93
+ var getActionWithType = (slate, type, actionId) => {
94
+ let action = getAction(slate, actionId);
95
+ if (action.type !== type) {
96
+ throw new ServiceError2(
97
+ badRequestError({
98
+ message: `Action with ID ${actionId} is not of type ${type}`
99
+ })
100
+ );
101
+ }
102
+ return action;
103
+ };
104
+ var mapAction = (_slate, a) => {
105
+ let base = {
106
+ id: a.key,
107
+ name: a.name,
108
+ description: a.description,
109
+ instructions: a.instructions,
110
+ constraints: a.constraints,
111
+ tags: a.tags,
112
+ metadata: a.metadata,
113
+ scopes: a.scopes,
114
+ inputSchema: toJsonSchema(a.inputSchema),
115
+ outputSchema: toJsonSchema(a.outputSchema)
116
+ };
117
+ if (a.type === "tool") {
118
+ return {
119
+ ...base,
120
+ type: "action.tool",
121
+ capabilities: {}
122
+ };
123
+ }
124
+ return {
125
+ ...base,
126
+ type: "action.trigger",
127
+ capabilities: {},
128
+ invocation: a.source === "polling" ? {
129
+ type: "polling",
130
+ intervalSeconds: a.polling.intervalInSeconds ?? SlateDefaultPollingIntervalSeconds
131
+ } : {
132
+ type: "webhook",
133
+ autoRegistration: !!a.autoRegisterWebhook,
134
+ autoUnregistration: !!a.autoUnregisterWebhook
135
+ }
136
+ };
137
+ };
138
+
139
+ // src/state.ts
140
+ var State = class {
141
+ #value;
142
+ get value() {
143
+ return this.#value;
144
+ }
145
+ get() {
146
+ return this.#value;
147
+ }
148
+ set(value) {
149
+ this.#value = value;
150
+ }
151
+ constructor(initialValue) {
152
+ this.#value = initialValue;
153
+ }
154
+ };
155
+
156
+ // src/index.ts
157
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
158
+ var getObjectKeyCount = (value) => isRecord(value) ? Object.keys(value).length : void 0;
159
+ var DOWNLOAD_ATTACHMENT_URL_KEYS = /* @__PURE__ */ new Set([
160
+ "downloadUrl",
161
+ "fileUrl",
162
+ "temporaryDownloadUrl",
163
+ "webContentLink"
164
+ ]);
165
+ var isAttachmentUrl = (value) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value);
166
+ var collectOutputUrlAttachments = (value, seen = /* @__PURE__ */ new Set()) => {
167
+ if (Array.isArray(value)) {
168
+ return value.flatMap((item) => collectOutputUrlAttachments(item, seen));
169
+ }
170
+ if (!isRecord(value)) {
171
+ return [];
172
+ }
173
+ let attachments = [];
174
+ for (let [key, nestedValue] of Object.entries(value)) {
175
+ if (DOWNLOAD_ATTACHMENT_URL_KEYS.has(key) && typeof nestedValue === "string" && nestedValue.length > 0 && isAttachmentUrl(nestedValue) && !seen.has(nestedValue)) {
176
+ seen.add(nestedValue);
177
+ attachments.push({
178
+ content: {
179
+ type: "url",
180
+ url: nestedValue
181
+ }
182
+ });
183
+ continue;
184
+ }
185
+ attachments.push(...collectOutputUrlAttachments(nestedValue, seen));
186
+ }
187
+ return attachments;
188
+ };
189
+ var mergeAttachments = (explicitAttachments, output) => {
190
+ let attachments = [...explicitAttachments ?? []];
191
+ let seen = new Set(attachments.map((attachment) => JSON.stringify(attachment)));
192
+ for (let attachment of collectOutputUrlAttachments(output)) {
193
+ let key = JSON.stringify(attachment);
194
+ if (seen.has(key)) continue;
195
+ seen.add(key);
196
+ attachments.push(attachment);
197
+ }
198
+ return attachments.length > 0 ? attachments : void 0;
199
+ };
200
+ var toErrorMetadata = (error) => {
201
+ if (error instanceof Error) {
202
+ return {
203
+ errorName: error.name,
204
+ errorMessage: error.message,
205
+ errorStack: error.stack
206
+ };
207
+ }
208
+ return {
209
+ errorValue: String(error)
210
+ };
211
+ };
212
+ var formatEntityLabel = (name, key) => `"${name}" (${key})`;
213
+ var resolveTraceMessage = (message, result) => typeof message === "function" ? message(result) : message;
214
+ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandler(async (manager) => {
215
+ let protocol = new State(null);
216
+ let participants = new State(null);
217
+ let auth = new State(null);
218
+ let config = new State(null);
219
+ let session = new State(null);
220
+ let logger = new SlateLogger(listeners);
221
+ let providerTrace = {
222
+ providerId: slate.spec.key,
223
+ providerName: slate.spec.name
224
+ };
225
+ let traceProviderCall = async (trace, handler) => {
226
+ let startedAt = Date.now();
227
+ logger.info({
228
+ ...providerTrace,
229
+ ...trace.metadata,
230
+ component: trace.component,
231
+ functionName: trace.functionName,
232
+ phase: "start",
233
+ message: trace.message
234
+ });
235
+ try {
236
+ let result = await handler();
237
+ let successMessage = resolveTraceMessage(trace.successMessage, result);
238
+ logger.info({
239
+ ...providerTrace,
240
+ ...trace.metadata,
241
+ ...trace.onSuccess?.(result) ?? {},
242
+ component: trace.component,
243
+ functionName: trace.functionName,
244
+ phase: "success",
245
+ durationMs: Date.now() - startedAt,
246
+ message: successMessage
247
+ });
248
+ return result;
249
+ } catch (error) {
250
+ logger.error({
251
+ ...providerTrace,
252
+ ...trace.metadata,
253
+ ...toErrorMetadata(error),
254
+ component: trace.component,
255
+ functionName: trace.functionName,
256
+ phase: "error",
257
+ durationMs: Date.now() - startedAt,
258
+ message: trace.errorMessage ?? `${typeof trace.successMessage === "string" ? trace.successMessage : trace.message} failed`
259
+ });
260
+ throw error;
261
+ }
262
+ };
263
+ let getContextBasic = () => {
264
+ let currentProtocol = protocol.get();
265
+ let currentParticipants = participants.get();
266
+ if (!currentProtocol || !currentParticipants) {
267
+ throw new ServiceError3(
268
+ preconditionFailedError({
269
+ message: "Connection context has not been initialized"
270
+ })
271
+ );
272
+ }
273
+ return {
274
+ protocol: currentProtocol,
275
+ participants: currentParticipants
276
+ };
277
+ };
278
+ let getContextFull = () => {
279
+ let basic = getContextBasic();
280
+ let currentConfig = config.get();
281
+ let currentSession = session.get();
282
+ let currentAuth = auth.get();
283
+ if (!currentConfig || !currentSession || !currentAuth && slate.spec.auth.authStack.length > 0) {
284
+ throw new ServiceError3(
285
+ preconditionFailedError({
286
+ message: "Session context has not been initialized"
287
+ })
288
+ );
289
+ }
290
+ return {
291
+ ...basic,
292
+ config: currentConfig.value,
293
+ session: currentSession,
294
+ auth: currentAuth
295
+ };
296
+ };
297
+ let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec, logger);
298
+ let withRequestTraces = (context, result) => {
299
+ let requestTraces = context.getHttpTraces();
300
+ return requestTraces.length > 0 ? { ...result, requestTraces } : result;
301
+ };
302
+ manager.onNotification("slates/hello", async ({ params }) => {
303
+ protocol.set(params.protocol);
304
+ });
305
+ manager.onNotification("slates/participant.set", async ({ params }) => {
306
+ if (!protocol.get()) {
307
+ throw new ServiceError3(
308
+ preconditionFailedError({ message: "Connection protocol has not been initialized" })
309
+ );
310
+ }
311
+ participants.set(params.participants);
312
+ });
313
+ manager.onNotification("slates/auth.set", async ({ params }) => {
314
+ getContextBasic();
315
+ getAuthMethod(slate, params.authenticationMethodId);
316
+ let valRes = validate(
317
+ slate.spec.authSchema,
318
+ params.output,
319
+ "auth",
320
+ `Invalid authentication output for method ID: ${params.authenticationMethodId}`
321
+ );
322
+ auth.set({
323
+ authenticationMethodId: params.authenticationMethodId,
324
+ output: valRes
325
+ });
326
+ });
327
+ manager.onNotification("slates/config.set", async ({ params }) => {
328
+ getContextBasic();
329
+ let value = validate(
330
+ slate.spec.configSchema,
331
+ params.config,
332
+ "config",
333
+ "Invalid configuration"
334
+ );
335
+ config.set({ value });
336
+ });
337
+ manager.onNotification("slates/session.start", async ({ params }) => {
338
+ getContextBasic();
339
+ session.set({
340
+ id: params.sessionId,
341
+ state: params.state
342
+ });
343
+ });
344
+ manager.onRequest("slates/config.changed", async ({ params }) => {
345
+ getContextBasic();
346
+ let newConfig = validate(
347
+ slate.spec.config.configSchema,
348
+ params.newConfig,
349
+ "config",
350
+ "Invalid configuration"
351
+ );
352
+ let configChanged = slate.spec.config.handlers.configChanged;
353
+ if (!configChanged) {
354
+ return { success: true, config: newConfig };
355
+ }
356
+ let context = getEmptyContext();
357
+ let updatedConfig = await traceProviderCall(
358
+ {
359
+ component: "config",
360
+ functionName: "configChanged",
361
+ message: "Running config change handler",
362
+ successMessage: "Config change handler completed",
363
+ metadata: {
364
+ hasPreviousConfig: params.previousConfig !== null,
365
+ newConfigKeyCount: getObjectKeyCount(newConfig)
366
+ },
367
+ onSuccess: (result) => ({
368
+ returnedConfig: !!result?.config
369
+ })
370
+ },
371
+ () => runWithContext(
372
+ context,
373
+ async () => configChanged({
374
+ previousConfig: params.previousConfig,
375
+ newConfig
376
+ })
377
+ )
378
+ );
379
+ return withRequestTraces(context, {
380
+ success: true,
381
+ config: updatedConfig?.config ?? newConfig
382
+ });
383
+ });
384
+ manager.onRequest("slates/config.get_default", async () => {
385
+ getContextBasic();
386
+ let getDefaultConfig = slate.spec.config.handlers.getDefaultConfig;
387
+ if (!getDefaultConfig) {
388
+ return { config: null };
389
+ }
390
+ let context = getEmptyContext();
391
+ let defaultConfig = await traceProviderCall(
392
+ {
393
+ component: "config",
394
+ functionName: "getDefaultConfig",
395
+ message: "Getting default config",
396
+ successMessage: "Default config retrieved",
397
+ onSuccess: (result) => ({
398
+ configKeyCount: getObjectKeyCount(result)
399
+ })
400
+ },
401
+ () => runWithContext(context, async () => getDefaultConfig())
402
+ );
403
+ return withRequestTraces(context, {
404
+ config: defaultConfig ?? null
405
+ });
406
+ });
407
+ manager.onRequest("slates/config.schema.get", async () => {
408
+ getContextBasic();
409
+ return { schema: toJsonSchema(slate.spec.configSchema) };
410
+ });
411
+ manager.onRequest("slates/provider.identify", async () => {
412
+ getContextBasic();
413
+ return {
414
+ protocol: SLATES_PROTOCOL_VERSION,
415
+ provider: {
416
+ type: "provider",
417
+ id: slate.spec.key,
418
+ name: slate.spec.name,
419
+ description: slate.spec.description,
420
+ metadata: slate.spec.parameters.metadata
421
+ }
422
+ };
423
+ });
424
+ manager.onRequest("slates/auth.methods.list", async () => {
425
+ getContextBasic();
426
+ return {
427
+ authenticationMethods: slate.spec.auth.authStack.map((m) => mapAuthMethod(slate, m))
428
+ };
429
+ });
430
+ manager.onRequest("slates/auth.method.get", async ({ params }) => {
431
+ getContextBasic();
432
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
433
+ return {
434
+ authenticationMethod: mapAuthMethod(slate, authMethod)
435
+ };
436
+ });
437
+ manager.onRequest("slates/auth.input.get_default", async ({ params }) => {
438
+ getContextBasic();
439
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
440
+ if (!authMethod.getDefaultInput) {
441
+ return { input: null };
442
+ }
443
+ let context = getEmptyContext();
444
+ let input = await traceProviderCall(
445
+ {
446
+ component: "auth",
447
+ functionName: "getDefaultInput",
448
+ message: "Getting default authentication input",
449
+ successMessage: "Default authentication input retrieved",
450
+ metadata: {
451
+ authenticationMethodId: params.authenticationMethodId,
452
+ authenticationMethodName: authMethod.name
453
+ },
454
+ onSuccess: (result) => ({
455
+ inputKeyCount: getObjectKeyCount(result)
456
+ })
457
+ },
458
+ () => runWithContext(context, () => authMethod.getDefaultInput())
459
+ );
460
+ return withRequestTraces(context, { input });
461
+ });
462
+ manager.onRequest("slates/auth.input.changed", async ({ params }) => {
463
+ getContextBasic();
464
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
465
+ if (!authMethod.onInputChanged) {
466
+ return { success: true, input: params.newInput };
467
+ }
468
+ let context = getEmptyContext();
469
+ let updatedInput = await traceProviderCall(
470
+ {
471
+ component: "auth",
472
+ functionName: "onInputChanged",
473
+ message: "Running authentication input change handler",
474
+ successMessage: "Authentication input change handler completed",
475
+ metadata: {
476
+ authenticationMethodId: params.authenticationMethodId,
477
+ authenticationMethodName: authMethod.name,
478
+ hasPreviousInput: params.previousInput !== null,
479
+ newInputKeyCount: getObjectKeyCount(params.newInput)
480
+ },
481
+ onSuccess: (result) => ({
482
+ returnedInput: !!result?.input
483
+ })
484
+ },
485
+ () => runWithContext(
486
+ context,
487
+ () => authMethod.onInputChanged({
488
+ previousInput: params.previousInput,
489
+ newInput: params.newInput
490
+ })
491
+ )
492
+ );
493
+ return withRequestTraces(context, {
494
+ success: true,
495
+ input: updatedInput?.input ?? params.newInput
496
+ });
497
+ });
498
+ manager.onRequest("slates/auth.output.get", async ({ params }) => {
499
+ getContextBasic();
500
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
501
+ let input = params.input;
502
+ if (authMethod.inputSchema) {
503
+ input = validate(
504
+ authMethod.inputSchema,
505
+ input,
506
+ "auth",
507
+ `Invalid authentication input for method ID: ${params.authenticationMethodId}`
508
+ );
509
+ }
510
+ if ("getOutput" in authMethod) {
511
+ let context = getEmptyContext();
512
+ let outputRes = await traceProviderCall(
513
+ {
514
+ component: "auth",
515
+ functionName: "getOutput",
516
+ message: "Getting authentication output",
517
+ successMessage: "Authentication output retrieved",
518
+ metadata: {
519
+ authenticationMethodId: params.authenticationMethodId,
520
+ authenticationMethodName: authMethod.name,
521
+ inputKeyCount: getObjectKeyCount(input)
522
+ },
523
+ onSuccess: (result) => ({
524
+ outputKeyCount: getObjectKeyCount(result.output)
525
+ })
526
+ },
527
+ () => runWithContext(context, () => authMethod.getOutput({ input }))
528
+ );
529
+ return withRequestTraces(context, { output: outputRes.output });
530
+ }
531
+ return { output: input };
532
+ });
533
+ manager.onRequest("slates/auth.authorization_callback.handle", async ({ params }) => {
534
+ getContextBasic();
535
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
536
+ if ("handleCallback" in authMethod) {
537
+ let context = getEmptyContext();
538
+ let callbackRes = await traceProviderCall(
539
+ {
540
+ component: "auth",
541
+ functionName: "handleCallback",
542
+ message: "Handling authentication callback",
543
+ successMessage: "Authentication callback handled",
544
+ metadata: {
545
+ authenticationMethodId: params.authenticationMethodId,
546
+ authenticationMethodName: authMethod.name,
547
+ scopeCount: params.scopes.length,
548
+ hasCallbackState: !!params.callbackState
549
+ },
550
+ onSuccess: (result) => ({
551
+ outputKeyCount: getObjectKeyCount(result.output),
552
+ returnedInput: !!result.input,
553
+ returnedScopeCount: result.scopes?.length
554
+ })
555
+ },
556
+ () => runWithContext(
557
+ context,
558
+ () => authMethod.handleCallback({
559
+ code: params.code,
560
+ state: params.state,
561
+ redirectUri: params.redirectUri,
562
+ input: params.input,
563
+ clientId: params.clientId,
564
+ clientSecret: params.clientSecret,
565
+ scopes: params.scopes,
566
+ callbackState: params.callbackState || {}
567
+ })
568
+ )
569
+ );
570
+ return withRequestTraces(context, {
571
+ output: callbackRes.output,
572
+ input: callbackRes.input,
573
+ scopes: callbackRes.scopes
574
+ });
575
+ }
576
+ throw new ServiceError3(
577
+ preconditionFailedError({
578
+ message: `Authentication method does not support authorization callback handling: ${params.authenticationMethodId}`
579
+ })
580
+ );
581
+ });
582
+ manager.onRequest("slates/auth.authorization_url.get", async ({ params }) => {
583
+ getContextBasic();
584
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
585
+ if ("getAuthorizationUrl" in authMethod) {
586
+ let context = getEmptyContext();
587
+ let urlRes = await traceProviderCall(
588
+ {
589
+ component: "auth",
590
+ functionName: "getAuthorizationUrl",
591
+ message: "Getting authentication authorization URL",
592
+ successMessage: "Authentication authorization URL retrieved",
593
+ metadata: {
594
+ authenticationMethodId: params.authenticationMethodId,
595
+ authenticationMethodName: authMethod.name,
596
+ scopeCount: params.scopes.length,
597
+ inputKeyCount: getObjectKeyCount(params.input)
598
+ },
599
+ onSuccess: (result) => ({
600
+ returnedInput: !!result.input,
601
+ hasCallbackState: !!result.callbackState
602
+ })
603
+ },
604
+ () => runWithContext(
605
+ context,
606
+ () => authMethod.getAuthorizationUrl({
607
+ redirectUri: params.redirectUri,
608
+ state: params.state,
609
+ input: params.input,
610
+ clientId: params.clientId,
611
+ clientSecret: params.clientSecret,
612
+ scopes: params.scopes
613
+ })
614
+ )
615
+ );
616
+ return withRequestTraces(context, {
617
+ authorizationUrl: urlRes.url,
618
+ input: urlRes.input,
619
+ callbackState: urlRes.callbackState
620
+ });
621
+ }
622
+ throw new ServiceError3(
623
+ preconditionFailedError({
624
+ message: `Authentication method does not support authorization URL retrieval: ${params.authenticationMethodId}`
625
+ })
626
+ );
627
+ });
628
+ manager.onRequest("slates/auth.profile.get", async ({ params }) => {
629
+ getContextBasic();
630
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
631
+ if (authMethod.getProfile) {
632
+ let context = getEmptyContext();
633
+ let profileRes = await traceProviderCall(
634
+ {
635
+ component: "auth",
636
+ functionName: "getProfile",
637
+ message: "Getting authentication profile",
638
+ successMessage: "Authentication profile retrieved",
639
+ metadata: {
640
+ authenticationMethodId: params.authenticationMethodId,
641
+ authenticationMethodName: authMethod.name,
642
+ scopeCount: params.scopes.length,
643
+ inputKeyCount: getObjectKeyCount(params.input),
644
+ outputKeyCount: getObjectKeyCount(params.output)
645
+ },
646
+ onSuccess: (result) => ({
647
+ profileKeyCount: getObjectKeyCount(result.profile)
648
+ })
649
+ },
650
+ () => runWithContext(
651
+ context,
652
+ () => authMethod.getProfile({
653
+ output: params.output,
654
+ input: params.input,
655
+ scopes: params.scopes
656
+ })
657
+ )
658
+ );
659
+ return withRequestTraces(context, {
660
+ profile: profileRes.profile
661
+ });
662
+ }
663
+ throw new ServiceError3(
664
+ preconditionFailedError({
665
+ message: `Authentication method does not support profile retrieval: ${params.authenticationMethodId}`
666
+ })
667
+ );
668
+ });
669
+ manager.onRequest("slates/auth.token_refresh.handle", async ({ params }) => {
670
+ getContextBasic();
671
+ let authMethod = getAuthMethod(slate, params.authenticationMethodId);
672
+ if ("handleTokenRefresh" in authMethod && authMethod.handleTokenRefresh) {
673
+ let context = getEmptyContext();
674
+ let refreshRes = await traceProviderCall(
675
+ {
676
+ component: "auth",
677
+ functionName: "handleTokenRefresh",
678
+ message: "Refreshing authentication token",
679
+ successMessage: "Authentication token refreshed",
680
+ metadata: {
681
+ authenticationMethodId: params.authenticationMethodId,
682
+ authenticationMethodName: authMethod.name,
683
+ scopeCount: params.scopes.length,
684
+ inputKeyCount: getObjectKeyCount(params.input),
685
+ outputKeyCount: getObjectKeyCount(params.output)
686
+ },
687
+ onSuccess: (result) => ({
688
+ refreshedOutputKeyCount: getObjectKeyCount(result.output),
689
+ returnedInput: !!result.input
690
+ })
691
+ },
692
+ () => runWithContext(
693
+ context,
694
+ () => authMethod.handleTokenRefresh({
695
+ output: params.output,
696
+ input: params.input,
697
+ clientId: params.clientId,
698
+ clientSecret: params.clientSecret,
699
+ scopes: params.scopes
700
+ })
701
+ )
702
+ );
703
+ return withRequestTraces(context, {
704
+ output: refreshRes.output,
705
+ input: refreshRes.input
706
+ });
707
+ }
708
+ throw new ServiceError3(
709
+ preconditionFailedError({
710
+ message: `Authentication method does not support token refresh handling: ${params.authenticationMethodId}`
711
+ })
712
+ );
713
+ });
714
+ manager.onRequest("slates/actions.list", async () => {
715
+ getContextBasic();
716
+ return {
717
+ actions: slate.actions.map((a) => mapAction(slate, a))
718
+ };
719
+ });
720
+ manager.onRequest("slates/action.get", async ({ params }) => {
721
+ getContextBasic();
722
+ let action = getAction(slate, params.actionId);
723
+ return {
724
+ action: mapAction(slate, action)
725
+ };
726
+ });
727
+ manager.onRequest("slates/action.tool.invoke", async ({ params }) => {
728
+ let ctx = getContextFull();
729
+ let action = getActionWithType(slate, "tool", params.actionId);
730
+ let input = validate(
731
+ action.inputSchema,
732
+ params.input,
733
+ "input",
734
+ `Invalid input for tool ID: ${params.actionId}`
735
+ );
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))
758
+ );
759
+ return withRequestTraces(context, {
760
+ output: res.output,
761
+ message: res.message,
762
+ attachments: mergeAttachments(res.attachments, res.output)
763
+ });
764
+ });
765
+ manager.onRequest("slates/action.trigger.map_event", async ({ params }) => {
766
+ let ctx = getContextFull();
767
+ let action = getActionWithType(slate, "trigger", params.actionId);
768
+ let input = validate(
769
+ action.inputSchema,
770
+ params.input,
771
+ "input",
772
+ `Invalid event for trigger ID: ${params.actionId}`
773
+ );
774
+ let context = new SlateContext(ctx.config, input, ctx.auth?.output, slate.spec, logger);
775
+ let res = await traceProviderCall(
776
+ {
777
+ component: "action",
778
+ functionName: "handleEvent",
779
+ message: `Mapping event for trigger ${formatEntityLabel(action.name, action.key)}`,
780
+ successMessage: (result) => `Mapped trigger event "${result.type}" for ${formatEntityLabel(action.name, action.key)}`,
781
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while mapping an event`,
782
+ metadata: {
783
+ actionId: action.key,
784
+ actionName: action.name,
785
+ actionType: action.type,
786
+ inputKeyCount: getObjectKeyCount(input)
787
+ },
788
+ onSuccess: (result) => ({
789
+ eventType: result.type,
790
+ hasEventId: !!result.id,
791
+ outputKeyCount: getObjectKeyCount(result.output)
792
+ })
793
+ },
794
+ () => runWithContext(context, () => action.handleEvent(context))
795
+ );
796
+ return withRequestTraces(context, { id: res.id, type: res.type, output: res.output });
797
+ });
798
+ manager.onRequest("slates/action.trigger.poll_events", async ({ params }) => {
799
+ let ctx = getContextFull();
800
+ let action = getActionWithType(slate, "trigger", params.actionId);
801
+ if (!action.pollEvents) {
802
+ throw new ServiceError3(
803
+ badRequestError2({
804
+ message: `Trigger action does not support polling: ${params.actionId}`
805
+ })
806
+ );
807
+ }
808
+ let context = new SlateContext(
809
+ ctx.config,
810
+ { state: params.state },
811
+ ctx.auth?.output,
812
+ slate.spec,
813
+ logger
814
+ );
815
+ let res = await traceProviderCall(
816
+ {
817
+ component: "action",
818
+ functionName: "pollEvents",
819
+ message: `Polling events for trigger ${formatEntityLabel(action.name, action.key)}`,
820
+ successMessage: (result) => `Polled ${result.inputs.length} event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
821
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while polling events`,
822
+ metadata: {
823
+ actionId: action.key,
824
+ actionName: action.name,
825
+ actionType: action.type,
826
+ hasPreviousState: params.state !== null
827
+ },
828
+ onSuccess: (result) => ({
829
+ inputCount: result.inputs.length,
830
+ hasUpdatedState: result.updatedState !== void 0
831
+ })
832
+ },
833
+ () => runWithContext(context, () => action.pollEvents(context))
834
+ );
835
+ return withRequestTraces(context, {
836
+ inputs: res.inputs,
837
+ updatedState: res.updatedState
838
+ });
839
+ });
840
+ manager.onRequest("slates/action.trigger.webhook_handle", async ({ params }) => {
841
+ let ctx = getContextFull();
842
+ let action = getActionWithType(slate, "trigger", params.actionId);
843
+ if (!action.handleRequest) {
844
+ throw new ServiceError3(
845
+ badRequestError2({
846
+ message: `Trigger action does not support webhook requests: ${params.actionId}`
847
+ })
848
+ );
849
+ }
850
+ let req = new Request(params.url, {
851
+ method: params.method,
852
+ headers: params.headers,
853
+ body: params.body ? Uint8Array.from(atob(params.body.content), (c) => c.charCodeAt(0)) : null
854
+ });
855
+ let context = new SlateContext(
856
+ ctx.config,
857
+ { request: req, state: params.state },
858
+ ctx.auth?.output,
859
+ slate.spec,
860
+ logger
861
+ );
862
+ let res = await traceProviderCall(
863
+ {
864
+ component: "action",
865
+ functionName: "handleRequest",
866
+ message: `Handling webhook request for trigger ${formatEntityLabel(action.name, action.key)}`,
867
+ successMessage: (result) => `Received ${result.inputs.length} webhook event(s) for trigger ${formatEntityLabel(action.name, action.key)}`,
868
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while handling a webhook request`,
869
+ metadata: {
870
+ actionId: action.key,
871
+ actionName: action.name,
872
+ actionType: action.type,
873
+ requestMethod: params.method,
874
+ hasRequestBody: !!params.body,
875
+ hasPreviousState: params.state !== null
876
+ },
877
+ onSuccess: (result) => ({
878
+ inputCount: result.inputs.length,
879
+ hasUpdatedState: result.updatedState !== void 0
880
+ })
881
+ },
882
+ () => runWithContext(context, () => action.handleRequest(context))
883
+ );
884
+ return withRequestTraces(context, {
885
+ inputs: res.inputs,
886
+ updatedState: res.updatedState
887
+ });
888
+ });
889
+ manager.onRequest("slates/action.trigger.webhook_register", async ({ params }) => {
890
+ let ctx = getContextFull();
891
+ let action = getActionWithType(slate, "trigger", params.actionId);
892
+ if (!action.autoRegisterWebhook) {
893
+ throw new ServiceError3(
894
+ badRequestError2({
895
+ message: `Trigger action does not support webhook auto-registration: ${params.actionId}`
896
+ })
897
+ );
898
+ }
899
+ let context = new SlateContext(
900
+ ctx.config,
901
+ { webhookBaseUrl: params.webhookBaseUrl },
902
+ ctx.auth?.output,
903
+ slate.spec,
904
+ logger
905
+ );
906
+ let res = await traceProviderCall(
907
+ {
908
+ component: "action",
909
+ functionName: "autoRegisterWebhook",
910
+ message: `Registering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
911
+ successMessage: `Registered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
912
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while registering a webhook`,
913
+ metadata: {
914
+ actionId: action.key,
915
+ actionName: action.name,
916
+ actionType: action.type
917
+ },
918
+ onSuccess: (result) => ({
919
+ hasRegistrationDetails: result.registrationDetails !== void 0,
920
+ hasState: result.state !== void 0
921
+ })
922
+ },
923
+ () => runWithContext(context, () => action.autoRegisterWebhook(context))
924
+ );
925
+ return withRequestTraces(context, {
926
+ registrationDetails: res.registrationDetails,
927
+ state: res.state
928
+ });
929
+ });
930
+ manager.onRequest("slates/action.trigger.webhook_unregister", async ({ params }) => {
931
+ let ctx = getContextFull();
932
+ let action = getActionWithType(slate, "trigger", params.actionId);
933
+ if (!action.autoUnregisterWebhook) {
934
+ throw new ServiceError3(
935
+ badRequestError2({
936
+ message: `Trigger action does not support webhook auto-unregistration: ${params.actionId}`
937
+ })
938
+ );
939
+ }
940
+ let context = new SlateContext(
941
+ ctx.config,
942
+ {
943
+ webhookBaseUrl: params.webhookBaseUrl,
944
+ registrationDetails: params.registrationDetails,
945
+ state: params.state
946
+ },
947
+ ctx.auth?.output,
948
+ slate.spec,
949
+ logger
950
+ );
951
+ await traceProviderCall(
952
+ {
953
+ component: "action",
954
+ functionName: "autoUnregisterWebhook",
955
+ message: `Unregistering webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
956
+ successMessage: `Unregistered webhook for trigger ${formatEntityLabel(action.name, action.key)}`,
957
+ errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while unregistering a webhook`,
958
+ metadata: {
959
+ actionId: action.key,
960
+ actionName: action.name,
961
+ actionType: action.type,
962
+ hasRegistrationDetails: params.registrationDetails !== null,
963
+ hasPreviousState: params.state !== null
964
+ }
965
+ },
966
+ () => runWithContext(context, () => action.autoUnregisterWebhook(context))
967
+ );
968
+ return withRequestTraces(context, {});
969
+ });
970
+ });
971
+ export {
972
+ createProviderHandler
973
+ };