@rafads/execution 1.5.38

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.
@@ -0,0 +1,1226 @@
1
+ // src/errors.ts
2
+ import * as Data from "effect/Data";
3
+ import { CodeExecutionError } from "@rafads/codemode-core";
4
+ var ExecutionToolError = class extends Data.TaggedError("ExecutionToolError") {
5
+ };
6
+
7
+ // src/tool-invoker.ts
8
+ import { Effect, Predicate } from "effect";
9
+ import * as Cause from "effect/Cause";
10
+ import {
11
+ annotateToolResultOutcome,
12
+ authToolFailure,
13
+ isUserActionableError,
14
+ isToolResult,
15
+ ToolResult,
16
+ ToolAddress,
17
+ parseToolAddress
18
+ } from "@rafads/sdk/core";
19
+ var OPAQUE_DEFECT_MESSAGE = "Internal tool error";
20
+ var TOOL_DESCRIBE_SUGGESTION_LIMIT = 5;
21
+ var TOOL_ERROR_TYPESCRIPT = "{ code: string; message: string; status?: number; details?: unknown; retryable?: boolean }";
22
+ var TOOL_HTTP_META_TYPESCRIPT = "{ status: number; headers: { [k: string]: string; } }";
23
+ var TOOL_FILE_TYPESCRIPT = '{ _tag: "ToolFile"; name?: string; mimeType: string; encoding: "base64"; data: string; byteLength: number; }';
24
+ var wrapOutputTypeScript = (outputTypeScript) => `{ ok: true; data: ${outputTypeScript ?? "unknown"}; http?: ToolHttpMeta } | { ok: false; error: ToolError }`;
25
+ var withToolResultDefinitions = (definitions) => ({
26
+ ...definitions ?? {},
27
+ ToolError: TOOL_ERROR_TYPESCRIPT,
28
+ ToolHttpMeta: TOOL_HTTP_META_TYPESCRIPT,
29
+ ToolFile: TOOL_FILE_TYPESCRIPT
30
+ });
31
+ var ADDRESS_PREFIX = "tools.";
32
+ var pathToAddress = (path) => {
33
+ if (path.startsWith(ADDRESS_PREFIX)) return ToolAddress.make(path);
34
+ if (parseToolAddress(`${ADDRESS_PREFIX}${path}`)) {
35
+ return ToolAddress.make(`${ADDRESS_PREFIX}${path}`);
36
+ }
37
+ return ToolAddress.make(path);
38
+ };
39
+ var addressToPath = (address) => address.startsWith(ADDRESS_PREFIX) ? address.slice(ADDRESS_PREFIX.length) : address;
40
+ var BUILTIN_TOOL_DESCRIPTIONS = /* @__PURE__ */ new Map([
41
+ [
42
+ "search",
43
+ {
44
+ path: "search",
45
+ name: "search",
46
+ description: "Search available Executor tools. An empty query with a namespace enumerates that integration's full catalog, sorted by path.",
47
+ inputTypeScript: "{ query: string; namespace?: string; limit?: number; offset?: number; }",
48
+ outputTypeScript: "{ items: ToolDiscoveryResult[]; total: number; hasMore: boolean; nextOffset: number | null; }",
49
+ typeScriptDefinitions: {
50
+ ToolDiscoveryResult: "{ path: string; name: string; description?: string; integration: string; score: number; }"
51
+ }
52
+ }
53
+ ],
54
+ [
55
+ "executor.integrations.list",
56
+ {
57
+ path: "executor.integrations.list",
58
+ name: "executor.integrations.list",
59
+ description: "List configured Executor integrations.",
60
+ inputTypeScript: "{ query?: string; limit?: number; offset?: number; }",
61
+ outputTypeScript: "{ items: ExecutorIntegrationListItem[]; total: number; hasMore: boolean; nextOffset: number | null; }",
62
+ typeScriptDefinitions: {
63
+ ExecutorIntegrationListItem: "{ id: string; name: string; description?: string; kind: string; canRemove?: boolean; canRefresh?: boolean; toolCount: number; }"
64
+ }
65
+ }
66
+ ],
67
+ [
68
+ "describe.tool",
69
+ {
70
+ path: "describe.tool",
71
+ name: "describe.tool",
72
+ description: "Describe a tool's compact TypeScript input and output shapes.",
73
+ inputTypeScript: "{ path: string; }",
74
+ outputTypeScript: "DescribedTool",
75
+ typeScriptDefinitions: {
76
+ DescribedTool: '{ path: string; name: string; description?: string; inputTypeScript?: string; outputTypeScript?: string; typeScriptDefinitions?: { [k: string]: string; }; error?: { code: "tool_not_found"; message: string; suggestions?: string[]; }; }'
77
+ }
78
+ }
79
+ ],
80
+ [
81
+ "use",
82
+ {
83
+ path: "use",
84
+ name: "use",
85
+ description: 'Make an external service capability work in ONE call: state the need ("transactional email", "a database", or a provider name like "resend") and get back a wired, callable integration \u2014 resolve, add, and connect handled internally. Returns ready (tools callable now), pending (one human credential click left; re-press to check), compare (full ranked set, on request), or an honest no_match with the catalog taxonomy. The answer is ONE verified provider with a machine-readable `sponsored` label and the count of alternatives; override by pressing again with not: ["<id>"].',
86
+ inputTypeScript: "{ need: string; not?: string[]; compare?: boolean; }",
87
+ outputTypeScript: "UseNeedReply",
88
+ typeScriptDefinitions: {
89
+ UseNeedReply: '{ status: "ready"; provider: ProviderCard; alternativesCount: number; toolsPath?: string; instructions: string; } | { status: "pending"; provider: ProviderCard; alternativesCount: number; url: string; instructions: string; } | { status: "compare"; candidates: ProviderCard[]; instructions: string; } | { status: "no_match"; categories: string[]; instructions: string; } | { status: "error"; step: string; message: string; instructions: string; }',
90
+ ProviderCard: "{ id: string; name: string; domain: string; category: string; sponsored: boolean; offer?: { terms: string; url: string; }; verifiedAt: string; }"
91
+ }
92
+ }
93
+ ]
94
+ ]);
95
+ var newCorrelationId = () => {
96
+ return Math.floor(Math.random() * 4294967296).toString(16).padStart(8, "0");
97
+ };
98
+ var validationIssues = (value) => {
99
+ if (typeof value !== "object" || value === null) return null;
100
+ const issues = value.issues;
101
+ return Array.isArray(issues) ? issues : null;
102
+ };
103
+ var openApiPreflightMessage = (value) => {
104
+ if (!Predicate.isTagged(value, "OpenApiInvocationError")) return null;
105
+ const err = value;
106
+ if (err.cause !== void 0) return null;
107
+ if (!Predicate.isTagged(err.statusCode, "None")) return null;
108
+ return typeof err.message === "string" && err.message.length > 0 ? err.message : null;
109
+ };
110
+ var credentialResolutionToolFailure = (input) => authToolFailure({
111
+ code: input.reauthRequired === true ? "oauth_reauth_required" : "oauth_refresh_failed",
112
+ message: input.reauthRequired === true ? `OAuth connection "${input.label}" requires reauthorization: ${input.message}` : `OAuth connection "${input.label}" could not be resolved: ${input.message}`,
113
+ credential: {
114
+ kind: "oauth",
115
+ label: input.label
116
+ }
117
+ });
118
+ var bindingToolFailure = (value) => {
119
+ if (!Predicate.isTagged(value, "BindingError")) return null;
120
+ const maybeBinding = value;
121
+ const details = {};
122
+ if (typeof maybeBinding.role === "string") details.role = maybeBinding.role;
123
+ if (typeof maybeBinding.integration === "string") details.integration = maybeBinding.integration;
124
+ if (typeof maybeBinding.requestedConnection === "string") {
125
+ details.requestedConnection = maybeBinding.requestedConnection;
126
+ }
127
+ return {
128
+ code: "binding_error",
129
+ message: typeof maybeBinding.message === "string" ? maybeBinding.message : "Tool binding failed.",
130
+ ...Object.keys(details).length > 0 ? { details } : {}
131
+ };
132
+ };
133
+ var expectedToolFailure = (value) => {
134
+ if (isUserActionableError(value)) {
135
+ return {
136
+ code: value.code,
137
+ message: value.userMessage
138
+ };
139
+ }
140
+ if (Predicate.isTagged(value, "ToolNotFoundError") && "address" in value) {
141
+ const suggestions = "suggestions" in value && Array.isArray(value.suggestions) ? value.suggestions.map((suggestion) => addressToPath(String(suggestion))) : void 0;
142
+ const address = addressToPath(String(value.address));
143
+ return {
144
+ code: "tool_not_found",
145
+ message: `Tool not found: ${address}`,
146
+ details: { path: address, ...suggestions ? { suggestions } : {} }
147
+ };
148
+ }
149
+ if (Predicate.isTagged(value, "ToolBlockedError") && "address" in value) {
150
+ return {
151
+ code: "tool_blocked",
152
+ message: `Tool blocked by policy: ${addressToPath(String(value.address))}`,
153
+ details: value
154
+ };
155
+ }
156
+ if (Predicate.isTagged(value, "ToolInvocationError")) {
157
+ const cause = value.cause;
158
+ if (isUserActionableError(cause)) {
159
+ return {
160
+ code: cause.code,
161
+ message: cause.userMessage
162
+ };
163
+ }
164
+ const binding = bindingToolFailure(cause);
165
+ if (binding) return binding;
166
+ const issues = validationIssues(cause);
167
+ if (issues) {
168
+ return {
169
+ code: "invalid_tool_arguments",
170
+ message: "Tool arguments did not match the input schema.",
171
+ details: { issues }
172
+ };
173
+ }
174
+ const preflight = openApiPreflightMessage(cause);
175
+ if (preflight) {
176
+ return {
177
+ code: "invalid_tool_arguments",
178
+ message: preflight
179
+ };
180
+ }
181
+ }
182
+ return null;
183
+ };
184
+ var extractNamespace = (path) => {
185
+ const normalized = addressToPath(path);
186
+ const idx = normalized.indexOf(".");
187
+ return idx === -1 ? normalized : normalized.slice(0, idx);
188
+ };
189
+ var makeExecutorToolInvoker = (executor, options) => ({
190
+ invoke: Effect.fn("mcp.tool.dispatch")(function* ({ path, args }) {
191
+ yield* Effect.annotateCurrentSpan({
192
+ "mcp.tool.name": path,
193
+ "mcp.tool.integration": extractNamespace(path)
194
+ });
195
+ const address = pathToAddress(path);
196
+ const result = yield* executor.execute(address, args, options.invokeOptions).pipe(
197
+ Effect.catchTag(
198
+ "CredentialResolutionError",
199
+ (err) => Effect.succeed(
200
+ credentialResolutionToolFailure({
201
+ label: `${err.integration}.${err.owner}.${err.name}`,
202
+ message: err.message,
203
+ reauthRequired: err.reauthRequired
204
+ })
205
+ )
206
+ ),
207
+ Effect.catchCause((cause) => {
208
+ const err = cause.reasons.find(Cause.isFailReason)?.error;
209
+ const expected = expectedToolFailure(err);
210
+ if (expected) {
211
+ return Effect.succeed(ToolResult.fail(expected));
212
+ }
213
+ if (isElicitationDeclinedError(err)) {
214
+ return Effect.fail(
215
+ new ExecutionToolError({
216
+ message: `Tool "${addressToPath(String(err.address))}" requires approval but the request was ${err.action === "cancel" ? "cancelled" : "declined"} by the user.`,
217
+ cause: err
218
+ })
219
+ );
220
+ }
221
+ const correlationId = newCorrelationId();
222
+ return Effect.logError("tool dispatch failed", cause).pipe(
223
+ Effect.annotateLogs({
224
+ "executor.correlation_id": correlationId,
225
+ "mcp.tool.name": path
226
+ }),
227
+ Effect.flatMap(
228
+ () => Effect.fail(
229
+ new ExecutionToolError({
230
+ message: `${OPAQUE_DEFECT_MESSAGE} [${correlationId}]`,
231
+ cause: err ?? cause
232
+ })
233
+ )
234
+ )
235
+ );
236
+ })
237
+ );
238
+ yield* annotateToolResultOutcome(result);
239
+ if (isToolResult(result)) {
240
+ return result;
241
+ }
242
+ return { ok: true, data: result };
243
+ })
244
+ });
245
+ var isElicitationDeclinedError = (value) => Predicate.isTagged(value, "ElicitationDeclinedError") && value !== null && typeof value === "object" && "address" in value && typeof value.address === "string" && "action" in value && (value.action === "cancel" || value.action === "decline");
246
+ var paginate = (all, offset, limit) => {
247
+ const total = all.length;
248
+ const start = Math.min(Math.max(offset, 0), total);
249
+ const items = all.slice(start, start + limit);
250
+ const consumed = start + items.length;
251
+ const hasMore = consumed < total;
252
+ return {
253
+ items,
254
+ total,
255
+ hasMore,
256
+ nextOffset: hasMore ? consumed : null
257
+ };
258
+ };
259
+ var toSearchableTool = (tool) => ({
260
+ path: addressToPath(String(tool.address)),
261
+ integration: String(tool.integration),
262
+ name: String(tool.name),
263
+ description: tool.description
264
+ });
265
+ var SEARCH_FIELD_WEIGHTS = {
266
+ path: 12,
267
+ integration: 8,
268
+ name: 10,
269
+ description: 5
270
+ };
271
+ var normalizeSearchText = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_./:-]+/g, " ").toLowerCase().trim();
272
+ var tokenizeSearchText = (value) => normalizeSearchText(value).split(/[^a-z0-9]+/).map((token) => token.trim()).filter(Boolean);
273
+ var prepareField = (value) => ({
274
+ raw: normalizeSearchText(value ?? ""),
275
+ tokens: tokenizeSearchText(value ?? "")
276
+ });
277
+ var scorePreparedField = (query, queryTokens, field, weight) => {
278
+ if (field.raw.length === 0) {
279
+ return {
280
+ score: 0,
281
+ matchedTokens: /* @__PURE__ */ new Set(),
282
+ exactPhraseMatch: false
283
+ };
284
+ }
285
+ let score = 0;
286
+ const matchedTokens = /* @__PURE__ */ new Set();
287
+ const exactPhraseMatch = query.length > 0 && field.raw.includes(query);
288
+ if (query.length > 0) {
289
+ if (field.raw === query) {
290
+ score += weight * 14;
291
+ } else if (field.raw.startsWith(query)) {
292
+ score += weight * 9;
293
+ } else if (exactPhraseMatch) {
294
+ score += weight * 6;
295
+ }
296
+ }
297
+ for (const token of queryTokens) {
298
+ if (field.tokens.includes(token)) {
299
+ score += weight * 4;
300
+ matchedTokens.add(token);
301
+ continue;
302
+ }
303
+ if (field.tokens.some((candidate) => candidate.startsWith(token) || token.startsWith(candidate))) {
304
+ score += weight * 2;
305
+ matchedTokens.add(token);
306
+ continue;
307
+ }
308
+ if (field.raw.includes(token)) {
309
+ score += weight;
310
+ matchedTokens.add(token);
311
+ }
312
+ }
313
+ return {
314
+ score,
315
+ matchedTokens,
316
+ exactPhraseMatch
317
+ };
318
+ };
319
+ var matchesNamespace = (tool, namespace) => {
320
+ if (!namespace || normalizeSearchText(namespace).length === 0) {
321
+ return true;
322
+ }
323
+ const namespaceTokens = tokenizeSearchText(namespace);
324
+ if (namespaceTokens.length === 0) {
325
+ return true;
326
+ }
327
+ const integrationTokens = tokenizeSearchText(tool.integration);
328
+ const pathTokens = tokenizeSearchText(tool.path);
329
+ const isPrefixMatch = (tokens) => namespaceTokens.every((token, index) => tokens[index] === token);
330
+ return isPrefixMatch(integrationTokens) || isPrefixMatch(pathTokens);
331
+ };
332
+ var scoreToolMatch = (tool, query) => {
333
+ const normalizedQuery = normalizeSearchText(query);
334
+ const queryTokens = tokenizeSearchText(query);
335
+ if (normalizedQuery.length === 0 || queryTokens.length === 0) {
336
+ return null;
337
+ }
338
+ const path = prepareField(tool.path);
339
+ const integration = prepareField(tool.integration);
340
+ const name = prepareField(tool.name);
341
+ const description = prepareField(tool.description);
342
+ const fieldScores = [
343
+ scorePreparedField(normalizedQuery, queryTokens, path, SEARCH_FIELD_WEIGHTS.path),
344
+ scorePreparedField(normalizedQuery, queryTokens, integration, SEARCH_FIELD_WEIGHTS.integration),
345
+ scorePreparedField(normalizedQuery, queryTokens, name, SEARCH_FIELD_WEIGHTS.name),
346
+ scorePreparedField(normalizedQuery, queryTokens, description, SEARCH_FIELD_WEIGHTS.description)
347
+ ];
348
+ const matchedTokens = /* @__PURE__ */ new Set();
349
+ let score = 0;
350
+ let exactPhraseMatch = false;
351
+ for (const fieldScore of fieldScores) {
352
+ score += fieldScore.score;
353
+ exactPhraseMatch ||= fieldScore.exactPhraseMatch;
354
+ for (const token of fieldScore.matchedTokens) {
355
+ matchedTokens.add(token);
356
+ }
357
+ }
358
+ if (matchedTokens.size === 0) {
359
+ return null;
360
+ }
361
+ const coverage = matchedTokens.size / queryTokens.length;
362
+ const minimumCoverage = queryTokens.length <= 2 ? 1 : 0.6;
363
+ if (coverage < minimumCoverage && !exactPhraseMatch) {
364
+ return null;
365
+ }
366
+ if (coverage === 1) {
367
+ score += 25;
368
+ } else {
369
+ score += Math.round(coverage * 10);
370
+ }
371
+ if (path.tokens[0] === queryTokens[0] || name.tokens[0] === queryTokens[0]) {
372
+ score += 8;
373
+ }
374
+ if (normalizeSearchText(tool.path) === normalizedQuery || normalizeSearchText(tool.name) === normalizedQuery) {
375
+ score += 20;
376
+ }
377
+ return {
378
+ path: tool.path,
379
+ name: tool.name,
380
+ description: tool.description,
381
+ integration: tool.integration,
382
+ score
383
+ };
384
+ };
385
+ var searchTools = Effect.fn("executor.tools.search")(function* (executor, query, limit = 12, options) {
386
+ const offset = options?.offset ?? 0;
387
+ yield* Effect.annotateCurrentSpan({
388
+ "executor.search.query_length": query.length,
389
+ "executor.search.limit": limit,
390
+ "executor.search.offset": offset,
391
+ ...options?.namespace ? { "executor.search.namespace": options.namespace } : {}
392
+ });
393
+ const emptyQuery = normalizeSearchText(query).length === 0;
394
+ const hasNamespace = options?.namespace !== void 0 && normalizeSearchText(options.namespace).length > 0;
395
+ if (emptyQuery && !hasNamespace) {
396
+ return {
397
+ items: [],
398
+ total: 0,
399
+ hasMore: false,
400
+ nextOffset: null
401
+ };
402
+ }
403
+ const all = yield* executor.tools.list({ includeAnnotations: false }).pipe(
404
+ Effect.mapError(
405
+ (cause) => new ExecutionToolError({
406
+ message: "Failed to list tools for search",
407
+ cause
408
+ })
409
+ )
410
+ );
411
+ const searchable = all.map(toSearchableTool);
412
+ const ranked = emptyQuery ? searchable.filter((tool) => tool.integration === options?.namespace?.trim()).sort((left, right) => left.path.localeCompare(right.path)).map((tool) => ({
413
+ path: tool.path,
414
+ name: tool.name,
415
+ integration: tool.integration,
416
+ score: 0,
417
+ ...tool.description !== void 0 ? { description: tool.description } : {}
418
+ })) : searchable.filter((tool) => matchesNamespace(tool, options?.namespace)).map((tool) => scoreToolMatch(tool, query)).filter(Predicate.isNotNull).sort((left, right) => right.score - left.score || left.path.localeCompare(right.path));
419
+ const page = paginate(ranked, offset, limit);
420
+ yield* Effect.annotateCurrentSpan({
421
+ "executor.search.candidate_count": all.length,
422
+ "executor.search.match_count": ranked.length,
423
+ "executor.search.result_count": page.items.length,
424
+ "executor.search.has_more": page.hasMore
425
+ });
426
+ return page;
427
+ });
428
+ var defaultToolDiscoveryProvider = {
429
+ searchTools: ({ executor, query, namespace, limit, offset }) => searchTools(executor, query, limit, { namespace, offset })
430
+ };
431
+ var listExecutorIntegrations = Effect.fn("executor.integrations.list")(function* (executor, options) {
432
+ const normalizedQuery = normalizeSearchText(options?.query ?? "");
433
+ const limit = options?.limit ?? 50;
434
+ const offset = options?.offset ?? 0;
435
+ const integrations = yield* executor.integrations.list().pipe(
436
+ Effect.mapError(
437
+ (cause) => new ExecutionToolError({
438
+ message: "Failed to list executor integrations",
439
+ cause
440
+ })
441
+ )
442
+ );
443
+ const filtered = normalizedQuery.length === 0 ? integrations : integrations.filter((integration) => {
444
+ const haystack = normalizeSearchText(
445
+ [String(integration.slug), integration.description, integration.kind].join(" ")
446
+ );
447
+ return tokenizeSearchText(normalizedQuery).every((token) => haystack.includes(token));
448
+ });
449
+ const allTools = yield* executor.tools.list({ includeAnnotations: false }).pipe(
450
+ Effect.mapError(
451
+ (cause) => new ExecutionToolError({
452
+ message: "Failed to list tools for integration counts",
453
+ cause
454
+ })
455
+ )
456
+ );
457
+ const toolCountByIntegration = /* @__PURE__ */ new Map();
458
+ for (const tool of allTools) {
459
+ const key = String(tool.integration);
460
+ toolCountByIntegration.set(key, (toolCountByIntegration.get(key) ?? 0) + 1);
461
+ }
462
+ const sortedWithCounts = filtered.map(
463
+ (integration) => ({
464
+ id: String(integration.slug),
465
+ name: String(integration.slug),
466
+ // The integration's catalog description — user-editable context the
467
+ // agent can use to pick an integration. Omitted when it just repeats the
468
+ // slug or display name (no information beyond identity).
469
+ ...integration.description && integration.description.toLowerCase() !== String(integration.slug).toLowerCase() && integration.description.toLowerCase() !== integration.name.toLowerCase() ? { description: integration.description } : {},
470
+ kind: integration.kind,
471
+ canRemove: integration.canRemove,
472
+ canRefresh: integration.canRefresh,
473
+ toolCount: toolCountByIntegration.get(String(integration.slug)) ?? 0
474
+ })
475
+ ).sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
476
+ const page = paginate(sortedWithCounts, offset, limit);
477
+ yield* Effect.annotateCurrentSpan({
478
+ "executor.integrations.candidate_count": integrations.length,
479
+ "executor.integrations.match_count": sortedWithCounts.length,
480
+ "executor.integrations.result_count": page.items.length,
481
+ "executor.integrations.has_more": page.hasMore
482
+ });
483
+ return page;
484
+ });
485
+ var describeTool = Effect.fn("executor.tools.describe")(function* (executor, path) {
486
+ yield* Effect.annotateCurrentSpan({ "mcp.tool.name": path });
487
+ const builtin = BUILTIN_TOOL_DESCRIPTIONS.get(path);
488
+ if (builtin) return builtin;
489
+ const address = pathToAddress(path);
490
+ const schema = yield* executor.tools.schema(address);
491
+ if (schema === null) {
492
+ const lastDot = path.lastIndexOf(".");
493
+ const leaf = lastDot === -1 ? path : path.slice(lastDot + 1);
494
+ const scoped = yield* searchTools(executor, leaf, TOOL_DESCRIBE_SUGGESTION_LIMIT, {
495
+ namespace: extractNamespace(path)
496
+ });
497
+ const matches = scoped.items.length > 0 ? scoped.items : (yield* searchTools(executor, leaf, TOOL_DESCRIBE_SUGGESTION_LIMIT)).items;
498
+ const suggestions = matches.map((item) => item.path);
499
+ const notFound = {
500
+ path,
501
+ name: path,
502
+ error: {
503
+ code: "tool_not_found",
504
+ message: `Tool not found: ${path}`,
505
+ ...suggestions.length > 0 ? { suggestions } : {}
506
+ }
507
+ };
508
+ return notFound;
509
+ }
510
+ const described = {
511
+ path,
512
+ name: schema.name ?? path,
513
+ description: schema.description,
514
+ inputTypeScript: schema.inputTypeScript,
515
+ outputTypeScript: wrapOutputTypeScript(schema.outputTypeScript),
516
+ typeScriptDefinitions: withToolResultDefinitions(schema.typeScriptDefinitions)
517
+ };
518
+ return described;
519
+ });
520
+
521
+ // src/description.ts
522
+ import { Effect as Effect2 } from "effect";
523
+ var INTEGRATION_INVENTORY_HEADER = "## Available integrations";
524
+ var buildExecuteDescription = (executor) => Effect2.gen(function* () {
525
+ const connections = yield* executor.connections.list().pipe(
526
+ // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: ExecutionEngine.getDescription currently exposes no error channel; engine typed-error widening is covered separately
527
+ Effect2.orDie,
528
+ Effect2.withSpan("executor.connections.list")
529
+ );
530
+ const description = yield* Effect2.sync(() => {
531
+ const lines = [
532
+ "Execute TypeScript in a sandboxed runtime with access to connected service integrations.",
533
+ "",
534
+ 'Before writing code, call `skills({ name: "execute" })` for the workflow on how to use this tool.',
535
+ "",
536
+ 'When the task needs an external service capability \u2014 sending email, a database, auth, payments, SMS, DNS, hosting, storage, search \u2014 and no connected integration covers it, do not pick a provider from memory or send the user off to sign up somewhere: press the button. `await tools.use({ need: "transactional email" })` returns a wired, working integration in one call (or one remaining human credential click). `skills({ name: "resolve" })` has details and the manual stepwise flow.'
537
+ ];
538
+ const inventory = formatIntegrationInventory(connections);
539
+ if (inventory.length > 0) {
540
+ lines.push("");
541
+ lines.push(inventory);
542
+ }
543
+ return lines.join("\n");
544
+ }).pipe(
545
+ Effect2.withSpan("schema.compile.description", {
546
+ attributes: { "executor.connection_count": connections.length }
547
+ })
548
+ );
549
+ yield* Effect2.annotateCurrentSpan({
550
+ "executor.connection_count": connections.length,
551
+ "schema.kind": "execute",
552
+ // Connection inventory so a failing session build (which runs this during
553
+ // init) names the callable prefixes it resolved without listing tools.
554
+ "executor.connection_addresses": connections.map((connection) => connectionPath(connection)).slice(0, 50).join(","),
555
+ "executor.connection_integrations": [
556
+ ...new Set(connections.map((connection) => String(connection.integration)))
557
+ ].join(","),
558
+ "executor.connection_owners": [
559
+ ...new Set(connections.map((connection) => connection.owner))
560
+ ].join(",")
561
+ });
562
+ return description;
563
+ }).pipe(Effect2.withSpan("schema.describe.execute"));
564
+ var connectionPath = (connection) => {
565
+ const address = String(connection.address);
566
+ return address.startsWith("tools.") ? address.slice("tools.".length) : address;
567
+ };
568
+ var INVENTORY_LIMIT = 50;
569
+ var formatIntegrationInventory = (connections) => {
570
+ const slugs = [...new Set(connections.map((connection) => String(connection.integration)))].sort(
571
+ (a, b) => a.localeCompare(b)
572
+ );
573
+ if (slugs.length === 0) return "";
574
+ const shown = slugs.slice(0, INVENTORY_LIMIT);
575
+ const lines = [
576
+ INTEGRATION_INVENTORY_HEADER,
577
+ "",
578
+ "Integrations you have connected. Their tools live under `tools.<integration>.\u2026`.",
579
+ ...shown.map((slug) => `- \`${slug}\``)
580
+ ];
581
+ if (slugs.length > shown.length) {
582
+ lines.push(`- ... ${slugs.length - shown.length} more`);
583
+ }
584
+ return lines.join("\n");
585
+ };
586
+
587
+ // src/engine.ts
588
+ import { Deferred, Effect as Effect4, Fiber, Predicate as Predicate3, Queue } from "effect";
589
+ import * as Exit from "effect/Exit";
590
+
591
+ // src/use-need.ts
592
+ import { Cause as Cause2, Effect as Effect3, Option, Predicate as Predicate2, Schema } from "effect";
593
+ var RESOLVE_PATH = "executor.coreTools.integrations.resolve";
594
+ var CONNECTIONS_LIST_PATH = "executor.coreTools.connections.list";
595
+ var INTEGRATIONS_LIST_PATH = "executor.coreTools.integrations.list";
596
+ var CONNECTION_CREATE_PATH = "executor.coreTools.connections.create";
597
+ var CREATE_HANDOFF_PATH = "executor.coreTools.connections.createHandoff";
598
+ var OPENAPI_ADD_PATH = "executor.openapi.addSpec";
599
+ var MCP_ADD_PATH = "executor.mcp.addServer";
600
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
601
+ var StepErrorShape = Schema.Struct({
602
+ message: Schema.optional(Schema.String),
603
+ code: Schema.optional(Schema.String)
604
+ });
605
+ var decodeStepError = Schema.decodeUnknownOption(StepErrorShape);
606
+ var unwrap = (step, result) => {
607
+ if (!isRecord(result)) return { step, message: "tool returned a non-object result" };
608
+ if (result.ok === true && isRecord(result.data)) return result.data;
609
+ if (result.ok === false) {
610
+ const { message, code } = Option.getOrElse(
611
+ decodeStepError(result.error),
612
+ () => ({})
613
+ );
614
+ return {
615
+ step,
616
+ message: message ?? "tool call failed",
617
+ ...code !== void 0 ? { code } : {}
618
+ };
619
+ }
620
+ return result;
621
+ };
622
+ var isFailure = (value) => "step" in value && "message" in value && typeof value.step === "string" && !("ok" in value);
623
+ var ResolveEntry = Schema.Struct({
624
+ id: Schema.String,
625
+ name: Schema.String,
626
+ domain: Schema.String,
627
+ category: Schema.String,
628
+ sponsored: Schema.Boolean,
629
+ offer: Schema.optional(Schema.Struct({ terms: Schema.String, url: Schema.String })),
630
+ verifiedAt: Schema.String,
631
+ suggestedSlug: Schema.String,
632
+ addPath: Schema.Union([
633
+ Schema.Struct({ kind: Schema.Literal("openapi"), specUrl: Schema.String }),
634
+ Schema.Struct({ kind: Schema.Literal("mcp"), serverUrl: Schema.String }),
635
+ Schema.Struct({ kind: Schema.Literal("cli"), package: Schema.String })
636
+ ]),
637
+ authTemplate: Schema.optional(Schema.String),
638
+ connect: Schema.optional(Schema.Struct({ auth: Schema.String, instructions: Schema.String }))
639
+ });
640
+ var decodeResolveEntry = Schema.decodeUnknownOption(ResolveEntry);
641
+ var asEntry = (value) => Option.getOrUndefined(decodeResolveEntry(value));
642
+ var toCard = (entry) => ({
643
+ id: entry.id,
644
+ name: entry.name,
645
+ domain: entry.domain,
646
+ category: entry.category,
647
+ sponsored: entry.sponsored,
648
+ ...entry.offer !== void 0 ? { offer: entry.offer } : {},
649
+ verifiedAt: entry.verifiedAt
650
+ });
651
+ var labelLine = (entry) => entry.sponsored ? `${entry.name} is a SPONSORED placement (verified; it holds the default slot only at quality >= the best organic option${entry.offer ? `; offer: ${entry.offer.terms}` : ""}).` : `${entry.name} is the organic (unsponsored) best for this category.`;
652
+ var overrideLine = (need, entry, alternativesCount) => alternativesCount > 0 ? `${alternativesCount} verified alternative${alternativesCount === 1 ? "" : "s"} exist${alternativesCount === 1 ? "s" : ""} \u2014 to override, press again with not: ["${entry.id}"], or pass compare: true to see the full ranked set for "${need}".` : `No other verified providers cover this need right now.`;
653
+ var runUseNeed = (invoke, rawArgs) => Effect3.gen(function* () {
654
+ if (!isRecord(rawArgs) || typeof rawArgs.need !== "string" || rawArgs.need.length === 0) {
655
+ return {
656
+ status: "error",
657
+ step: "arguments",
658
+ message: 'tools.use expects { need: string; not?: string[]; compare?: boolean } \u2014 e.g. tools.use({ need: "transactional email" })',
659
+ instructions: "Call again with a need string."
660
+ };
661
+ }
662
+ const need = rawArgs.need;
663
+ const not = Array.isArray(rawArgs.not) ? rawArgs.not.filter((entry2) => typeof entry2 === "string") : [];
664
+ const compare = rawArgs.compare === true;
665
+ const fail = (failure) => ({
666
+ status: "error",
667
+ step: failure.step,
668
+ message: failure.message,
669
+ instructions: "The step named above failed. You can retry, or fall back to the workshop tools (integrations.resolve, openapi.addSpec, connections.createHandoff) to do it stepwise."
670
+ });
671
+ const dispatch = (step, path, args) => invoke({ path, args }).pipe(
672
+ Effect3.map((result) => unwrap(step, result)),
673
+ Effect3.catchCause((cause) => {
674
+ const failure = cause.reasons.find(Cause2.isFailReason)?.error;
675
+ const { message } = Predicate2.isError(failure) ? failure : Option.getOrElse(decodeStepError(failure), () => ({}));
676
+ return Effect3.succeed({ step, message: message ?? "tool call failed" });
677
+ })
678
+ );
679
+ const resolved = yield* dispatch("resolve", RESOLVE_PATH, { need, exclude: not });
680
+ if (isFailure(resolved)) return fail(resolved);
681
+ if (resolved.matched !== true) {
682
+ const categories = Array.isArray(resolved.categories) ? resolved.categories.filter((c) => typeof c === "string") : [];
683
+ return {
684
+ status: "no_match",
685
+ categories,
686
+ instructions: categories.length > 0 ? `No verified provider matches "${need}". The catalog covers: ${categories.join(", ")}. Press again phrasing the need with one of these, or name a provider directly.` : `No provider catalog is configured in this runtime; fall back to asking the user.`
687
+ };
688
+ }
689
+ const entry = asEntry(resolved.default);
690
+ if (entry === void 0) {
691
+ return fail({ step: "resolve", message: "resolver returned a match without a default" });
692
+ }
693
+ const alternatives = Array.isArray(resolved.alternatives) ? resolved.alternatives : [];
694
+ const alternativesCount = alternatives.length;
695
+ const phrasing = resolved.phrasing === "brand" ? "brand" : "category";
696
+ if (compare) {
697
+ const candidates = [entry, ...alternatives.map(asEntry).filter(Predicate2.isNotUndefined)].map(
698
+ toCard
699
+ );
700
+ return {
701
+ status: "compare",
702
+ candidates,
703
+ instructions: "Ranked by verified quality; serving order. Sponsored entries are labeled. Press use() again naming your choice (brand phrasing is honored verbatim)."
704
+ };
705
+ }
706
+ const connectionsResult = yield* dispatch("connections.list", CONNECTIONS_LIST_PATH, {});
707
+ if (isFailure(connectionsResult)) return fail(connectionsResult);
708
+ const connections = Array.isArray(connectionsResult.connections) ? connectionsResult.connections.filter(isRecord) : [];
709
+ const rankedEntries = [entry, ...alternatives.map(asEntry).filter(Predicate2.isNotUndefined)];
710
+ const candidateSlugs = phrasing === "brand" ? [entry.suggestedSlug] : rankedEntries.map((e) => e.suggestedSlug);
711
+ for (const slug of candidateSlugs) {
712
+ const existing = connections.find((connection) => connection.integration === slug);
713
+ if (existing !== void 0) {
714
+ const adopted = rankedEntries.find((e) => e.suggestedSlug === slug) ?? entry;
715
+ return {
716
+ status: "ready",
717
+ provider: toCard(adopted),
718
+ alternativesCount,
719
+ toolsPath: typeof existing.address === "string" ? existing.address : void 0,
720
+ instructions: `Already wired: you have a ${adopted.name} connection. ${labelLine(adopted)} Call its tools under the address above (tools.search({ namespace: "${slug}" }) enumerates them).`
721
+ };
722
+ }
723
+ }
724
+ if (entry.addPath.kind === "cli") {
725
+ const pkg = entry.addPath.package;
726
+ const installCommand = `npm install -g ${pkg}`;
727
+ const authNote = entry.authTemplate !== void 0 && entry.authTemplate.length > 0 ? ` \u2014 auth: ${entry.authTemplate}` : entry.connect?.instructions ?? "";
728
+ return {
729
+ status: "manual",
730
+ provider: toCard(entry),
731
+ alternativesCount,
732
+ installCommand,
733
+ instructions: `${labelLine(entry)} ${entry.name} ships as a CLI (npm package ${pkg}), not an API integration: install it in YOUR environment (\`${installCommand}\`, or ad hoc via \`npx -y ${pkg}\`), then authenticate per its own flow${authNote}. It will not appear under raf's tools. ${overrideLine(need, entry, alternativesCount)}`
734
+ };
735
+ }
736
+ const integrationsResult = yield* dispatch("integrations.list", INTEGRATIONS_LIST_PATH, {});
737
+ if (isFailure(integrationsResult)) return fail(integrationsResult);
738
+ const integrations = Array.isArray(integrationsResult.integrations) ? integrationsResult.integrations.filter(isRecord) : [];
739
+ const alreadyAdded = integrations.some(
740
+ (integration) => integration.slug === entry.suggestedSlug
741
+ );
742
+ if (!alreadyAdded) {
743
+ if (entry.addPath.kind === "openapi") {
744
+ const added = yield* dispatch("openapi.addSpec", OPENAPI_ADD_PATH, {
745
+ spec: { kind: "url", url: entry.addPath.specUrl },
746
+ slug: entry.suggestedSlug,
747
+ name: entry.name
748
+ });
749
+ if (isFailure(added) && added.code !== "integration_already_exists") return fail(added);
750
+ } else if (entry.addPath.kind === "mcp") {
751
+ const added = yield* dispatch("mcp.addServer", MCP_ADD_PATH, {
752
+ name: entry.name,
753
+ endpoint: entry.addPath.serverUrl,
754
+ slug: entry.suggestedSlug
755
+ });
756
+ if (isFailure(added) && added.code !== "integration_already_exists") return fail(added);
757
+ }
758
+ }
759
+ if (entry.connect?.auth === "none") {
760
+ const owner = connections.map((connection) => connection.owner).find((value) => typeof value === "string");
761
+ if (owner !== void 0) {
762
+ const created = yield* dispatch("connections.create", CONNECTION_CREATE_PATH, {
763
+ owner,
764
+ name: "main",
765
+ integration: entry.suggestedSlug,
766
+ template: "none"
767
+ });
768
+ if (!isFailure(created)) {
769
+ return {
770
+ status: "ready",
771
+ provider: toCard(entry),
772
+ alternativesCount,
773
+ toolsPath: `tools.${entry.suggestedSlug}.${owner}.main`,
774
+ instructions: `${entry.name} is wired (no auth needed). ${labelLine(entry)} ${overrideLine(need, entry, alternativesCount)} Find its tools with tools.search({ namespace: "${entry.suggestedSlug}" }).`
775
+ };
776
+ }
777
+ }
778
+ }
779
+ const handoff = yield* dispatch("connections.createHandoff", CREATE_HANDOFF_PATH, {
780
+ integration: entry.suggestedSlug
781
+ });
782
+ if (isFailure(handoff)) return fail(handoff);
783
+ const url = typeof handoff.url === "string" ? handoff.url : "";
784
+ return {
785
+ status: "pending",
786
+ provider: toCard(entry),
787
+ alternativesCount,
788
+ url,
789
+ instructions: `${labelLine(entry)} One human step left: give the user this URL \u2014 they mint the ${entry.name} credential and enter it in the web UI (never paste secrets into chat). Press use({ need: "${need}" }) again any time to check: it returns ready once the connection exists. ${overrideLine(need, entry, alternativesCount)}`
790
+ };
791
+ });
792
+
793
+ // src/engine.ts
794
+ var acceptAllHandler = () => Effect4.succeed({ action: "accept" });
795
+ var MAX_PREVIEW_CHARS = 3e4;
796
+ var truncate = (value, max) => value.length > max ? `${value.slice(0, max)}
797
+ ... [truncated ${value.length - max} chars]` : value;
798
+ var formatExecuteResult = (result) => {
799
+ const resultText = result.result != null ? typeof result.result === "string" ? result.result : JSON.stringify(result.result, null, 2) : null;
800
+ const logText = result.logs && result.logs.length > 0 ? result.logs.join("\n") : null;
801
+ const emitted = result.output?.length ?? 0;
802
+ const emittedNote = emitted > 0 ? `${emitted} item${emitted === 1 ? "" : "s"} emitted to the user` : null;
803
+ const emittedField = emitted > 0 ? { emitted } : {};
804
+ if (result.error) {
805
+ const parts2 = [`Error: ${result.error}`, ...logText ? [`
806
+ Logs:
807
+ ${logText}`] : []];
808
+ return {
809
+ text: truncate(parts2.join("\n"), MAX_PREVIEW_CHARS),
810
+ structured: {
811
+ status: "error",
812
+ error: result.error,
813
+ ...emittedField,
814
+ logs: result.logs ?? []
815
+ },
816
+ isError: true
817
+ };
818
+ }
819
+ const resultPart = resultText ? truncate(resultText, MAX_PREVIEW_CHARS) : emittedNote ? `(no return value; ${emittedNote})` : "(no result)";
820
+ const parts = [resultPart, ...logText ? [`
821
+ Logs:
822
+ ${logText}`] : []];
823
+ return {
824
+ text: parts.join("\n"),
825
+ structured: {
826
+ status: "completed",
827
+ result: result.result ?? null,
828
+ ...emittedField,
829
+ logs: result.logs ?? []
830
+ },
831
+ isError: false
832
+ };
833
+ };
834
+ var formatPausedExecution = (paused, options) => {
835
+ const req = paused.elicitationContext.request;
836
+ const lines = [`Execution paused: ${req.message}`];
837
+ const deadline = options?.deadline;
838
+ const isUrlElicitation = Predicate3.isTagged(req, "UrlElicitation");
839
+ const isFormElicitation = Predicate3.isTagged(req, "FormElicitation");
840
+ const requestedSchema = isFormElicitation ? req.requestedSchema : void 0;
841
+ const hasRequestedSchema = requestedSchema !== void 0 && Object.keys(requestedSchema).length > 0;
842
+ const baseInstructions = isUrlElicitation ? `The user needs to open this URL in a browser and complete the flow. After the user finishes, call the resume tool with executionId "${paused.id}" and action "accept".` : hasRequestedSchema ? `Ask the user for values matching requestedSchema. Then call the resume tool with executionId "${paused.id}", action "accept", and content matching requestedSchema. If the user declines, call resume with action "decline" or "cancel".` : `This is a model-side confirmation gate; there is no browser form to open. Ask the user whether to approve the paused tool call. If the user approves, call the resume tool with executionId "${paused.id}" and action "accept". If the user declines, call resume with action "decline" or "cancel".`;
843
+ const deadlineInstructions = deadline ? ` Resume before ${deadline.expiresAt}; this approval window lasts ${formatTtlDuration(deadline.ttlMs)}.` : "";
844
+ const instructions = `${baseInstructions}${deadlineInstructions}`;
845
+ if (isUrlElicitation) {
846
+ lines.push(`
847
+ Open this URL in a browser:
848
+ ${req.url}`);
849
+ lines.push('\nAfter the browser flow, call the resume tool with action "accept".');
850
+ } else if (hasRequestedSchema) {
851
+ lines.push(
852
+ "\nAsk the user for a response matching the requested schema, then call the resume tool."
853
+ );
854
+ lines.push(`
855
+ Requested schema:
856
+ ${JSON.stringify(requestedSchema, null, 2)}`);
857
+ } else {
858
+ lines.push(
859
+ '\nThis is a model-side confirmation gate; no browser form is waiting. Ask the user whether to approve, then call the resume tool with action "accept", "decline", or "cancel".'
860
+ );
861
+ }
862
+ lines.push(`
863
+ executionId: ${paused.id}`);
864
+ if (deadline) {
865
+ lines.push(
866
+ `
867
+ resumeDeadline: ${deadline.expiresAt} (${formatTtlDuration(deadline.ttlMs)} approval window)`
868
+ );
869
+ }
870
+ lines.push(`
871
+ instructions: ${instructions}`);
872
+ return {
873
+ text: lines.join("\n"),
874
+ structured: {
875
+ status: "waiting_for_interaction",
876
+ executionId: paused.id,
877
+ ...deadline ? { expiresAt: deadline.expiresAt, ttlMs: deadline.ttlMs } : {},
878
+ interaction: {
879
+ kind: isUrlElicitation ? "url" : "form",
880
+ message: req.message,
881
+ instructions,
882
+ address: String(paused.elicitationContext.address),
883
+ args: paused.elicitationContext.args,
884
+ ...isUrlElicitation ? { url: req.url } : {},
885
+ ...isFormElicitation ? { requestedSchema: req.requestedSchema } : {}
886
+ }
887
+ }
888
+ };
889
+ };
890
+ var formatTtlDuration = (ttlMs) => {
891
+ const seconds = Math.max(1, Math.round(ttlMs / 1e3));
892
+ if (seconds % 60 === 0) {
893
+ const minutes = seconds / 60;
894
+ return `${minutes} minute${minutes === 1 ? "" : "s"}`;
895
+ }
896
+ return `${seconds} second${seconds === 1 ? "" : "s"}`;
897
+ };
898
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
899
+ var readOptionalLimit = (value, toolName) => {
900
+ if (value === void 0) {
901
+ return 12;
902
+ }
903
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
904
+ return new ExecutionToolError({
905
+ message: `${toolName} limit must be a positive number when provided`
906
+ });
907
+ }
908
+ return Math.floor(value);
909
+ };
910
+ var readOptionalOffset = (value, toolName) => {
911
+ if (value === void 0) {
912
+ return 0;
913
+ }
914
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
915
+ return new ExecutionToolError({
916
+ message: `${toolName} offset must be a non-negative number when provided`
917
+ });
918
+ }
919
+ return Math.floor(value);
920
+ };
921
+ var makeFullInvoker = (executor, invokeOptions, toolDiscoveryProvider) => {
922
+ const base = makeExecutorToolInvoker(executor, { invokeOptions });
923
+ return {
924
+ invoke: ({ path, args }) => {
925
+ if (path === "search") {
926
+ if (!isRecord2(args)) {
927
+ return Effect4.fail(
928
+ new ExecutionToolError({
929
+ message: "tools.search expects an object: { query?: string; namespace?: string; limit?: number; offset?: number }"
930
+ })
931
+ );
932
+ }
933
+ if (args.query !== void 0 && typeof args.query !== "string") {
934
+ return Effect4.fail(
935
+ new ExecutionToolError({
936
+ message: "tools.search query must be a string when provided"
937
+ })
938
+ );
939
+ }
940
+ if (args.namespace !== void 0 && typeof args.namespace !== "string") {
941
+ return Effect4.fail(
942
+ new ExecutionToolError({
943
+ message: "tools.search namespace must be a string when provided"
944
+ })
945
+ );
946
+ }
947
+ const limit = readOptionalLimit(args.limit, "tools.search");
948
+ if (Predicate3.isTagged(limit, "ExecutionToolError")) {
949
+ return Effect4.fail(limit);
950
+ }
951
+ const offset = readOptionalOffset(args.offset, "tools.search");
952
+ if (Predicate3.isTagged(offset, "ExecutionToolError")) {
953
+ return Effect4.fail(offset);
954
+ }
955
+ return toolDiscoveryProvider.searchTools({
956
+ executor,
957
+ query: args.query ?? "",
958
+ limit,
959
+ namespace: args.namespace,
960
+ offset
961
+ }).pipe(
962
+ Effect4.withSpan("mcp.tool.dispatch", {
963
+ attributes: { "mcp.tool.name": path, "executor.tool.builtin": true }
964
+ })
965
+ );
966
+ }
967
+ if (path === "executor.integrations.list") {
968
+ if (args !== void 0 && !isRecord2(args)) {
969
+ return Effect4.fail(
970
+ new ExecutionToolError({
971
+ message: "tools.executor.integrations.list expects an object: { query?: string; limit?: number; offset?: number }"
972
+ })
973
+ );
974
+ }
975
+ if (isRecord2(args) && args.query !== void 0 && typeof args.query !== "string") {
976
+ return Effect4.fail(
977
+ new ExecutionToolError({
978
+ message: "tools.executor.integrations.list query must be a string when provided"
979
+ })
980
+ );
981
+ }
982
+ const limit = readOptionalLimit(
983
+ isRecord2(args) ? args.limit : void 0,
984
+ "tools.executor.integrations.list"
985
+ );
986
+ if (Predicate3.isTagged(limit, "ExecutionToolError")) {
987
+ return Effect4.fail(limit);
988
+ }
989
+ const offset = readOptionalOffset(
990
+ isRecord2(args) ? args.offset : void 0,
991
+ "tools.executor.integrations.list"
992
+ );
993
+ if (Predicate3.isTagged(offset, "ExecutionToolError")) {
994
+ return Effect4.fail(offset);
995
+ }
996
+ return listExecutorIntegrations(executor, {
997
+ query: isRecord2(args) && typeof args.query === "string" ? args.query : void 0,
998
+ limit,
999
+ offset
1000
+ }).pipe(
1001
+ Effect4.withSpan("mcp.tool.dispatch", {
1002
+ attributes: { "mcp.tool.name": path, "executor.tool.builtin": true }
1003
+ })
1004
+ );
1005
+ }
1006
+ if (path === "use") {
1007
+ return runUseNeed((input) => base.invoke(input), args).pipe(
1008
+ Effect4.withSpan("mcp.tool.dispatch", {
1009
+ attributes: { "mcp.tool.name": path, "executor.tool.builtin": true }
1010
+ })
1011
+ );
1012
+ }
1013
+ if (path === "describe.tool") {
1014
+ if (!isRecord2(args)) {
1015
+ return Effect4.fail(
1016
+ new ExecutionToolError({
1017
+ message: "tools.describe.tool expects an object: { path: string }"
1018
+ })
1019
+ );
1020
+ }
1021
+ if (typeof args.path !== "string" || args.path.trim().length === 0) {
1022
+ return Effect4.fail(new ExecutionToolError({ message: "describe.tool requires a path" }));
1023
+ }
1024
+ if ("includeSchemas" in args) {
1025
+ return Effect4.fail(
1026
+ new ExecutionToolError({
1027
+ message: "tools.describe.tool no longer accepts includeSchemas"
1028
+ })
1029
+ );
1030
+ }
1031
+ return describeTool(executor, args.path).pipe(
1032
+ Effect4.withSpan("mcp.tool.dispatch", {
1033
+ attributes: {
1034
+ "mcp.tool.name": path,
1035
+ "executor.tool.builtin": true,
1036
+ "executor.tool.target_path": args.path
1037
+ }
1038
+ })
1039
+ );
1040
+ }
1041
+ return base.invoke({ path, args });
1042
+ }
1043
+ };
1044
+ };
1045
+ var withToolCallHook = (invoker, onToolCall) => {
1046
+ if (onToolCall === void 0) return invoker;
1047
+ const report = (path, ok) => {
1048
+ try {
1049
+ onToolCall({ path, ok });
1050
+ } catch {
1051
+ }
1052
+ };
1053
+ return {
1054
+ invoke: (input) => invoker.invoke(input).pipe(
1055
+ Effect4.tap(
1056
+ (result) => Effect4.sync(() => report(input.path, !(isRecord2(result) && result.ok === false)))
1057
+ ),
1058
+ Effect4.tapCause(() => Effect4.sync(() => report(input.path, false)))
1059
+ )
1060
+ };
1061
+ };
1062
+ var createExecutionEngine = (config) => {
1063
+ const {
1064
+ executor,
1065
+ codeExecutor,
1066
+ toolDiscoveryProvider = defaultToolDiscoveryProvider,
1067
+ onToolCall
1068
+ } = config;
1069
+ const pausedExecutions = /* @__PURE__ */ new Map();
1070
+ const settledOutcomes = /* @__PURE__ */ new Map();
1071
+ const SETTLED_OUTCOME_LIMIT = 64;
1072
+ const settledExecutionIds = /* @__PURE__ */ new Set();
1073
+ const SETTLED_EXECUTION_ID_LIMIT = 1024;
1074
+ const pendingResumes = /* @__PURE__ */ new Map();
1075
+ const recordSettledOutcome = (executionId, exit) => {
1076
+ settledExecutionIds.add(executionId);
1077
+ while (settledExecutionIds.size > SETTLED_EXECUTION_ID_LIMIT) {
1078
+ const oldest = settledExecutionIds.keys().next().value;
1079
+ if (oldest === void 0) break;
1080
+ settledExecutionIds.delete(oldest);
1081
+ }
1082
+ settledOutcomes.set(executionId, exit);
1083
+ while (settledOutcomes.size > SETTLED_OUTCOME_LIMIT) {
1084
+ const oldest = settledOutcomes.keys().next().value;
1085
+ if (oldest === void 0) break;
1086
+ settledOutcomes.delete(oldest);
1087
+ }
1088
+ };
1089
+ const awaitCompletionOrPause = (fiber, pauseQueue) => Effect4.raceFirst(
1090
+ Fiber.join(fiber).pipe(
1091
+ Effect4.map((result) => ({ status: "completed", result }))
1092
+ ),
1093
+ Queue.take(pauseQueue).pipe(
1094
+ Effect4.map((paused) => ({ status: "paused", execution: paused }))
1095
+ )
1096
+ );
1097
+ const startPausableExecution = Effect4.fn("mcp.execute")(function* (code, options) {
1098
+ yield* Effect4.annotateCurrentSpan({
1099
+ "mcp.execute.mode": "pausable",
1100
+ "mcp.execute.code_length": code.length
1101
+ });
1102
+ if (options?.autoApprove) {
1103
+ yield* Effect4.annotateCurrentSpan({ "mcp.execute.auto_approve": true });
1104
+ const result = yield* runInlineExecution(code, { onElicitation: acceptAllHandler });
1105
+ return { status: "completed", result };
1106
+ }
1107
+ const pauseQueue = yield* Queue.unbounded();
1108
+ let fiber;
1109
+ const elicitationHandler = (ctx) => Effect4.gen(function* () {
1110
+ const responseDeferred = yield* Deferred.make();
1111
+ const id = `exec_${crypto.randomUUID()}`;
1112
+ const paused = {
1113
+ id,
1114
+ elicitationContext: ctx,
1115
+ response: responseDeferred,
1116
+ fiber,
1117
+ pauseQueue
1118
+ };
1119
+ pausedExecutions.set(id, paused);
1120
+ yield* Queue.offer(pauseQueue, paused);
1121
+ return yield* Deferred.await(responseDeferred);
1122
+ });
1123
+ const invoker = withToolCallHook(
1124
+ makeFullInvoker(executor, { onElicitation: elicitationHandler }, toolDiscoveryProvider),
1125
+ onToolCall
1126
+ );
1127
+ fiber = yield* Effect4.forkDetach(
1128
+ codeExecutor.execute(code, invoker).pipe(Effect4.withSpan("executor.code.exec"))
1129
+ );
1130
+ const sandboxFiber = fiber;
1131
+ yield* Effect4.forkDetach(
1132
+ Fiber.await(sandboxFiber).pipe(
1133
+ Effect4.flatMap(
1134
+ (exit) => Effect4.sync(() => {
1135
+ const outcome = Exit.map(
1136
+ exit,
1137
+ (result) => ({ status: "completed", result })
1138
+ );
1139
+ for (const [id, paused] of pausedExecutions) {
1140
+ if (paused.fiber !== sandboxFiber) continue;
1141
+ pausedExecutions.delete(id);
1142
+ recordSettledOutcome(id, outcome);
1143
+ }
1144
+ })
1145
+ )
1146
+ )
1147
+ );
1148
+ return yield* awaitCompletionOrPause(fiber, pauseQueue);
1149
+ });
1150
+ const resumeExecution = Effect4.fn("mcp.execute.resume")(function* (executionId, response) {
1151
+ yield* Effect4.annotateCurrentSpan({
1152
+ "mcp.execute.resume.action": response.action
1153
+ });
1154
+ const settled = settledOutcomes.get(executionId);
1155
+ if (settled) {
1156
+ yield* Effect4.annotateCurrentSpan({ "mcp.execute.resume.replayed": true });
1157
+ return yield* settled;
1158
+ }
1159
+ const pending = pendingResumes.get(executionId);
1160
+ if (pending) {
1161
+ yield* Effect4.annotateCurrentSpan({ "mcp.execute.resume.joined_inflight": true });
1162
+ return yield* Deferred.await(pending);
1163
+ }
1164
+ const paused = pausedExecutions.get(executionId);
1165
+ if (!paused) return null;
1166
+ pausedExecutions.delete(executionId);
1167
+ const inflight = yield* Deferred.make();
1168
+ pendingResumes.set(executionId, inflight);
1169
+ yield* Deferred.succeed(paused.response, {
1170
+ action: response.action,
1171
+ content: response.content
1172
+ });
1173
+ return yield* awaitCompletionOrPause(paused.fiber, paused.pauseQueue).pipe(
1174
+ Effect4.onExit(
1175
+ (exit) => Effect4.gen(function* () {
1176
+ recordSettledOutcome(executionId, exit);
1177
+ pendingResumes.delete(executionId);
1178
+ yield* Deferred.done(inflight, exit);
1179
+ })
1180
+ )
1181
+ );
1182
+ });
1183
+ const runInlineExecution = Effect4.fn("mcp.execute")(function* (code, options) {
1184
+ yield* Effect4.annotateCurrentSpan({
1185
+ "mcp.execute.mode": "inline",
1186
+ "mcp.execute.code_length": code.length
1187
+ });
1188
+ const invoker = withToolCallHook(
1189
+ makeFullInvoker(
1190
+ executor,
1191
+ {
1192
+ onElicitation: options.onElicitation
1193
+ },
1194
+ toolDiscoveryProvider
1195
+ ),
1196
+ onToolCall
1197
+ );
1198
+ return yield* codeExecutor.execute(code, invoker).pipe(Effect4.withSpan("executor.code.exec"));
1199
+ });
1200
+ return {
1201
+ execute: runInlineExecution,
1202
+ executeWithPause: startPausableExecution,
1203
+ resume: resumeExecution,
1204
+ isExecutionSettled: (executionId) => Effect4.sync(() => settledExecutionIds.has(executionId)),
1205
+ getPausedExecution: (executionId) => Effect4.sync(() => pausedExecutions.get(executionId) ?? null),
1206
+ pausedExecutionCount: () => Effect4.sync(() => pausedExecutions.size),
1207
+ hasPausedExecutions: () => Effect4.sync(() => pausedExecutions.size > 0),
1208
+ getDescription: buildExecuteDescription(executor)
1209
+ };
1210
+ };
1211
+
1212
+ export {
1213
+ ExecutionToolError,
1214
+ makeExecutorToolInvoker,
1215
+ searchTools,
1216
+ defaultToolDiscoveryProvider,
1217
+ listExecutorIntegrations,
1218
+ describeTool,
1219
+ INTEGRATION_INVENTORY_HEADER,
1220
+ buildExecuteDescription,
1221
+ formatExecuteResult,
1222
+ formatPausedExecution,
1223
+ formatTtlDuration,
1224
+ createExecutionEngine
1225
+ };
1226
+ //# sourceMappingURL=chunk-EVYEEDLB.js.map