@slates/provider-handler 1.0.0-rc.31 → 1.0.0-rc.34

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
@@ -112,6 +112,7 @@ var mapAuthMethod = (slate, m) => ({
112
112
  title: s.title,
113
113
  description: s.description
114
114
  })) : void 0,
115
+ adapters: "adapters" in m ? m.adapters : void 0,
115
116
  inputSchema: toJsonSchema(m.inputSchema ?? import_zod.default.object({})),
116
117
  outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
117
118
  capabilities: {
@@ -187,6 +188,24 @@ var mapAction = (_slate, a) => {
187
188
  triggerGroupId: a.triggerGroup.key
188
189
  };
189
190
  };
191
+ var isMappableTrigger = (action) => action.type !== "trigger" || !!action.triggerGroup;
192
+ var getMappableAction = (slate, actionId) => {
193
+ let action = getAction(slate, actionId);
194
+ if (!isMappableTrigger(action)) {
195
+ throw new import_error2.ServiceError((0, import_error2.notFoundError)(`action`, actionId));
196
+ }
197
+ return action;
198
+ };
199
+ var getExposedActions = (slate, includeAdapterActions) => slate.actions.filter(
200
+ (action) => isMappableTrigger(action) && (includeAdapterActions || !action.adapter)
201
+ );
202
+ var getExposedAction = (slate, actionId, includeAdapterActions) => {
203
+ let action = getMappableAction(slate, actionId);
204
+ if (action.adapter && !includeAdapterActions) {
205
+ throw new import_error2.ServiceError((0, import_error2.notFoundError)(`action`, actionId));
206
+ }
207
+ return action;
208
+ };
190
209
  var getTriggerGroup = (slate, triggerGroupId) => {
191
210
  let group = slate.triggerGroups.find((g) => g.key === triggerGroupId);
192
211
  if (!group) {
@@ -195,8 +214,16 @@ var getTriggerGroup = (slate, triggerGroupId) => {
195
214
  return group;
196
215
  };
197
216
  var getTriggersForGroup = (slate, triggerGroupId) => slate.actions.filter(
198
- (action) => action.type === "trigger" && action.triggerGroup.key === triggerGroupId
217
+ (action) => action.type === "trigger" && action.triggerGroup?.key === triggerGroupId
199
218
  );
219
+ var getExposedTriggerGroups = (slate, includeAdapterActions) => {
220
+ let triggerGroupIds = new Set(
221
+ getExposedActions(slate, includeAdapterActions).flatMap(
222
+ (action) => action.type === "trigger" && action.triggerGroup ? [action.triggerGroup.key] : []
223
+ )
224
+ );
225
+ return slate.triggerGroups.filter((group) => triggerGroupIds.has(group.key));
226
+ };
200
227
  var evaluateTriggerMatches = (slate, triggerGroupId, payload) => getTriggersForGroup(slate, triggerGroupId).filter((trigger) => trigger.matches(payload)).map((trigger) => trigger.key);
201
228
  var getWebhookAutoRegistration = (group) => {
202
229
  if (group.source !== "webhook" || !group.webhook?.autoRegistration) {
@@ -230,8 +257,12 @@ var mapTriggerGroup = (group) => ({
230
257
  type: "webhook",
231
258
  registration: group.webhook?.manualRegistration ? {
232
259
  mode: "manual",
233
- userConfigSchema: toJsonSchema(group.webhook.manualRegistration.userConfigSchema),
234
- fullConfigSchema: toJsonSchema(group.webhook.manualRegistration.fullConfigSchema)
260
+ userConfigSchema: toJsonSchema(
261
+ group.webhook.manualRegistration.userConfigSchema
262
+ ),
263
+ fullConfigSchema: toJsonSchema(
264
+ group.webhook.manualRegistration.fullConfigSchema
265
+ )
235
266
  } : { mode: "auto" }
236
267
  }
237
268
  });
@@ -415,6 +446,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
415
446
  };
416
447
  };
417
448
  let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
449
+ let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
418
450
  let logger = new import_provider2.SlateLogger(listeners);
419
451
  let providerTrace = {
420
452
  providerId: slate.spec.key,
@@ -557,7 +589,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
557
589
  liveInvocation: true
558
590
  },
559
591
  provider: {
560
- triggerGroups: true
592
+ triggerGroups: (slate.triggerGroups?.length ?? 0) > 0 && (slate.actions ?? []).every(isMappableTrigger)
561
593
  }
562
594
  }
