@dual-os/sdk 0.0.0-stage → 0.8.0-next.0

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.
Files changed (214) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +19 -2
  3. package/dist/Action.d.ts +201 -0
  4. package/dist/Action.d.ts.map +1 -0
  5. package/dist/Action.js +172 -0
  6. package/dist/Action.js.map +1 -0
  7. package/dist/AgentChannel.d.ts +1717 -0
  8. package/dist/AgentChannel.d.ts.map +1 -0
  9. package/dist/AgentChannel.js +708 -0
  10. package/dist/AgentChannel.js.map +1 -0
  11. package/dist/AgentChannelConformance.d.ts +1543 -0
  12. package/dist/AgentChannelConformance.d.ts.map +1 -0
  13. package/dist/AgentChannelConformance.js +163 -0
  14. package/dist/AgentChannelConformance.js.map +1 -0
  15. package/dist/AgentChannelRuntime.d.ts +78 -0
  16. package/dist/AgentChannelRuntime.d.ts.map +1 -0
  17. package/dist/AgentChannelRuntime.js +270 -0
  18. package/dist/AgentChannelRuntime.js.map +1 -0
  19. package/dist/AiProvider.d.ts +132 -0
  20. package/dist/AiProvider.d.ts.map +1 -0
  21. package/dist/AiProvider.js +100 -0
  22. package/dist/AiProvider.js.map +1 -0
  23. package/dist/AuthMethod.d.ts +381 -0
  24. package/dist/AuthMethod.d.ts.map +1 -0
  25. package/dist/AuthMethod.js +225 -0
  26. package/dist/AuthMethod.js.map +1 -0
  27. package/dist/AuthMethodBinding.d.ts +77 -0
  28. package/dist/AuthMethodBinding.d.ts.map +1 -0
  29. package/dist/AuthMethodBinding.js +31 -0
  30. package/dist/AuthMethodBinding.js.map +1 -0
  31. package/dist/AuthMethodExecution.d.ts +223 -0
  32. package/dist/AuthMethodExecution.d.ts.map +1 -0
  33. package/dist/AuthMethodExecution.js +277 -0
  34. package/dist/AuthMethodExecution.js.map +1 -0
  35. package/dist/ConnectionResolver.d.ts +219 -0
  36. package/dist/ConnectionResolver.d.ts.map +1 -0
  37. package/dist/ConnectionResolver.js +153 -0
  38. package/dist/ConnectionResolver.js.map +1 -0
  39. package/dist/DefinitionError.d.ts +67 -0
  40. package/dist/DefinitionError.d.ts.map +1 -0
  41. package/dist/DefinitionError.js +82 -0
  42. package/dist/DefinitionError.js.map +1 -0
  43. package/dist/DualRegistry.d.ts +71 -0
  44. package/dist/DualRegistry.d.ts.map +1 -0
  45. package/dist/DualRegistry.js +96 -0
  46. package/dist/DualRegistry.js.map +1 -0
  47. package/dist/DualRuntime.d.ts +279 -0
  48. package/dist/DualRuntime.d.ts.map +1 -0
  49. package/dist/DualRuntime.js +311 -0
  50. package/dist/DualRuntime.js.map +1 -0
  51. package/dist/Files.d.ts +80 -0
  52. package/dist/Files.d.ts.map +1 -0
  53. package/dist/Files.js +66 -0
  54. package/dist/Files.js.map +1 -0
  55. package/dist/Group.d.ts +106 -0
  56. package/dist/Group.d.ts.map +1 -0
  57. package/dist/Group.js +86 -0
  58. package/dist/Group.js.map +1 -0
  59. package/dist/Http.d.ts +342 -0
  60. package/dist/Http.d.ts.map +1 -0
  61. package/dist/Http.js +677 -0
  62. package/dist/Http.js.map +1 -0
  63. package/dist/Mcp.d.ts +109 -0
  64. package/dist/Mcp.d.ts.map +1 -0
  65. package/dist/Mcp.js +176 -0
  66. package/dist/Mcp.js.map +1 -0
  67. package/dist/OAuth2TokenRefresher.d.ts +18 -0
  68. package/dist/OAuth2TokenRefresher.d.ts.map +1 -0
  69. package/dist/OAuth2TokenRefresher.js +19 -0
  70. package/dist/OAuth2TokenRefresher.js.map +1 -0
  71. package/dist/OAuthFlow.d.ts +71 -0
  72. package/dist/OAuthFlow.d.ts.map +1 -0
  73. package/dist/OAuthFlow.js +488 -0
  74. package/dist/OAuthFlow.js.map +1 -0
  75. package/dist/Plugin.d.ts +383 -0
  76. package/dist/Plugin.d.ts.map +1 -0
  77. package/dist/Plugin.js +249 -0
  78. package/dist/Plugin.js.map +1 -0
  79. package/dist/PluginRuntime.d.ts +3186 -0
  80. package/dist/PluginRuntime.d.ts.map +1 -0
  81. package/dist/PluginRuntime.js +331 -0
  82. package/dist/PluginRuntime.js.map +1 -0
  83. package/dist/Runner.d.ts +1 -0
  84. package/dist/Runner.js +124 -0
  85. package/dist/Runner.js.map +1 -0
  86. package/dist/ToolFile.d.ts +23 -0
  87. package/dist/ToolFile.d.ts.map +1 -0
  88. package/dist/ToolFile.js +33 -0
  89. package/dist/ToolFile.js.map +1 -0
  90. package/dist/Trigger.d.ts +358 -0
  91. package/dist/Trigger.d.ts.map +1 -0
  92. package/dist/Trigger.js +287 -0
  93. package/dist/Trigger.js.map +1 -0
  94. package/dist/_virtual/_rolldown/runtime.js +13 -0
  95. package/dist/index.d.ts +24 -0
  96. package/dist/index.js +24 -0
  97. package/dist/internal/auth-context.d.ts +23 -0
  98. package/dist/internal/auth-context.d.ts.map +1 -0
  99. package/dist/internal/auth-context.js +1 -0
  100. package/dist/internal/auth-execution.d.ts +27 -0
  101. package/dist/internal/auth-execution.d.ts.map +1 -0
  102. package/dist/internal/auth-execution.js +66 -0
  103. package/dist/internal/auth-execution.js.map +1 -0
  104. package/dist/internal/auth-runtime.d.ts +5 -0
  105. package/dist/internal/auth-runtime.d.ts.map +1 -0
  106. package/dist/internal/auth-runtime.js +16 -0
  107. package/dist/internal/auth-runtime.js.map +1 -0
  108. package/dist/internal/capabilities.d.ts +24 -0
  109. package/dist/internal/capabilities.d.ts.map +1 -0
  110. package/dist/internal/capabilities.js +22 -0
  111. package/dist/internal/capabilities.js.map +1 -0
  112. package/dist/internal/children.d.ts +34 -0
  113. package/dist/internal/children.d.ts.map +1 -0
  114. package/dist/internal/children.js +53 -0
  115. package/dist/internal/children.js.map +1 -0
  116. package/dist/internal/collection-utils.d.ts +4 -0
  117. package/dist/internal/collection-utils.d.ts.map +1 -0
  118. package/dist/internal/collection-utils.js +6 -0
  119. package/dist/internal/collection-utils.js.map +1 -0
  120. package/dist/internal/dual/actions.d.ts +33 -0
  121. package/dist/internal/dual/actions.d.ts.map +1 -0
  122. package/dist/internal/dual/actions.js +26 -0
  123. package/dist/internal/dual/actions.js.map +1 -0
  124. package/dist/internal/dual/invoke.d.ts +63 -0
  125. package/dist/internal/dual/invoke.d.ts.map +1 -0
  126. package/dist/internal/dual/invoke.js +75 -0
  127. package/dist/internal/dual/invoke.js.map +1 -0
  128. package/dist/internal/dual/registry.d.ts +125 -0
  129. package/dist/internal/dual/registry.d.ts.map +1 -0
  130. package/dist/internal/dual/registry.js +172 -0
  131. package/dist/internal/dual/registry.js.map +1 -0
  132. package/dist/internal/dual/triggers.d.ts +29 -0
  133. package/dist/internal/dual/triggers.d.ts.map +1 -0
  134. package/dist/internal/dual/triggers.js +26 -0
  135. package/dist/internal/dual/triggers.js.map +1 -0
  136. package/dist/internal/handler-context.d.ts +9 -0
  137. package/dist/internal/handler-context.d.ts.map +1 -0
  138. package/dist/internal/handler-context.js +31 -0
  139. package/dist/internal/handler-context.js.map +1 -0
  140. package/dist/internal/identifier.d.ts +6 -0
  141. package/dist/internal/identifier.d.ts.map +1 -0
  142. package/dist/internal/identifier.js +28 -0
  143. package/dist/internal/identifier.js.map +1 -0
  144. package/dist/internal/plugin-handlers.d.ts +38 -0
  145. package/dist/internal/plugin-handlers.d.ts.map +1 -0
  146. package/dist/internal/plugin-handlers.js +52 -0
  147. package/dist/internal/plugin-handlers.js.map +1 -0
  148. package/dist/internal/proto.d.ts +7 -0
  149. package/dist/internal/proto.d.ts.map +1 -0
  150. package/dist/internal/proto.js +10 -0
  151. package/dist/internal/proto.js.map +1 -0
  152. package/dist/internal/schema-runtime.d.ts +6 -0
  153. package/dist/internal/schema-runtime.d.ts.map +1 -0
  154. package/dist/internal/schema-runtime.js +10 -0
  155. package/dist/internal/schema-runtime.js.map +1 -0
  156. package/dist/internal/trigger-runtime-shared.d.ts +41 -0
  157. package/dist/internal/trigger-runtime-shared.d.ts.map +1 -0
  158. package/dist/internal/trigger-runtime-shared.js +132 -0
  159. package/dist/internal/trigger-runtime-shared.js.map +1 -0
  160. package/dist/internal/trigger-runtime.d.ts +12 -0
  161. package/dist/internal/trigger-runtime.d.ts.map +1 -0
  162. package/dist/internal/trigger-runtime.js +95 -0
  163. package/dist/internal/trigger-runtime.js.map +1 -0
  164. package/dist/internal/types.d.ts +7 -0
  165. package/dist/internal/types.d.ts.map +1 -0
  166. package/dist/internal/types.js +1 -0
  167. package/dist/remote-runtime.d.ts +585 -0
  168. package/dist/remote-runtime.d.ts.map +1 -0
  169. package/dist/remote-runtime.js +367 -0
  170. package/dist/remote-runtime.js.map +1 -0
  171. package/package.json +55 -4
  172. package/src/Action.ts +370 -0
  173. package/src/AgentChannel.ts +860 -0
  174. package/src/AgentChannelConformance.ts +124 -0
  175. package/src/AgentChannelRuntime.ts +616 -0
  176. package/src/AiProvider.ts +255 -0
  177. package/src/AuthMethod.ts +954 -0
  178. package/src/AuthMethodBinding.ts +112 -0
  179. package/src/AuthMethodExecution.ts +542 -0
  180. package/src/ConnectionResolver.ts +265 -0
  181. package/src/DefinitionError.ts +115 -0
  182. package/src/DualRegistry.ts +152 -0
  183. package/src/DualRuntime.ts +1081 -0
  184. package/src/Files.ts +89 -0
  185. package/src/Group.ts +292 -0
  186. package/src/Http.ts +1205 -0
  187. package/src/Mcp.ts +299 -0
  188. package/src/OAuth2TokenRefresher.ts +22 -0
  189. package/src/OAuthFlow.ts +874 -0
  190. package/src/Plugin.ts +912 -0
  191. package/src/PluginRuntime.ts +414 -0
  192. package/src/Runner.ts +248 -0
  193. package/src/ToolFile.ts +28 -0
  194. package/src/Trigger.ts +722 -0
  195. package/src/index.ts +158 -0
  196. package/src/internal/auth-context.ts +27 -0
  197. package/src/internal/auth-execution.ts +241 -0
  198. package/src/internal/auth-runtime.ts +20 -0
  199. package/src/internal/capabilities.ts +55 -0
  200. package/src/internal/children.ts +126 -0
  201. package/src/internal/collection-utils.ts +8 -0
  202. package/src/internal/dual/actions.ts +116 -0
  203. package/src/internal/dual/invoke.ts +307 -0
  204. package/src/internal/dual/registry.ts +340 -0
  205. package/src/internal/dual/triggers.ts +128 -0
  206. package/src/internal/handler-context.ts +57 -0
  207. package/src/internal/identifier.ts +27 -0
  208. package/src/internal/plugin-handlers.ts +146 -0
  209. package/src/internal/proto.ts +8 -0
  210. package/src/internal/schema-runtime.ts +15 -0
  211. package/src/internal/trigger-runtime-shared.ts +449 -0
  212. package/src/internal/trigger-runtime.ts +223 -0
  213. package/src/internal/types.ts +12 -0
  214. package/src/remote-runtime.ts +469 -0
