@slates/provider-handler 1.0.0-rc.22 → 1.0.0-rc.24

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/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(index_exports);
36
36
  var import_error4 = require("@lowerdeck/error");
37
37
  var import_proto = require("@slates/proto");
38
38
  var import_provider2 = require("@slates/provider");
39
+ var import_p_queue = __toESM(require("p-queue"), 1);
39
40
 
40
41
  // src/spec.ts
41
42
  var import_error2 = require("@lowerdeck/error");
@@ -94,8 +95,7 @@ var mapAuthMethod = (slate, m) => ({
94
95
  scopes: "scopes" in m ? m.scopes.map((s) => ({
95
96
  id: s.scope,
96
97
  title: s.title,
97
- description: s.description,
98
- defaultChecked: s.defaultChecked
98
+ description: s.description
99
99
  })) : void 0,
100
100
  inputSchema: toJsonSchema(m.inputSchema ?? import_zod.default.object({})),
101
101
  outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
@@ -253,6 +253,36 @@ var serializeWebhookHttpResponse = async (response) => {
253
253
  };
254
254
 
255
255
  // src/index.ts
256
+ var DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 1e8;
257
+ var routeAttachmentsThroughDirectUpload = async (attachments, live) => {
258
+ if (!attachments || attachments.length === 0 || !live) return attachments;
259
+ let contentEntries = attachments.map((attachment, index) => ({ attachment, index })).filter((entry) => entry.attachment.content.type === "content");
260
+ if (contentEntries.length === 0) return attachments;
261
+ let queue = new import_p_queue.default({ concurrency: 10 });
262
+ let replacements = /* @__PURE__ */ new Map();
263
+ await Promise.all(
264
+ contentEntries.map(
265
+ (entry) => queue.add(async () => {
266
+ try {
267
+ let body = entry.attachment.content.type === "content" ? entry.attachment.content.encoding === "base64" ? Buffer.from(entry.attachment.content.content, "base64") : Buffer.from(entry.attachment.content.content, "utf-8") : Buffer.alloc(0);
268
+ replacements.set(
269
+ entry.index,
270
+ await (0, import_provider2.uploadAttachmentDirect)({
271
+ live,
272
+ mimeType: entry.attachment.mimeType,
273
+ body
274
+ })
275
+ );
276
+ } catch {
277
+ replacements.set(entry.index, null);
278
+ }
279
+ })
280
+ )
281
+ );
282
+ return attachments.map(
283
+ (attachment, index) => replacements.has(index) ? replacements.get(index) : attachment
284
+ ).filter((a) => a != null);
285
+ };
256
286
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
257
287
  var getObjectKeyCount = (value) => isRecord(value) ? Object.keys(value).length : void 0;
258
288
  var DOWNLOAD_ATTACHMENT_URL_KEYS = /* @__PURE__ */ new Set([
@@ -316,6 +346,19 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
316
346
  let auth = new State(null);
317
347
  let config = new State(null);
318
348
  let session = new State(null);
349
+ let hubCapabilities = new State(null);
350
+ let liveInvocation = new State(
351
+ null
352
+ );
353
+ let getLiveInvocation = () => {
354
+ let directUpload = hubCapabilities.get()?.attachments?.directUpload;
355
+ let live = liveInvocation.get();
356
+ if (!directUpload?.enabled || !live) return null;
357
+ return {
358
+ ...live,
359
+ maxAttachmentSizeBytes: directUpload.maxAttachmentSizeBytes ?? DEFAULT_MAX_ATTACHMENT_SIZE_BYTES
360
+ };
361
+ };
319
362
  let logger = new import_provider2.SlateLogger(listeners);
320
363
  let providerTrace = {
321
364
  providerId: slate.spec.key,
@@ -442,6 +485,15 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
442
485
  state: params.state
443
486
  });
444
487
  });
488
+ manager.onNotification("slates/hub.capabilities.set", async ({ params }) => {
489
+ hubCapabilities.set(params.capabilities);
490
+ });
491
+ manager.onNotification("slates/hub.live_invocation.set", async ({ params }) => {
492
+ liveInvocation.set({
493
+ token: params.token,
494
+ baseUrl: params.baseUrl
495
+ });
496
+ });
445
497
  manager.onRequest("slates/config.changed", async ({ params }) => {
446
498
  getContextBasic();
447
499
  let newConfig = validate(
@@ -906,19 +958,39 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
906
958
  },
907
959
  () => (0, import_provider2.runWithContext)(context, () => action.handleInvocation(context))
908
960
  );
961
+ let contextAttachments = await context._finalizeAttachments();
962
+ let merged = mergeAttachments(
963
+ [...res.attachments ?? [], ...contextAttachments],
964
+ res.output
965
+ );
966
+ let redacted = (0, import_provider2.redactUrlAttachmentSecrets)(
967
+ merged,
968
+ context._getAuthConfigForRedaction()
969
+ );
970
+ let finalAttachments = await routeAttachmentsThroughDirectUpload(
971
+ redacted,
972
+ getLiveInvocation()
973
+ );
909
974
  return withRequestTraces(context, {
910
975
  output: res.output,
911
976
  message: res.message,
912
- attachments: mergeAttachments(res.attachments, res.output)
977
+ attachments: finalAttachments
913
978
  });
914
979
  };
915
980
  if (action.isPublic) {
916
981
  getContextBasic();
917
- return invoke(new import_provider2.SlatePublicContext(input, slate.spec, logger));
982
+ return invoke(new import_provider2.SlatePublicContext(input, slate.spec, logger, getLiveInvocation()));
918
983
  }
919
984
  let ctx = getContextFull();
920
985
  return invoke(
921
- new import_provider2.SlateContext(ctx.config, input, ctx.auth?.output, slate.spec, logger)
986
+ new import_provider2.SlateContext(
987
+ ctx.config,
988
+ input,
989
+ ctx.auth?.output,
990
+ slate.spec,
991
+ logger,
992
+ getLiveInvocation()
993
+ )
922
994
  );
923
995
  });