563
595
  }));
@@ -968,7 +1000,10 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
968
1000
  });
969
1001
  manager.onRequest("slates/actions.list", async ({ params }) => {
970
1002
  getContextBasic();
971
- let actions = params.includeAdapterActions ? slate.actions : slate.actions.filter((action) => !action.adapter);
1003
+ let actions = getExposedActions(
1004
+ slate,
1005
+ !!params.includeAdapterActions && supportsAdapters()
1006
+ );
972
1007
  if (!supportsTriggerGroups()) {
973
1008
  actions = actions.filter((action) => action.type !== "trigger");
974
1009
  }
@@ -979,11 +1014,14 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
979
1014
  manager.onRequest("slates/adapters.list", async () => {
980
1015
  getContextBasic();
981
1016
  return {
982
- adapters: slate.adapters.map(mapAdapter)
1017
+ adapters: supportsAdapters() ? slate.adapters.map(mapAdapter) : []
983
1018
  };
984
1019
  });
985
1020
  manager.onRequest("slates/adapter.get", async ({ params }) => {
986
1021
  getContextBasic();
1022
+ if (!supportsAdapters()) {
1023
+ throw new import_error4.ServiceError((0, import_error4.notFoundError)(`adapter`, params.adapterId));
1024
+ }
987
1025
  let adapter = getAdapter(slate, params.adapterId);
988
1026
  return {
989
1027
  adapter: mapAdapter(adapter)
@@ -991,7 +1029,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
991
1029
  });
992
1030
  manager.onRequest("slates/action.get", async ({ params }) => {
993
1031
  getContextBasic();
994
- let action = getAction(slate, params.actionId);
1032
+ let action = getExposedAction(slate, params.actionId, supportsAdapters());
995
1033
  return {
996
1034
  action: mapAction(slate, action)
997
1035
  };
@@ -999,7 +1037,7 @@ var createProviderHandler = (slate, listeners) => (0, import_proto.createSlatesP
999
1037
  manager.onRequest("slates/trigger_groups.list", async () => {
1000
1038
  getContextBasic();
1001
1039
  return {
1002
- triggerGroups: slate.triggerGroups.map(mapTriggerGroup)
1040
+ triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
1003
1041
  };
1004
1042
  });
1005
1043
  manager.onRequest("slates/trigger_group.get", async ({ params }) => {
@@ -1,5 +1,10 @@
1
1
  // src/index.ts
2
- import { badRequestError as badRequestError3, preconditionFailedError, ServiceError as ServiceError4 } from "@lowerdeck/error";
2
+ import {
3
+ badRequestError as badRequestError3,
4
+ notFoundError as notFoundError2,
5
+ preconditionFailedError,
6
+ ServiceError as ServiceError4
7
+ } from "@lowerdeck/error";
3
8
  import {
4
9
  createSlatesProviderProtoHandler,
5
10
  SLATES_PROTOCOL_VERSION
@@ -90,6 +95,7 @@ var mapAuthMethod = (slate, m) => ({
90
95
  title: s.title,
91
96
  description: s.description
92
97
  })) : void 0,
98
+ adapters: "adapters" in m ? m.adapters : void 0,
93
99
  inputSchema: toJsonSchema(m.inputSchema ?? z.object({})),
94
100
  outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
95
101
  capabilities: {
@@ -165,6 +171,24 @@ var mapAction = (_slate, a) => {
165
171
  triggerGroupId: a.triggerGroup.key
166
172
  };
167
173
  };
174
+ var isMappableTrigger = (action) => action.type !== "trigger" || !!action.triggerGroup;
175
+ var getMappableAction = (slate, actionId) => {
176
+ let action = getAction(slate, actionId);
177
+ if (!isMappableTrigger(action)) {
178
+ throw new ServiceError2(notFoundError(`action`, actionId));
179
+ }
180
+ return action;
181
+ };
182
+ var getExposedActions = (slate, includeAdapterActions) => slate.actions.filter(
183
+ (action) => isMappableTrigger(action) && (includeAdapterActions || !action.adapter)
184
+ );
185
+ var getExposedAction = (slate, actionId, includeAdapterActions) => {
186
+ let action = getMappableAction(slate, actionId);
187
+ if (action.adapter && !includeAdapterActions) {
188
+ throw new ServiceError2(notFoundError(`action`, actionId));
189
+ }
190
+ return action;
191
+ };
168
192
  var getTriggerGroup = (slate, triggerGroupId) => {
169
193
  let group = slate.triggerGroups.find((g) => g.key === triggerGroupId);
170
194
  if (!group) {
@@ -173,8 +197,16 @@ var getTriggerGroup = (slate, triggerGroupId) => {
173
197
  return group;
174
198
  };
175
199
  var getTriggersForGroup = (slate, triggerGroupId) => slate.actions.filter(
176
- (action) => action.type === "trigger" && action.triggerGroup.key === triggerGroupId
200
+ (action) => action.type === "trigger" && action.triggerGroup?.key === triggerGroupId
177
201
  );
202
+ var getExposedTriggerGroups = (slate, includeAdapterActions) => {
203
+ let triggerGroupIds = new Set(
204
+ getExposedActions(slate, includeAdapterActions).flatMap(
205
+ (action) => action.type === "trigger" && action.triggerGroup ? [action.triggerGroup.key] : []
206
+ )
207
+ );
208
+ return slate.triggerGroups.filter((group) => triggerGroupIds.has(group.key));
209
+ };
178
210
  var evaluateTriggerMatches = (slate, triggerGroupId, payload) => getTriggersForGroup(slate, triggerGroupId).filter((trigger) => trigger.matches(payload)).map((trigger) => trigger.key);
179
211
  var getWebhookAutoRegistration = (group) => {
180
212
  if (group.source !== "webhook" || !group.webhook?.autoRegistration) {
@@ -208,8 +240,12 @@ var mapTriggerGroup = (group) => ({
208
240
  type: "webhook",
209
241
  registration: group.webhook?.manualRegistration ? {
210
242
  mode: "manual",
211
- userConfigSchema: toJsonSchema(group.webhook.manualRegistration.userConfigSchema),
212
- fullConfigSchema: toJsonSchema(group.webhook.manualRegistration.fullConfigSchema)
243
+ userConfigSchema: toJsonSchema(
244
+ group.webhook.manualRegistration.userConfigSchema
245
+ ),
246
+ fullConfigSchema: toJsonSchema(
247
+ group.webhook.manualRegistration.fullConfigSchema
248
+ )
213
249
  } : { mode: "auto" }
214
250
  }
215
251
  });
@@ -393,6 +429,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
393
429
  };
394
430
  };
395
431
  let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
432
+ let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
396
433
  let logger = new SlateLogger(listeners);
397
434
  let providerTrace = {
398
435
  providerId: slate.spec.key,
@@ -535,7 +572,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
535
572
  liveInvocation: true
536
573
  },
537
574
  provider: {
538
- triggerGroups: true
575
+ triggerGroups: (slate.triggerGroups?.length ?? 0) > 0 && (slate.actions ?? []).every(isMappableTrigger)
539
576
  }
540
577
  }
541
578
  }));
@@ -946,7 +983,10 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
946
983
  });
947
984
  manager.onRequest("slates/actions.list", async ({ params }) => {
948
985
  getContextBasic();
949
- let actions = params.includeAdapterActions ? slate.actions : slate.actions.filter((action) => !action.adapter);
986
+ let actions = getExposedActions(
987
+ slate,
988
+ !!params.includeAdapterActions && supportsAdapters()
989
+ );
950
990
  if (!supportsTriggerGroups()) {
951
991
  actions = actions.filter((action) => action.type !== "trigger");
952
992
  }
@@ -957,11 +997,14 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
957
997
  manager.onRequest("slates/adapters.list", async () => {
958
998
  getContextBasic();
959
999
  return {
960
- adapters: slate.adapters.map(mapAdapter)
1000
+ adapters: supportsAdapters() ? slate.adapters.map(mapAdapter) : []
961
1001
  };
962
1002
  });
963
1003
  manager.onRequest("slates/adapter.get", async ({ params }) => {
964
1004
  getContextBasic();
1005
+ if (!supportsAdapters()) {
1006
+ throw new ServiceError4(notFoundError2(`adapter`, params.adapterId));
1007
+ }
965
1008
  let adapter = getAdapter(slate, params.adapterId);
966
1009
  return {
967
1010
  adapter: mapAdapter(adapter)
@@ -969,7 +1012,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
969
1012
  });
970
1013
  manager.onRequest("slates/action.get", async ({ params }) => {
971
1014
  getContextBasic();
972
- let action = getAction(slate, params.actionId);
1015
+ let action = getExposedAction(slate, params.actionId, supportsAdapters());
973
1016
  return {
974
1017
  action: mapAction(slate, action)
975
1018
  };
@@ -977,7 +1020,7 @@ var createProviderHandler = (slate, listeners) => createSlatesProviderProtoHandl
977
1020
  manager.onRequest("slates/trigger_groups.list", async () => {
978
1021
  getContextBasic();
979
1022
  return {
980
- triggerGroups: slate.triggerGroups.map(mapTriggerGroup)
1023
+ triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
981
1024
  };
982
1025
  });
983
1026
  manager.onRequest("slates/trigger_group.get", async ({ params }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider-handler",
3
- "version": "1.0.0-rc.31",
3
+ "version": "1.0.0-rc.34",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -32,8 +32,8 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@lowerdeck/error": "^1.1.0",
35
- "@slates/proto": "1.0.0-rc.19",
36
- "@slates/provider": "1.0.0-rc.23",
35
+ "@slates/proto": "1.0.0-rc.20",
36
+ "@slates/provider": "1.0.0-rc.24",
37
37
  "p-queue": "^6.6.2",
38
38
  "zod": "^4.2.1"
39
39
  },
@@ -0,0 +1,161 @@
1
+ import { SLATES_PROTOCOL_VERSION, SlatesProviderProtoHandlerManager } from '@slates/proto';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { z } from 'zod';
4
+ import { createProviderHandler } from './index';
5
+
6
+ let createManager = async () => {
7
+ let providerGroup = {
8
+ key: 'provider_group',
9
+ name: 'Provider Group',
10
+ description: undefined,
11
+ metadata: {},
12
+ source: 'polling',
13
+ polling: { intervalSeconds: 60 }
14
+ };
15
+ let adapterGroup = {
16
+ key: 'adapter_group',
17
+ name: 'Adapter Group',
18
+ description: undefined,
19
+ metadata: {},
20
+ source: 'polling',
21
+ polling: { intervalSeconds: 60 }
22
+ };
23
+ let emptyGroup = {
24
+ key: 'empty_group',
25
+ name: 'Empty Group',
26
+ description: undefined,
27
+ metadata: {},
28
+ source: 'polling',
29
+ polling: { intervalSeconds: 60 }
30
+ };
31
+ let action = {
32
+ name: 'Action',
33
+ description: undefined,
34
+ instructions: undefined,
35
+ constraints: undefined,
36
+ tags: [],
37
+ metadata: {},
38
+ scopes: [],
39
+ authMethods: [],
40
+ docs: [],
41
+ inputSchema: z.object({}),
42
+ outputSchema: z.object({}),
43
+ matches: () => true,
44
+ map: async () => ({ type: 'event', output: {} })
45
+ };
46
+ let slate = {
47
+ spec: { key: 'test', name: 'Test' },
48
+ adapters: [{ id: 'chat', name: 'Chat', capabilities: [] }],
49
+ actions: [
50
+ { ...action, key: 'provider_tool', type: 'tool', isPublic: false },
51
+ {
52
+ ...action,
53
+ key: 'adapter_tool',
54
+ type: 'tool',
55
+ adapter: 'chat',
56
+ isPublic: false
57
+ },
58
+ { ...action, key: 'provider_trigger', type: 'trigger', triggerGroup: providerGroup },
59
+ {
60
+ ...action,
61
+ key: 'adapter_trigger',
62
+ type: 'trigger',
63
+ adapter: 'chat',
64
+ triggerGroup: adapterGroup
65
+ }
66
+ ],
67
+ triggerGroups: [providerGroup, adapterGroup, emptyGroup]
68
+ };
69
+ let manager = await createProviderHandler(slate as any, []).run();
70
+
71
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
72
+ jsonrpc: '2.0',
73
+ method: 'slates/hello',
74
+ params: { protocol: SLATES_PROTOCOL_VERSION }
75
+ });
76
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
77
+ jsonrpc: '2.0',
78
+ method: 'slates/participant.set',
79
+ params: { participants: [] }
80
+ });
81
+
82
+ return manager;
83
+ };
84
+
85
+ let request = async (
86
+ manager: SlatesProviderProtoHandlerManager,
87
+ method:
88
+ | 'slates/actions.list'
89
+ | 'slates/action.get'
90
+ | 'slates/adapters.list'
91
+ | 'slates/adapter.get'
92
+ | 'slates/trigger_groups.list',
93
+ params: Record<string, unknown> = {}
94
+ ) =>
95
+ SlatesProviderProtoHandlerManager.handleInput(manager, {
96
+ jsonrpc: '2.0',
97
+ id: method,
98
+ method,
99
+ params
100
+ } as any);
101
+
102
+ describe('adapter capability exposure', () => {
103
+ it('hides adapter surfaces from a legacy hub that only supports triggers', async () => {
104
+ let manager = await createManager();
105
+
106
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
107
+ jsonrpc: '2.0',
108
+ method: 'slates/hub.capabilities.set',
109
+ params: { capabilities: { triggers: true } }
110
+ });
111
+
112
+ expect(await request(manager, 'slates/adapters.list')).toMatchObject({
113
+ result: { adapters: [] }
114
+ });
115
+ expect(
116
+ await request(manager, 'slates/actions.list', { includeAdapterActions: true })
117
+ ).toMatchObject({
118
+ result: { actions: [{ id: 'provider_tool' }, { id: 'provider_trigger' }] }
119
+ });
120
+ expect(await request(manager, 'slates/trigger_groups.list')).toMatchObject({
121
+ result: { triggerGroups: [{ id: 'provider_group' }] }
122
+ });
123
+ expect(await request(manager, 'slates/adapter.get', { adapterId: 'chat' })).toHaveProperty(
124
+ 'error'
125
+ );
126
+ expect(
127
+ await request(manager, 'slates/action.get', { actionId: 'adapter_tool' })
128
+ ).toHaveProperty('error');
129
+ });
130
+
131
+ it('exposes adapters and their actions when the hub enables them', async () => {
132
+ let manager = await createManager();
133
+
134
+ await SlatesProviderProtoHandlerManager.handleInput(manager, {
135
+ jsonrpc: '2.0',
136
+ method: 'slates/hub.capabilities.set',
137
+ params: { capabilities: { adapters: true, triggers: true } }
138
+ });
139
+
140
+ expect(await request(manager, 'slates/adapters.list')).toMatchObject({
141
+ result: { adapters: [{ id: 'chat' }] }
142
+ });
143
+ expect(
144
+ await request(manager, 'slates/actions.list', { includeAdapterActions: true })
145
+ ).toMatchObject({
146
+ result: {
147
+ actions: [
148
+ { id: 'provider_tool' },
149
+ { id: 'adapter_tool' },
150
+ { id: 'provider_trigger' },
151
+ { id: 'adapter_trigger' }
152
+ ]
153
+ }
154
+ });
155
+ expect(await request(manager, 'slates/trigger_groups.list')).toMatchObject({
156
+ result: {
157
+ triggerGroups: [{ id: 'provider_group' }, { id: 'adapter_group' }]
158
+ }
159
+ });
160
+ });
161
+ });
package/src/index.ts CHANGED
@@ -1,4 +1,9 @@
1
- import { badRequestError, preconditionFailedError, ServiceError } from '@lowerdeck/error';
1
+ import {
2
+ badRequestError,
3
+ notFoundError,
4
+ preconditionFailedError,
5
+ ServiceError
6
+ } from '@lowerdeck/error';
2
7
  import {
3
8
  createSlatesProviderProtoHandler,
4
9
  SLATES_PROTOCOL_VERSION,
@@ -19,13 +24,16 @@ import {
19
24
  import { PQueue } from './pQueue';
20
25
  import {
21
26
  evaluateTriggerMatches,
22
- getAction,
23
27
  getActionWithType,
24
28
  getAdapter,
25
29
  getAuthMethod,
30
+ getExposedAction,
31
+ getExposedActions,
32
+ getExposedTriggerGroups,
26
33
  getTriggerGroup,
27
34
  getWebhookAutoRegistration,
28
35
  getWebhookManualRegistration,
36
+ isMappableTrigger,
29
37
  mapAction,
30
38
  mapAdapter,
31
39
  mapAuthMethod,
@@ -190,6 +198,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
190
198
 
191
199
  let hubCapabilities = new State<{
192
200
  attachments?: { directUpload?: { enabled: boolean; maxAttachmentSizeBytes?: number } };
201
+ adapters?: boolean;
193
202
  triggers?: boolean;
194
203
  } | null>(null);
195
204
  let liveInvocation = new State<Pick<SlateLiveInvocationInfo, 'token' | 'baseUrl'> | null>(
@@ -210,6 +219,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
210
219
  };
211
220
 
212
221
  let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
222
+ let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
213
223
 
214
224
  let logger = new SlateLogger(listeners);
215
225
  let providerTrace = {
@@ -402,7 +412,9 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
402
412
  liveInvocation: true
403
413
  },
404
414
  provider: {
405
- triggerGroups: true
415
+ triggerGroups:
416
+ (slate.triggerGroups?.length ?? 0) > 0 &&
417
+ (slate.actions ?? []).every(isMappableTrigger)
406
418
  }
407
419
  }
408
420
  }));
@@ -864,9 +876,10 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
864
876
  manager.onRequest('slates/actions.list', async ({ params }) => {
865
877
  getContextBasic();
866
878
 
867
- let actions = params.includeAdapterActions
868
- ? slate.actions
869
- : slate.actions.filter(action => !action.adapter);
879
+ let actions = getExposedActions(
880
+ slate,
881
+ !!params.includeAdapterActions && supportsAdapters()
882
+ );
870
883
 
871
884
  if (!supportsTriggerGroups()) {
872
885
  actions = actions.filter(action => action.type !== 'trigger');
@@ -881,12 +894,15 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
881
894
  getContextBasic();
882
895
 
883
896
  return {
884
- adapters: slate.adapters.map(mapAdapter)
897
+ adapters: supportsAdapters() ? slate.adapters.map(mapAdapter) : []
885
898
  };
886
899
  });
887
900
 
888
901
  manager.onRequest('slates/adapter.get', async ({ params }) => {
889
902
  getContextBasic();
903
+ if (!supportsAdapters()) {
904
+ throw new ServiceError(notFoundError(`adapter`, params.adapterId));
905
+ }
890
906
  let adapter = getAdapter(slate, params.adapterId);
891
907
 
892
908
  return {
@@ -896,7 +912,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
896
912
 
897
913
  manager.onRequest('slates/action.get', async ({ params }) => {
898
914
  getContextBasic();
899
- let action = getAction(slate, params.actionId);
915
+ let action = getExposedAction(slate, params.actionId, supportsAdapters());
900
916
 
901
917
  return {
902
918
  action: mapAction(slate, action)
@@ -907,7 +923,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
907
923
  getContextBasic();
908
924
 
909
925
  return {
910
- triggerGroups: slate.triggerGroups.map(mapTriggerGroup)
926
+ triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
911
927
  };
912
928
  });
913
929
 
@@ -0,0 +1,134 @@
1
+ import { isServiceError } from '@lowerdeck/error';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { z } from 'zod';
4
+ import {
5
+ getExposedAction,
6
+ getExposedActions,
7
+ getExposedTriggerGroups,
8
+ getMappableAction,
9
+ getTriggersForGroup,
10
+ isMappableTrigger,
11
+ mapAction
12
+ } from './spec';
13
+
14
+ let baseAction = {
15
+ key: 'do_thing',
16
+ name: 'Do Thing',
17
+ description: 'desc',
18
+ instructions: undefined,
19
+ constraints: undefined,
20
+ tags: [],
21
+ metadata: {},
22
+ scopes: [],
23
+ authMethods: [],
24
+ docs: [],
25
+ adapter: undefined,
26
+ inputSchema: z.object({}),
27
+ outputSchema: z.object({})
28
+ };
29
+
30
+ let toolAction: any = { ...baseAction, type: 'tool', isPublic: false };
31
+
32
+ let validTriggerGroup = { key: 'my_group', name: 'My Group' };
33
+ let validTrigger: any = {
34
+ ...baseAction,
35
+ type: 'trigger',
36
+ triggerGroup: validTriggerGroup,
37
+ matches: () => true,
38
+ map: () => ({})
39
+ };
40
+
41
+ let incompatibleTrigger: any = { ...baseAction, type: 'trigger', triggerGroup: undefined };
42
+
43
+ describe('isMappableTrigger', () => {
44
+ it('is true for tool actions', () => {
45
+ expect(isMappableTrigger(toolAction)).toBe(true);
46
+ });
47
+
48
+ it('is true for triggers with a trigger group', () => {
49
+ expect(isMappableTrigger(validTrigger)).toBe(true);
50
+ });
51
+
52
+ it('is false for triggers missing a trigger group', () => {
53
+ expect(isMappableTrigger(incompatibleTrigger)).toBe(false);
54
+ });
55
+ });
56
+
57
+ describe('mapAction', () => {
58
+ it('maps a well-formed trigger', () => {
59
+ let mapped = mapAction({} as any, validTrigger);
60
+ expect(mapped).toMatchObject({ type: 'action.trigger', triggerGroupId: 'my_group' });
61
+ });
62
+ });
63
+
64
+ describe('getTriggersForGroup', () => {
65
+ it('filters out triggers missing a trigger group instead of throwing', () => {
66
+ let slate = { actions: [toolAction, validTrigger, incompatibleTrigger] } as any;
67
+
68
+ expect(getTriggersForGroup(slate, 'my_group')).toEqual([validTrigger]);
69
+ expect(getTriggersForGroup(slate, 'other_group')).toEqual([]);
70
+ });
71
+ });
72
+
73
+ describe('getMappableAction', () => {
74
+ it('returns tool and well-formed trigger actions', () => {
75
+ let slate = { actions: [toolAction, validTrigger] } as any;
76
+
77
+ expect(getMappableAction(slate, 'do_thing')).toBe(toolAction);
78
+ });
79
+
80
+ it('treats a trigger missing its group as not found instead of crashing', () => {
81
+ let slate = { actions: [incompatibleTrigger] } as any;
82
+
83
+ try {
84
+ getMappableAction(slate, 'do_thing');
85
+ expect.unreachable();
86
+ } catch (e) {
87
+ expect(isServiceError(e)).toBe(true);
88
+ expect((e as any).data.status).toBe(404);
89
+ }
90
+ });
91
+ });
92
+
93
+ describe('adapter exposure', () => {
94
+ let providerTool: any = { ...toolAction, key: 'provider_tool' };
95
+ let adapterTool: any = { ...toolAction, key: 'adapter_tool', adapter: 'chat' };
96
+ let providerGroup = { key: 'provider_group', name: 'Provider Group' };
97
+ let adapterGroup = { key: 'adapter_group', name: 'Adapter Group' };
98
+ let emptyGroup = { key: 'empty_group', name: 'Empty Group' };
99
+ let providerTrigger: any = {
100
+ ...validTrigger,
101
+ key: 'provider_trigger',
102
+ triggerGroup: providerGroup
103
+ };
104
+ let adapterTrigger: any = {
105
+ ...validTrigger,
106
+ key: 'adapter_trigger',
107
+ adapter: 'chat',
108
+ triggerGroup: adapterGroup
109
+ };
110
+ let slate = {
111
+ actions: [providerTool, adapterTool, providerTrigger, adapterTrigger],
112
+ triggerGroups: [providerGroup, adapterGroup, emptyGroup]
113
+ } as any;
114
+
115
+ it('only exposes adapter actions when adapters are enabled', () => {
116
+ expect(getExposedActions(slate, false)).toEqual([providerTool, providerTrigger]);
117
+ expect(getExposedActions(slate, true)).toEqual([
118
+ providerTool,
119
+ adapterTool,
120
+ providerTrigger,
121
+ adapterTrigger
122
+ ]);
123
+ });
124
+
125
+ it('does not expose an adapter action directly when adapters are disabled', () => {
126
+ expect(() => getExposedAction(slate, 'adapter_tool', false)).toThrow();
127
+ expect(getExposedAction(slate, 'adapter_tool', true)).toBe(adapterTool);
128
+ });
129
+
130
+ it('only exposes trigger groups that contain an exposed trigger', () => {
131
+ expect(getExposedTriggerGroups(slate, false)).toEqual([providerGroup]);
132
+ expect(getExposedTriggerGroups(slate, true)).toEqual([providerGroup, adapterGroup]);
133
+ });
134
+ });
package/src/spec.ts CHANGED
@@ -8,8 +8,8 @@ import type {
8
8
  import {
9
9
  type Slate,
10
10
  type SlateAdapter,
11
- type SlateTrigger,
12
- SlateDefaultPollingIntervalSeconds
11
+ SlateDefaultPollingIntervalSeconds,
12
+ type SlateTrigger
13
13
  } from '@slates/provider';
14
14
  import z from 'zod';
15
15
  import { toJsonSchema } from './validation';
@@ -47,6 +47,8 @@ export let mapAuthMethod = <ConfigType extends {}, AuthType extends {}>(
47
47
  }))
48
48
  : undefined,
49
49
 
50
+ adapters: 'adapters' in m ? m.adapters : undefined,
51
+
50
52
  inputSchema: toJsonSchema(m.inputSchema ?? z.object({})),
51
53
  outputSchema: toJsonSchema(slate.spec.auth.outputSchema),
52
54
 
@@ -155,6 +157,43 @@ export let mapAction = <ConfigType extends {}, AuthType extends {}>(
155
157
  };
156
158
  };
157
159
 
160
+ export let isMappableTrigger = <ConfigType extends {}, AuthType extends {}>(
161
+ action: Slate<ConfigType, AuthType>['actions'][number]
162
+ ): boolean => action.type !== 'trigger' || !!action.triggerGroup;
163
+
164
+ export let getMappableAction = <ConfigType extends {}, AuthType extends {}>(
165
+ slate: Slate<ConfigType, AuthType>,
166
+ actionId: string
167
+ ) => {
168
+ let action = getAction(slate, actionId);
169
+ if (!isMappableTrigger(action)) {
170
+ throw new ServiceError(notFoundError(`action`, actionId));
171
+ }
172
+
173
+ return action;
174
+ };
175
+
176
+ export let getExposedActions = <ConfigType extends {}, AuthType extends {}>(
177
+ slate: Slate<ConfigType, AuthType>,
178
+ includeAdapterActions: boolean
179
+ ) =>
180
+ slate.actions.filter(
181
+ action => isMappableTrigger(action) && (includeAdapterActions || !action.adapter)
182
+ );
183
+
184
+ export let getExposedAction = <ConfigType extends {}, AuthType extends {}>(
185
+ slate: Slate<ConfigType, AuthType>,
186
+ actionId: string,
187
+ includeAdapterActions: boolean
188
+ ) => {
189
+ let action = getMappableAction(slate, actionId);
190
+ if (action.adapter && !includeAdapterActions) {
191
+ throw new ServiceError(notFoundError(`action`, actionId));
192
+ }
193
+
194
+ return action;
195
+ };
196
+
158
197
  export let getTriggerGroup = <ConfigType extends {}, AuthType extends {}>(
159
198
  slate: Slate<ConfigType, AuthType>,
160
199
  triggerGroupId: string
@@ -173,9 +212,22 @@ export let getTriggersForGroup = <ConfigType extends {}, AuthType extends {}>(
173
212
  ): SlateTrigger<ConfigType, AuthType, any, any>[] =>
174
213
  slate.actions.filter(
175
214
  (action): action is SlateTrigger<ConfigType, AuthType, any, any> =>
176
- action.type === 'trigger' && action.triggerGroup.key === triggerGroupId
215
+ action.type === 'trigger' && action.triggerGroup?.key === triggerGroupId
177
216
  );
178
217
 
218
+ export let getExposedTriggerGroups = <ConfigType extends {}, AuthType extends {}>(
219
+ slate: Slate<ConfigType, AuthType>,
220
+ includeAdapterActions: boolean
221
+ ) => {
222
+ let triggerGroupIds = new Set(
223
+ getExposedActions(slate, includeAdapterActions).flatMap(action =>
224
+ action.type === 'trigger' && action.triggerGroup ? [action.triggerGroup.key] : []
225
+ )
226
+ );
227
+
228
+ return slate.triggerGroups.filter(group => triggerGroupIds.has(group.key));
229
+ };
230
+
179
231
  export let evaluateTriggerMatches = <ConfigType extends {}, AuthType extends {}>(
180
232
  slate: Slate<ConfigType, AuthType>,
181
233
  triggerGroupId: string,
@@ -231,8 +283,12 @@ export let mapTriggerGroup = <ConfigType extends {}, AuthType extends {}>(
231
283
  registration: group.webhook?.manualRegistration
232
284
  ? {
233
285
  mode: 'manual',
234
- userConfigSchema: toJsonSchema(group.webhook.manualRegistration.userConfigSchema),
235
- fullConfigSchema: toJsonSchema(group.webhook.manualRegistration.fullConfigSchema)
286
+ userConfigSchema: toJsonSchema(
287
+ group.webhook.manualRegistration.userConfigSchema
288
+ ),
289
+ fullConfigSchema: toJsonSchema(
290
+ group.webhook.manualRegistration.fullConfigSchema
291
+ )
236
292
  }
237
293
  : { mode: 'auto' }
238
294
  }