@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.
- package/LICENSE +4 -0
- package/README.md +19 -2
- package/dist/Action.d.ts +201 -0
- package/dist/Action.d.ts.map +1 -0
- package/dist/Action.js +172 -0
- package/dist/Action.js.map +1 -0
- package/dist/AgentChannel.d.ts +1717 -0
- package/dist/AgentChannel.d.ts.map +1 -0
- package/dist/AgentChannel.js +708 -0
- package/dist/AgentChannel.js.map +1 -0
- package/dist/AgentChannelConformance.d.ts +1543 -0
- package/dist/AgentChannelConformance.d.ts.map +1 -0
- package/dist/AgentChannelConformance.js +163 -0
- package/dist/AgentChannelConformance.js.map +1 -0
- package/dist/AgentChannelRuntime.d.ts +78 -0
- package/dist/AgentChannelRuntime.d.ts.map +1 -0
- package/dist/AgentChannelRuntime.js +270 -0
- package/dist/AgentChannelRuntime.js.map +1 -0
- package/dist/AiProvider.d.ts +132 -0
- package/dist/AiProvider.d.ts.map +1 -0
- package/dist/AiProvider.js +100 -0
- package/dist/AiProvider.js.map +1 -0
- package/dist/AuthMethod.d.ts +381 -0
- package/dist/AuthMethod.d.ts.map +1 -0
- package/dist/AuthMethod.js +225 -0
- package/dist/AuthMethod.js.map +1 -0
- package/dist/AuthMethodBinding.d.ts +77 -0
- package/dist/AuthMethodBinding.d.ts.map +1 -0
- package/dist/AuthMethodBinding.js +31 -0
- package/dist/AuthMethodBinding.js.map +1 -0
- package/dist/AuthMethodExecution.d.ts +223 -0
- package/dist/AuthMethodExecution.d.ts.map +1 -0
- package/dist/AuthMethodExecution.js +277 -0
- package/dist/AuthMethodExecution.js.map +1 -0
- package/dist/ConnectionResolver.d.ts +219 -0
- package/dist/ConnectionResolver.d.ts.map +1 -0
- package/dist/ConnectionResolver.js +153 -0
- package/dist/ConnectionResolver.js.map +1 -0
- package/dist/DefinitionError.d.ts +67 -0
- package/dist/DefinitionError.d.ts.map +1 -0
- package/dist/DefinitionError.js +82 -0
- package/dist/DefinitionError.js.map +1 -0
- package/dist/DualRegistry.d.ts +71 -0
- package/dist/DualRegistry.d.ts.map +1 -0
- package/dist/DualRegistry.js +96 -0
- package/dist/DualRegistry.js.map +1 -0
- package/dist/DualRuntime.d.ts +279 -0
- package/dist/DualRuntime.d.ts.map +1 -0
- package/dist/DualRuntime.js +311 -0
- package/dist/DualRuntime.js.map +1 -0
- package/dist/Files.d.ts +80 -0
- package/dist/Files.d.ts.map +1 -0
- package/dist/Files.js +66 -0
- package/dist/Files.js.map +1 -0
- package/dist/Group.d.ts +106 -0
- package/dist/Group.d.ts.map +1 -0
- package/dist/Group.js +86 -0
- package/dist/Group.js.map +1 -0
- package/dist/Http.d.ts +342 -0
- package/dist/Http.d.ts.map +1 -0
- package/dist/Http.js +677 -0
- package/dist/Http.js.map +1 -0
- package/dist/Mcp.d.ts +109 -0
- package/dist/Mcp.d.ts.map +1 -0
- package/dist/Mcp.js +176 -0
- package/dist/Mcp.js.map +1 -0
- package/dist/OAuth2TokenRefresher.d.ts +18 -0
- package/dist/OAuth2TokenRefresher.d.ts.map +1 -0
- package/dist/OAuth2TokenRefresher.js +19 -0
- package/dist/OAuth2TokenRefresher.js.map +1 -0
- package/dist/OAuthFlow.d.ts +71 -0
- package/dist/OAuthFlow.d.ts.map +1 -0
- package/dist/OAuthFlow.js +488 -0
- package/dist/OAuthFlow.js.map +1 -0
- package/dist/Plugin.d.ts +383 -0
- package/dist/Plugin.d.ts.map +1 -0
- package/dist/Plugin.js +249 -0
- package/dist/Plugin.js.map +1 -0
- package/dist/PluginRuntime.d.ts +3186 -0
- package/dist/PluginRuntime.d.ts.map +1 -0
- package/dist/PluginRuntime.js +331 -0
- package/dist/PluginRuntime.js.map +1 -0
- package/dist/Runner.d.ts +1 -0
- package/dist/Runner.js +124 -0
- package/dist/Runner.js.map +1 -0
- package/dist/ToolFile.d.ts +23 -0
- package/dist/ToolFile.d.ts.map +1 -0
- package/dist/ToolFile.js +33 -0
- package/dist/ToolFile.js.map +1 -0
- package/dist/Trigger.d.ts +358 -0
- package/dist/Trigger.d.ts.map +1 -0
- package/dist/Trigger.js +287 -0
- package/dist/Trigger.js.map +1 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +24 -0
- package/dist/internal/auth-context.d.ts +23 -0
- package/dist/internal/auth-context.d.ts.map +1 -0
- package/dist/internal/auth-context.js +1 -0
- package/dist/internal/auth-execution.d.ts +27 -0
- package/dist/internal/auth-execution.d.ts.map +1 -0
- package/dist/internal/auth-execution.js +66 -0
- package/dist/internal/auth-execution.js.map +1 -0
- package/dist/internal/auth-runtime.d.ts +5 -0
- package/dist/internal/auth-runtime.d.ts.map +1 -0
- package/dist/internal/auth-runtime.js +16 -0
- package/dist/internal/auth-runtime.js.map +1 -0
- package/dist/internal/capabilities.d.ts +24 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +22 -0
- package/dist/internal/capabilities.js.map +1 -0
- package/dist/internal/children.d.ts +34 -0
- package/dist/internal/children.d.ts.map +1 -0
- package/dist/internal/children.js +53 -0
- package/dist/internal/children.js.map +1 -0
- package/dist/internal/collection-utils.d.ts +4 -0
- package/dist/internal/collection-utils.d.ts.map +1 -0
- package/dist/internal/collection-utils.js +6 -0
- package/dist/internal/collection-utils.js.map +1 -0
- package/dist/internal/dual/actions.d.ts +33 -0
- package/dist/internal/dual/actions.d.ts.map +1 -0
- package/dist/internal/dual/actions.js +26 -0
- package/dist/internal/dual/actions.js.map +1 -0
- package/dist/internal/dual/invoke.d.ts +63 -0
- package/dist/internal/dual/invoke.d.ts.map +1 -0
- package/dist/internal/dual/invoke.js +75 -0
- package/dist/internal/dual/invoke.js.map +1 -0
- package/dist/internal/dual/registry.d.ts +125 -0
- package/dist/internal/dual/registry.d.ts.map +1 -0
- package/dist/internal/dual/registry.js +172 -0
- package/dist/internal/dual/registry.js.map +1 -0
- package/dist/internal/dual/triggers.d.ts +29 -0
- package/dist/internal/dual/triggers.d.ts.map +1 -0
- package/dist/internal/dual/triggers.js +26 -0
- package/dist/internal/dual/triggers.js.map +1 -0
- package/dist/internal/handler-context.d.ts +9 -0
- package/dist/internal/handler-context.d.ts.map +1 -0
- package/dist/internal/handler-context.js +31 -0
- package/dist/internal/handler-context.js.map +1 -0
- package/dist/internal/identifier.d.ts +6 -0
- package/dist/internal/identifier.d.ts.map +1 -0
- package/dist/internal/identifier.js +28 -0
- package/dist/internal/identifier.js.map +1 -0
- package/dist/internal/plugin-handlers.d.ts +38 -0
- package/dist/internal/plugin-handlers.d.ts.map +1 -0
- package/dist/internal/plugin-handlers.js +52 -0
- package/dist/internal/plugin-handlers.js.map +1 -0
- package/dist/internal/proto.d.ts +7 -0
- package/dist/internal/proto.d.ts.map +1 -0
- package/dist/internal/proto.js +10 -0
- package/dist/internal/proto.js.map +1 -0
- package/dist/internal/schema-runtime.d.ts +6 -0
- package/dist/internal/schema-runtime.d.ts.map +1 -0
- package/dist/internal/schema-runtime.js +10 -0
- package/dist/internal/schema-runtime.js.map +1 -0
- package/dist/internal/trigger-runtime-shared.d.ts +41 -0
- package/dist/internal/trigger-runtime-shared.d.ts.map +1 -0
- package/dist/internal/trigger-runtime-shared.js +132 -0
- package/dist/internal/trigger-runtime-shared.js.map +1 -0
- package/dist/internal/trigger-runtime.d.ts +12 -0
- package/dist/internal/trigger-runtime.d.ts.map +1 -0
- package/dist/internal/trigger-runtime.js +95 -0
- package/dist/internal/trigger-runtime.js.map +1 -0
- package/dist/internal/types.d.ts +7 -0
- package/dist/internal/types.d.ts.map +1 -0
- package/dist/internal/types.js +1 -0
- package/dist/remote-runtime.d.ts +585 -0
- package/dist/remote-runtime.d.ts.map +1 -0
- package/dist/remote-runtime.js +367 -0
- package/dist/remote-runtime.js.map +1 -0
- package/package.json +55 -4
- package/src/Action.ts +370 -0
- package/src/AgentChannel.ts +860 -0
- package/src/AgentChannelConformance.ts +124 -0
- package/src/AgentChannelRuntime.ts +616 -0
- package/src/AiProvider.ts +255 -0
- package/src/AuthMethod.ts +954 -0
- package/src/AuthMethodBinding.ts +112 -0
- package/src/AuthMethodExecution.ts +542 -0
- package/src/ConnectionResolver.ts +265 -0
- package/src/DefinitionError.ts +115 -0
- package/src/DualRegistry.ts +152 -0
- package/src/DualRuntime.ts +1081 -0
- package/src/Files.ts +89 -0
- package/src/Group.ts +292 -0
- package/src/Http.ts +1205 -0
- package/src/Mcp.ts +299 -0
- package/src/OAuth2TokenRefresher.ts +22 -0
- package/src/OAuthFlow.ts +874 -0
- package/src/Plugin.ts +912 -0
- package/src/PluginRuntime.ts +414 -0
- package/src/Runner.ts +248 -0
- package/src/ToolFile.ts +28 -0
- package/src/Trigger.ts +722 -0
- package/src/index.ts +158 -0
- package/src/internal/auth-context.ts +27 -0
- package/src/internal/auth-execution.ts +241 -0
- package/src/internal/auth-runtime.ts +20 -0
- package/src/internal/capabilities.ts +55 -0
- package/src/internal/children.ts +126 -0
- package/src/internal/collection-utils.ts +8 -0
- package/src/internal/dual/actions.ts +116 -0
- package/src/internal/dual/invoke.ts +307 -0
- package/src/internal/dual/registry.ts +340 -0
- package/src/internal/dual/triggers.ts +128 -0
- package/src/internal/handler-context.ts +57 -0
- package/src/internal/identifier.ts +27 -0
- package/src/internal/plugin-handlers.ts +146 -0
- package/src/internal/proto.ts +8 -0
- package/src/internal/schema-runtime.ts +15 -0
- package/src/internal/trigger-runtime-shared.ts +449 -0
- package/src/internal/trigger-runtime.ts +223 -0
- package/src/internal/types.ts +12 -0
- package/src/remote-runtime.ts +469 -0
|
@@ -0,0 +1,1081 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual action invocation, trigger lifecycle, and auth runtime.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.0.0
|
|
5
|
+
*/
|
|
6
|
+
import {
|
|
7
|
+
Array,
|
|
8
|
+
Context,
|
|
9
|
+
Effect,
|
|
10
|
+
Function as Func,
|
|
11
|
+
Layer,
|
|
12
|
+
Predicate,
|
|
13
|
+
Result,
|
|
14
|
+
Schema,
|
|
15
|
+
pipe,
|
|
16
|
+
} from "effect";
|
|
17
|
+
|
|
18
|
+
import { type HttpClientRequest, Url } from "effect/http";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
ActionHandlerError,
|
|
22
|
+
RetryableActionHandlerError,
|
|
23
|
+
type ActionHandlerFailure,
|
|
24
|
+
} from "./Action.ts";
|
|
25
|
+
import * as AgentChannelRuntime from "./AgentChannelRuntime.ts";
|
|
26
|
+
import * as AiProvider from "./AiProvider.ts";
|
|
27
|
+
import type * as AuthMethod from "./AuthMethod.ts";
|
|
28
|
+
import * as AuthMethodBinding from "./AuthMethodBinding.ts";
|
|
29
|
+
import * as AuthMethodExecution from "./AuthMethodExecution.ts";
|
|
30
|
+
import {
|
|
31
|
+
ConnectionIdNotAllowedError,
|
|
32
|
+
ConnectionReauthorizationRequiredError,
|
|
33
|
+
ConnectionRequiredError,
|
|
34
|
+
ConnectionSelectionRequiredError,
|
|
35
|
+
ConnectionResolverStoreError,
|
|
36
|
+
InvalidConnectionIdError,
|
|
37
|
+
InvalidResolvedConnectionError,
|
|
38
|
+
type ConnectionResolutionContext,
|
|
39
|
+
} from "./ConnectionResolver.ts";
|
|
40
|
+
import { BuilderCollisionError } from "./DefinitionError.ts";
|
|
41
|
+
import { TriggerNotFoundError } from "./DualRegistry.ts";
|
|
42
|
+
import { authRuntimeContext } from "./internal/auth-execution.ts";
|
|
43
|
+
import { resolveAction, type PluginActions } from "./internal/dual/actions.ts";
|
|
44
|
+
import {
|
|
45
|
+
ActionNotFoundError,
|
|
46
|
+
FieldIssue,
|
|
47
|
+
InvalidActionInputError,
|
|
48
|
+
InvalidActionOutputError,
|
|
49
|
+
ActionHandlerNotFoundError,
|
|
50
|
+
type InvokeError,
|
|
51
|
+
type InvokeMeta,
|
|
52
|
+
type InvokeRequest,
|
|
53
|
+
type InvokeRequirementsFor,
|
|
54
|
+
type InvokeSuccess,
|
|
55
|
+
type PluginRequirements,
|
|
56
|
+
invokeResolvedAction,
|
|
57
|
+
invokeResolvedActionJson,
|
|
58
|
+
} from "./internal/dual/invoke.ts";
|
|
59
|
+
import { resolveRuntimeTrigger } from "./internal/dual/triggers.ts";
|
|
60
|
+
import { indexGroups } from "./internal/handler-context.ts";
|
|
61
|
+
import type * as Plugin from "./Plugin.ts";
|
|
62
|
+
import * as Trigger from "./Trigger.ts";
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* One installed plugin release.
|
|
66
|
+
*
|
|
67
|
+
* @since 0.7.0
|
|
68
|
+
* @category models
|
|
69
|
+
*/
|
|
70
|
+
export interface PluginInstallation<P extends Plugin.Any = Plugin.Any> {
|
|
71
|
+
readonly plugin: P;
|
|
72
|
+
/** The plugin's package version. */
|
|
73
|
+
readonly version: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The plugin releases and AI providers one runtime executes, with the services their handlers need.
|
|
78
|
+
*
|
|
79
|
+
* @since 0.7.0
|
|
80
|
+
* @category models
|
|
81
|
+
*/
|
|
82
|
+
export interface App<
|
|
83
|
+
out P extends Plugin.Any = Plugin.Any,
|
|
84
|
+
LayerOutput = never,
|
|
85
|
+
out LayerError = never,
|
|
86
|
+
out LayerRequirements = never,
|
|
87
|
+
out AiProviders extends readonly AiProvider.Any[] = readonly AiProvider.Any[],
|
|
88
|
+
> {
|
|
89
|
+
readonly identifier: string;
|
|
90
|
+
readonly aiProviders: AiProviders;
|
|
91
|
+
readonly plugins: readonly PluginInstallation<P>[];
|
|
92
|
+
readonly layer: Layer.Layer<LayerOutput, LayerError, LayerRequirements>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Constraint type for working with any {@link App}.
|
|
97
|
+
*
|
|
98
|
+
* @since 0.7.0
|
|
99
|
+
* @category type-level
|
|
100
|
+
*/
|
|
101
|
+
export interface AnyApp extends App<Plugin.Any, never, any, any, readonly AiProvider.Any[]> {}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Install one plugin release.
|
|
105
|
+
*
|
|
106
|
+
* @since 0.7.0
|
|
107
|
+
* @category constructors
|
|
108
|
+
*/
|
|
109
|
+
export const install = <P extends Plugin.Any>(
|
|
110
|
+
plugin: P,
|
|
111
|
+
version: string,
|
|
112
|
+
): PluginInstallation<P> => ({ plugin, version });
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The runtime of one plugin release, as the plugin's own process runs it. `version` defaults to
|
|
116
|
+
* `0.0.0` for tests that do not need the package version.
|
|
117
|
+
*
|
|
118
|
+
* @since 0.7.0
|
|
119
|
+
* @category constructors
|
|
120
|
+
*/
|
|
121
|
+
export const fromPlugin = <const P extends Plugin.Any>(
|
|
122
|
+
plugin: P,
|
|
123
|
+
version = "0.0.0",
|
|
124
|
+
): App<
|
|
125
|
+
P,
|
|
126
|
+
Plugin.LayerOutput<readonly [P]>,
|
|
127
|
+
Plugin.LayerError<readonly [P]>,
|
|
128
|
+
Plugin.LayerRequirements<readonly [P]>,
|
|
129
|
+
readonly []
|
|
130
|
+
> =>
|
|
131
|
+
({
|
|
132
|
+
aiProviders: [],
|
|
133
|
+
identifier: plugin.identifier,
|
|
134
|
+
layer: plugin.layer,
|
|
135
|
+
plugins: [install(plugin, version)],
|
|
136
|
+
}) as never;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* An app runtime without plugins, for {@link withPlugin} and {@link withPlugins} to fill.
|
|
140
|
+
*
|
|
141
|
+
* @since 0.7.0
|
|
142
|
+
* @category constructors
|
|
143
|
+
*/
|
|
144
|
+
export const make = (identifier: string): App<never, never, never, never, readonly []> => ({
|
|
145
|
+
aiProviders: [],
|
|
146
|
+
identifier,
|
|
147
|
+
layer: Layer.empty,
|
|
148
|
+
plugins: [],
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
type AppPlugin<A extends AnyApp> = A extends App<infer P, any, any, any, any> ? P : never;
|
|
152
|
+
type AppLayerOutput<A extends AnyApp> =
|
|
153
|
+
A extends App<any, infer Output, any, any, any> ? Output : never;
|
|
154
|
+
type AppLayerError<A extends AnyApp> =
|
|
155
|
+
A extends App<any, any, infer Error, any, any> ? Error : never;
|
|
156
|
+
type AppLayerRequirements<A extends AnyApp> =
|
|
157
|
+
A extends App<any, any, any, infer Requirements, any> ? Requirements : never;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Result of adding a plugin release to an app runtime. A named interface keeps declaration emit
|
|
161
|
+
* from expanding every plugin and layer member at large call sites.
|
|
162
|
+
*
|
|
163
|
+
* @since 0.7.0
|
|
164
|
+
* @category type-level
|
|
165
|
+
*/
|
|
166
|
+
export interface WithPlugin<Self extends AnyApp, Added extends Plugin.Any> extends App<
|
|
167
|
+
AppPlugin<Self> | Added,
|
|
168
|
+
AppLayerOutput<Self> | Plugin.LayerOutput<readonly [Added]>,
|
|
169
|
+
AppLayerError<Self> | Plugin.LayerError<readonly [Added]>,
|
|
170
|
+
| Plugin.LayerRequirements<readonly [Added]>
|
|
171
|
+
| Exclude<AppLayerRequirements<Self>, Plugin.LayerOutput<readonly [Added]>>,
|
|
172
|
+
Self["aiProviders"]
|
|
173
|
+
// oxlint-disable-next-line typescript/no-empty-object-type -- TypeScript declaration emit expands a type alias at every app call site (TS7056).
|
|
174
|
+
> {}
|
|
175
|
+
|
|
176
|
+
// Match the runtime's left-to-right provideMerge fold: later plugins can
|
|
177
|
+
// satisfy earlier requirements, but earlier outputs cannot satisfy later ones.
|
|
178
|
+
type BulkLayerRequirements<
|
|
179
|
+
Added extends readonly Plugin.Any[],
|
|
180
|
+
Requirements,
|
|
181
|
+
> = number extends Added["length"]
|
|
182
|
+
? Requirements | Layer.Services<Added[number]["layer"]>
|
|
183
|
+
: Added extends readonly [
|
|
184
|
+
infer Head extends Plugin.Any,
|
|
185
|
+
...infer Tail extends readonly Plugin.Any[],
|
|
186
|
+
]
|
|
187
|
+
? BulkLayerRequirements<
|
|
188
|
+
Tail,
|
|
189
|
+
Layer.Services<Head["layer"]> | Exclude<Requirements, Layer.Success<Head["layer"]>>
|
|
190
|
+
>
|
|
191
|
+
: Requirements;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Result of adding several plugin releases to an app runtime in one step.
|
|
195
|
+
*
|
|
196
|
+
* @since 0.7.0
|
|
197
|
+
* @category type-level
|
|
198
|
+
*/
|
|
199
|
+
export interface WithPlugins<Self extends AnyApp, Added extends readonly Plugin.Any[]> extends App<
|
|
200
|
+
AppPlugin<Self> | Added[number],
|
|
201
|
+
AppLayerOutput<Self> | Layer.Success<Added[number]["layer"]>,
|
|
202
|
+
AppLayerError<Self> | Layer.Error<Added[number]["layer"]>,
|
|
203
|
+
BulkLayerRequirements<Added, AppLayerRequirements<Self>>,
|
|
204
|
+
Self["aiProviders"]
|
|
205
|
+
// oxlint-disable-next-line typescript/no-empty-object-type -- TypeScript declaration emit expands a type alias at every app call site (TS7056).
|
|
206
|
+
> {}
|
|
207
|
+
|
|
208
|
+
/** A plugin and the package version of its release. */
|
|
209
|
+
type PluginEntry = readonly [plugin: Plugin.Any, version: string];
|
|
210
|
+
|
|
211
|
+
type PluginsOf<Entries extends readonly PluginEntry[]> = {
|
|
212
|
+
readonly [K in keyof Entries]: Entries[K][0];
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
function addPlugins(app: AnyApp, entries: readonly PluginEntry[]): AnyApp {
|
|
216
|
+
const seen = new Set(Array.map(app.plugins, installation => installation.plugin.identifier));
|
|
217
|
+
for (const [plugin] of entries) {
|
|
218
|
+
if (seen.has(plugin.identifier)) {
|
|
219
|
+
throw new BuilderCollisionError({
|
|
220
|
+
childKind: "plugin",
|
|
221
|
+
existingChildKind: "plugin",
|
|
222
|
+
identifier: plugin.identifier,
|
|
223
|
+
ownerIdentifier: app.identifier,
|
|
224
|
+
ownerKind: "dual",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
seen.add(plugin.identifier);
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
...app,
|
|
231
|
+
layer: Array.reduce(entries, app.layer, (layer, [plugin]) =>
|
|
232
|
+
Layer.provideMerge(layer, plugin.layer),
|
|
233
|
+
),
|
|
234
|
+
plugins: [
|
|
235
|
+
...app.plugins,
|
|
236
|
+
...Array.map(entries, ([plugin, version]) => install(plugin, version)),
|
|
237
|
+
],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Add one plugin release: the plugin and its package version.
|
|
243
|
+
*
|
|
244
|
+
* @since 0.7.0
|
|
245
|
+
* @category combinators
|
|
246
|
+
*/
|
|
247
|
+
export const withPlugin: {
|
|
248
|
+
<const P extends Plugin.Any>(
|
|
249
|
+
plugin: P,
|
|
250
|
+
version: string,
|
|
251
|
+
): <Self extends AnyApp>(app: Self) => WithPlugin<Self, P>;
|
|
252
|
+
<Self extends AnyApp, const P extends Plugin.Any>(
|
|
253
|
+
app: Self,
|
|
254
|
+
plugin: P,
|
|
255
|
+
version: string,
|
|
256
|
+
): WithPlugin<Self, P>;
|
|
257
|
+
} = Func.dual(3, (app: AnyApp, plugin: Plugin.Any, version: string) =>
|
|
258
|
+
addPlugins(app, [[plugin, version]]),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Add several plugin releases in one step, which keeps a large app's inferred type compact.
|
|
263
|
+
*
|
|
264
|
+
* @since 0.7.0
|
|
265
|
+
* @category combinators
|
|
266
|
+
*/
|
|
267
|
+
export const withPlugins: {
|
|
268
|
+
<const E extends readonly PluginEntry[]>(
|
|
269
|
+
entries: E,
|
|
270
|
+
): <Self extends AnyApp>(app: Self) => WithPlugins<Self, PluginsOf<E>>;
|
|
271
|
+
<Self extends AnyApp, const E extends readonly PluginEntry[]>(
|
|
272
|
+
app: Self,
|
|
273
|
+
entries: E,
|
|
274
|
+
): WithPlugins<Self, PluginsOf<E>>;
|
|
275
|
+
} = Func.dual(2, addPlugins);
|
|
276
|
+
|
|
277
|
+
const pluginOf = (app: AnyApp, identifier: string): Plugin.Any | undefined =>
|
|
278
|
+
app.plugins.find(installation => installation.plugin.identifier === identifier)?.plugin;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Request payload accepted by {@link invoke}.
|
|
282
|
+
*
|
|
283
|
+
* @since 0.0.0
|
|
284
|
+
* @category models
|
|
285
|
+
*/
|
|
286
|
+
export type { InvokeRequest };
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Metadata returned alongside successful invocation data.
|
|
290
|
+
*
|
|
291
|
+
* @since 0.0.0
|
|
292
|
+
* @category models
|
|
293
|
+
*/
|
|
294
|
+
export type { InvokeMeta };
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Successful invocation envelope returned by {@link invoke}.
|
|
298
|
+
*
|
|
299
|
+
* @since 0.0.0
|
|
300
|
+
* @category models
|
|
301
|
+
*/
|
|
302
|
+
export type { InvokeSuccess };
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Structured field-level validation issue surfaced by {@link InvalidActionInputError}.
|
|
306
|
+
*
|
|
307
|
+
* @since 0.0.0
|
|
308
|
+
* @category models
|
|
309
|
+
*/
|
|
310
|
+
export { FieldIssue };
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Raised when an invocation references an unknown action id.
|
|
314
|
+
*
|
|
315
|
+
* @since 0.0.0
|
|
316
|
+
* @category errors
|
|
317
|
+
*/
|
|
318
|
+
export { ActionNotFoundError };
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @since 0.0.0
|
|
322
|
+
* @category errors
|
|
323
|
+
*/
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Raised when invocation input fails schema validation.
|
|
327
|
+
*
|
|
328
|
+
* @since 0.0.0
|
|
329
|
+
* @category errors
|
|
330
|
+
*/
|
|
331
|
+
export { InvalidActionInputError };
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Raised when action handler output fails schema validation.
|
|
335
|
+
*
|
|
336
|
+
* @since 0.0.0
|
|
337
|
+
* @category errors
|
|
338
|
+
*/
|
|
339
|
+
export { InvalidActionOutputError };
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Raised when the runtime action handler service is missing.
|
|
343
|
+
*
|
|
344
|
+
* @since 0.0.0
|
|
345
|
+
* @category errors
|
|
346
|
+
*/
|
|
347
|
+
export { ActionHandlerNotFoundError };
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Non-retryable failure reported by a trusted action handler.
|
|
351
|
+
*
|
|
352
|
+
* @since 0.0.0
|
|
353
|
+
* @category errors
|
|
354
|
+
*/
|
|
355
|
+
export { ActionHandlerError };
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Retryable failure reported by a trusted action handler.
|
|
359
|
+
*
|
|
360
|
+
* @since 0.0.0
|
|
361
|
+
* @category errors
|
|
362
|
+
*/
|
|
363
|
+
export { RetryableActionHandlerError };
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Stable trusted action handler failure contract.
|
|
367
|
+
*
|
|
368
|
+
* @since 0.0.0
|
|
369
|
+
* @category errors
|
|
370
|
+
*/
|
|
371
|
+
export type { ActionHandlerFailure };
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Raised when an action does not accept a connection but a connectionId was still provided.
|
|
375
|
+
*
|
|
376
|
+
* @since 0.0.0
|
|
377
|
+
* @category errors
|
|
378
|
+
*/
|
|
379
|
+
export { ConnectionIdNotAllowedError };
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Raised when invocation requires a connection and no compatible binding is available.
|
|
383
|
+
*
|
|
384
|
+
* @since 0.0.0
|
|
385
|
+
* @category errors
|
|
386
|
+
*/
|
|
387
|
+
export { ConnectionRequiredError };
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Raised when several compatible connection bindings exist and the caller must choose one.
|
|
391
|
+
*
|
|
392
|
+
* @since 0.0.0
|
|
393
|
+
* @category errors
|
|
394
|
+
*/
|
|
395
|
+
export { ConnectionSelectionRequiredError };
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Raised when the provider refuses a connection's stored grant and the user must sign in again.
|
|
399
|
+
*
|
|
400
|
+
* @since 0.0.0
|
|
401
|
+
* @category errors
|
|
402
|
+
*/
|
|
403
|
+
export { ConnectionReauthorizationRequiredError };
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Raised when the host connection store is unavailable.
|
|
407
|
+
*
|
|
408
|
+
* @since 0.0.0
|
|
409
|
+
* @category errors
|
|
410
|
+
*/
|
|
411
|
+
export { ConnectionResolverStoreError };
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Raised when a connectionId does not resolve to a compatible binding.
|
|
415
|
+
*
|
|
416
|
+
* @since 0.0.0
|
|
417
|
+
* @category errors
|
|
418
|
+
*/
|
|
419
|
+
export { InvalidConnectionIdError };
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Raised when a resolved connection binding does not satisfy its auth method schema.
|
|
423
|
+
*
|
|
424
|
+
* @since 0.0.0
|
|
425
|
+
* @category errors
|
|
426
|
+
*/
|
|
427
|
+
export { InvalidResolvedConnectionError };
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Structured SDK-owned invocation failures.
|
|
431
|
+
*
|
|
432
|
+
* @since 0.0.0
|
|
433
|
+
* @category errors
|
|
434
|
+
*/
|
|
435
|
+
export type { InvokeError };
|
|
436
|
+
export type { InvokeRequirementsFor };
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Invoke an action directly in-process.
|
|
440
|
+
*
|
|
441
|
+
* The invocation boundary resolves an action by canonical machine id, validates
|
|
442
|
+
* unknown input against the resolved runtime schema, runs the handler, and
|
|
443
|
+
* returns a standard success envelope. This mirrors the server trusted invoke
|
|
444
|
+
* boundary: runtime schema validation is authoritative.
|
|
445
|
+
*
|
|
446
|
+
* @since 0.0.0
|
|
447
|
+
* @category execution
|
|
448
|
+
*/
|
|
449
|
+
const failInvokeActionNotFound = (
|
|
450
|
+
actionId: string,
|
|
451
|
+
): Effect.Effect<InvokeSuccess, ActionNotFoundError> =>
|
|
452
|
+
Effect.fail(new ActionNotFoundError({ actionId }));
|
|
453
|
+
|
|
454
|
+
const invokeRaw = <
|
|
455
|
+
const P extends Plugin.Any,
|
|
456
|
+
LayerOutput,
|
|
457
|
+
LayerError,
|
|
458
|
+
LayerRequirements,
|
|
459
|
+
const AiProviders extends readonly AiProvider.Any[],
|
|
460
|
+
>(
|
|
461
|
+
app: App<P, LayerOutput, LayerError, LayerRequirements, AiProviders>,
|
|
462
|
+
request: InvokeRequest,
|
|
463
|
+
json = false,
|
|
464
|
+
): Effect.Effect<InvokeSuccess, InvokeError, RuntimeRequirementsFor<P>> => {
|
|
465
|
+
const resolved = resolveAction(app.plugins, request.actionId, request.actionVersion);
|
|
466
|
+
|
|
467
|
+
if (
|
|
468
|
+
resolved === undefined ||
|
|
469
|
+
(request.pluginKey !== undefined && resolved.providerId !== request.pluginKey)
|
|
470
|
+
) {
|
|
471
|
+
return failInvokeActionNotFound(request.actionId);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// The runtime entry is resolved from erased plugin trees, so the precise
|
|
475
|
+
// per-action requirement union in the return annotation cannot be recovered
|
|
476
|
+
// here; the annotation is the contract, the cast is the boundary.
|
|
477
|
+
return (json ? invokeResolvedActionJson : invokeResolvedAction)(
|
|
478
|
+
resolved.providerId,
|
|
479
|
+
resolved.actionId,
|
|
480
|
+
resolved.pluginVersion,
|
|
481
|
+
resolved.action,
|
|
482
|
+
resolved.authMethods,
|
|
483
|
+
request,
|
|
484
|
+
) as never;
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
export const invoke = <
|
|
488
|
+
const P extends Plugin.Any,
|
|
489
|
+
LayerOutput,
|
|
490
|
+
LayerError,
|
|
491
|
+
LayerRequirements,
|
|
492
|
+
const AiProviders extends readonly AiProvider.Any[],
|
|
493
|
+
>(
|
|
494
|
+
app: App<P, LayerOutput, LayerError, LayerRequirements, AiProviders>,
|
|
495
|
+
request: InvokeRequest,
|
|
496
|
+
): Effect.Effect<
|
|
497
|
+
InvokeSuccess,
|
|
498
|
+
InvokeError | LayerError,
|
|
499
|
+
LayerRequirements | Exclude<RuntimeRequirementsFor<P>, LayerOutput>
|
|
500
|
+
> => invokeRaw(app, request).pipe(Effect.provide(app.layer));
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Runtime-first invocation request accepted by {@link Service}.
|
|
504
|
+
*
|
|
505
|
+
* @since 0.0.0
|
|
506
|
+
* @category models
|
|
507
|
+
*/
|
|
508
|
+
export type RuntimeInvokeRequest = InvokeRequest;
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Runtime-first trigger lifecycle request accepted by {@link Service}.
|
|
512
|
+
*
|
|
513
|
+
* @since 0.0.0
|
|
514
|
+
* @category models
|
|
515
|
+
*/
|
|
516
|
+
export interface RuntimeTriggerLifecycleRequest {
|
|
517
|
+
readonly triggerId: string;
|
|
518
|
+
readonly pluginKey?: string;
|
|
519
|
+
/** The exact plugin release the caller pinned; another installed release does not run. */
|
|
520
|
+
readonly triggerVersion?: string | undefined;
|
|
521
|
+
readonly config: unknown;
|
|
522
|
+
readonly state?: unknown | undefined;
|
|
523
|
+
readonly connectionId?: string | undefined;
|
|
524
|
+
readonly context?: ConnectionResolutionContext | undefined;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export interface RuntimeWebhookActivationRequest extends RuntimeTriggerLifecycleRequest {
|
|
528
|
+
readonly webhookUrl: string;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export interface RuntimeWebhookTriggerLifecycleRequest extends RuntimeWebhookActivationRequest {
|
|
532
|
+
readonly request: Trigger.WebhookRequest;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export interface AuthMethodRequest {
|
|
536
|
+
readonly providerId: string;
|
|
537
|
+
readonly authMethodId: string;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export interface DecodeAuthMethodSetupRequest extends AuthMethodRequest {
|
|
541
|
+
readonly encoding?: "json";
|
|
542
|
+
readonly setup: unknown;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export interface PrepareAuthMethodRequest extends AuthMethodRequest {
|
|
546
|
+
readonly input: unknown;
|
|
547
|
+
readonly setup: unknown;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export interface PrepareOAuth2TokensRequest extends AuthMethodRequest {
|
|
551
|
+
readonly tokens: AuthMethod.OAuth2TokenResponse;
|
|
552
|
+
readonly setup: unknown;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export interface RefreshAuthMethodRequest extends AuthMethodRequest {
|
|
556
|
+
readonly material: unknown;
|
|
557
|
+
readonly setup: unknown;
|
|
558
|
+
readonly now: number;
|
|
559
|
+
readonly minValidityMs: number;
|
|
560
|
+
readonly reason: AuthMethod.RefreshReason;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export interface ResolveAuthMethodRequest extends AuthMethodRequest {
|
|
564
|
+
/** `json` when the material and setup arrive in their canonical JSON encoding, as in the sandbox. */
|
|
565
|
+
readonly encoding?: "json";
|
|
566
|
+
readonly material: unknown;
|
|
567
|
+
readonly setup: unknown;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export interface ResolveAiModelConfigRequest extends ResolveAuthMethodRequest {
|
|
571
|
+
readonly model: AiProvider.RuntimeModel;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export class AuthMethodNotFoundError extends Schema.TaggedError<AuthMethodNotFoundError>()(
|
|
575
|
+
"AuthMethodNotFoundError",
|
|
576
|
+
{ authMethodId: Schema.String, providerId: Schema.String },
|
|
577
|
+
) {}
|
|
578
|
+
|
|
579
|
+
export type AuthMethodExecutionError =
|
|
580
|
+
| AuthMethodNotFoundError
|
|
581
|
+
| Schema.SchemaError
|
|
582
|
+
| AuthMethod.ExpectedOAuth2MethodError
|
|
583
|
+
| AuthMethod.OAuth2RefreshTokenUnavailableError
|
|
584
|
+
| AuthMethod.AuthMethodHookError;
|
|
585
|
+
|
|
586
|
+
export class AiProviderRuntimeNotConfiguredError extends Schema.TaggedError<AiProviderRuntimeNotConfiguredError>()(
|
|
587
|
+
"AiProviderRuntimeNotConfiguredError",
|
|
588
|
+
{ providerId: Schema.String },
|
|
589
|
+
) {}
|
|
590
|
+
|
|
591
|
+
export type AiModelConfigError = AuthMethodExecutionError | AiProviderRuntimeNotConfiguredError;
|
|
592
|
+
|
|
593
|
+
/** Raised when a provider declares no sandbox access. */
|
|
594
|
+
export class SandboxAccessNotDeclaredError extends Schema.TaggedError<SandboxAccessNotDeclaredError>()(
|
|
595
|
+
"SandboxAccessNotDeclaredError",
|
|
596
|
+
{ providerId: Schema.String },
|
|
597
|
+
) {}
|
|
598
|
+
|
|
599
|
+
/** Raised when the Connection may not be sent with a sandbox request: another origin, or a request the provider refuses. */
|
|
600
|
+
export class SandboxRequestNotAllowedError extends Schema.TaggedError<SandboxRequestNotAllowedError>()(
|
|
601
|
+
"SandboxRequestNotAllowedError",
|
|
602
|
+
{ origin: Schema.String, path: Schema.String, providerId: Schema.String },
|
|
603
|
+
) {}
|
|
604
|
+
|
|
605
|
+
export type SandboxAccessError = AuthMethodExecutionError | SandboxAccessNotDeclaredError;
|
|
606
|
+
|
|
607
|
+
/** One Connection's sandbox access: where its credential may go, and how a request gets it. */
|
|
608
|
+
export interface ResolvedSandboxAccess {
|
|
609
|
+
readonly origins: readonly string[];
|
|
610
|
+
readonly authorize: (
|
|
611
|
+
request: HttpClientRequest.HttpClientRequest,
|
|
612
|
+
) => Effect.Effect<HttpClientRequest.HttpClientRequest, SandboxRequestNotAllowedError>;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Runtime service materialized from an {@link App}.
|
|
617
|
+
*
|
|
618
|
+
* @since 0.0.0
|
|
619
|
+
* @category services
|
|
620
|
+
*/
|
|
621
|
+
export interface RuntimeService {
|
|
622
|
+
readonly executeAuthJson: (
|
|
623
|
+
request: AuthMethodExecution.JsonRequest,
|
|
624
|
+
) => Effect.Effect<unknown, AuthMethodExecutionError>;
|
|
625
|
+
readonly invokeJson: (
|
|
626
|
+
request: RuntimeInvokeRequest,
|
|
627
|
+
) => Effect.Effect<InvokeSuccess<string, Schema.Json>, InvokeError | Schema.SchemaError>;
|
|
628
|
+
readonly decodeAgentChannelDefinitionSettings: AgentChannelRuntime.Interface["decodeDefinitionSettings"];
|
|
629
|
+
readonly decodeAgentChannelDeploymentSettings: AgentChannelRuntime.Interface["decodeDeploymentSettings"];
|
|
630
|
+
readonly getAgentChannel: AgentChannelRuntime.Interface["get"];
|
|
631
|
+
readonly checkAccessAgentChannel: AgentChannelRuntime.Interface["checkAccess"];
|
|
632
|
+
readonly executeAgentChannel: AgentChannelRuntime.Interface["execute"];
|
|
633
|
+
readonly invoke: (request: RuntimeInvokeRequest) => Effect.Effect<InvokeSuccess, InvokeError>;
|
|
634
|
+
readonly activateTrigger: (
|
|
635
|
+
request: RuntimeWebhookActivationRequest,
|
|
636
|
+
) => Effect.Effect<Trigger.ActivateSuccess, unknown>;
|
|
637
|
+
readonly deactivateTrigger: (
|
|
638
|
+
request: RuntimeWebhookActivationRequest,
|
|
639
|
+
) => Effect.Effect<Trigger.DeactivateSuccess, unknown>;
|
|
640
|
+
readonly verifyTrigger: (
|
|
641
|
+
request: RuntimeWebhookTriggerLifecycleRequest,
|
|
642
|
+
) => Effect.Effect<Trigger.VerifySuccess, unknown>;
|
|
643
|
+
readonly parseTrigger: (
|
|
644
|
+
request: RuntimeWebhookTriggerLifecycleRequest,
|
|
645
|
+
) => Effect.Effect<Trigger.EmittedEventsSuccess<string, "parse">, unknown>;
|
|
646
|
+
readonly pollTrigger: (
|
|
647
|
+
request: RuntimeTriggerLifecycleRequest,
|
|
648
|
+
) => Effect.Effect<Trigger.EmittedEventsSuccess<string, "poll">, unknown>;
|
|
649
|
+
readonly getAuthMethod: (
|
|
650
|
+
request: AuthMethodRequest,
|
|
651
|
+
) => Effect.Effect<AuthMethod.Any, AuthMethodNotFoundError>;
|
|
652
|
+
// TODO(live-schema-validation): expose live action/trigger schema decoders by capability version.
|
|
653
|
+
// Server authoring/runtime must validate against SDK declarations, not JSON Schema projections.
|
|
654
|
+
readonly decodeAuthMethodSetup: (
|
|
655
|
+
request: DecodeAuthMethodSetupRequest,
|
|
656
|
+
) => Effect.Effect<unknown, AuthMethodExecutionError>;
|
|
657
|
+
readonly prepareAuthMethod: (
|
|
658
|
+
request: PrepareAuthMethodRequest,
|
|
659
|
+
) => Effect.Effect<unknown, AuthMethodExecutionError>;
|
|
660
|
+
readonly prepareOAuth2Tokens: (
|
|
661
|
+
request: PrepareOAuth2TokensRequest,
|
|
662
|
+
) => Effect.Effect<unknown, AuthMethodExecutionError>;
|
|
663
|
+
readonly refreshAuthMethod: (
|
|
664
|
+
request: RefreshAuthMethodRequest,
|
|
665
|
+
) => Effect.Effect<unknown | undefined, AuthMethodExecutionError>;
|
|
666
|
+
readonly resolveAuthMethod: (
|
|
667
|
+
request: ResolveAuthMethodRequest,
|
|
668
|
+
) => Effect.Effect<AuthMethodBinding.Binding, AuthMethodExecutionError>;
|
|
669
|
+
readonly resolveAiModelConfig: (
|
|
670
|
+
request: ResolveAiModelConfigRequest,
|
|
671
|
+
) => Effect.Effect<AiProvider.RuntimeConfig, AiModelConfigError>;
|
|
672
|
+
readonly resolveSandboxAccess: (
|
|
673
|
+
request: ResolveAuthMethodRequest,
|
|
674
|
+
) => Effect.Effect<ResolvedSandboxAccess, SandboxAccessError>;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Effect service provided by {@link toLayer}.
|
|
679
|
+
*
|
|
680
|
+
* @since 0.0.0
|
|
681
|
+
* @category services
|
|
682
|
+
*/
|
|
683
|
+
export class Service extends Context.Service<Service, RuntimeService>()(
|
|
684
|
+
"@dual-os/sdk/Dual/Service",
|
|
685
|
+
) {}
|
|
686
|
+
|
|
687
|
+
export type RuntimeRequirementsFor<P extends Plugin.Any> =
|
|
688
|
+
| AgentChannelRuntime.Requirements<P>
|
|
689
|
+
| (P extends Plugin.Any ? PluginRequirements<P, PluginActions<P>> : never);
|
|
690
|
+
|
|
691
|
+
type AuthProvider = Plugin.Any | AiProvider.Any;
|
|
692
|
+
|
|
693
|
+
type AuthMethodFor<Providers extends AuthProvider> = Providers["authMethods"][number];
|
|
694
|
+
|
|
695
|
+
type AuthMethodSetupDecodeRequirements<Method extends AuthMethod.Any> =
|
|
696
|
+
AuthMethod.SetupSchema<Method> extends Schema.Top
|
|
697
|
+
? Schema.Codec.DecodingServices<AuthMethod.SetupSchema<Method>>
|
|
698
|
+
: never;
|
|
699
|
+
|
|
700
|
+
type AuthMethodInputDecodeRequirements<Method extends AuthMethod.Any> =
|
|
701
|
+
AuthMethod.InputSchema<Method> extends Schema.Top
|
|
702
|
+
? Schema.Codec.DecodingServices<AuthMethod.InputSchema<Method>>
|
|
703
|
+
: never;
|
|
704
|
+
|
|
705
|
+
type AuthMethodExecutionRequirementsFor<Providers extends AuthProvider> =
|
|
706
|
+
AuthMethodFor<Providers> extends infer Method
|
|
707
|
+
? Method extends AuthMethod.Any
|
|
708
|
+
?
|
|
709
|
+
| AuthMethod.Requirements<Method>
|
|
710
|
+
| AuthMethodSetupDecodeRequirements<Method>
|
|
711
|
+
| AuthMethodInputDecodeRequirements<Method>
|
|
712
|
+
| Schema.Codec.DecodingServices<AuthMethod.MaterialSchema<Method>>
|
|
713
|
+
| Schema.Codec.DecodingServices<AuthMethod.ValueSchema<Method>>
|
|
714
|
+
| Schema.Codec.EncodingServices<AuthMethod.MaterialSchema<Method>>
|
|
715
|
+
| Schema.Codec.EncodingServices<AuthMethod.ValueSchema<Method>>
|
|
716
|
+
| (AuthMethod.SetupSchema<Method> extends Schema.Top
|
|
717
|
+
? Schema.Codec.EncodingServices<AuthMethod.SetupSchema<Method>>
|
|
718
|
+
: never)
|
|
719
|
+
: never
|
|
720
|
+
: never;
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Requirements still needed by {@link toLayer} for an app.
|
|
724
|
+
*
|
|
725
|
+
* Hosts (the server, a worker, a test harness) never know an app's concrete
|
|
726
|
+
* implementations: they talk to it exclusively through the generic
|
|
727
|
+
* {@link Service} layer. App-specific dependencies — a plugin's API client, an
|
|
728
|
+
* auth hook's token service — are discharged where they are known, at the
|
|
729
|
+
* plugin or app definition (the `Exclude<..., LayerOutput>` below cancels
|
|
730
|
+
* them). Only what the definition did not discharge surfaces here,
|
|
731
|
+
* generically, and the host entrypoint forces it to `never` before anything
|
|
732
|
+
* runs.
|
|
733
|
+
*
|
|
734
|
+
* @since 0.0.0
|
|
735
|
+
* @category type-level
|
|
736
|
+
*/
|
|
737
|
+
export type ToLayerRequirements<A extends AnyApp> =
|
|
738
|
+
A extends App<
|
|
739
|
+
infer P extends Plugin.Any,
|
|
740
|
+
infer LayerOutput,
|
|
741
|
+
any,
|
|
742
|
+
infer LayerRequirements,
|
|
743
|
+
infer AiProviders extends readonly AiProvider.Any[]
|
|
744
|
+
>
|
|
745
|
+
?
|
|
746
|
+
| LayerRequirements
|
|
747
|
+
| Exclude<RuntimeRequirementsFor<P>, LayerOutput>
|
|
748
|
+
| Exclude<AuthMethodExecutionRequirementsFor<P | AiProviders[number]>, LayerOutput>
|
|
749
|
+
: never;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Layer-construction errors surfaced by {@link toLayer} for an app.
|
|
753
|
+
*
|
|
754
|
+
* @since 0.0.0
|
|
755
|
+
* @category type-level
|
|
756
|
+
*/
|
|
757
|
+
export type ToLayerError<A extends AnyApp> =
|
|
758
|
+
A extends App<any, any, infer LayerError, any, any> ? LayerError : never;
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Turn an {@link App} into a runtime Effect service layer.
|
|
762
|
+
*
|
|
763
|
+
* @since 0.0.0
|
|
764
|
+
* @category layers
|
|
765
|
+
*/
|
|
766
|
+
export const toLayer = <
|
|
767
|
+
const P extends Plugin.Any,
|
|
768
|
+
LayerOutput,
|
|
769
|
+
LayerError,
|
|
770
|
+
LayerRequirements,
|
|
771
|
+
const AiProviders extends readonly AiProvider.Any[],
|
|
772
|
+
>(
|
|
773
|
+
app: App<P, LayerOutput, LayerError, LayerRequirements, AiProviders>,
|
|
774
|
+
): Layer.Layer<
|
|
775
|
+
Service,
|
|
776
|
+
LayerError,
|
|
777
|
+
| LayerRequirements
|
|
778
|
+
| Exclude<RuntimeRequirementsFor<P>, LayerOutput>
|
|
779
|
+
| Exclude<AuthMethodExecutionRequirementsFor<P | AiProviders[number]>, LayerOutput>
|
|
780
|
+
> => {
|
|
781
|
+
const fullAuthMethodId = (providerId: string, method: AuthMethod.Any): string =>
|
|
782
|
+
`${providerId}/${method.identifier}`;
|
|
783
|
+
|
|
784
|
+
const findAuthMethod = (
|
|
785
|
+
providerId: string,
|
|
786
|
+
authMethodId: string,
|
|
787
|
+
): Effect.Effect<AuthMethod.Any, AuthMethodNotFoundError> => {
|
|
788
|
+
const provider =
|
|
789
|
+
pluginOf(app, providerId) ??
|
|
790
|
+
app.aiProviders.find(candidate => AiProvider.registryId(candidate.identifier) === providerId);
|
|
791
|
+
const method = provider?.authMethods.find(
|
|
792
|
+
candidate =>
|
|
793
|
+
candidate.identifier === authMethodId ||
|
|
794
|
+
fullAuthMethodId(providerId, candidate) === authMethodId,
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
return method === undefined
|
|
798
|
+
? Effect.fail(new AuthMethodNotFoundError({ authMethodId, providerId }))
|
|
799
|
+
: Effect.succeed(method);
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
const serviceLayer = Layer.effect(
|
|
803
|
+
Service,
|
|
804
|
+
Effect.gen(function* () {
|
|
805
|
+
const channels = yield* AgentChannelRuntime.Service;
|
|
806
|
+
const context = yield* Effect.context<
|
|
807
|
+
RuntimeRequirementsFor<P> | AuthMethodExecutionRequirementsFor<P | AiProviders[number]>
|
|
808
|
+
>();
|
|
809
|
+
|
|
810
|
+
const appContext = indexGroups(context);
|
|
811
|
+
|
|
812
|
+
const provideRuntimeContext = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
813
|
+
effect.pipe(Effect.updateContext(input => Context.merge(appContext, input)));
|
|
814
|
+
|
|
815
|
+
// Requirements are satisfied by the context captured at layer build time;
|
|
816
|
+
// the cast erases them so service methods expose closed effects.
|
|
817
|
+
const runWithCapturedAppContext = <A, E>(
|
|
818
|
+
effect: Effect.Effect<A, E, unknown>,
|
|
819
|
+
): Effect.Effect<A, E> => provideRuntimeContext(effect) as never;
|
|
820
|
+
|
|
821
|
+
const getAuthMethod = (request: AuthMethodRequest) =>
|
|
822
|
+
findAuthMethod(request.providerId, request.authMethodId);
|
|
823
|
+
|
|
824
|
+
const getTrigger = (request: RuntimeTriggerLifecycleRequest) => {
|
|
825
|
+
const resolved = resolveRuntimeTrigger(
|
|
826
|
+
app.plugins,
|
|
827
|
+
request.triggerId,
|
|
828
|
+
request.triggerVersion,
|
|
829
|
+
);
|
|
830
|
+
return resolved === undefined ||
|
|
831
|
+
(request.pluginKey !== undefined && resolved.providerId !== request.pluginKey)
|
|
832
|
+
? Effect.fail(new TriggerNotFoundError({ triggerId: request.triggerId }))
|
|
833
|
+
: Effect.succeed(resolved);
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const triggerLifecycleRequest = (
|
|
837
|
+
request: RuntimeTriggerLifecycleRequest,
|
|
838
|
+
): Trigger.LifecycleRequest => ({
|
|
839
|
+
config: request.config,
|
|
840
|
+
...(request.state === undefined ? {} : { state: request.state }),
|
|
841
|
+
...(request.connectionId === undefined ? {} : { connectionId: request.connectionId }),
|
|
842
|
+
...(request.context === undefined ? {} : { context: request.context }),
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
const webhookActivationRequest = (
|
|
846
|
+
request: RuntimeWebhookActivationRequest,
|
|
847
|
+
): Trigger.WebhookActivationRequest => ({
|
|
848
|
+
...triggerLifecycleRequest(request),
|
|
849
|
+
webhookUrl: request.webhookUrl,
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
// A host can pass the raw body as text; handlers verify signatures over its bytes.
|
|
853
|
+
const webhookTriggerLifecycleRequest = (
|
|
854
|
+
request: RuntimeWebhookTriggerLifecycleRequest,
|
|
855
|
+
): Trigger.WebhookExecutionRequest => ({
|
|
856
|
+
...webhookActivationRequest(request),
|
|
857
|
+
request: Predicate.isString(request.request.rawBody)
|
|
858
|
+
? { ...request.request, rawBody: new TextEncoder().encode(request.request.rawBody) }
|
|
859
|
+
: request.request,
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
const activateTrigger = (request: RuntimeWebhookActivationRequest) =>
|
|
863
|
+
runWithCapturedAppContext(
|
|
864
|
+
Effect.gen(function* () {
|
|
865
|
+
const entry = yield* getTrigger(request);
|
|
866
|
+
return yield* Trigger.activate(entry, webhookActivationRequest(request));
|
|
867
|
+
}),
|
|
868
|
+
);
|
|
869
|
+
|
|
870
|
+
const deactivateTrigger = (request: RuntimeWebhookActivationRequest) =>
|
|
871
|
+
runWithCapturedAppContext(
|
|
872
|
+
Effect.gen(function* () {
|
|
873
|
+
const entry = yield* getTrigger(request);
|
|
874
|
+
return yield* Trigger.deactivate(entry, webhookActivationRequest(request));
|
|
875
|
+
}),
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
const verifyTrigger = (request: RuntimeWebhookTriggerLifecycleRequest) =>
|
|
879
|
+
runWithCapturedAppContext(
|
|
880
|
+
Effect.gen(function* () {
|
|
881
|
+
const entry = yield* getTrigger(request);
|
|
882
|
+
return yield* Trigger.verify(entry, webhookTriggerLifecycleRequest(request));
|
|
883
|
+
}),
|
|
884
|
+
);
|
|
885
|
+
|
|
886
|
+
const parseTrigger = (request: RuntimeWebhookTriggerLifecycleRequest) =>
|
|
887
|
+
runWithCapturedAppContext(
|
|
888
|
+
Effect.gen(function* () {
|
|
889
|
+
const entry = yield* getTrigger(request);
|
|
890
|
+
return yield* Trigger.parse(entry, webhookTriggerLifecycleRequest(request));
|
|
891
|
+
}),
|
|
892
|
+
);
|
|
893
|
+
|
|
894
|
+
const pollTrigger = (request: RuntimeTriggerLifecycleRequest) =>
|
|
895
|
+
runWithCapturedAppContext(
|
|
896
|
+
Effect.gen(function* () {
|
|
897
|
+
const entry = yield* getTrigger(request);
|
|
898
|
+
return yield* Trigger.poll(entry, triggerLifecycleRequest(request));
|
|
899
|
+
}),
|
|
900
|
+
);
|
|
901
|
+
|
|
902
|
+
const decodeAuthMethodSetup = (request: DecodeAuthMethodSetupRequest) =>
|
|
903
|
+
runWithCapturedAppContext(
|
|
904
|
+
getAuthMethod(request).pipe(
|
|
905
|
+
Effect.flatMap(method => {
|
|
906
|
+
if (request.encoding === "json" && method.setup !== undefined) {
|
|
907
|
+
return Schema.decodeUnknownEffect(Schema.toCodecJson(method.setup))(request.setup);
|
|
908
|
+
}
|
|
909
|
+
return AuthMethodExecution.decodeSetup(method, request.setup);
|
|
910
|
+
}),
|
|
911
|
+
),
|
|
912
|
+
);
|
|
913
|
+
|
|
914
|
+
const withAuthMethodSetup = <A, E, R>(
|
|
915
|
+
request: DecodeAuthMethodSetupRequest,
|
|
916
|
+
execute: (
|
|
917
|
+
method: AuthMethod.Any,
|
|
918
|
+
setup: AuthMethod.Setup<AuthMethod.Any>,
|
|
919
|
+
) => Effect.Effect<A, E, R>,
|
|
920
|
+
) =>
|
|
921
|
+
runWithCapturedAppContext(
|
|
922
|
+
getAuthMethod(request).pipe(
|
|
923
|
+
Effect.flatMap(method =>
|
|
924
|
+
AuthMethodExecution.decodeSetup(method, request.setup).pipe(
|
|
925
|
+
Effect.flatMap(setup => execute(method, setup)),
|
|
926
|
+
),
|
|
927
|
+
),
|
|
928
|
+
),
|
|
929
|
+
);
|
|
930
|
+
|
|
931
|
+
const prepareAuthMethod = (request: PrepareAuthMethodRequest) =>
|
|
932
|
+
withAuthMethodSetup(request, (method, setup) =>
|
|
933
|
+
AuthMethodExecution.prepare(method, request.input, { setup }),
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
const prepareOAuth2Tokens = (request: PrepareOAuth2TokensRequest) =>
|
|
937
|
+
withAuthMethodSetup(request, (method, setup) =>
|
|
938
|
+
AuthMethodExecution.prepareOAuth2Tokens(method, request.tokens, { setup }),
|
|
939
|
+
);
|
|
940
|
+
|
|
941
|
+
const refreshAuthMethod = (request: RefreshAuthMethodRequest) =>
|
|
942
|
+
runWithCapturedAppContext(
|
|
943
|
+
getAuthMethod(request).pipe(
|
|
944
|
+
Effect.flatMap(method =>
|
|
945
|
+
AuthMethodExecution.decodeSetup(method, request.setup).pipe(
|
|
946
|
+
Effect.flatMap(setup =>
|
|
947
|
+
AuthMethodExecution.refreshUnknown(method, request.material, {
|
|
948
|
+
minValidityMs: request.minValidityMs,
|
|
949
|
+
now: request.now,
|
|
950
|
+
reason: request.reason,
|
|
951
|
+
setup,
|
|
952
|
+
}),
|
|
953
|
+
),
|
|
954
|
+
),
|
|
955
|
+
),
|
|
956
|
+
),
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
const resolveAuthMethod = (request: ResolveAuthMethodRequest) =>
|
|
960
|
+
runWithCapturedAppContext(
|
|
961
|
+
Effect.gen(function* () {
|
|
962
|
+
const method = yield* getAuthMethod(request);
|
|
963
|
+
const setup = yield* decodeAuthMethodSetup(request);
|
|
964
|
+
const value =
|
|
965
|
+
request.encoding === "json"
|
|
966
|
+
? yield* AuthMethodExecution.resolveDecoded(
|
|
967
|
+
method,
|
|
968
|
+
yield* Schema.decodeUnknownEffect(Schema.toCodecJson(method.material))(
|
|
969
|
+
request.material,
|
|
970
|
+
),
|
|
971
|
+
{ setup },
|
|
972
|
+
)
|
|
973
|
+
: yield* AuthMethodExecution.resolve(method, request.material, { setup });
|
|
974
|
+
return AuthMethodBinding.makeBinding({
|
|
975
|
+
connectionId: undefined,
|
|
976
|
+
method,
|
|
977
|
+
providerId: request.providerId,
|
|
978
|
+
value,
|
|
979
|
+
});
|
|
980
|
+
}),
|
|
981
|
+
);
|
|
982
|
+
|
|
983
|
+
const resolveAiModelConfig = (request: ResolveAiModelConfigRequest) =>
|
|
984
|
+
runWithCapturedAppContext(
|
|
985
|
+
Effect.gen(function* () {
|
|
986
|
+
const provider = app.aiProviders.find(
|
|
987
|
+
candidate => AiProvider.registryId(candidate.identifier) === request.providerId,
|
|
988
|
+
);
|
|
989
|
+
if (provider?.resolveRuntimeConfig === undefined) {
|
|
990
|
+
return yield* new AiProviderRuntimeNotConfiguredError({
|
|
991
|
+
providerId: request.providerId,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
const binding = yield* resolveAuthMethod(request);
|
|
995
|
+
// SAFETY: the resolver belongs to this provider and the binding was
|
|
996
|
+
// resolved from the same provider's declared auth-method list.
|
|
997
|
+
const resolveRuntimeConfig =
|
|
998
|
+
provider.resolveRuntimeConfig as AiProvider.RuntimeConfigResolver<
|
|
999
|
+
readonly AuthMethod.Any[]
|
|
1000
|
+
>;
|
|
1001
|
+
return yield* Schema.decodeUnknownEffect(AiProvider.RuntimeConfig)(
|
|
1002
|
+
resolveRuntimeConfig({ binding, model: request.model }),
|
|
1003
|
+
);
|
|
1004
|
+
}),
|
|
1005
|
+
);
|
|
1006
|
+
|
|
1007
|
+
const resolveSandboxAccess = (request: ResolveAuthMethodRequest) =>
|
|
1008
|
+
runWithCapturedAppContext(
|
|
1009
|
+
Effect.gen(function* () {
|
|
1010
|
+
const access = pluginOf(app, request.providerId)?.sandboxAccess;
|
|
1011
|
+
if (access === undefined) {
|
|
1012
|
+
return yield* new SandboxAccessNotDeclaredError({ providerId: request.providerId });
|
|
1013
|
+
}
|
|
1014
|
+
const context = authRuntimeContext(yield* resolveAuthMethod(request));
|
|
1015
|
+
// Declared origins compare as URL origins, so `https://GHES.example:443` matches requests.
|
|
1016
|
+
const origins = Array.map(
|
|
1017
|
+
access.origins(context),
|
|
1018
|
+
origin => Result.getOrUndefined(Url.fromString(origin))?.origin ?? origin,
|
|
1019
|
+
);
|
|
1020
|
+
const authorize = (outgoing: HttpClientRequest.HttpClientRequest) => {
|
|
1021
|
+
const url = Result.getOrUndefined(Url.fromString(outgoing.url));
|
|
1022
|
+
const refused = new SandboxRequestNotAllowedError({
|
|
1023
|
+
origin: url?.origin ?? outgoing.url,
|
|
1024
|
+
path: url?.pathname ?? "",
|
|
1025
|
+
providerId: request.providerId,
|
|
1026
|
+
});
|
|
1027
|
+
if (url === undefined || !origins.includes(url.origin)) return Effect.fail(refused);
|
|
1028
|
+
return Effect.fromOption(access.authorize(outgoing, context), () => refused);
|
|
1029
|
+
};
|
|
1030
|
+
return { authorize, origins } satisfies ResolvedSandboxAccess;
|
|
1031
|
+
}),
|
|
1032
|
+
);
|
|
1033
|
+
|
|
1034
|
+
return Service.of({
|
|
1035
|
+
activateTrigger,
|
|
1036
|
+
checkAccessAgentChannel: channels.checkAccess,
|
|
1037
|
+
deactivateTrigger,
|
|
1038
|
+
decodeAgentChannelDefinitionSettings: channels.decodeDefinitionSettings,
|
|
1039
|
+
decodeAgentChannelDeploymentSettings: channels.decodeDeploymentSettings,
|
|
1040
|
+
decodeAuthMethodSetup,
|
|
1041
|
+
executeAgentChannel: channels.execute,
|
|
1042
|
+
executeAuthJson: request =>
|
|
1043
|
+
runWithCapturedAppContext(
|
|
1044
|
+
pipe(
|
|
1045
|
+
getAuthMethod(request),
|
|
1046
|
+
Effect.flatMap(method => AuthMethodExecution.executeJson(method, request)),
|
|
1047
|
+
),
|
|
1048
|
+
),
|
|
1049
|
+
getAgentChannel: channels.get,
|
|
1050
|
+
getAuthMethod,
|
|
1051
|
+
invoke: request => runWithCapturedAppContext(invokeRaw(app, request)),
|
|
1052
|
+
invokeJson: request =>
|
|
1053
|
+
runWithCapturedAppContext(
|
|
1054
|
+
Effect.gen(function* () {
|
|
1055
|
+
const result = yield* invokeRaw(app, request, true);
|
|
1056
|
+
return {
|
|
1057
|
+
...result,
|
|
1058
|
+
data: yield* Schema.decodeUnknownEffect(Schema.Json)(result.data),
|
|
1059
|
+
};
|
|
1060
|
+
}),
|
|
1061
|
+
),
|
|
1062
|
+
parseTrigger,
|
|
1063
|
+
pollTrigger,
|
|
1064
|
+
prepareAuthMethod,
|
|
1065
|
+
prepareOAuth2Tokens,
|
|
1066
|
+
refreshAuthMethod,
|
|
1067
|
+
resolveAiModelConfig,
|
|
1068
|
+
resolveAuthMethod,
|
|
1069
|
+
resolveSandboxAccess,
|
|
1070
|
+
verifyTrigger,
|
|
1071
|
+
});
|
|
1072
|
+
}),
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
return serviceLayer.pipe(
|
|
1076
|
+
Layer.provide(AgentChannelRuntime.layer(app.plugins.map(installation => installation.plugin))),
|
|
1077
|
+
Layer.provide(app.layer),
|
|
1078
|
+
);
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
export * as DualRuntime from "./DualRuntime.ts";
|