@assemblyline-agents/core 0.1.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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -0
  3. package/dist/adapter-metadata.d.ts +7 -0
  4. package/dist/adapter-metadata.d.ts.map +1 -0
  5. package/dist/adapter-metadata.js +232 -0
  6. package/dist/adapter-metadata.js.map +1 -0
  7. package/dist/artifact-dir.d.ts +21 -0
  8. package/dist/artifact-dir.d.ts.map +1 -0
  9. package/dist/artifact-dir.js +35 -0
  10. package/dist/artifact-dir.js.map +1 -0
  11. package/dist/brand.d.ts +37 -0
  12. package/dist/brand.d.ts.map +1 -0
  13. package/dist/brand.js +42 -0
  14. package/dist/brand.js.map +1 -0
  15. package/dist/connection-auth.d.ts +27 -0
  16. package/dist/connection-auth.d.ts.map +1 -0
  17. package/dist/connection-auth.js +75 -0
  18. package/dist/connection-auth.js.map +1 -0
  19. package/dist/connection-types.d.ts +318 -0
  20. package/dist/connection-types.d.ts.map +1 -0
  21. package/dist/connection-types.js +2 -0
  22. package/dist/connection-types.js.map +1 -0
  23. package/dist/definitions.d.ts +98 -0
  24. package/dist/definitions.d.ts.map +1 -0
  25. package/dist/definitions.js +193 -0
  26. package/dist/definitions.js.map +1 -0
  27. package/dist/deploy.d.ts +151 -0
  28. package/dist/deploy.d.ts.map +1 -0
  29. package/dist/deploy.js +25 -0
  30. package/dist/deploy.js.map +1 -0
  31. package/dist/env-alias.d.ts +35 -0
  32. package/dist/env-alias.d.ts.map +1 -0
  33. package/dist/env-alias.js +79 -0
  34. package/dist/env-alias.js.map +1 -0
  35. package/dist/harness.d.ts +299 -0
  36. package/dist/harness.d.ts.map +1 -0
  37. package/dist/harness.js +22 -0
  38. package/dist/harness.js.map +1 -0
  39. package/dist/index.d.ts +487 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +23 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/json-schema-validate.d.ts +17 -0
  44. package/dist/json-schema-validate.d.ts.map +1 -0
  45. package/dist/json-schema-validate.js +79 -0
  46. package/dist/json-schema-validate.js.map +1 -0
  47. package/dist/manifest-types.d.ts +248 -0
  48. package/dist/manifest-types.d.ts.map +1 -0
  49. package/dist/manifest-types.js +2 -0
  50. package/dist/manifest-types.js.map +1 -0
  51. package/dist/plugin.d.ts +134 -0
  52. package/dist/plugin.d.ts.map +1 -0
  53. package/dist/plugin.js +830 -0
  54. package/dist/plugin.js.map +1 -0
  55. package/dist/provider.d.ts +43 -0
  56. package/dist/provider.d.ts.map +1 -0
  57. package/dist/provider.js +33 -0
  58. package/dist/provider.js.map +1 -0
  59. package/dist/resource-types.d.ts +119 -0
  60. package/dist/resource-types.d.ts.map +1 -0
  61. package/dist/resource-types.js +2 -0
  62. package/dist/resource-types.js.map +1 -0
  63. package/dist/sandbox-filesystem-contract.d.ts +28 -0
  64. package/dist/sandbox-filesystem-contract.d.ts.map +1 -0
  65. package/dist/sandbox-filesystem-contract.js +64 -0
  66. package/dist/sandbox-filesystem-contract.js.map +1 -0
  67. package/dist/sandbox-paths.d.ts +24 -0
  68. package/dist/sandbox-paths.d.ts.map +1 -0
  69. package/dist/sandbox-paths.js +107 -0
  70. package/dist/sandbox-paths.js.map +1 -0
  71. package/dist/sandbox-security.d.ts +10 -0
  72. package/dist/sandbox-security.d.ts.map +1 -0
  73. package/dist/sandbox-security.js +34 -0
  74. package/dist/sandbox-security.js.map +1 -0
  75. package/dist/sandbox-types.d.ts +114 -0
  76. package/dist/sandbox-types.d.ts.map +1 -0
  77. package/dist/sandbox-types.js +2 -0
  78. package/dist/sandbox-types.js.map +1 -0
  79. package/package.json +47 -0