924
996
  manager.onRequest("slates/action.trigger.map_event", async ({ params }) => {
@@ -5,11 +5,14 @@ import {
5
5
  SLATES_PROTOCOL_VERSION
6
6
  } from "@slates/proto";
7
7
  import {
8
+ redactUrlAttachmentSecrets,
8
9
  runWithContext,
9
10
  SlateContext,
10
11
  SlateLogger,
11
- SlatePublicContext
12
+ SlatePublicContext,
13
+ uploadAttachmentDirect
12
14
  } from "@slates/provider";
15
+ import PQueue from "p-queue";
13
16
 
14
17
  // src/spec.ts
15
18
  import { badRequestError, notFoundError, ServiceError as ServiceError2 } from "@lowerdeck/error";
@@ -70,8 +73,7 @@ var mapAuthMethod = (slate, m) => ({
70
73
  scopes: "scopes" in m ? m.scopes.map((s) => ({
71
74
  id: s.scope,
72
75
  title: s.title,
73
- description: s.description,
74
- defaultChecked: s.defaultChecked
76
+ description: s.description
75
77
  })) : void 0,
76
78
  inputSchema: toJsonSchema(m.inputSchema ?? z.object({})),
77
79
  outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
@@ -229,6 +231,36 @@ var serializeWebhookHttpResponse = async (response) => {
229
231
  };
230
232
 
231
233
  // src/index.ts
234
+ var DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 1e8;
235
+ var routeAttachmentsThroughDirectUpload = async (attachments, live) => {
236
+ if (!attachments || attachments.length === 0 || !live) return attachments;
237
+ let contentEntries = attachments.map((attachment, index) => ({ attachment, index })).filter((entry) => entry.attachment.content.type === "content");
238
+ if (contentEntries.length === 0) return attachments;
239
+ let queue = new PQueue({ concurrency: 10 });
240
+ let replacements = /* @__PURE__ */ new Map();
241
+ await Promise.all(
242
+ contentEntries.map(
243
+ (entry) => queue.add(async () => {
244
+ try {
245
+ let body = entry.attachment.content.type === "content" ? entry.attachment.content.encoding === "base64" ? Buffer.from(entry.attachment.content.content, "base64") : Buffer.from(entry.attachment.content.content, "utf-8") : Buffer.alloc(0);
246
+ replacements.set(
247
+ entry.index,
248
+ await uploadAttachmentDirect({
249
+ live,
250
+ mimeType: entry.attachment.mimeType,
251
+ body
252
+ })
253
+ );
254
+ } catch {
255
+ replacements.set(entry.index, null);
256
+ }
257
+ })
258
+ )
259
+ );
260
+ return attachments.map(
261
+ (attachment, index) => replacements.has(index) ? replacements.get(index) : attachment
262
+ ).filter((a) => a != null);
263
+ };
232
264
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
233
265
  var getObjectKeyCount = (value) => isRecord(value) ? Object.keys(value).length : void 0;
234
266
  var DOWNLOAD_ATTACHMENT_URL_KEYS = /* @__PURE__ */ new Set([
@@ -292,6 +324,19 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
292
324
  let auth = new State(null);
293
325
  let config = new State(null);
294
326
  let session = new State(null);
327
+ let hubCapabilities = new State(null);
328
+ let liveInvocation = new State(
329
+ null
330
+ );
331
+ let getLiveInvocation = () => {
332
+ let directUpload = hubCapabilities.get()?.attachments?.directUpload;
333
+ let live = liveInvocation.get();
334
+ if (!directUpload?.enabled || !live) return null;
335
+ return {
336
+ ...live,
337
+ maxAttachmentSizeBytes: directUpload.maxAttachmentSizeBytes ?? DEFAULT_MAX_ATTACHMENT_SIZE_BYTES
338
+ };
339
+ };
295
340
  let logger = new SlateLogger(listeners);
296
341
  let providerTrace = {
297
342
  providerId: slate.spec.key,
@@ -418,6 +463,15 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
418
463
  state: params.state
419
464
  });
420
465
  });
466
+ manager.onNotification("slates/hub.capabilities.set", async ({ params }) => {
467
+ hubCapabilities.set(params.capabilities);
468
+ });
469
+ manager.onNotification("slates/hub.live_invocation.set", async ({ params }) => {
470
+ liveInvocation.set({
471
+ token: params.token,
472
+ baseUrl: params.baseUrl
473
+ });
474
+ });
421
475
  manager.onRequest("slates/config.changed", async ({ params }) => {
422
476
  getContextBasic();
423
477
  let newConfig = validate(
@@ -882,19 +936,39 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
882
936
  },
883
937
  () => runWithContext(context, () => action.handleInvocation(context))
884
938
  );
939
+ let contextAttachments = await context._finalizeAttachments();
940
+ let merged = mergeAttachments(
941
+ [...res.attachments ?? [], ...contextAttachments],
942
+ res.output
943
+ );
944
+ let redacted = redactUrlAttachmentSecrets(
945
+ merged,
946
+ context._getAuthConfigForRedaction()
947
+ );
948
+ let finalAttachments = await routeAttachmentsThroughDirectUpload(
949
+ redacted,
950
+ getLiveInvocation()
951
+ );
885
952
  return withRequestTraces(context, {
886
953
  output: res.output,
887
954
  message: res.message,
888
- attachments: mergeAttachments(res.attachments, res.output)
955
+ attachments: finalAttachments
889
956
  });
890
957
  };
891
958
  if (action.isPublic) {
892
959
  getContextBasic();
893
- return invoke(new SlatePublicContext(input, slate.spec, logger));
960
+ return invoke(new SlatePublicContext(input, slate.spec, logger, getLiveInvocation()));
894
961
  }
895
962
  let ctx = getContextFull();
896
963
  return invoke(
897
- new SlateContext(ctx.config, input, ctx.auth?.output, slate.spec, logger)
964
+ new SlateContext(
965
+ ctx.config,
966
+ input,
967
+ ctx.auth?.output,
968
+ slate.spec,
969
+ logger,
970
+ getLiveInvocation()
971
+ )
898
972
  );
899
973
  });
900
974
  manager.onRequest("slates/action.trigger.map_event", async ({ params }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider-handler",
3
- "version": "1.0.0-rc.22",
3
+ "version": "1.0.0-rc.24",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,8 +32,9 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@lowerdeck/error": "^1.1.0",
35
- "@slates/proto": "1.0.0-rc.15",
36
- "@slates/provider": "1.0.0-rc.18",
35
+ "@slates/proto": "1.0.0-rc.16",
36
+ "@slates/provider": "1.0.0-rc.19",
37
+ "p-queue": "^6.6.2",
37
38
  "zod": "^4.2.1"
38
39
  },
39
40
  "devDependencies": {
@@ -0,0 +1,171 @@
1
+ import { SLATES_PROTOCOL_VERSION, SlatesProviderProtoHandlerManager } from '@slates/proto';
2
+ import {
3
+ Slate,
4
+ SlateAuth,
5
+ SlateConfig,
6
+ SlatePublicTool,
7
+ SlateSpecification
8
+ } from '@slates/provider';
9
+ import { afterEach, describe, expect, it, vi } from 'vitest';
10
+ import { z } from 'zod';
11
+ import { createProviderHandler } from './index';
12
+
13
+ let createManager = async () => {
14
+ let spec = SlateSpecification.create({
15
+ key: 'attachments-test',
16
+ name: 'Attachments Test',
17
+ config: SlateConfig.create(z.object({})),
18
+ auth: SlateAuth.create().output(z.object({}))
19
+ });
20
+ let tool = SlatePublicTool.create(spec, {
21
+ key: 'attach',
22
+ name: 'Attach'
23
+ })
24
+ .input(z.object({}))
25
+ .output(z.object({}))
26
+ .handleInvocation(async ctx => {
27
+ await ctx.addAttachment({
28
+ type: 'content',
29
+ content: new Response('hello', {
30
+ headers: { 'content-type': 'text/plain' }
31
+ }),
32
+ filename: 'hello.txt'
33
+ });
34
+
35
+ return { output: {}, message: 'ok' };
36
+ })
37
+ .build();
38
+ let slate = Slate.create({ spec, triggers: [], tools: [tool] });
39
+ let manager = await createProviderHandler(slate, []).run();
40
+
41
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
42
+ jsonrpc: '2.0',
43
+ method: 'slates/hello',
44
+ params: { protocol: SLATES_PROTOCOL_VERSION }
45
+ });
46
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
47
+ jsonrpc: '2.0',
48
+ method: 'slates/participant.set',
49
+ params: {
50
+ participants: [
51
+ { type: 'consumer', id: 'consumer', name: 'Consumer' },
52
+ { type: 'hub', id: 'hub', name: 'Hub' }
53
+ ]
54
+ }
55
+ });
56
+
57
+ return manager;
58
+ };
59
+
60
+ let invoke = async (manager: SlatesProviderProtoHandlerManager) =>
61
+ SlatesProviderProtoHandlerManager.handleInput(manager, {
62
+ jsonrpc: '2.0',
63
+ id: 'request',
64
+ method: 'slates/action.tool.invoke',
65
+ params: { actionId: 'attach', input: {} }
66
+ });
67
+
68
+ let expectInlineAttachment = (response: Awaited<ReturnType<typeof invoke>>) => {
69
+ expect(response).toMatchObject({
70
+ result: {
71
+ attachments: [
72
+ {
73
+ mimeType: 'text/plain',
74
+ content: {
75
+ type: 'content',
76
+ encoding: 'base64',
77
+ content: Buffer.from('hello').toString('base64')
78
+ }
79
+ }
80
+ ]
81
+ }
82
+ });
83
+ };
84
+
85
+ describe('addAttachment fallback', () => {
86
+ afterEach(() => {
87
+ vi.restoreAllMocks();
88
+ });
89
+
90
+ it('returns a standard inline attachment when capabilities and a live token are absent', async () => {
91
+ let fetchSpy = vi.spyOn(globalThis, 'fetch');
92
+ let response = await invoke(await createManager());
93
+
94
+ expectInlineAttachment(response);
95
+ expect(fetchSpy).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it('returns a standard inline attachment when direct upload is enabled without a live token', async () => {
99
+ let manager = await createManager();
100
+ let fetchSpy = vi.spyOn(globalThis, 'fetch');
101
+
102
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
103
+ jsonrpc: '2.0',
104
+ method: 'slates/hub.capabilities.set',
105
+ params: { capabilities: { attachments: { directUpload: { enabled: true } } } }
106
+ });
107
+
108
+ expectInlineAttachment(await invoke(manager));
109
+ expect(fetchSpy).not.toHaveBeenCalled();
110
+ });
111
+
112
+ it('returns a standard inline attachment when a live token exists without the capability', async () => {
113
+ let manager = await createManager();
114
+ let fetchSpy = vi.spyOn(globalThis, 'fetch');
115
+
116
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
117
+ jsonrpc: '2.0',
118
+ method: 'slates/hub.live_invocation.set',
119
+ params: { token: 'token', baseUrl: 'https://hub.example' }
120
+ });
121
+
122
+ expectInlineAttachment(await invoke(manager));
123
+ expect(fetchSpy).not.toHaveBeenCalled();
124
+ });
125
+
126
+ it('uses direct upload only when both the capability and live token exist', async () => {
127
+ let manager = await createManager();
128
+ let fetchSpy = vi
129
+ .spyOn(globalThis, 'fetch')
130
+ .mockResolvedValueOnce(
131
+ new Response(
132
+ JSON.stringify({
133
+ attachments: [
134
+ {
135
+ referenceId: 'attachment-reference',
136
+ uploadUrl: 'https://uploads.example/attachment'
137
+ }
138
+ ]
139
+ }),
140
+ { status: 200, headers: { 'content-type': 'application/json' } }
141
+ )
142
+ )
143
+ .mockResolvedValueOnce(new Response(null, { status: 200 }));
144
+
145
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
146
+ jsonrpc: '2.0',
147
+ method: 'slates/hub.capabilities.set',
148
+ params: { capabilities: { attachments: { directUpload: { enabled: true } } } }
149
+ });
150
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
151
+ jsonrpc: '2.0',
152
+ method: 'slates/hub.live_invocation.set',
153
+ params: { token: 'token', baseUrl: 'https://hub.example' }
154
+ });
155
+
156
+ expect(await invoke(manager)).toMatchObject({
157
+ result: {
158
+ attachments: [
159
+ {
160
+ mimeType: 'text/plain',
161
+ content: {
162
+ type: 'upload_reference',
163
+ referenceId: 'attachment-reference'
164
+ }
165
+ }
166
+ ]
167
+ }
168
+ });
169
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
170
+ });
171
+ });
package/src/index.ts CHANGED
@@ -5,14 +5,18 @@ import {
5
5
  type SlatesParticipant
6
6
  } from '@slates/proto';