package/src/Plugin.ts ADDED
@@ -0,0 +1,912 @@
1
+ /**
2
+ * Plugin definition builders and types for the Dual SDK.
3
+ *
4
+ * A plugin is one integration: its metadata, authentication alternatives, actions, groups,
5
+ * triggers and Agent Channels, plus the Layer implementing them. A release is the plugin's
6
+ * package version.
7
+ *
8
+ * @since 0.0.0
9
+ */
10
+ import type { Pipeable, Scope } from "effect";
11
+ import { Array, Effect, Function as Func, Layer, Option, Predicate, Result, String } from "effect";
12
+
13
+ import type { HttpClientRequest } from "effect/http";
14
+ import { Url, UrlParams } from "effect/http";
15
+
16
+ import type * as Action from "./Action.ts";
17
+ import * as AgentChannel from "./AgentChannel.ts";
18
+ import type * as AuthMethod from "./AuthMethod.ts";
19
+ import type { ConnectionResolutionContext } from "./ConnectionResolver.ts";
20
+ import type * as Group from "./Group.ts";
21
+ import type { ContextForContract } from "./internal/auth-context.ts";
22
+ import { assertNoDuplicateAuthMethodIdentifiers } from "./internal/auth-runtime.ts";
23
+ import {
24
+ assertUniqueMixedChildIdentifiers,
25
+ assertUniqueStructuralChildIdentifiers,
26
+ type FilterChildren,
27
+ partitionChildren,
28
+ type StructuralChildKind,
29
+ } from "./internal/children.ts";
30
+ import { appendElements } from "./internal/collection-utils.ts";
31
+ import { validateIdentifier } from "./internal/identifier.ts";
32
+ import {
33
+ type HandlerServices as PluginHandlerServices,
34
+ toHandlers,
35
+ } from "./internal/plugin-handlers.ts";
36
+ import { PipeableProto } from "./internal/proto.ts";
37
+ import type * as Trigger from "./Trigger.ts";
38
+
39
+ /**
40
+ * @since 0.0.0
41
+ * @category type ids
42
+ */
43
+ export const TypeId = "@dual-os/sdk/Plugin" as const;
44
+
45
+ /**
46
+ * @since 0.0.0
47
+ * @category type ids
48
+ */
49
+ export type TypeId = typeof TypeId;
50
+
51
+ /** @since 0.0.0 @category models */
52
+ export const categories = [
53
+ "sales_crm",
54
+ "marketing",
55
+ "customer_support",
56
+ "calendar_scheduling",
57
+ "communication",
58
+ "project_management",
59
+ "developer_tools",
60
+ "data_storage",
61
+ "docs_productivity",
62
+ "commerce_payments",
63
+ "finance_accounting",
64
+ "human_resources",
65
+ "analytics",
66
+ "design",
67
+ "it_service_management",
68
+ "search",
69
+ "telephony",
70
+ "web_scraping",
71
+ ] as const;
72
+
73
+ /** @since 0.0.0 @category models */
74
+ export type Category = (typeof categories)[number];
75
+
76
+ /**
77
+ * How a command in a run sandbox uses the plugin's Connection over HTTPS without reading its
78
+ * credential: Dual's sandbox gateway adds the credential to each request that the plugin allows.
79
+ *
80
+ * @since 0.0.0
81
+ * @category models
82
+ */
83
+ export interface SandboxAccess<
84
+ Methods extends readonly AuthMethod.Any[] = readonly AuthMethod.Any[],
85
+ > {
86
+ /** Origins, such as `https://github.com`, that may receive the Connection's credential. */
87
+ origins(context: Action.HandlerContext<Methods>): readonly string[];
88
+ /**
89
+ * Adds the Connection's credential to a request for one of the origins, or refuses the request.
90
+ * Refuse every request that can read or create a credential, such as a token rotation.
91
+ */
92
+ authorize(
93
+ request: HttpClientRequest.HttpClientRequest,
94
+ context: Action.HandlerContext<Methods>,
95
+ ): Option.Option<HttpClientRequest.HttpClientRequest>;
96
+ }
97
+
98
+ const gitServices = ["git-upload-pack", "git-receive-pack"];
99
+
100
+ /**
101
+ * Whether a request is a Git smart HTTP request of a fetch, clone or push: `GET <repository>/info/refs?service=<service>`
102
+ * or `POST <repository>/<service>` with the service's request content type.
103
+ *
104
+ * @since 0.0.0
105
+ * @category predicates
106
+ */
107
+ export function isGitRequest(request: HttpClientRequest.HttpClientRequest) {
108
+ return Result.match(Url.fromString(request.url), {
109
+ onFailure: () => false,
110
+ onSuccess: url => {
111
+ const service = Option.orElse(UrlParams.getFirst(Url.urlParams(url), "service"), () =>
112
+ UrlParams.getFirst(request.urlParams, "service"),
113
+ );
114
+ return Array.some(
115
+ gitServices,
116
+ name =>
117
+ (request.method === "GET" &&
118
+ String.endsWith("/info/refs")(url.pathname) &&
119
+ Option.contains(service, name)) ||
120
+ (request.method === "POST" &&
121
+ String.endsWith(`/${name}`)(url.pathname) &&
122
+ request.headers["content-type"] === `application/x-${name}-request`),
123
+ );
124
+ },
125
+ });
126
+ }
127
+
128
+ /**
129
+ * Constraint type for working with any {@link Plugin}.
130
+ *
131
+ * @since 0.0.0
132
+ * @category type-level
133
+ */
134
+ export interface Any extends Pipeable.Pipeable {
135
+ readonly [TypeId]: TypeId;
136
+ readonly identifier: string;
137
+ readonly name: string | undefined;
138
+ readonly description: string | undefined;
139
+ readonly iconUrl: string | undefined;
140
+ readonly iconUrlDark: string | undefined;
141
+ readonly categories: readonly Category[];
142
+ readonly homepageUrl: string | undefined;
143
+ readonly docsUrl: string | undefined;
144
+ readonly authMethods: readonly AuthMethod.Any[];
145
+ readonly groups: readonly Group.Any[];
146
+ readonly actions: readonly Action.Any[];
147
+ readonly triggers: readonly Trigger.Any[];
148
+ readonly agentChannels: readonly AgentChannel.Any[];
149
+ readonly sandboxAccess: SandboxAccess | undefined;
150
+ readonly layer: Layer.Layer<never, any, any>;
151
+ }
152
+
153
+ /**
154
+ * A typed plugin definition.
155
+ *
156
+ * @since 0.0.0
157
+ * @category models
158
+ */
159
+ export interface Plugin<
160
+ out Identifier extends string = string,
161
+ out AuthMethods extends readonly AuthMethod.Any[] = readonly [],
162
+ out Groups extends readonly Group.Any[] = readonly [],
163
+ out Actions extends readonly Action.Any[] = readonly [],
164
+ out Triggers extends readonly Trigger.Any[] = readonly [],
165
+ out Channels extends readonly AgentChannel.Any[] = readonly [],
166
+ LayerOutput = never,
167
+ out LayerError = never,
168
+ out LayerRequirements = never,
169
+ >
170
+ extends Pipeable.Pipeable {
171
+ readonly [TypeId]: TypeId;
172
+ readonly identifier: Identifier;
173
+ readonly name: string | undefined;
174
+ readonly description: string | undefined;
175
+ readonly iconUrl: string | undefined;
176
+ readonly iconUrlDark: string | undefined;
177
+ readonly categories: readonly Category[];
178
+ readonly homepageUrl: string | undefined;
179
+ readonly docsUrl: string | undefined;
180
+ readonly authMethods: AuthMethods;
181
+ readonly groups: Groups;
182
+ readonly actions: Actions;
183
+ readonly triggers: Triggers;
184
+ readonly agentChannels: Channels;
185
+ readonly sandboxAccess: SandboxAccess | undefined;
186
+ readonly layer: Layer.Layer<LayerOutput, LayerError, LayerRequirements>;
187
+ }
188
+
189
+ /**
190
+ * The contract of a plugin without its Layer.
191
+ *
192
+ * @since 0.6.0
193
+ * @category models
194
+ */
195
+ export type Definition<
196
+ Identifier extends string,
197
+ Methods extends readonly AuthMethod.Any[] = readonly [],
198
+ Groups extends readonly Group.Any[] = readonly [],
199
+ Actions extends readonly Action.Any[] = readonly [],
200
+ Triggers extends readonly Trigger.Any[] = readonly [],
201
+ Channels extends readonly AgentChannel.Any[] = readonly [],
202
+ > = Plugin<Identifier, Methods, Groups, Actions, Triggers, Channels>;
203
+
204
+ /**
205
+ * A plugin contract with the services its Layer provides.
206
+ *
207
+ * @since 0.7.0
208
+ * @category models
209
+ */
210
+ export type Provided<
211
+ P extends Any,
212
+ LayerOutput,
213
+ LayerError = never,
214
+ LayerRequirements = never,
215
+ > = Plugin<
216
+ P["identifier"],
217
+ P["authMethods"],
218
+ P["groups"],
219
+ P["actions"],
220
+ P["triggers"],
221
+ P["agentChannels"],
222
+ LayerOutput,
223
+ LayerError,
224
+ LayerRequirements
225
+ >;
226
+
227
+ /**
228
+ * Runtime services produced by plugin layers.
229
+ *
230
+ * @since 0.0.0
231
+ * @category type-level
232
+ */
233
+ export type LayerOutput<Plugins extends readonly Any[]> =
234
+ Plugins[number] extends Plugin<any, any, any, any, any, any, infer Output, any, any>
235
+ ? Output
236
+ : never;
237
+
238
+ /**
239
+ * Errors surfaced while constructing plugin layers.
240
+ *
241
+ * @since 0.0.0
242
+ * @category type-level
243
+ */
244
+ export type LayerError<Plugins extends readonly Any[]> =
245
+ Plugins[number] extends Plugin<any, any, any, any, any, any, any, infer Error, any>
246
+ ? Error
247
+ : never;
248
+
249
+ /**
250
+ * Runtime services required by plugin layers.
251
+ *
252
+ * @since 0.0.0
253
+ * @category type-level
254
+ */
255
+ export type LayerRequirements<Plugins extends readonly Any[]> =
256
+ Plugins[number] extends Plugin<any, any, any, any, any, any, any, any, infer Requirements>
257
+ ? Requirements
258
+ : never;
259
+
260
+ /** @since 0.0.0 @category type-level */
261
+ export type StructuralChild = Group.Any | Action.Any | Trigger.Any;
262
+
263
+ /** @since 0.0.0 @category type-level */
264
+ export type GroupsFromChildren<Children extends readonly StructuralChild[]> = FilterChildren<
265
+ Children,
266
+ Group.Any
267
+ >;
268
+
269
+ /** @since 0.0.0 @category type-level */
270
+ export type ActionsFromChildren<Children extends readonly StructuralChild[]> = FilterChildren<
271
+ Children,
272
+ Action.Any
273
+ >;
274
+
275
+ /** @since 0.0.0 @category type-level */
276
+ export type TriggersFromChildren<Children extends readonly StructuralChild[]> = FilterChildren<
277
+ Children,
278
+ Trigger.Any
279
+ >;
280
+
281
+ /**
282
+ * Result of appending capabilities to a plugin.
283
+ *
284
+ * @since 0.0.0
285
+ * @category type-level
286
+ */
287
+ export type AddChildren<
288
+ P extends Any,
289
+ AddedGroups extends readonly Group.Any[] = readonly [],
290
+ AddedActions extends readonly Action.Any[] = readonly [],
291
+ AddedTriggers extends readonly Trigger.Any[] = readonly [],
292
+ AddedChannels extends readonly AgentChannel.Any[] = readonly [],
293
+ > =
294
+ P extends Plugin<
295
+ infer Identifier,
296
+ infer Methods,
297
+ infer Groups,
298
+ infer Actions,
299
+ infer Triggers,
300
+ infer Channels,
301
+ infer Output,
302
+ infer Error,
303
+ infer Requirements
304
+ >
305
+ ? Plugin<
306
+ Identifier,
307
+ Methods,
308
+ readonly [...Groups, ...AddedGroups],
309
+ readonly [...Actions, ...AddedActions],
310
+ readonly [...Triggers, ...AddedTriggers],
311
+ readonly [...Channels, ...AddedChannels],
312
+ Output,
313
+ Error,
314
+ Requirements
315
+ >
316
+ : never;
317
+
318
+ /**
319
+ * Result of appending auth methods to a plugin.
320
+ *
321
+ * @since 0.0.0
322
+ * @category type-level
323
+ */
324
+ export type AddAuthMethods<P extends Any, AddedMethods extends readonly AuthMethod.Any[]> =
325
+ P extends Plugin<
326
+ infer Identifier,
327
+ infer Methods,
328
+ infer Groups,
329
+ infer Actions,
330
+ infer Triggers,
331
+ infer Channels,
332
+ infer Output,
333
+ infer Error,
334
+ infer Requirements
335
+ >
336
+ ? Plugin<
337
+ Identifier,
338
+ readonly [...Methods, ...AddedMethods],
339
+ Groups,
340
+ Actions,
341
+ Triggers,
342
+ Channels,
343
+ Output,
344
+ Error,
345
+ Requirements
346
+ >
347
+ : never;
348
+
349
+ interface Fields {
350
+ readonly identifier: string;
351
+ readonly name: string | undefined;
352
+ readonly description: string | undefined;
353
+ readonly iconUrl: string | undefined;
354
+ readonly iconUrlDark: string | undefined;
355
+ readonly categories: readonly Category[];
356
+ readonly homepageUrl: string | undefined;
357
+ readonly docsUrl: string | undefined;
358
+ readonly authMethods: readonly AuthMethod.Any[];
359
+ readonly groups: readonly Group.Any[];
360
+ readonly actions: readonly Action.Any[];
361
+ readonly triggers: readonly Trigger.Any[];
362
+ readonly agentChannels: readonly AgentChannel.Any[];
363
+ readonly sandboxAccess: SandboxAccess | undefined;
364
+ readonly layer: Layer.Layer<never, any, any>;
365
+ }
366
+
367
+ const Proto = { ...PipeableProto, [TypeId]: TypeId };
368
+
369
+ const makePlugin = (fields: Fields): Any => {
370
+ validateIdentifier("plugin", fields.identifier);
371
+ assertNoDuplicateAuthMethodIdentifiers(fields.identifier, fields.authMethods);
372
+ return Object.assign(Object.create(Proto), fields);
373
+ };
374
+
375
+ const update = (plugin: Any, fields: Partial<Fields>): Any => makePlugin({ ...plugin, ...fields });
376
+
377
+ /**
378
+ * Create an empty {@link Plugin}.
379
+ *
380
+ * @since 0.0.0
381
+ * @category constructors
382
+ */
383
+ export const make = <const Identifier extends string>(identifier: Identifier): Plugin<Identifier> =>
384
+ makePlugin({
385
+ actions: [],
386
+ agentChannels: [],
387
+ authMethods: [],
388
+ categories: [],
389
+ description: undefined,
390
+ docsUrl: undefined,
391
+ groups: [],
392
+ homepageUrl: undefined,
393
+ iconUrl: undefined,
394
+ iconUrlDark: undefined,
395
+ identifier,
396
+ layer: Layer.empty,
397
+ name: undefined,
398
+ sandboxAccess: undefined,
399
+ triggers: [],
400
+ }) as never;
401
+
402
+ /**
403
+ * Check if a value is a {@link Plugin}.
404
+ *
405
+ * @since 0.0.0
406
+ * @category guards
407
+ */
408
+ export const isPlugin = (u: unknown): u is Any => Predicate.hasProperty(u, TypeId);
409
+
410
+ const metadata = <
411
+ Key extends "name" | "description" | "iconUrl" | "iconUrlDark" | "homepageUrl" | "docsUrl",
412
+ >(
413
+ key: Key,
414
+ ): {
415
+ (value: string): <P extends Any>(plugin: P) => P;
416
+ <P extends Any>(plugin: P, value: string): P;
417
+ } => Func.dual(2, (plugin: Any, value: string) => update(plugin, { [key]: value })) as never;
418
+
419
+ /** @since 0.0.0 @category combinators */
420
+ export const withName = metadata("name");
421
+ /** @since 0.0.0 @category combinators */
422
+ export const withDescription = metadata("description");
423
+ /** @since 0.6.0 @category combinators */
424
+ export const withIconUrl = metadata("iconUrl");
425
+ /** @since 0.6.0 @category combinators */
426
+ export const withIconUrlDark = metadata("iconUrlDark");
427
+ /** @since 0.6.0 @category combinators */
428
+ export const withHomepageUrl = metadata("homepageUrl");
429
+ /** @since 0.6.0 @category combinators */
430
+ export const withDocsUrl = metadata("docsUrl");
431
+
432
+ /** @since 0.6.0 @category combinators */
433
+ export const withCategories: {
434
+ (categories: readonly Category[]): <P extends Any>(plugin: P) => P;
435
+ <P extends Any>(plugin: P, categories: readonly Category[]): P;
436
+ } = Func.dual(2, (plugin: Any, categories: readonly Category[]) =>
437
+ update(plugin, { categories: [...categories] }),
438
+ );
439
+
440
+ /**
441
+ * Add authentication alternatives. Actions and triggers accept every method.
442
+ *
443
+ * @since 0.6.0
444
+ * @category combinators
445
+ */
446
+ export const withAuthMethods =
447
+ <const Methods extends readonly AuthMethod.Any[]>(methods: Methods) =>
448
+ <P extends Any>(plugin: P): AddAuthMethods<P, Methods> => {
449
+ if (plugin.sandboxAccess !== undefined) {
450
+ throw new Error(
451
+ `plugin "${plugin.identifier}" must declare auth methods before its sandbox access`,
452
+ );
453
+ }
454
+ return update(plugin, { authMethods: appendElements(plugin.authMethods, methods) }) as never;
455
+ };
456
+
457
+ /**
458
+ * Add an authentication alternative. Actions and triggers accept every method.
459
+ *
460
+ * @since 0.6.0
461
+ * @category combinators
462
+ */
463
+ export const withAuthMethod: {
464
+ <const Method extends AuthMethod.Any>(
465
+ method: Method,
466
+ ): <P extends Any>(plugin: P) => AddAuthMethods<P, readonly [Method]>;
467
+ <P extends Any, const Method extends AuthMethod.Any>(
468
+ plugin: P,
469
+ method: Method,
470
+ ): AddAuthMethods<P, readonly [Method]>;
471
+ } = Func.dual(2, (plugin: Any, method: AuthMethod.Any) => withAuthMethods([method])(plugin));
472
+
473
+ /**
474
+ * Lets commands in a run sandbox use the plugin's Connection through Dual's sandbox gateway.
475
+ *
476
+ * @since 0.6.0
477
+ * @category combinators
478
+ */
479
+ export const withSandboxAccess =
480
+ <Methods extends readonly AuthMethod.Any[]>(access: SandboxAccess<Methods>) =>
481
+ <P extends Any>(plugin: P & { readonly authMethods: Methods }): P =>
482
+ update(plugin, { sandboxAccess: access as SandboxAccess }) as P;
483
+
484
+ const appendChildren = (
485
+ plugin: Any,
486
+ children: readonly StructuralChild[],
487
+ childKind?: StructuralChildKind,
488
+ ): Any => {
489
+ if (childKind === undefined) {
490
+ assertUniqueMixedChildIdentifiers("plugin", plugin.identifier, plugin, children);
491
+ } else {
492
+ assertUniqueStructuralChildIdentifiers(
493
+ "plugin",
494
+ plugin.identifier,
495
+ plugin,
496
+ children,
497
+ childKind,
498
+ );
499
+ }
500
+ const partitioned = partitionChildren(children);
501
+ return update(plugin, {
502
+ actions: appendElements(plugin.actions, partitioned.actions),
503
+ groups: appendElements(plugin.groups, partitioned.groups),
504
+ triggers: appendElements(plugin.triggers, partitioned.triggers),
505
+ });
506
+ };
507
+
508
+ /** @since 0.6.0 @category combinators */
509
+ export const withAction: {
510
+ <const Added extends Action.Any>(
511
+ action: Added,
512
+ ): <P extends Any>(plugin: P) => AddChildren<P, readonly [], readonly [Added]>;
513
+ <P extends Any, const Added extends Action.Any>(
514
+ plugin: P,
515
+ action: Added,
516
+ ): AddChildren<P, readonly [], readonly [Added]>;
517
+ } = Func.dual(2, (plugin: Any, action: Action.Any) => appendChildren(plugin, [action], "action"));
518
+
519
+ /** @since 0.6.0 @category combinators */
520
+ export const withGroup: {
521
+ <const Added extends Group.Any>(
522
+ group: Added,
523
+ ): <P extends Any>(plugin: P) => AddChildren<P, readonly [Added]>;
524
+ <P extends Any, const Added extends Group.Any>(
525
+ plugin: P,
526
+ group: Added,
527
+ ): AddChildren<P, readonly [Added]>;
528
+ } = Func.dual(2, (plugin: Any, group: Group.Any) => appendChildren(plugin, [group], "group"));
529
+
530
+ /** @since 0.6.0 @category combinators */
531
+ export const withTrigger: {
532
+ <const Added extends Trigger.Any>(
533
+ trigger: Added,
534
+ ): <P extends Any>(plugin: P) => AddChildren<P, readonly [], readonly [], readonly [Added]>;
535
+ <P extends Any, const Added extends Trigger.Any>(
536
+ plugin: P,
537
+ trigger: Added,
538
+ ): AddChildren<P, readonly [], readonly [], readonly [Added]>;
539
+ } = Func.dual(2, (plugin: Any, trigger: Trigger.Any) =>
540
+ appendChildren(plugin, [trigger], "trigger"),
541
+ );
542
+
543
+ /** Add the generated action groups. @since 0.6.0 @category combinators */
544
+ export const withGroups =
545
+ <const Groups extends readonly Group.Any[]>(groups: Groups) =>
546
+ <P extends Any>(plugin: P): AddChildren<P, Groups> =>
547
+ appendChildren(plugin, groups, "group") as never;
548
+
549
+ /** Add trigger definitions from an overlay. @since 0.6.0 @category combinators */
550
+ export const withTriggers =
551
+ <const Triggers extends readonly Trigger.Any[]>(triggers: Triggers) =>
552
+ <P extends Any>(plugin: P): AddChildren<P, readonly [], readonly [], Triggers> =>
553
+ appendChildren(plugin, triggers, "trigger") as never;
554
+
555
+ /** Add grouped capabilities. @since 0.6.0 @category combinators */
556
+ export const add =
557
+ <const Children extends readonly StructuralChild[]>(...children: Children) =>
558
+ <P extends Any>(
559
+ plugin: P,
560
+ ): AddChildren<
561
+ P,
562
+ GroupsFromChildren<Children>,
563
+ ActionsFromChildren<Children>,
564
+ TriggersFromChildren<Children>
565
+ > =>
566
+ appendChildren(plugin, children) as never;
567
+
568
+ /** Add an Agent Channel; its definition owns its exact auth subset. @since 0.6.0 @category combinators */
569
+ export const withAgentChannel =
570
+ <const Channel extends AgentChannel.Any>(channel: Channel) =>
571
+ <P extends Any>(
572
+ plugin: P,
573
+ ): AddChildren<P, readonly [], readonly [], readonly [], readonly [Channel]> => {
574
+ if (
575
+ [...plugin.actions, ...plugin.triggers, ...plugin.groups, ...plugin.agentChannels].some(
576
+ child => child.identifier === channel.identifier,
577
+ )
578
+ ) {
579
+ throw new AgentChannel.ChannelError({ code: "invalid_definition" });
580
+ }
581
+ AgentChannel.validateDefinition(channel.definition, plugin.identifier, plugin.authMethods);
582
+ return update(plugin, { agentChannels: [...plugin.agentChannels, channel] }) as never;
583
+ };
584
+
585
+ /**
586
+ * Attach runtime services to a plugin definition.
587
+ *
588
+ * @since 0.0.0
589
+ * @category combinators
590
+ */
591
+ export const provide: {
592
+ <AddedOutput, AddedError, AddedRequirements>(
593
+ layer: Layer.Layer<AddedOutput, AddedError, AddedRequirements>,
594
+ ): <P extends Any>(
595
+ plugin: P,
596
+ ) => Provided<
597
+ P,
598
+ LayerOutput<readonly [P]> | AddedOutput,
599
+ LayerError<readonly [P]> | AddedError,
600
+ AddedRequirements | Exclude<LayerRequirements<readonly [P]>, AddedOutput>
601
+ >;
602
+ <P extends Any, AddedOutput, AddedError, AddedRequirements>(
603
+ plugin: P,
604
+ layer: Layer.Layer<AddedOutput, AddedError, AddedRequirements>,
605
+ ): Provided<
606
+ P,
607
+ LayerOutput<readonly [P]> | AddedOutput,
608
+ LayerError<readonly [P]> | AddedError,
609
+ AddedRequirements | Exclude<LayerRequirements<readonly [P]>, AddedOutput>
610
+ >;
611
+ } = Func.dual(2, (plugin: Any, layer: Layer.Layer<never, any, any>) =>
612
+ update(plugin, { layer: Layer.provideMerge(plugin.layer, layer) }),
613
+ );
614
+
615
+ /** Runtime handler services contributed by a plugin. @since 0.6.0 @category type-level */
616
+ export type HandlerServices<P extends Any> = PluginHandlerServices<P>;
617
+
618
+ /** The service carrying a plugin's action handlers. @since 0.7.0 @category type-level */
619
+ export type { ActionHandlerService, ActionHandlerServiceOf } from "./internal/plugin-handlers.ts";
620
+
621
+ interface Node {
622
+ readonly actions: readonly Action.Any[];
623
+ readonly groups: readonly Group.Any[];
624
+ readonly triggers: readonly Trigger.Any[];
625
+ readonly agentChannels?: readonly AgentChannel.Any[];
626
+ }
627
+
628
+ type ActionName<N extends Node> = N["actions"][number]["identifier"];
629
+ type GroupName<N extends Node> = N["groups"][number]["identifier"];
630
+ type TriggerName<N extends Node> = N["triggers"][number]["identifier"];
631
+
632
+ type ActionNamed<N extends Node, Name> = Extract<
633
+ N["actions"][number],
634
+ { readonly identifier: Name }
635
+ >;
636
+ type GroupNamed<N extends Node, Name> = Extract<N["groups"][number], { readonly identifier: Name }>;
637
+ type TriggerNamed<N extends Node, Name> = Extract<
638
+ N["triggers"][number],
639
+ { readonly identifier: Name }
640
+ >;
641
+
642
+ type Channels<N extends Node> = N extends {
643
+ readonly agentChannels: infer C extends readonly AgentChannel.Any[];
644
+ }
645
+ ? C[number]
646
+ : never;
647
+ type ChannelName<N extends Node> = Channels<N>["identifier"];
648
+ type ChannelNamed<N extends Node, Name> = Extract<Channels<N>, { readonly identifier: Name }>;
649
+ type Names<N extends Node> = ActionName<N> | GroupName<N> | TriggerName<N> | ChannelName<N>;
650
+
651
+ // Concrete parameter types, not a generic handler, let `Effect.fn` infer its arguments.
652
+ type ActionHandler<A extends Action.Any, Methods extends readonly AuthMethod.Any[], R> = (
653
+ input: Action.Input<A>,
654
+ context: ContextForContract<Methods, A["requireAuth"]>,
655
+ ) => Effect.Effect<Action.Output<A>, Action.ActionHandlerFailure, R>;
656
+
657
+ type TriggerContext<
658
+ T extends Trigger.Any,
659
+ Methods extends readonly AuthMethod.Any[],
660
+ > = ContextForContract<Methods, T["requireAuth"]> & ConnectionResolutionContext;
661
+
662
+ type Emit<T extends Trigger.Any> = Trigger.EmitResult<Trigger.Output<T>, Trigger.State<T>>;
663
+
664
+ type WebhookInput<T extends Trigger.Any> = Trigger.WebhookLifecycleInput<
665
+ Trigger.Config<T>,
666
+ Trigger.State<T>
667
+ >;
668
+ type ActivationInput<T extends Trigger.Any> = Trigger.WebhookActivationInput<
669
+ Trigger.Config<T>,
670
+ Trigger.State<T>
671
+ >;
672
+
673
+ /** Lifecycle handlers each trigger kind must implement; each hook infers its own requirements. */
674
+ type TriggerLifecycle<
675
+ T extends Trigger.Any,
676
+ Methods extends readonly AuthMethod.Any[],
677
+ RActivate,
678
+ RDeactivate,
679
+ RVerify,
680
+ REmit,
681
+ > = T["kind"] extends "webhook"
682
+ ? {
683
+ readonly activate?: (
684
+ input: ActivationInput<T>,
685
+ context: TriggerContext<T, Methods>,
686
+ ) => Effect.Effect<Trigger.ActivationResult<Trigger.State<T>>, unknown, RActivate>;
687
+ readonly deactivate?: (
688
+ input: ActivationInput<T>,
689
+ context: TriggerContext<T, Methods>,
690
+ ) => Effect.Effect<void, unknown, RDeactivate>;
691
+ readonly verify?: (
692
+ input: WebhookInput<T>,
693
+ context: TriggerContext<T, Methods>,
694
+ ) => Effect.Effect<void | Trigger.VerifyResult<Trigger.State<T>>, unknown, RVerify>;
695
+ readonly parse: (
696
+ input: WebhookInput<T>,
697
+ context: TriggerContext<T, Methods>,
698
+ ) => Effect.Effect<Emit<T>, unknown, REmit>;
699
+ }
700
+ : {
701
+ readonly poll: (
702
+ input: Trigger.PollingInput<Trigger.Config<T>, Trigger.State<T>>,
703
+ context: TriggerContext<T, Methods>,
704
+ ) => Effect.Effect<Emit<T>, unknown, REmit>;
705
+ };
706
+
707
+ type UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (
708
+ value: infer I,
709
+ ) => void
710
+ ? I
711
+ : never;
712
+
713
+ /** Typed lifecycles exported by a trigger overlay. @since 0.6.0 @category handlers */
714
+ export type TriggerHandlers<
715
+ Triggers extends readonly Trigger.Any[],
716
+ Methods extends readonly AuthMethod.Any[],
717
+ > = {
718
+ readonly [T in Triggers[number] as T["identifier"]]: TriggerLifecycle<
719
+ T,
720
+ Methods,
721
+ any,
722
+ any,
723
+ any,
724
+ any
725
+ >;
726
+ };
727
+
728
+ type LifecycleRequirements<H> = H extends (...args: any[]) => Effect.Effect<any, any, infer R>
729
+ ? R
730
+ : H extends object
731
+ ? { [K in keyof H]: LifecycleRequirements<H[K]> }[keyof H]
732
+ : never;
733
+
734
+ /** Rejects a union of names, which would mark several capabilities handled by one handler. */
735
+ type Single<Name> = [Name] extends [UnionToIntersection<Name>] ? unknown : never;
736
+
737
+ /**
738
+ * Immutable handler collection for one plugin or group. Each registration
739
+ * removes its name from the names still accepted.
740
+ *
741
+ * @since 0.6.0
742
+ * @category handlers
743
+ */
744
+ export interface HandlerCollection<
745
+ N extends Node,
746
+ Methods extends readonly AuthMethod.Any[],
747
+ Handled extends PropertyKey = never,
748
+ R = never,
749
+ > {
750
+ readonly "~node": N;
751
+ readonly "~handled": Handled;
752
+ readonly "~requirements": R;
753
+ /** @internal */
754
+ readonly actions: Readonly<Record<string, unknown>>;
755
+ /** @internal */
756
+ readonly triggers: Readonly<Record<string, unknown>>;
757
+ readonly agentChannels: Readonly<Record<string, unknown>>;
758
+
759
+ /** Implement a Channel declared by the plugin. */
760
+ channel<const Name extends Exclude<ChannelName<N>, Handled>, R1>(
761
+ name: Name & Single<Name>,
762
+ lifecycle: AgentChannel.RuntimeContract<ChannelNamed<N, Name>["definition"], R1>,
763
+ ): HandlerCollection<N, Methods, Handled | Name, R | R1>;
764
+
765
+ /** Register the lifecycles from a typed trigger overlay. */
766
+ withTriggers<H extends TriggerHandlers<N["triggers"], Methods>>(
767
+ lifecycles: H &
768
+ Record<Exclude<keyof H, TriggerName<N>>, never> &
769
+ ([Extract<Handled, TriggerName<N>>] extends [never] ? unknown : never),
770
+ ): HandlerCollection<N, Methods, Handled | TriggerName<N>, R | LifecycleRequirements<H>>;
771
+
772
+ /** Implement an action declared directly on this plugin or group. */
773
+ action<const Name extends Exclude<ActionName<N>, Handled>, R1>(
774
+ name: Name & Single<Name>,
775
+ handler: ActionHandler<ActionNamed<N, Name>, Methods, R1>,
776
+ ): HandlerCollection<N, Methods, Handled | Name, R | R1>;
777
+
778
+ /** Implement every capability of a nested group. */
779
+ group<const Name extends Exclude<GroupName<N>, Handled>, Return>(
780
+ name: Name & Single<Name>,
781
+ build: (
782
+ handlers: HandlerCollection<GroupNamed<N, Name>, Methods>,
783
+ ) => ValidateHandlers<Return, GroupNamed<N, Name>>,
784
+ ): HandlerCollection<N, Methods, Handled | Name, R | Requirements<Return>>;
785
+
786
+ /** Implement the lifecycle of a trigger declared directly on this plugin or group. */
787
+ trigger<
788
+ const Name extends Exclude<TriggerName<N>, Handled>,
789
+ RActivate = never,
790
+ RDeactivate = never,
791
+ RVerify = never,
792
+ REmit = never,
793
+ >(
794
+ name: Name & Single<Name>,
795
+ lifecycle: TriggerLifecycle<
796
+ TriggerNamed<N, Name>,
797
+ Methods,
798
+ RActivate,
799
+ RDeactivate,
800
+ RVerify,
801
+ REmit
802
+ >,
803
+ ): HandlerCollection<N, Methods, Handled | Name, R | RActivate | RDeactivate | RVerify | REmit>;
804
+ }
805
+
806
+ type Built<Return> = Return extends Effect.Effect<infer H, any, any> ? H : Return;
807
+
808
+ /** Accept the expected node's handler collection once every capability is implemented. */
809
+ type ValidateHandlers<H, N extends Node> =
810
+ H extends HandlerCollection<N, any, infer Handled, any>
811
+ ? [Exclude<Names<N>, Handled>] extends [never]
812
+ ? H
813
+ : `Capability not handled: ${Exclude<Names<N>, Handled> & string}`
814
+ : "Must return the handlers of this plugin or group";
815
+
816
+ /**
817
+ * Accept the plugin's handlers, or an Effect building them, once every
818
+ * declared capability is implemented.
819
+ *
820
+ * @since 0.6.0
821
+ * @category type-level
822
+ */
823
+ export type ValidateReturn<Return, N extends Node> = [ValidateHandlers<Built<Return>, N>] extends [
824
+ HandlerCollection<any, any, any, any>,
825
+ ]
826
+ ? Return
827
+ : ValidateHandlers<Built<Return>, N>;
828
+
829
+ /**
830
+ * Services the handlers need, including those needed to build them.
831
+ *
832
+ * @since 0.6.0
833
+ * @category type-level
834
+ */
835
+ export type Requirements<Return> =
836
+ Return extends Effect.Effect<HandlerCollection<any, any, any, infer R>, any, infer RX>
837
+ ? R | RX
838
+ : Return extends HandlerCollection<any, any, any, infer R>
839
+ ? R
840
+ : never;
841
+
842
+ /**
843
+ * Failures raised while building the handlers.
844
+ *
845
+ * @since 0.6.0
846
+ * @category type-level
847
+ */
848
+ export type BuildError<Return> = Return extends Effect.Effect<any, infer E, any> ? E : never;
849
+
850
+ const makeHandlers = (
851
+ actions: Readonly<Record<string, unknown>>,
852
+ triggers: Readonly<Record<string, unknown>>,
853
+ agentChannels: Readonly<Record<string, unknown>> = {},
854
+ ): HandlerCollection<any, any> =>
855
+ ({
856
+ action: (name: string, handler: unknown) =>
857
+ makeHandlers({ ...actions, [name]: handler }, triggers, agentChannels),
858
+ actions,
859
+ agentChannels,
860
+ channel: (name: string, lifecycle: unknown) =>
861
+ makeHandlers(actions, triggers, { ...agentChannels, [name]: lifecycle }),
862
+ group: (
863
+ name: string,
864
+ build: (handlers: HandlerCollection<any, any>) => HandlerCollection<any, any>,
865
+ ) => {
866
+ const group = build(makeHandlers({}, {}));
867
+ return makeHandlers(
868
+ { ...actions, [name]: group.actions },
869
+ { ...triggers, [name]: group.triggers },
870
+ agentChannels,
871
+ );
872
+ },
873
+ trigger: (name: string, lifecycle: unknown) =>
874
+ makeHandlers(actions, { ...triggers, [name]: lifecycle }, agentChannels),
875
+ triggers,
876
+ withTriggers: (lifecycles: Readonly<Record<string, unknown>>) =>
877
+ makeHandlers(actions, { ...triggers, ...lifecycles }, agentChannels),
878
+ }) as never;
879
+
880
+ /**
881
+ * Handler collection passed to {@link toLayer}.
882
+ *
883
+ * @since 0.6.0
884
+ * @category handlers
885
+ */
886
+ export type Handlers<P extends Any> = HandlerCollection<P, P["authMethods"]>;
887
+
888
+ /**
889
+ * Implement a plugin's capabilities. `build` registers one handler per action,
890
+ * trigger and Channel; the Layer is rejected until every capability is implemented.
891
+ *
892
+ * @since 0.6.0
893
+ * @category handlers
894
+ */
895
+ export const toLayer = <P extends Any, Return>(
896
+ plugin: P,
897
+ build: (handlers: Handlers<P>) => ValidateReturn<Return, P>,
898
+ ): Layer.Layer<
899
+ HandlerServices<P>,
900
+ BuildError<Return>,
901
+ Exclude<Requirements<Return>, Scope.Scope>
902
+ > =>
903
+ Layer.effectContext(
904
+ Effect.gen(function* () {
905
+ const result = build(makeHandlers({}, {})) as unknown;
906
+ const handlers = (Effect.isEffect(result) ? yield* result : result) as HandlerCollection<
907
+ any,
908
+ any
909
+ >;
910
+ return yield* toHandlers(plugin, handlers);
911
+ }),
912
+ ) as never;