package/dist/plugin.js ADDED
@@ -0,0 +1,830 @@
1
+ import { LEGACY_PLUGIN_EXPORT_NAME, PLUGIN_EXPORT_NAME, readAliasedExport } from "./brand.js";
2
+ import { defineMcpClientConnection, defineMcpRelayConnection, defineMcpStdioConnection, defineOpenAPIConnection, defineSandboxCliConnection } from "./definitions.js";
3
+ export function definePlugin(module) {
4
+ return module;
5
+ }
6
+ /**
7
+ * Reads `assemblyLinePlugin` (falling back to the legacy `openevePlugin`
8
+ * name) from a dynamically imported module namespace. Exporting both names
9
+ * with different values is a packaging error.
10
+ */
11
+ export function pluginModuleFromImport(imported) {
12
+ const candidates = [imported, imported?.default];
13
+ for (const candidate of candidates) {
14
+ if (!candidate || typeof candidate !== "object")
15
+ continue;
16
+ const { value, conflict } = readAliasedExport(candidate, PLUGIN_EXPORT_NAME, LEGACY_PLUGIN_EXPORT_NAME);
17
+ if (conflict) {
18
+ throw new Error(`Module exports both ${PLUGIN_EXPORT_NAME} and ${LEGACY_PLUGIN_EXPORT_NAME} with different values; export one (they are aliases).`);
19
+ }
20
+ if (isPluginModule(value))
21
+ return value;
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Provider packages use this factory instead of implementing transport or
27
+ * credential plumbing. The developer still owns the provider application,
28
+ * token/OAuth grant, and any required MCP bridge.
29
+ */
30
+ export function defineMcpPluginConnection(plugin, options) {
31
+ if (plugin.protocol !== "mcp") {
32
+ throw new Error(`${plugin.helper}: ${plugin.provider} uses OpenAPI; call defineOpenAPIPluginConnection instead.`);
33
+ }
34
+ if ((plugin.transport ?? "http") !== "http") {
35
+ throw new Error(`${plugin.helper}: ${plugin.provider} uses stdio; call defineStdioMcpPluginConnection instead.`);
36
+ }
37
+ validatePluginAccess(plugin, options.access);
38
+ const url = options.url ?? environmentValue(plugin.urlEnv) ?? plugin.defaultUrl;
39
+ if (!url)
40
+ throw new Error(`${plugin.helper}: set ${plugin.urlEnv ?? "a connection URL"} or pass url.`);
41
+ const definition = {
42
+ transport: "http",
43
+ url,
44
+ description: options.description ?? plugin.description,
45
+ access: pluginAccessDefinition(plugin, options.access),
46
+ required: options.required ?? true,
47
+ subject: options.subject ?? plugin.subject ?? "user",
48
+ metadata: {
49
+ provider: plugin.provider,
50
+ plugin: plugin.packageName ?? plugin.provider,
51
+ ...(options.metadata ?? {})
52
+ }
53
+ };
54
+ const auth = options.auth === false || plugin.tokenHeader
55
+ ? (options.auth === false ? undefined : options.auth)
56
+ : options.auth ?? (plugin.tokenRequired === false ? undefined : tokenAuth(plugin.tokenEnv));
57
+ if (auth)
58
+ definition.auth = auth;
59
+ const tokenHeaders = options.auth === undefined && plugin.tokenHeader
60
+ ? tokenHeader(plugin.tokenHeader, plugin.tokenEnv)
61
+ : undefined;
62
+ const headers = mergeHeaders(tokenHeaders, options.headers);
63
+ if (headers)
64
+ definition.headers = headers;
65
+ const tools = options.tools ?? cloneToolFilter(plugin.tools);
66
+ if (tools)
67
+ definition.tools = tools;
68
+ return defineMcpClientConnection(definition);
69
+ }
70
+ export function defineStdioMcpPluginConnection(plugin, options) {
71
+ if (plugin.protocol !== "mcp" || plugin.transport !== "stdio") {
72
+ throw new Error(`${plugin.helper}: ${plugin.provider} uses HTTP; call defineMcpPluginConnection instead.`);
73
+ }
74
+ validatePluginAccess(plugin, options.access);
75
+ const definition = {
76
+ transport: "stdio",
77
+ command: options.command,
78
+ ...(options.args ? { args: [...options.args] } : {}),
79
+ ...(options.cwd ? { cwd: options.cwd } : {}),
80
+ ...(options.env ? { env: { ...options.env } } : {}),
81
+ description: options.description ?? plugin.description,
82
+ access: pluginAccessDefinition(plugin, options.access),
83
+ required: options.required ?? true,
84
+ subject: options.subject ?? plugin.subject ?? "environment",
85
+ metadata: {
86
+ provider: plugin.provider,
87
+ plugin: plugin.packageName ?? plugin.provider,
88
+ ...(options.metadata ?? {})
89
+ }
90
+ };
91
+ const tools = options.tools ?? cloneToolFilter(plugin.tools);
92
+ if (tools)
93
+ definition.tools = tools;
94
+ return defineMcpStdioConnection(definition);
95
+ }
96
+ export function defineRelayMcpPluginConnection(plugin, options) {
97
+ if (plugin.protocol !== "mcp" || plugin.transport !== "relay") {
98
+ throw new Error(`${plugin.helper}: ${plugin.provider} does not use the OpenEve relay transport.`);
99
+ }
100
+ validatePluginAccess(plugin, options.access);
101
+ const url = options.url ?? environmentValue(plugin.urlEnv) ?? plugin.defaultUrl;
102
+ if (!url)
103
+ throw new Error(`${plugin.helper}: set ${plugin.urlEnv ?? "a relay URL"} or pass url.`);
104
+ const credentialEnv = options.credentialEnv ?? plugin.credentialEnv;
105
+ if (!credentialEnv)
106
+ throw new Error(`${plugin.helper}: relay metadata must declare credentialEnv.`);
107
+ const definition = {
108
+ transport: "relay",
109
+ url,
110
+ credentialEnv,
111
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
112
+ description: options.description ?? plugin.description,
113
+ access: pluginAccessDefinition(plugin, options.access),
114
+ required: options.required ?? true,
115
+ subject: options.subject ?? plugin.subject ?? "environment",
116
+ metadata: {
117
+ provider: plugin.provider,
118
+ plugin: plugin.packageName ?? plugin.provider,
119
+ ...(options.metadata ?? {})
120
+ }
121
+ };
122
+ const tools = options.tools ?? cloneToolFilter(plugin.tools);
123
+ if (tools)
124
+ definition.tools = tools;
125
+ return defineMcpRelayConnection(definition);
126
+ }
127
+ /** Build a direct HTTP API connection from a provider's reviewed OpenAPI metadata. */
128
+ export function defineOpenAPIPluginConnection(plugin, options) {
129
+ if (plugin.protocol !== "openapi") {
130
+ throw new Error(`${plugin.helper}: ${plugin.provider} uses MCP; call defineMcpPluginConnection instead.`);
131
+ }
132
+ validatePluginAccess(plugin, options.access);
133
+ const spec = options.spec ?? environmentValue(plugin.specEnv) ?? plugin.defaultSpec;
134
+ if (!spec)
135
+ throw new Error(`${plugin.helper}: set ${plugin.specEnv ?? "an OpenAPI specification"} or pass spec.`);
136
+ const baseUrl = options.baseUrl ?? environmentValue(plugin.baseUrlEnv) ?? plugin.defaultBaseUrl;
137
+ const definition = {
138
+ spec,
139
+ ...(baseUrl ? { baseUrl } : {}),
140
+ description: options.description ?? plugin.description,
141
+ access: pluginAccessDefinition(plugin, options.access),
142
+ required: options.required ?? true,
143
+ subject: options.subject ?? plugin.subject ?? "user",
144
+ metadata: {
145
+ provider: plugin.provider,
146
+ plugin: plugin.packageName ?? plugin.provider,
147
+ ...(options.metadata ?? {})
148
+ }
149
+ };
150
+ const auth = options.auth === false
151
+ ? undefined
152
+ : options.auth ?? (plugin.tokenRequired === false ? undefined : tokenAuth(plugin.tokenEnv));
153
+ if (auth)
154
+ definition.auth = auth;
155
+ if (options.headers)
156
+ definition.headers = options.headers;
157
+ const operations = options.operations ?? cloneToolFilter(plugin.tools);
158
+ if (operations)
159
+ definition.operations = operations;
160
+ return defineOpenAPIConnection(definition);
161
+ }
162
+ /**
163
+ * Build a connection whose reviewed operations run through a provider CLI in
164
+ * the agent sandbox rather than on the OpenEve gateway host.
165
+ */
166
+ export function defineSandboxCliPluginConnection(plugin, options) {
167
+ if (plugin.protocol !== "cli" || plugin.transport !== "sandbox") {
168
+ throw new Error(`${plugin.helper}: ${plugin.provider} does not use the sandbox CLI transport.`);
169
+ }
170
+ validatePluginAccess(plugin, options.access);
171
+ const command = options.command ?? plugin.command;
172
+ if (!command)
173
+ throw new Error(`${plugin.helper}: sandbox CLI metadata must declare command.`);
174
+ return defineSandboxCliConnection({
175
+ transport: "sandbox",
176
+ command,
177
+ description: options.description ?? plugin.description,
178
+ tools: options.tools,
179
+ access: pluginAccessDefinition(plugin, options.access),
180
+ ...(options.auth ? { auth: options.auth } : {}),
181
+ ...(options.maxOutputChars !== undefined ? { maxOutputChars: options.maxOutputChars } : {}),
182
+ required: options.required ?? true,
183
+ subject: options.subject ?? plugin.subject ?? "environment",
184
+ metadata: {
185
+ provider: plugin.provider,
186
+ plugin: plugin.packageName ?? plugin.provider,
187
+ ...(options.metadata ?? {})
188
+ }
189
+ });
190
+ }
191
+ export function connectionPluginMetadata(kind) {
192
+ return CONNECTION_PLUGIN_CATALOG.find((entry) => entry.kind === kind);
193
+ }
194
+ export function connectionPluginMetadataForHelper(helper) {
195
+ return helper ? CONNECTION_PLUGIN_CATALOG.find((entry) => entry.helper === helper) : undefined;
196
+ }
197
+ export function allConnectionPluginMetadata() {
198
+ return CONNECTION_PLUGIN_CATALOG.map(cloneMetadata);
199
+ }
200
+ const READ_PATTERNS = [
201
+ "regex:(^|[-_])(search|read|list|fetch|get|retrieve|query|lookup|find|describe|details?|discover)([-_]|$)"
202
+ ];
203
+ const WRITE_PATTERNS = [
204
+ "regex:(^|[-_])(add|append|archive|assign|book|buy|cancel|checkout|close|complete|create|delete|draft|duplicate|edit|execute|insert|invite|kick|link|merge|modify|move|mutate|open|pay|post|publish|purchase|push|react|reaction|remove|rename|reply|reserve|resolve|reopen|run|schedule|send|set|share|submit|transfer|transition|unassign|unlink|update|upload|write)([-_]|$)"
205
+ ];
206
+ function catalogEntry(input) {
207
+ const requiredEnv = [
208
+ ...(input.urlEnv && !input.defaultUrl ? [input.urlEnv] : []),
209
+ ...(input.tokenEnv && input.tokenRequired !== false ? [input.tokenEnv] : []),
210
+ ...(input.credentialEnv ? [input.credentialEnv] : [])
211
+ ];
212
+ const optionalEnv = [
213
+ ...(input.urlEnv && input.defaultUrl ? [input.urlEnv] : []),
214
+ ...(input.tokenEnv && input.tokenRequired === false ? [input.tokenEnv] : []),
215
+ ...(input.optionalEnv ?? [])
216
+ ];
217
+ return {
218
+ kind: input.kind,
219
+ role: "connection",
220
+ packageName: input.packageName ?? `@assemblyline-agents/${input.kind}`,
221
+ helper: input.helper,
222
+ protocol: "mcp",
223
+ transport: input.transport ?? "http",
224
+ provider: input.provider ?? input.kind,
225
+ description: input.description,
226
+ ...(input.defaultUrl ? { defaultUrl: input.defaultUrl } : {}),
227
+ ...(input.urlEnv ? { urlEnv: input.urlEnv } : {}),
228
+ ...(input.tokenEnv ? { tokenEnv: input.tokenEnv } : {}),
229
+ ...(input.tokenEnv ? { tokenRequired: input.tokenRequired !== false } : {}),
230
+ ...(input.tokenHeader ? { tokenHeader: input.tokenHeader } : {}),
231
+ ...(input.credentialEnv ? { credentialEnv: input.credentialEnv } : {}),
232
+ requiredEnv,
233
+ optionalEnv,
234
+ capabilities: ["mcp", "live-tools", input.transport === "stdio" ? "stdio" : input.transport === "relay" ? "e2ee-device-relay" : "streamable-http"],
235
+ scopes: [...(input.scopes ?? [])],
236
+ subject: input.subject ?? "user",
237
+ ...(input.tools ? { tools: cloneToolFilter(input.tools) } : {}),
238
+ ...(input.hostRequirements ? { hostRequirements: cloneHostRequirements(input.hostRequirements) } : {}),
239
+ readToolPatterns: input.readToolPatterns ?? READ_PATTERNS,
240
+ writeToolPatterns: input.writeToolPatterns ?? (input.write ? WRITE_PATTERNS : []),
241
+ skill: input.kind,
242
+ setup: [{
243
+ kind: "connection",
244
+ name: `${input.kind}-developer-configuration`,
245
+ required: true,
246
+ message: input.setupMessage ?? `Configure ${input.displayName} credentials, OAuth application, or MCP bridge for this deployment.`
247
+ }]
248
+ };
249
+ }
250
+ const HIGGSFIELD_PLUGIN = {
251
+ kind: "higgsfield",
252
+ role: "connection",
253
+ packageName: "@assemblyline-agents/higgsfield",
254
+ helper: "defineHiggsfieldConnection",
255
+ protocol: "cli",
256
+ transport: "sandbox",
257
+ provider: "higgsfield",
258
+ description: "Higgsfield creative media generation, analysis, and editing through the official CLI inside the agent sandbox.",
259
+ command: "higgsfield",
260
+ requiredEnv: [],
261
+ optionalEnv: [],
262
+ capabilities: ["sandbox-cli", "native-file-inputs", "media-generation", "video-analysis", "video-editing"],
263
+ scopes: [],
264
+ subject: "user",
265
+ readToolPatterns: ["read"],
266
+ writeToolPatterns: ["write"],
267
+ skills: [
268
+ "higgsfield-generate",
269
+ "higgsfield-soul-id",
270
+ "higgsfield-product-photoshoot"
271
+ ],
272
+ setup: [{
273
+ kind: "sandbox",
274
+ name: "higgsfield-cli",
275
+ required: true,
276
+ message: "Install the official Higgsfield CLI in the sandbox image with `curl -fsSL https://raw.githubusercontent.com/higgsfield-ai/cli/main/install.sh | sh`, then run `higgsfield auth login` interactively inside each persistent, user-scoped sandbox. Do not bake account credentials into a shared image. Higgsfield generation and editing consume account credits."
277
+ }]
278
+ };
279
+ const MIRROR_PLUGIN = {
280
+ kind: "mirror",
281
+ role: "connection",
282
+ packageName: "@assemblyline-agents/mirror",
283
+ helper: "defineMirrorConnection",
284
+ protocol: "mcp",
285
+ transport: "http",
286
+ provider: "mirror",
287
+ description: "User-scoped connected accounts, mirrored source records, live reads, and approval-gated actions through Mirror.",
288
+ defaultUrl: "https://mirror.artificialillumination.co/mcp",
289
+ urlEnv: "MIRROR_MCP_URL",
290
+ requiredEnv: ["MIRROR_OAUTH_CLIENT_ID"],
291
+ optionalEnv: ["MIRROR_MCP_URL", "MIRROR_OAUTH_ISSUER", "MIRROR_OAUTH_REDIRECT_URI"],
292
+ capabilities: ["mcp", "oauth", "pkce", "live-tools", "streamable-http", "connected-accounts", "approval-gated-writes"],
293
+ scopes: [],
294
+ subject: "user",
295
+ tools: {
296
+ allow: [
297
+ "mirror.connected_providers",
298
+ "mirror.connected_provider_call",
299
+ "mirror.create_connections_link",
300
+ "mirror.query_records",
301
+ "mirror.get_source",
302
+ "mirror.live_read",
303
+ "mirror.sync_now",
304
+ "mirror.request_action",
305
+ "mirror.get_pending_approvals",
306
+ "mirror.approve_code",
307
+ "mirror.agentcash_wallet_export",
308
+ "mirror.register_agent",
309
+ "mirror.whoami"
310
+ ]
311
+ },
312
+ readToolPatterns: [
313
+ "mirror.connected_providers",
314
+ "mirror.query_records",
315
+ "mirror.get_source",
316
+ "mirror.live_read",
317
+ "mirror.get_pending_approvals",
318
+ "mirror.whoami"
319
+ ],
320
+ writeToolPatterns: [
321
+ "mirror.connected_provider_call",
322
+ "mirror.create_connections_link",
323
+ "mirror.sync_now",
324
+ "mirror.request_action",
325
+ "mirror.approve_code",
326
+ "mirror.agentcash_wallet_export",
327
+ "mirror.register_agent"
328
+ ],
329
+ skill: "mirror",
330
+ setup: [{
331
+ kind: "connection",
332
+ name: "mirror-oauth-configuration",
333
+ required: true,
334
+ message: "Choose a stable public client id for this agent, set MIRROR_OAUTH_CLIENT_ID, and deploy with a reachable OpenEve connection callback URL. Mirror uses OAuth Authorization Code + PKCE and stores provider credentials behind the user's Mirror account."
335
+ }]
336
+ };
337
+ const PROVENANCE_PLUGIN = {
338
+ kind: "provenance",
339
+ role: "connection",
340
+ packageName: "@assemblyline-agents/provenance",
341
+ helper: "defineProvenanceConnection",
342
+ protocol: "mcp",
343
+ transport: "http",
344
+ provider: "provenance",
345
+ description: "User-scoped append-only agent evidence, session reconstruction, tool traces, handoff bundles, and artifacts through Provenance.",
346
+ defaultUrl: "https://provenance.artificialillumination.co/mcp",
347
+ urlEnv: "PROVENANCE_MCP_URL",
348
+ requiredEnv: ["PROVENANCE_OAUTH_CLIENT_ID"],
349
+ optionalEnv: [
350
+ "PROVENANCE_MCP_URL",
351
+ "PROVENANCE_OAUTH_ISSUER",
352
+ "PROVENANCE_OAUTH_REDIRECT_URI",
353
+ "PROVENANCE_AGENT_ID",
354
+ "PROVENANCE_AGENT_NAME",
355
+ "PROVENANCE_AGENT_RUNTIME"
356
+ ],
357
+ capabilities: ["mcp", "oauth", "pkce", "live-tools", "streamable-http", "append-only-ledger", "agent-handoffs"],
358
+ scopes: ["provenance:ledger"],
359
+ subject: "user",
360
+ tools: {
361
+ allow: [
362
+ "provenance.search_events",
363
+ "provenance.get_event",
364
+ "provenance.get_event_range",
365
+ "provenance.get_turn_trace",
366
+ "provenance.get_tool_trace",
367
+ "provenance.get_related_events",
368
+ "provenance.list_sessions",
369
+ "provenance.handoff_bundle",
370
+ "provenance.get_artifact",
371
+ "provenance.get_session_definition",
372
+ "provenance.register_agent",
373
+ "provenance.list_agents",
374
+ "provenance.agent_activity"
375
+ ]
376
+ },
377
+ readToolPatterns: [
378
+ "provenance.search_events",
379
+ "provenance.get_event",
380
+ "provenance.get_event_range",
381
+ "provenance.get_turn_trace",
382
+ "provenance.get_tool_trace",
383
+ "provenance.get_related_events",
384
+ "provenance.list_sessions",
385
+ "provenance.handoff_bundle",
386
+ "provenance.get_artifact",
387
+ "provenance.get_session_definition",
388
+ "provenance.list_agents",
389
+ "provenance.agent_activity"
390
+ ],
391
+ writeToolPatterns: ["provenance.register_agent"],
392
+ skills: [
393
+ "provenance-reconstruction",
394
+ "provenance-handoff-bundle",
395
+ "provenance-ambient-capture"
396
+ ],
397
+ setup: [{
398
+ kind: "connection",
399
+ name: "provenance-oauth-configuration",
400
+ required: true,
401
+ message: "Register this OpenEve deployment as a Provenance public OAuth client with its exact /openeve/connections/callback URI, then set PROVENANCE_OAUTH_CLIENT_ID. Provenance uses Authorization Code + PKCE and binds the issued token to the authenticated user and agent identity."
402
+ }]
403
+ };
404
+ const CONNECTION_PLUGIN_CATALOG = [
405
+ HIGGSFIELD_PLUGIN,
406
+ MIRROR_PLUGIN,
407
+ PROVENANCE_PLUGIN,
408
+ {
409
+ kind: "arcads",
410
+ role: "connection",
411
+ packageName: "@assemblyline-agents/arcads",
412
+ helper: "defineArcadsConnection",
413
+ protocol: "mcp",
414
+ transport: "http",
415
+ provider: "arcads",
416
+ description: "Arcads media analysis, generation, and editing through the official hosted MCP server.",
417
+ defaultUrl: "https://mcp.arcads.ai",
418
+ requiredEnv: ["ARCADS_MCP_CLIENT_ID"],
419
+ optionalEnv: ["ARCADS_MCP_REDIRECT_URI"],
420
+ capabilities: ["mcp", "oauth", "live-tools", "streamable-http"],
421
+ scopes: ["mcp", "offline_access"],
422
+ subject: "user",
423
+ readToolPatterns: [
424
+ "regex:^arcads_(get_(?!upload_url$)|list|search|watch)"
425
+ ],
426
+ writeToolPatterns: [
427
+ "arcads_get_upload_url",
428
+ "regex:^arcads_(?!get_(?!upload_url$)|list|search|watch).+"
429
+ ],
430
+ skill: "arcads",
431
+ setup: [{
432
+ kind: "connection",
433
+ name: "arcads-oauth-configuration",
434
+ required: true,
435
+ message: "Register an OAuth client with https://api.arcads.ai/oauth/register, set ARCADS_MCP_CLIENT_ID, and register the OpenEve connection callback URL. Arcads generation and editing consume account credits."
436
+ }]
437
+ },
438
+ catalogEntry({
439
+ kind: "gmail",
440
+ displayName: "Gmail",
441
+ helper: "defineGmailConnection",
442
+ description: "Gmail threads, messages, attachments, drafts, and sends through an independently authorized MCP connection.",
443
+ urlEnv: "GMAIL_MCP_URL",
444
+ tokenEnv: "GMAIL_MCP_TOKEN",
445
+ scopes: [
446
+ "openid",
447
+ "email",
448
+ "profile",
449
+ "https://www.googleapis.com/auth/gmail.readonly",
450
+ "https://www.googleapis.com/auth/gmail.compose",
451
+ "https://www.googleapis.com/auth/gmail.send"
452
+ ],
453
+ readToolPatterns: ["rest_get", ...READ_PATTERNS],
454
+ writeToolPatterns: ["rest_request", ...WRITE_PATTERNS],
455
+ write: true,
456
+ setupMessage: "Configure a Gmail-only OAuth grant or MCP bridge for this deployment. Do not reuse an umbrella Google grant that also exposes Calendar or Drive."
457
+ }),
458
+ catalogEntry({
459
+ kind: "google-calendar",
460
+ displayName: "Google Calendar",
461
+ helper: "defineGoogleCalendarConnection",
462
+ provider: "google_calendar",
463
+ description: "Google Calendar events, availability, and event changes through an independently authorized MCP connection.",
464
+ urlEnv: "GOOGLE_CALENDAR_MCP_URL",
465
+ tokenEnv: "GOOGLE_CALENDAR_MCP_TOKEN",
466
+ scopes: [
467
+ "openid",
468
+ "email",
469
+ "profile",
470
+ "https://www.googleapis.com/auth/calendar.readonly",
471
+ "https://www.googleapis.com/auth/calendar.events"
472
+ ],
473
+ readToolPatterns: ["rest_get", ...READ_PATTERNS],
474
+ writeToolPatterns: ["rest_request", ...WRITE_PATTERNS],
475
+ write: true,
476
+ setupMessage: "Configure a Google Calendar-only OAuth grant or MCP bridge for this deployment. Do not reuse an umbrella Google grant that also exposes Gmail or Drive."
477
+ }),
478
+ catalogEntry({
479
+ kind: "google-drive",
480
+ displayName: "Google Drive",
481
+ helper: "defineGoogleDriveConnection",
482
+ provider: "google_drive",
483
+ description: "Google Drive file search, downloads, uploads, organization, and sharing through an independently authorized MCP connection.",
484
+ urlEnv: "GOOGLE_DRIVE_MCP_URL",
485
+ tokenEnv: "GOOGLE_DRIVE_MCP_TOKEN",
486
+ scopes: [
487
+ "openid",
488
+ "email",
489
+ "profile",
490
+ "https://www.googleapis.com/auth/drive"
491
+ ],
492
+ readToolPatterns: ["rest_get", ...READ_PATTERNS],
493
+ writeToolPatterns: ["rest_request", ...WRITE_PATTERNS],
494
+ write: true,
495
+ setupMessage: "Configure a Google Drive-only OAuth grant or MCP bridge for this deployment. Do not reuse an umbrella Google grant that also exposes Gmail or Calendar."
496
+ }),
497
+ catalogEntry({ kind: "github", displayName: "GitHub", helper: "defineGitHubMcpConnection", packageName: "@assemblyline-agents/github", description: "GitHub repositories, issues, pull requests, and Actions through GitHub MCP.", defaultUrl: "https://api.githubcopilot.com/mcp/", urlEnv: "GITHUB_MCP_URL", tokenEnv: "GITHUB_MCP_TOKEN", write: true }),
498
+ catalogEntry({ kind: "slack", displayName: "Slack", helper: "defineSlackConnection", packageName: "@assemblyline-agents/slack", description: "Slack workspace search, channels, messages, files, and users through Slack MCP.", defaultUrl: "https://mcp.slack.com/mcp", urlEnv: "SLACK_MCP_URL", tokenEnv: "SLACK_MCP_TOKEN", write: true }),
499
+ catalogEntry({ kind: "telegram", displayName: "Telegram", helper: "defineTelegramConnection", packageName: "@assemblyline-agents/telegram", description: "Telegram account tools through a developer-operated MCP bridge.", urlEnv: "TELEGRAM_MCP_URL", tokenEnv: "TELEGRAM_MCP_TOKEN" }),
500
+ catalogEntry({ kind: "strava", displayName: "Strava", helper: "defineStravaConnection", description: "Strava profile and fitness activity tools through a developer-configured MCP server.", urlEnv: "STRAVA_MCP_URL", tokenEnv: "STRAVA_MCP_TOKEN" }),
501
+ catalogEntry({ kind: "polar", displayName: "Polar", helper: "definePolarConnection", description: "Polar workout and heart-rate tools through a developer-configured MCP server.", urlEnv: "POLAR_MCP_URL", tokenEnv: "POLAR_MCP_TOKEN" }),
502
+ catalogEntry({ kind: "spotify", displayName: "Spotify", helper: "defineSpotifyConnection", description: "Spotify library, playlist, and playback tools through a developer-configured MCP server.", urlEnv: "SPOTIFY_MCP_URL", tokenEnv: "SPOTIFY_MCP_TOKEN", write: true }),
503
+ catalogEntry({ kind: "xai", displayName: "xAI", helper: "defineXaiConnection", description: "xAI and X search tools through a developer-configured MCP server.", urlEnv: "XAI_MCP_URL", tokenEnv: "XAI_MCP_TOKEN" }),
504
+ catalogEntry({
505
+ kind: "exa",
506
+ displayName: "Exa",
507
+ helper: "defineExaConnection",
508
+ description: "Current web search, page retrieval, and advanced filtered search through Exa's official hosted MCP server.",
509
+ defaultUrl: "https://mcp.exa.ai/mcp",
510
+ urlEnv: "EXA_MCP_URL",
511
+ tokenEnv: "EXA_API_KEY",
512
+ tokenRequired: false,
513
+ subject: "environment",
514
+ tools: {
515
+ allow: [
516
+ "web_search_exa",
517
+ "web_fetch_exa",
518
+ "web_search_advanced_exa"
519
+ ]
520
+ },
521
+ readToolPatterns: [
522
+ "web_search_exa",
523
+ "web_fetch_exa",
524
+ "web_search_advanced_exa"
525
+ ],
526
+ setupMessage: "Exa's hosted MCP works without credentials. Optionally set EXA_API_KEY to raise production rate limits; OpenEve sends it only as the x-api-key header."
527
+ }),
528
+ catalogEntry({ kind: "plaid", displayName: "Plaid", helper: "definePlaidConnection", description: "Read-only Plaid financial tools through a developer-configured MCP bridge.", urlEnv: "PLAID_MCP_URL", tokenEnv: "PLAID_MCP_TOKEN" }),
529
+ catalogEntry({ kind: "granola", displayName: "Granola", helper: "defineGranolaConnection", description: "Granola meeting notes, transcripts, and action items through Granola MCP.", defaultUrl: "https://mcp.granola.ai/mcp", urlEnv: "GRANOLA_MCP_URL", tokenEnv: "GRANOLA_MCP_TOKEN" }),
530
+ catalogEntry({ kind: "linear", displayName: "Linear", helper: "defineLinearConnection", description: "Linear issues, projects, cycles, and teams through Linear MCP.", defaultUrl: "https://mcp.linear.app/mcp", urlEnv: "LINEAR_MCP_URL", tokenEnv: "LINEAR_MCP_TOKEN", write: true }),
531
+ catalogEntry({ kind: "notion", displayName: "Notion", helper: "defineNotionConnection", description: "Notion pages, databases, comments, and workspace search through Notion MCP.", defaultUrl: "https://mcp.notion.com/mcp", urlEnv: "NOTION_MCP_URL", tokenEnv: "NOTION_MCP_TOKEN", write: true }),
532
+ catalogEntry({ kind: "monday", displayName: "monday.com", helper: "defineMondayConnection", description: "monday.com boards, items, workspaces, and docs through Platform MCP.", defaultUrl: "https://mcp.monday.com/mcp", urlEnv: "MONDAY_MCP_URL", tokenEnv: "MONDAY_MCP_TOKEN", write: true }),
533
+ catalogEntry({ kind: "jira", displayName: "Jira", helper: "defineJiraConnection", description: "Jira issues, projects, transitions, and JQL search through Atlassian MCP.", defaultUrl: "https://mcp.atlassian.com/v1/mcp/authv2", urlEnv: "JIRA_MCP_URL", tokenEnv: "JIRA_MCP_TOKEN", write: true }),
534
+ catalogEntry({ kind: "hubspot", displayName: "HubSpot", helper: "defineHubSpotConnection", description: "HubSpot CRM records and associations through HubSpot MCP.", defaultUrl: "https://mcp.hubspot.com", urlEnv: "HUBSPOT_MCP_URL", tokenEnv: "HUBSPOT_MCP_TOKEN" }),
535
+ catalogEntry({ kind: "figma", displayName: "Figma", helper: "defineFigmaConnection", description: "Figma design, FigJam, Slides, and Dev Mode context through Figma MCP.", defaultUrl: "https://mcp.figma.com/mcp", urlEnv: "FIGMA_MCP_URL", tokenEnv: "FIGMA_MCP_TOKEN", write: true }),
536
+ catalogEntry({ kind: "paper", displayName: "Paper", helper: "definePaperConnection", description: "Paper design tools through a developer-exposed local MCP bridge.", urlEnv: "PAPER_MCP_URL", tokenEnv: "PAPER_MCP_TOKEN", tokenRequired: false, write: true }),
537
+ catalogEntry({ kind: "sentry", displayName: "Sentry", helper: "defineSentryConnection", description: "Sentry issues, events, projects, and teams through Sentry MCP.", defaultUrl: "https://mcp.sentry.dev/mcp", urlEnv: "SENTRY_MCP_URL", tokenEnv: "SENTRY_MCP_TOKEN", write: true }),
538
+ catalogEntry({ kind: "supabase", displayName: "Supabase", helper: "defineSupabaseConnection", description: "Supabase projects, databases, docs, and Edge Functions through Supabase MCP.", defaultUrl: "https://mcp.supabase.com/mcp", urlEnv: "SUPABASE_MCP_URL", tokenEnv: "SUPABASE_MCP_TOKEN", write: true }),
539
+ catalogEntry({ kind: "metabase", displayName: "Metabase", helper: "defineMetabaseConnection", description: "Metabase semantic-layer queries, questions, and dashboards through an instance MCP endpoint.", urlEnv: "METABASE_MCP_URL", tokenEnv: "METABASE_MCP_TOKEN", tokenRequired: false, write: true }),
540
+ catalogEntry({ kind: "cloudflare", displayName: "Cloudflare", helper: "defineCloudflareConnection", description: "Cloudflare API and account tools through Cloudflare MCP.", defaultUrl: "https://mcp.cloudflare.com/mcp", urlEnv: "CLOUDFLARE_MCP_URL", tokenEnv: "CLOUDFLARE_MCP_TOKEN", write: true }),
541
+ catalogEntry({ kind: "vercel", displayName: "Vercel", helper: "defineVercelConnection", description: "Vercel projects, deployments, logs, and teams through Vercel MCP.", defaultUrl: "https://mcp.vercel.com", urlEnv: "VERCEL_MCP_URL", tokenEnv: "VERCEL_MCP_TOKEN", write: true }),
542
+ catalogEntry({ kind: "railway", displayName: "Railway", helper: "defineRailwayConnection", packageName: "@assemblyline-agents/railway", description: "Railway projects, services, variables, and deployments through Railway MCP.", defaultUrl: "https://mcp.railway.com", urlEnv: "RAILWAY_MCP_URL", tokenEnv: "RAILWAY_MCP_TOKEN", write: true }),
543
+ catalogEntry({ kind: "refero", displayName: "Refero", helper: "defineReferoConnection", description: "Refero design references, screens, and product flows through Refero MCP.", defaultUrl: "https://api.refero.design/mcp", urlEnv: "REFERO_MCP_URL", tokenEnv: "REFERO_MCP_BEARER_TOKEN" }),
544
+ catalogEntry({ kind: "agentcash", displayName: "AgentCash", helper: "defineAgentCashConnection", description: "Paid API discovery, schema, balance, and request tools through an AgentCash MCP bridge.", urlEnv: "AGENTCASH_MCP_URL", tokenEnv: "AGENTCASH_MCP_BRIDGE_TOKEN", write: true }),
545
+ {
546
+ kind: "soundcloud",
547
+ role: "connection",
548
+ packageName: "@assemblyline-agents/soundcloud",
549
+ helper: "defineSoundCloudConnection",
550
+ protocol: "openapi",
551
+ provider: "soundcloud",
552
+ description: "SoundCloud tracks, playlists, users, likes, follows, reposts, and comments through the official API.",
553
+ defaultSpec: "https://developers.soundcloud.com/docs/api/explorer/api.json",
554
+ defaultBaseUrl: "https://api.soundcloud.com",
555
+ requiredEnv: ["SOUNDCLOUD_CLIENT_ID", "SOUNDCLOUD_CLIENT_SECRET"],
556
+ optionalEnv: ["SOUNDCLOUD_REDIRECT_URI"],
557
+ capabilities: ["openapi", "oauth", "live-tools"],
558
+ scopes: [],
559
+ subject: "user",
560
+ tools: { block: ["post_tracks"] },
561
+ readToolPatterns: ["regex:^get_"],
562
+ writeToolPatterns: ["regex:^(post|put|patch|delete)_"],
563
+ skill: "soundcloud",
564
+ setup: [{
565
+ kind: "connection",
566
+ name: "soundcloud-developer-configuration",
567
+ required: true,
568
+ message: "Register a SoundCloud application and set SOUNDCLOUD_CLIENT_ID and SOUNDCLOUD_CLIENT_SECRET. OpenEve handles OAuth 2.1 PKCE; the developer owns the app and redirect URI."
569
+ }]
570
+ },
571
+ catalogEntry({
572
+ kind: "browser-use",
573
+ displayName: "Browser Use Cloud",
574
+ helper: "defineBrowserUseConnection",
575
+ description: "Hosted browser automation sessions through Browser Use Cloud MCP.",
576
+ defaultUrl: "https://api.browser-use.com/v3/mcp",
577
+ tokenEnv: "BROWSER_USE_API_KEY",
578
+ tokenHeader: "x-browser-use-api-key",
579
+ subject: "environment",
580
+ tools: { allow: ["get_session", "get_session_messages", "list_sessions", "list_browser_profiles", "run_session", "send_task", "stop_session"] },
581
+ readToolPatterns: ["get_session", "get_session_messages", "list_sessions", "list_browser_profiles"],
582
+ writeToolPatterns: ["run_session", "send_task", "stop_session"],
583
+ setupMessage: "Set BROWSER_USE_API_KEY for the OpenEve host. Browser profiles, cost limits, and write authority remain developer-controlled."
584
+ }),
585
+ catalogEntry({
586
+ kind: "orgo",
587
+ displayName: "Orgo",
588
+ helper: "defineOrgoConnection",
589
+ description: "Persistent Orgo cloud desktops controlled through a packaged, credential-scrubbing REST bridge.",
590
+ transport: "stdio",
591
+ tokenEnv: "ORGO_API_KEY",
592
+ subject: "environment",
593
+ tools: {
594
+ allow: [
595
+ "list_workspaces",
596
+ "create_workspace",
597
+ "list_computers",
598
+ "get_computer",
599
+ "create_computer",
600
+ "delete_computer",
601
+ "start_computer",
602
+ "stop_computer",
603
+ "restart_computer",
604
+ "screenshot",
605
+ "click",
606
+ "drag",
607
+ "type_text",
608
+ "press_key",
609
+ "scroll",
610
+ "wait",
611
+ "execute_bash",
612
+ "execute_python"
613
+ ]
614
+ },
615
+ readToolPatterns: [
616
+ "list_workspaces",
617
+ "list_computers",
618
+ "get_computer",
619
+ "screenshot"
620
+ ],
621
+ writeToolPatterns: [
622
+ "create_workspace",
623
+ "create_computer",
624
+ "delete_computer",
625
+ "start_computer",
626
+ "stop_computer",
627
+ "restart_computer",
628
+ "click",
629
+ "drag",
630
+ "type_text",
631
+ "press_key",
632
+ "scroll",
633
+ "wait",
634
+ "execute_bash",
635
+ "execute_python"
636
+ ],
637
+ optionalEnv: ["ORGO_API_BASE_URL"],
638
+ setupMessage: "Set ORGO_API_KEY on the OpenEve runtime host. The developer owns Orgo workspaces, account limits, auto-stop policy, login state, and billing; the packaged bridge strips VNC credentials from results."
639
+ }),
640
+ catalogEntry({
641
+ kind: "peekaboo",
642
+ displayName: "Peekaboo",
643
+ helper: "definePeekabooConnection",
644
+ description: "Same-host macOS accessibility and input automation through Peekaboo's stdio MCP server.",
645
+ transport: "stdio",
646
+ subject: "environment",
647
+ tools: { allow: ["inspect_ui", "list", "permissions", "click", "type", "hotkey", "scroll", "drag", "swipe", "move", "set_value", "perform_action", "app", "window", "menu", "dock", "dialog", "space"] },
648
+ hostRequirements: {
649
+ deployTargets: ["local"],
650
+ platforms: ["darwin"],
651
+ message: "Peekaboo is a same-host macOS connection. Run this agent with the local deploy target on the Mac being controlled; hosted Railway, Docker, Fly, and VPS deployments cannot reach back to that Mac."
652
+ },
653
+ readToolPatterns: ["inspect_ui", "list", "permissions"],
654
+ writeToolPatterns: ["click", "type", "hotkey", "scroll", "drag", "swipe", "move", "set_value", "perform_action", "app", "window", "menu", "dock", "dialog", "space"],
655
+ setupMessage: "Local deployment only: install Peekaboo on the OpenEve Mac (macOS 15+), grant that host process Accessibility and Screen Recording permissions, and use the local deploy target. Hosted targets are rejected."
656
+ }),
657
+ catalogEntry({
658
+ kind: "computer-use",
659
+ displayName: "OpenEve Computer Use",
660
+ helper: "defineComputerUseConnection",
661
+ description: "End-to-end encrypted accessibility and input automation on a paired Mac through the OpenEve device relay.",
662
+ transport: "relay",
663
+ defaultUrl: "https://computer-use.openeve.dev/v1",
664
+ urlEnv: "OPENEVE_COMPUTER_USE_RELAY_URL",
665
+ credentialEnv: "OPENEVE_COMPUTER_USE_BINDING",
666
+ subject: "environment",
667
+ tools: { allow: ["inspect_ui", "list", "permissions", "click", "type", "hotkey", "scroll", "drag", "swipe", "move", "set_value", "perform_action", "app", "window", "menu", "dock", "dialog", "space"] },
668
+ readToolPatterns: ["inspect_ui", "list", "permissions"],
669
+ writeToolPatterns: ["click", "type", "hotkey", "scroll", "drag", "swipe", "move", "set_value", "perform_action", "app", "window", "menu", "dock", "dialog", "space"],
670
+ setupMessage: "Install OpenEve Agent Builder on the Mac, enable Computer Host, pair this deployment, and set the generated OPENEVE_COMPUTER_USE_BINDING secret on the remote runtime. The binding is device-scoped and must not be placed in prompts or source control."
671
+ }),
672
+ catalogEntry({
673
+ kind: "ffmpeg",
674
+ displayName: "FFmpeg",
675
+ helper: "defineFfmpegConnection",
676
+ description: "Workspace-scoped media inspection, trimming, transcoding, stitching, audio replacement, frame extraction, and vertical reframing through FFmpeg.",
677
+ transport: "stdio",
678
+ subject: "environment",
679
+ tools: {
680
+ allow: [
681
+ "probe_media",
682
+ "trim_media",
683
+ "transcode_media",
684
+ "concat_media",
685
+ "replace_audio",
686
+ "extract_frames",
687
+ "reframe_vertical"
688
+ ]
689
+ },
690
+ readToolPatterns: ["probe_media"],
691
+ writeToolPatterns: [
692
+ "trim_media",
693
+ "transcode_media",
694
+ "concat_media",
695
+ "replace_audio",
696
+ "extract_frames",
697
+ "reframe_vertical"
698
+ ],
699
+ setupMessage: "Install ffmpeg and ffprobe on the OpenEve runtime host. The plugin keeps all media paths inside its configured workspace root and exposes typed operations instead of arbitrary FFmpeg flags."
700
+ }),
701
+ catalogEntry({
702
+ kind: "remotion",
703
+ displayName: "Remotion",
704
+ helper: "defineRemotionConnection",
705
+ description: "Workspace-scoped Remotion composition discovery, version checks, video renders, and still renders through the project CLI.",
706
+ transport: "stdio",
707
+ subject: "environment",
708
+ tools: {
709
+ allow: [
710
+ "list_compositions",
711
+ "check_versions",
712
+ "render_video",
713
+ "render_still"
714
+ ]
715
+ },
716
+ readToolPatterns: ["list_compositions", "check_versions"],
717
+ writeToolPatterns: ["render_video", "render_still"],
718
+ setupMessage: "Install Remotion and @remotion/cli in the configured project. OpenEve uses Remotion's supported CLI and Agent Skills workflow; it does not depend on Remotion's deprecated hosted MCP server."
719
+ })
720
+ ];
721
+ function validatePluginAccess(plugin, access) {
722
+ if (!access)
723
+ throw new Error(`${plugin.helper}: access must explicitly select read and write authority.`);
724
+ if (access.write !== false && plugin.writeToolPatterns.length === 0) {
725
+ throw new Error(`${plugin.helper}: ${plugin.provider} is a read-only plugin and cannot enable writes.`);
726
+ }
727
+ }
728
+ function pluginAccessDefinition(plugin, access) {
729
+ return {
730
+ read: { tools: [...plugin.readToolPatterns] },
731
+ write: access.write === false
732
+ ? false
733
+ : {
734
+ tools: [...plugin.writeToolPatterns],
735
+ approval: normalizePluginApproval(access.write.approval)
736
+ }
737
+ };
738
+ }
739
+ function tokenAuth(tokenEnv) {
740
+ if (!tokenEnv)
741
+ return undefined;
742
+ return ({ env }) => ({
743
+ principalType: "user",
744
+ displayName: `${tokenEnv} bearer token`,
745
+ getToken: async () => {
746
+ const token = env[tokenEnv]?.trim();
747
+ if (!token)
748
+ throw new Error(`Missing ${tokenEnv}. Configure the provider credential or pass a custom auth definition.`);
749
+ return { token };
750
+ }
751
+ });
752
+ }
753
+ function environmentValue(name) {
754
+ if (!name || typeof process === "undefined")
755
+ return undefined;
756
+ return process.env[name]?.trim() || undefined;
757
+ }
758
+ function tokenHeader(name, tokenEnv) {
759
+ if (!tokenEnv)
760
+ return undefined;
761
+ return ({ env }) => {
762
+ const token = env[tokenEnv]?.trim();
763
+ if (!token)
764
+ throw new Error(`Missing ${tokenEnv}. Configure the provider credential or pass custom headers.`);
765
+ return { [name]: token };
766
+ };
767
+ }
768
+ function mergeHeaders(first, second) {
769
+ if (!first)
770
+ return second;
771
+ if (!second)
772
+ return first;
773
+ return async (context) => ({
774
+ ...(await resolveHeaders(first, context)),
775
+ ...(await resolveHeaders(second, context))
776
+ });
777
+ }
778
+ async function resolveHeaders(definition, context) {
779
+ if (typeof definition === "function")
780
+ return definition(context);
781
+ const resolved = {};
782
+ for (const [name, value] of Object.entries(definition)) {
783
+ resolved[name] = await resolveHeaderValue(value, context);
784
+ }
785
+ return resolved;
786
+ }
787
+ async function resolveHeaderValue(value, context) {
788
+ return typeof value === "function" ? value(context) : value;
789
+ }
790
+ function cloneToolFilter(filter) {
791
+ if (!filter)
792
+ return undefined;
793
+ return "allow" in filter ? { allow: [...filter.allow] } : { block: [...filter.block] };
794
+ }
795
+ function cloneHostRequirements(requirements) {
796
+ return {
797
+ ...requirements,
798
+ ...(requirements.deployTargets ? { deployTargets: [...requirements.deployTargets] } : {}),
799
+ ...(requirements.platforms ? { platforms: [...requirements.platforms] } : {})
800
+ };
801
+ }
802
+ function normalizePluginApproval(approval) {
803
+ if (approval === "always" || approval === "once" || approval === "never")
804
+ return { mode: approval };
805
+ return approval;
806
+ }
807
+ function cloneMetadata(entry) {
808
+ return {
809
+ ...entry,
810
+ requiredEnv: [...(entry.requiredEnv ?? [])],
811
+ optionalEnv: [...(entry.optionalEnv ?? [])],
812
+ capabilities: [...(entry.capabilities ?? [])],
813
+ scopes: [...(entry.scopes ?? [])],
814
+ readToolPatterns: [...entry.readToolPatterns],
815
+ writeToolPatterns: [...entry.writeToolPatterns],
816
+ ...(entry.skills ? { skills: [...entry.skills] } : {}),
817
+ ...(entry.tools ? { tools: cloneToolFilter(entry.tools) } : {}),
818
+ ...(entry.hostRequirements ? { hostRequirements: cloneHostRequirements(entry.hostRequirements) } : {})
819
+ };
820
+ }
821
+ function isPluginModule(value) {
822
+ if (!value || typeof value !== "object")
823
+ return false;
824
+ const connections = value.connections;
825
+ return connections === undefined || (Array.isArray(connections) && connections.every((entry) => Boolean(entry) && entry.role === "connection" && typeof entry.kind === "string" && typeof entry.helper === "string"));
826
+ }
827
+ export function connectionPluginBinding(metadata) {
828
+ return { kind: metadata.kind };
829
+ }
830
+ //# sourceMappingURL=plugin.js.map