7
7
  import {
8
+ redactUrlAttachmentSecrets,
8
9
  runWithContext,
9
10
  type Slate,
10
11
  type SlateAttachment,
11
12
  SlateContext,
13
+ type SlateLiveInvocationInfo,
12
14
  SlateLogger,
13
15
  type SlateLogListener,
14
- SlatePublicContext
16
+ SlatePublicContext,
17
+ uploadAttachmentDirect
15
18
  } from '@slates/provider';
19
+ import PQueue from 'p-queue';
16
20
  import {
17
21
  getAction,
18
22
  getActionWithType,
@@ -26,6 +30,57 @@ import { State } from './state';
26
30
  import { toJsonSchema, validate } from './validation';
27
31
  import { serializeWebhookHttpResponse } from './webhook';
28
32
 
33
+ let DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 100_000_000;
34
+
35
+ let routeAttachmentsThroughDirectUpload = async (
36
+ attachments: SlateAttachment[] | undefined,
37
+ live: SlateLiveInvocationInfo | null
38
+ ): Promise<SlateAttachment[] | undefined> => {
39
+ if (!attachments || attachments.length === 0 || !live) return attachments;
40
+
41
+ let contentEntries = attachments
42
+ .map((attachment, index) => ({ attachment, index }))
43
+ .filter(entry => entry.attachment.content.type === 'content');
44
+ if (contentEntries.length === 0) return attachments;
45
+
46
+ let queue = new PQueue({ concurrency: 10 });
47
+ let replacements = new Map<number, SlateAttachment | null>();
48
+
49
+ await Promise.all(
50
+ contentEntries.map(entry =>
51
+ queue.add(async () => {
52
+ try {
53
+ let body =
54
+ entry.attachment.content.type === 'content'
55
+ ? entry.attachment.content.encoding === 'base64'
56
+ ? Buffer.from(entry.attachment.content.content, 'base64')
57
+ : Buffer.from(entry.attachment.content.content, 'utf-8')
58
+ : Buffer.alloc(0);
59
+
60
+ replacements.set(
61
+ entry.index,
62
+ await uploadAttachmentDirect({
63
+ live,
64
+ mimeType: entry.attachment.mimeType,
65
+ body
66
+ })
67
+ );
68
+ } catch {
69
+ // Upload failed or was interrupted -- we simply don't have this attachment, not a
70
+ // failed tool call.
71
+ replacements.set(entry.index, null);
72
+ }
73
+ })
74
+ )
75
+ );
76
+
77
+ return attachments
78
+ .map((attachment, index) =>
79
+ replacements.has(index) ? replacements.get(index) : attachment
80
+ )
81
+ .filter((a): a is SlateAttachment => a != null);
82
+ };
83
+
29
84
  let isRecord = (value: unknown): value is Record<string, unknown> =>
30
85
  typeof value === 'object' && value !== null && !Array.isArray(value);
31
86
 
@@ -128,6 +183,26 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
128
183
  let config = new State<{ value: ConfigType } | null>(null);
129
184
  let session = new State<{ id: string; state: any } | null>(null);
130
185
 
186
+ let hubCapabilities = new State<{
187
+ attachments?: { directUpload?: { enabled: boolean; maxAttachmentSizeBytes?: number } };
188
+ } | null>(null);
189
+ let liveInvocation = new State<Pick<SlateLiveInvocationInfo, 'token' | 'baseUrl'> | null>(
190
+ null
191
+ );
192
+
193
+ let getLiveInvocation = (): SlateLiveInvocationInfo | null => {
194
+ let directUpload = hubCapabilities.get()?.attachments?.directUpload;
195
+ let live = liveInvocation.get();
196
+
197
+ if (!directUpload?.enabled || !live) return null;
198
+
199
+ return {
200
+ ...live,
201
+ maxAttachmentSizeBytes:
202
+ directUpload.maxAttachmentSizeBytes ?? DEFAULT_MAX_ATTACHMENT_SIZE_BYTES
203
+ };
204
+ };
205
+
131
206
  let logger = new SlateLogger(listeners);
132
207
  let providerTrace = {
133
208
  providerId: slate.spec.key,
@@ -301,6 +376,17 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
301
376
  });
