@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/index.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @since 0.0.0
3
+ */
4
+
5
+ /**
6
+ * Canonical action definition builders and types for the Dual SDK.
7
+ *
8
+ * @since 0.0.0
9
+ */
10
+ export * as Action from "./Action.ts";
11
+
12
+ export * as AgentChannel from "./AgentChannel.ts";
13
+
14
+ /**
15
+ * Replays operations that providers declare safe to replay. At-most-once delivery executes once;
16
+ * the durable host owns its replay prohibition. Use an isolated provider test installation: this
17
+ * suite performs provider effects. The host supplies stable keys; the plugin must recover the same
18
+ * external subscription and emit the same provider event keys, even for a fresh receipt key.
19
+ * Boundary rejection and feature/auth matrices are covered by the SDK's public runtime tests.
20
+ */
21
+ export * as AgentChannelConformance from "./AgentChannelConformance.ts";
22
+
23
+ export * as AgentChannelRuntime from "./AgentChannelRuntime.ts";
24
+
25
+ /**
26
+ * AI provider definitions for model authentication.
27
+ *
28
+ * AI providers share provider metadata and auth methods with Tools providers,
29
+ * but cannot contribute actions, triggers, or groups. Their identifier is a
30
+ * stable key matched exactly against Pi's `Model.provider` value.
31
+ *
32
+ * @since 0.0.0
33
+ */
34
+ export * as AiProvider from "./AiProvider.ts";
35
+
36
+ /**
37
+ * Canonical auth-method definition builders and types for the Dual SDK.
38
+ *
39
+ * A plugin's auth methods are shared by all its actions and triggers.
40
+ *
41
+ * @since 0.0.0
42
+ */
43
+ export * as AuthMethod from "./AuthMethod.ts";
44
+
45
+ /**
46
+ * Resolved auth method bindings exposed to action and trigger handlers.
47
+ *
48
+ * @since 0.0.0
49
+ */
50
+ export * as AuthMethodBinding from "./AuthMethodBinding.ts";
51
+
52
+ /**
53
+ * Credential decoding, hook execution, material refresh, and value resolution.
54
+ *
55
+ * @since 0.0.0
56
+ */
57
+ export * as AuthMethodExecution from "./AuthMethodExecution.ts";
58
+
59
+ /**
60
+ * Host-provided connection resolution contracts for SDK execution flows.
61
+ *
62
+ * @since 0.0.0
63
+ */
64
+ export * as ConnectionResolver from "./ConnectionResolver.ts";
65
+
66
+ /**
67
+ * Public SDK definition errors.
68
+ *
69
+ * @since 0.0.0
70
+ */
71
+ export * as DefinitionError from "./DefinitionError.ts";
72
+
73
+ /**
74
+ * Dual registry resolution and materialization.
75
+ *
76
+ * @since 0.0.0
77
+ */
78
+ export * as DualRegistry from "./DualRegistry.ts";
79
+
80
+ /**
81
+ * Dual action invocation, trigger lifecycle, and auth runtime.
82
+ *
83
+ * @since 0.0.0
84
+ */
85
+ export * as DualRuntime from "./DualRuntime.ts";
86
+
87
+ /**
88
+ * Files that actions take and return: a small reference in action data, with the bytes kept by the
89
+ * host and read or written through the host's file service.
90
+ *
91
+ * @since 0.1.0
92
+ */
93
+ export * as Files from "./Files.ts";
94
+
95
+ /**
96
+ * Canonical group definition builders and types for the Dual SDK.
97
+ *
98
+ * Groups are structural namespaces nested beneath providers or other groups.
99
+ *
100
+ * @since 0.0.0
101
+ */
102
+ export * as Group from "./Group.ts";
103
+
104
+ /**
105
+ * The HTTP runtime of generated API plugins: each operation's contract and call, request encoding,
106
+ * response decoding, continuation links, redirects and uploads.
107
+ *
108
+ * @since 0.0.0
109
+ */
110
+ export * as Http from "./Http.ts";
111
+
112
+ /**
113
+ * A Model Context Protocol client over Streamable HTTP: the session generated plugins call tools
114
+ * through and the CLI lists them with.
115
+ *
116
+ * @since 0.0.0
117
+ */
118
+ export * as Mcp from "./Mcp.ts";
119
+
120
+ /**
121
+ * Host service that refreshes OAuth2 tokens for SDK auth execution.
122
+ *
123
+ * @since 0.0.0
124
+ */
125
+ export * as OAuth2TokenRefresher from "./OAuth2TokenRefresher.ts";
126
+
127
+ /**
128
+ * Canonical plugin definition builders and types for the Dual SDK.
129
+ *
130
+ * A plugin is one integration: its auth methods, actions, triggers and Agent Channels.
131
+ *
132
+ * @since 0.0.0
133
+ */
134
+ export * as Plugin from "./Plugin.ts";
135
+
136
+ /**
137
+ * Data-only SDK lifecycle requests for an installed, isolated integration plugin.
138
+ *
139
+ * @since 0.3.0
140
+ */
141
+ export * as RemoteRuntime from "./remote-runtime.ts";
142
+
143
+ /**
144
+ * JSON-safe file results that hosts can persist without reconstructing displayed text.
145
+ *
146
+ * @since 0.0.0
147
+ */
148
+ export * as ToolFile from "./ToolFile.ts";
149
+
150
+ /**
151
+ * Canonical trigger definition builders, runtime contracts, and execution helpers for the Dual SDK.
152
+ *
153
+ * @since 0.0.0
154
+ */
155
+ export * as Trigger from "./Trigger.ts";
156
+
157
+ export * as PluginRuntime from "./PluginRuntime.ts";
158
+ export * as OAuthFlow from "./OAuthFlow.ts";
@@ -0,0 +1,27 @@
1
+ import type * as AuthMethod from "../AuthMethod.ts";
2
+ import type * as AuthMethodBinding from "../AuthMethodBinding.ts";
3
+
4
+ /**
5
+ * Runtime handler context for a known effective provider auth-method set.
6
+ *
7
+ * Empty method sets erase auth. Concrete non-empty tuples require auth. Widened
8
+ * arrays remain optional because their emptiness is not statically known.
9
+ */
10
+ export type ContextForMethods<Methods extends readonly AuthMethod.Any[]> =
11
+ Methods extends readonly []
12
+ ? {}
13
+ : number extends Methods["length"]
14
+ ? { readonly auth?: AuthMethodBinding.Context<Methods> }
15
+ : { readonly auth: AuthMethodBinding.Context<Methods> };
16
+
17
+ /**
18
+ * Handler context for one contract. Auth is required only when the contract
19
+ * requires it for certain; `requireAuth: false` or an unknown flag keeps it optional.
20
+ */
21
+ export type ContextForContract<Methods extends readonly AuthMethod.Any[], RequireAuth> = [
22
+ RequireAuth,
23
+ ] extends [true]
24
+ ? ContextForMethods<Methods>
25
+ : Methods extends readonly []
26
+ ? {}
27
+ : { readonly auth?: AuthMethodBinding.Context<Methods> };
@@ -0,0 +1,241 @@
1
+ import type { Schema } from "effect";
2
+ import { Effect } from "effect";
3
+
4
+ import type * as AuthMethod from "../AuthMethod.ts";
5
+ import * as AuthMethodBinding from "../AuthMethodBinding.ts";
6
+ import * as AuthMethodExecution from "../AuthMethodExecution.ts";
7
+ import {
8
+ type ConnectionReauthorizationRequiredError,
9
+ type ConnectionRequiredError,
10
+ type ConnectionSelectionRequiredError,
11
+ type ConnectionResolverStoreError,
12
+ type InvalidConnectionIdError,
13
+ ConnectionIdNotAllowedError,
14
+ ConnectionResolver,
15
+ type ConnectionResolutionContext,
16
+ type ConnectionTargetKind,
17
+ InvalidResolvedConnectionError,
18
+ } from "../ConnectionResolver.ts";
19
+
20
+ import { decodeJson } from "./schema-runtime.ts";
21
+
22
+ export type NonEmptyReadonlyArray<A> = readonly [A, ...A[]];
23
+
24
+ export type AuthSchemaRequirements<Methods extends readonly AuthMethod.Any[]> =
25
+ Methods[number] extends infer Method
26
+ ? Method extends AuthMethod.Any
27
+ ? Schema.Codec.DecodingServices<AuthMethod.ValueSchema<Method>>
28
+ : never
29
+ : never;
30
+
31
+ /**
32
+ * Everything connection resolution may touch for one of the allowed methods:
33
+ * setup/material/value decoding plus the method's hook requirements.
34
+ *
35
+ * Hook dependencies are app-defined services a host cannot know about (a
36
+ * plugin's API client, a token issuer); surfacing them here keeps them in the
37
+ * requirement algebra so they are either discharged at the plugin/app
38
+ * definition via `provide` or demanded generically from whoever invokes.
39
+ */
40
+ export type AuthResolutionRequirements<Methods extends readonly AuthMethod.Any[]> =
41
+ Methods[number] extends infer Method
42
+ ? Method extends AuthMethod.Any
43
+ ?
44
+ | (AuthMethod.SetupSchema<Method> extends Schema.Top
45
+ ? Schema.Codec.DecodingServices<AuthMethod.SetupSchema<Method>>
46
+ : never)
47
+ | Schema.Codec.DecodingServices<AuthMethod.MaterialSchema<Method>>
48
+ | Schema.Codec.DecodingServices<AuthMethod.ValueSchema<Method>>
49
+ | AuthMethod.Requirements<Method>
50
+ : never
51
+ : never;
52
+
53
+ export type RuntimeConnectionErrorsFor<Methods extends readonly AuthMethod.Any[]> =
54
+ | ConnectionIdNotAllowedError
55
+ | ([Methods[number]] extends [never]
56
+ ? never
57
+ :
58
+ | ConnectionRequiredError
59
+ | ConnectionSelectionRequiredError
60
+ | InvalidConnectionIdError
61
+ | InvalidResolvedConnectionError
62
+ | ConnectionReauthorizationRequiredError
63
+ | ConnectionResolverStoreError);
64
+
65
+ const knownAuthMethodIds = (methods: readonly AuthMethod.Any[]) =>
66
+ methods.map(method => method.identifier);
67
+
68
+ const fullAuthMethodId = (providerId: string, method: AuthMethod.Any): string =>
69
+ `${providerId}/${method.identifier}`;
70
+
71
+ const findMethod = (
72
+ providerId: string,
73
+ methods: readonly AuthMethod.Any[],
74
+ methodId: string,
75
+ ): AuthMethod.Any | undefined =>
76
+ methods.find(
77
+ method => method.identifier === methodId || fullAuthMethodId(providerId, method) === methodId,
78
+ );
79
+
80
+ const invalidResolvedAuth = (
81
+ targetKind: ConnectionTargetKind,
82
+ targetId: string,
83
+ phase: string | undefined,
84
+ providerId: string,
85
+ methodId: string,
86
+ description: string,
87
+ ): InvalidResolvedConnectionError =>
88
+ new InvalidResolvedConnectionError({
89
+ description,
90
+ methodId,
91
+ phase,
92
+ providerId,
93
+ targetId,
94
+ targetKind,
95
+ });
96
+
97
+ const makeAuthContext: <Methods extends readonly AuthMethod.Any[]>(
98
+ binding: AuthMethodBinding.BindingFor<Methods>,
99
+ ) => AuthMethodBinding.Context<Methods> = (binding: AuthMethodBinding.Binding) => {
100
+ const context = {
101
+ current: binding,
102
+ is(
103
+ methodOrBinding: AuthMethod.Any | AuthMethodBinding.Binding,
104
+ maybeMethod?: AuthMethod.Any,
105
+ ): boolean {
106
+ return maybeMethod === undefined
107
+ ? AuthMethodBinding.isBindingForMethod(context.current, methodOrBinding as AuthMethod.Any)
108
+ : AuthMethodBinding.isBindingForMethod(
109
+ methodOrBinding as AuthMethodBinding.Binding,
110
+ maybeMethod,
111
+ );
112
+ },
113
+ match(cases: Record<string, (binding: AuthMethodBinding.Binding) => unknown>) {
114
+ return cases[context.current.methodId]!(context.current);
115
+ },
116
+ };
117
+
118
+ return context as never;
119
+ };
120
+
121
+ export const noAuthRuntimeContext = (): Record<never, never> => ({});
122
+
123
+ export const authRuntimeContext = <Methods extends readonly AuthMethod.Any[]>(
124
+ binding: AuthMethodBinding.BindingFor<Methods>,
125
+ ): { readonly auth: AuthMethodBinding.Context<Methods> } => ({ auth: makeAuthContext(binding) });
126
+
127
+ export const toNonEmptyArray = <A>(values: readonly A[]): NonEmptyReadonlyArray<A> | undefined =>
128
+ values.length === 0 ? undefined : [values[0]!, ...values.slice(1)];
129
+
130
+ export const failConnectionIdNotAllowed = (
131
+ targetKind: ConnectionTargetKind,
132
+ targetId: string,
133
+ phase: string | undefined,
134
+ providerId: string,
135
+ connectionId: string,
136
+ ): Effect.Effect<never, ConnectionIdNotAllowedError> =>
137
+ Effect.fail(
138
+ new ConnectionIdNotAllowedError({ connectionId, phase, providerId, targetId, targetKind }),
139
+ );
140
+
141
+ export const resolveRuntimeConnection: <Methods extends NonEmptyReadonlyArray<AuthMethod.Any>>(
142
+ targetKind: ConnectionTargetKind,
143
+ targetId: string,
144
+ phase: string | undefined,
145
+ providerId: string,
146
+ methods: Methods,
147
+ connectionId: string | undefined,
148
+ context: ConnectionResolutionContext | undefined,
149
+ ) => Effect.Effect<
150
+ AuthMethodBinding.BindingFor<Methods>,
151
+ | ConnectionRequiredError
152
+ | ConnectionSelectionRequiredError
153
+ | InvalidConnectionIdError
154
+ | InvalidResolvedConnectionError
155
+ | ConnectionReauthorizationRequiredError
156
+ | ConnectionResolverStoreError,
157
+ ConnectionResolver.Service | AuthSchemaRequirements<Methods> | AuthResolutionRequirements<Methods>
158
+ > = (
159
+ targetKind: ConnectionTargetKind,
160
+ targetId: string,
161
+ phase: string | undefined,
162
+ providerId: string,
163
+ methods: NonEmptyReadonlyArray<AuthMethod.Any>,
164
+ connectionId: string | undefined,
165
+ context: ConnectionResolutionContext | undefined,
166
+ ) =>
167
+ Effect.gen(function* () {
168
+ const resolver = yield* ConnectionResolver.Service;
169
+ const resolved = yield* resolver.resolve({
170
+ target: { providerId, targetId, targetKind, ...(phase === undefined ? {} : { phase }) },
171
+ ...(connectionId === undefined ? {} : { connectionId }),
172
+ ...(context === undefined ? {} : { context }),
173
+ });
174
+
175
+ if (resolved.providerId !== providerId) {
176
+ return yield* invalidResolvedAuth(
177
+ targetKind,
178
+ targetId,
179
+ phase,
180
+ providerId,
181
+ resolved.methodId,
182
+ `expected provider "${providerId}", got "${resolved.providerId}"`,
183
+ );
184
+ }
185
+
186
+ const method = findMethod(providerId, methods, resolved.methodId);
187
+ if (method === undefined) {
188
+ return yield* invalidResolvedAuth(
189
+ targetKind,
190
+ targetId,
191
+ phase,
192
+ providerId,
193
+ resolved.methodId,
194
+ `method "${resolved.methodId}" is not registered for provider "${providerId}"; known methods: ${knownAuthMethodIds(methods).join(", ")}`,
195
+ );
196
+ }
197
+
198
+ const setup = yield* (
199
+ resolved.encoding === "json" && method.setup !== undefined
200
+ ? decodeJson(method.setup, resolved.setup)
201
+ : AuthMethodExecution.decodeSetup(method, resolved.setup)
202
+ ).pipe(
203
+ Effect.mapError(error =>
204
+ invalidResolvedAuth(
205
+ targetKind,
206
+ targetId,
207
+ phase,
208
+ providerId,
209
+ method.identifier,
210
+ error.message,
211
+ ),
212
+ ),
213
+ );
214
+ const value = yield* (
215
+ resolved.encoding === "json"
216
+ ? decodeJson(method.material, resolved.material).pipe(
217
+ Effect.flatMap(material =>
218
+ AuthMethodExecution.resolveDecoded(method, material, { setup }),
219
+ ),
220
+ )
221
+ : AuthMethodExecution.resolveUnknown(method, resolved.material, { setup })
222
+ ).pipe(
223
+ Effect.mapError(error =>
224
+ invalidResolvedAuth(
225
+ targetKind,
226
+ targetId,
227
+ phase,
228
+ providerId,
229
+ method.identifier,
230
+ error instanceof Error ? error.message : String(error),
231
+ ),
232
+ ),
233
+ );
234
+
235
+ return AuthMethodBinding.makeBinding({
236
+ connectionId: resolved.connectionId,
237
+ method,
238
+ providerId,
239
+ value,
240
+ });
241
+ }) as never;
@@ -0,0 +1,20 @@
1
+ import type * as AuthMethod from "../AuthMethod.ts";
2
+ import { DuplicateAuthMethodIdentifierError } from "../DefinitionError.ts";
3
+
4
+ export const assertNoDuplicateAuthMethodIdentifiers = (
5
+ providerIdentifier: string,
6
+ methods: readonly AuthMethod.Any[],
7
+ ): void => {
8
+ const seen = new Set<string>();
9
+
10
+ for (const method of methods) {
11
+ if (seen.has(method.identifier)) {
12
+ throw new DuplicateAuthMethodIdentifierError({
13
+ identifier: method.identifier,
14
+ providerIdentifier,
15
+ });
16
+ }
17
+
18
+ seen.add(method.identifier);
19
+ }
20
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Shared provider-tree traversal for capability nodes.
3
+ *
4
+ * Actions and triggers live in the same structural tree (provider -> nested
5
+ * groups) and need identical machine-id derivation, so the recursive walk
6
+ * lives here once and callers pick the node kind via `select`.
7
+ */
8
+ import type * as Action from "../Action.ts";
9
+ import type * as Group from "../Group.ts";
10
+ import type * as Trigger from "../Trigger.ts";
11
+
12
+ import { MachineIdSeparator } from "./identifier.ts";
13
+
14
+ type StructuralOwner = {
15
+ readonly groups: readonly Group.Any[];
16
+ readonly actions: readonly Action.Any[];
17
+ readonly triggers: readonly Trigger.Any[];
18
+ };
19
+
20
+ export type WalkedCapability<Node> = {
21
+ /** Machine id relative to the provider (group path included). */
22
+ readonly localId: string;
23
+ /** Machine id including the provider prefix. */
24
+ readonly canonicalId: string;
25
+ readonly node: Node;
26
+ };
27
+
28
+ export const walkCapabilities = <Node extends { readonly identifier: string }>(
29
+ provider: StructuralOwner & { readonly identifier: string },
30
+ select: (owner: StructuralOwner) => readonly Node[],
31
+ ): readonly WalkedCapability<Node>[] => {
32
+ const entries: WalkedCapability<Node>[] = [];
33
+
34
+ const visit = (owner: StructuralOwner, prefix: string): void => {
35
+ for (const node of select(owner)) {
36
+ const localId =
37
+ prefix === "" ? node.identifier : `${prefix}${MachineIdSeparator}${node.identifier}`;
38
+ entries.push({
39
+ canonicalId: `${provider.identifier}${MachineIdSeparator}${localId}`,
40
+ localId,
41
+ node,
42
+ });
43
+ }
44
+
45
+ for (const group of owner.groups) {
46
+ visit(
47
+ group,
48
+ prefix === "" ? group.identifier : `${prefix}${MachineIdSeparator}${group.identifier}`,
49
+ );
50
+ }
51
+ };
52
+
53
+ visit(provider, "");
54
+ return entries;
55
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Shared structural-children machinery for Group and Plugin builders.
3
+ *
4
+ * Both owners accept the same mixed child set (groups, actions, triggers) and
5
+ * need identical type-level partitioning, runtime partitioning, and collision
6
+ * checks, so the logic lives here once.
7
+ */
8
+ import * as Action from "../Action.ts";
9
+ import { BuilderCollisionError } from "../DefinitionError.ts";
10
+ import * as Group from "../Group.ts";
11
+ import * as Trigger from "../Trigger.ts";
12
+
13
+ export type StructuralChild = Group.Any | Action.Any | Trigger.Any;
14
+
15
+ export type StructuralChildKind = "group" | "action" | "trigger" | "agent-channel";
16
+
17
+ /**
18
+ * Keep the children assignable to `Target`, preserving tuple order.
19
+ */
20
+ export type FilterChildren<
21
+ Children extends readonly StructuralChild[],
22
+ Target,
23
+ > = Children extends readonly [infer Head, ...infer Tail extends readonly StructuralChild[]]
24
+ ? Head extends Target
25
+ ? readonly [Head, ...FilterChildren<Tail, Target>]
26
+ : FilterChildren<Tail, Target>
27
+ : readonly [];
28
+
29
+ export type PartitionedChildren = {
30
+ readonly groups: readonly Group.Any[];
31
+ readonly actions: readonly Action.Any[];
32
+ readonly triggers: readonly Trigger.Any[];
33
+ };
34
+
35
+ export const partitionChildren = (children: readonly StructuralChild[]): PartitionedChildren => ({
36
+ actions: children.filter(Action.isAction),
37
+ groups: children.filter(Group.isGroup),
38
+ triggers: children.filter(Trigger.isTrigger),
39
+ });
40
+
41
+ const childKind = (child: StructuralChild): StructuralChildKind =>
42
+ Group.isGroup(child) ? "group" : Action.isAction(child) ? "action" : "trigger";
43
+
44
+ type IdentifiedChild = { readonly identifier: string };
45
+
46
+ type ExistingChildren = {
47
+ readonly groups: readonly IdentifiedChild[];
48
+ readonly actions: readonly IdentifiedChild[];
49
+ readonly triggers: readonly IdentifiedChild[];
50
+ readonly agentChannels?: readonly IdentifiedChild[];
51
+ };
52
+
53
+ const seenChildren = (existing: ExistingChildren): Map<string, StructuralChildKind> => {
54
+ const seen = new Map<string, StructuralChildKind>();
55
+
56
+ for (const group of existing.groups) {
57
+ seen.set(group.identifier, "group");
58
+ }
59
+
60
+ for (const action of existing.actions) {
61
+ seen.set(action.identifier, "action");
62
+ }
63
+
64
+ for (const trigger of existing.triggers) {
65
+ seen.set(trigger.identifier, "trigger");
66
+ }
67
+
68
+ for (const channel of existing.agentChannels ?? []) seen.set(channel.identifier, "agent-channel");
69
+ return seen;
70
+ };
71
+
72
+ const assertAdditions = (
73
+ ownerKind: "group" | "plugin",
74
+ ownerIdentifier: string,
75
+ seen: Map<string, StructuralChildKind>,
76
+ additions: readonly (readonly [IdentifiedChild, StructuralChildKind])[],
77
+ ): void => {
78
+ for (const [addition, kind] of additions) {
79
+ const existingChildKind = seen.get(addition.identifier);
80
+
81
+ if (existingChildKind !== undefined) {
82
+ throw new BuilderCollisionError({
83
+ childKind: kind,
84
+ existingChildKind,
85
+ identifier: addition.identifier,
86
+ ownerIdentifier,
87
+ ownerKind,
88
+ });
89
+ }
90
+
91
+ seen.set(addition.identifier, kind);
92
+ }
93
+ };
94
+
95
+ export const assertUniqueStructuralChildIdentifiers = (
96
+ ownerKind: "group" | "plugin",
97
+ ownerIdentifier: string,
98
+ existing: ExistingChildren,
99
+ additions: readonly IdentifiedChild[],
100
+ childKind: StructuralChildKind,
101
+ ): void => {
102
+ assertAdditions(
103
+ ownerKind,
104
+ ownerIdentifier,
105
+ seenChildren(existing),
106
+ additions.map(addition => [addition, childKind] as const),
107
+ );
108
+ };
109
+
110
+ /**
111
+ * Mixed-kind variant used by `add`: additions are checked in declaration order
112
+ * so a collision between two added children of different kinds is still caught.
113
+ */
114
+ export const assertUniqueMixedChildIdentifiers = (
115
+ ownerKind: "group" | "plugin",
116
+ ownerIdentifier: string,
117
+ existing: ExistingChildren,
118
+ additions: readonly StructuralChild[],
119
+ ): void => {
120
+ assertAdditions(
121
+ ownerKind,
122
+ ownerIdentifier,
123
+ seenChildren(existing),
124
+ additions.map(addition => [addition, childKind(addition)] as const),
125
+ );
126
+ };
@@ -0,0 +1,8 @@
1
+ export const appendElements = <
2
+ Existing extends readonly unknown[],
3
+ Added extends readonly unknown[],
4
+ >(
5
+ existing: Existing,
6
+ additions: Added,
7
+ ): readonly [...Existing, ...Added] =>
8
+ [...existing, ...additions] as readonly [...Existing, ...Added];