302
377
  });
303
378
 
379
+ manager.onNotification('slates/hub.capabilities.set', async ({ params }) => {
380
+ hubCapabilities.set(params.capabilities);
381
+ });
382
+
383
+ manager.onNotification('slates/hub.live_invocation.set', async ({ params }) => {
384
+ liveInvocation.set({
385
+ token: params.token,
386
+ baseUrl: params.baseUrl
387
+ });
388
+ });
389
+
304
390
  manager.onRequest('slates/config.changed', async ({ params }) => {
305
391
  getContextBasic();
306
392
 
@@ -828,21 +914,43 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
828
914
  () => runWithContext(context, () => action.handleInvocation(context as any))
829
915
  );
830
916
 
917
+ let contextAttachments = await context._finalizeAttachments();
918
+ let merged = mergeAttachments(
919
+ [...(res.attachments ?? []), ...contextAttachments],
920
+ res.output
921
+ );
922
+
923
+ let redacted = redactUrlAttachmentSecrets(
924
+ merged,
925
+ context._getAuthConfigForRedaction()
926
+ );
927
+ let finalAttachments = await routeAttachmentsThroughDirectUpload(
928
+ redacted,
929
+ getLiveInvocation()
930
+ );
931
+
831
932
  return withRequestTraces(context, {
832
933
  output: res.output,
833
934
  message: res.message,
834
- attachments: mergeAttachments(res.attachments, res.output)
935
+ attachments: finalAttachments
835
936
  });
836
937
  };
837
938
 
838
939
  if (action.isPublic) {
839
940
  getContextBasic();
840
- return invoke(new SlatePublicContext(input, slate.spec, logger));
941
+ return invoke(new SlatePublicContext(input, slate.spec, logger, getLiveInvocation()));
841
942
  }
842
943
 
843
944
  let ctx = getContextFull();
844
945
  return invoke(
845
- new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger)
946
+ new SlateContext(
947
+ ctx.config,
948
+ input,
949
+ ctx.auth?.output!,
950
+ slate.spec,
951
+ logger,
952
+ getLiveInvocation()
953
+ )
846
954
  );
847
955
  });
848
956
 
package/src/spec.ts CHANGED
@@ -41,8 +41,7 @@ export let mapAuthMethod = <ConfigType extends {}, AuthType extends {}>(
41
41
  ? m.scopes.map(s => ({
42
42
  id: s.scope,
43
43
  title: s.title,
44
- description: s.description,
45
- defaultChecked: s.defaultChecked
44
+ description: s.description
46
45
  }))
47
46
  : undefined,
48
47