@executor-js/execution 1.4.33 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -51,6 +51,25 @@ console.log(result);
51
51
  // { result: 12, logs: [...] }
52
52
  ```
53
53
 
54
+ ## Custom tool discovery
55
+
56
+ `tools.search(...)` uses Executor's built-in lexical tool discovery by default. Hosts can provide their own implementation, such as an indexed or semantic search provider, without replacing the sandbox runtime:
57
+
58
+ ```ts
59
+ import { createExecutionEngine, type ToolDiscoveryProvider } from "@executor-js/execution";
60
+
61
+ const toolDiscoveryProvider: ToolDiscoveryProvider = {
62
+ searchTools: ({ query, namespace, limit, offset }) =>
63
+ mySearchIndex.searchTools({ query, namespace, limit, offset }),
64
+ };
65
+
66
+ const engine = createExecutionEngine({
67
+ executor,
68
+ codeExecutor: makeQuickJsExecutor(),
69
+ toolDiscoveryProvider,
70
+ });
71
+ ```
72
+
54
73
  ## Pause/resume for elicitation
55
74
 
56
75
  When the host doesn't support inline elicitation, use `executeWithPause` to intercept the first request as a pause point:
@@ -7,7 +7,13 @@ var ExecutionToolError = class extends Data.TaggedError("ExecutionToolError") {
7
7
  // src/tool-invoker.ts
8
8
  import { Effect, Predicate } from "effect";
9
9
  import * as Cause from "effect/Cause";
10
- import { isToolResult, ToolResult } from "@executor-js/sdk/core";
10
+ import {
11
+ authToolFailure,
12
+ isToolResult,
13
+ ToolResult,
14
+ ToolAddress,
15
+ parseToolAddress
16
+ } from "@executor-js/sdk/core";
11
17
  var OPAQUE_DEFECT_MESSAGE = "Internal tool error";
12
18
  var TOOL_ERROR_TYPESCRIPT = "{ code: string; message: string; status?: number; details?: unknown; retryable?: boolean }";
13
19
  var wrapOutputTypeScript = (outputTypeScript) => `{ ok: true; data: ${outputTypeScript ?? "unknown"} } | { ok: false; error: ToolError }`;
@@ -15,6 +21,15 @@ var withToolResultDefinitions = (definitions) => ({
15
21
  ...definitions ?? {},
16
22
  ToolError: TOOL_ERROR_TYPESCRIPT
17
23
  });
24
+ var ADDRESS_PREFIX = "tools.";
25
+ var pathToAddress = (path) => {
26
+ if (path.startsWith(ADDRESS_PREFIX)) return ToolAddress.make(path);
27
+ if (parseToolAddress(`${ADDRESS_PREFIX}${path}`)) {
28
+ return ToolAddress.make(`${ADDRESS_PREFIX}${path}`);
29
+ }
30
+ return ToolAddress.make(path);
31
+ };
32
+ var addressToPath = (address) => address.startsWith(ADDRESS_PREFIX) ? address.slice(ADDRESS_PREFIX.length) : address;
18
33
  var BUILTIN_TOOL_DESCRIPTIONS = /* @__PURE__ */ new Map([
19
34
  [
20
35
  "search",
@@ -25,7 +40,7 @@ var BUILTIN_TOOL_DESCRIPTIONS = /* @__PURE__ */ new Map([
25
40
  inputTypeScript: "{ query: string; namespace?: string; limit?: number; offset?: number; }",
26
41
  outputTypeScript: "{ items: ToolDiscoveryResult[]; total: number; hasMore: boolean; nextOffset: number | null; }",
27
42
  typeScriptDefinitions: {
28
- ToolDiscoveryResult: "{ path: string; name: string; description?: string; sourceId: string; score: number; }"
43
+ ToolDiscoveryResult: "{ path: string; name: string; description?: string; integration: string; score: number; }"
29
44
  }
30
45
  }
31
46
  ],
@@ -34,11 +49,11 @@ var BUILTIN_TOOL_DESCRIPTIONS = /* @__PURE__ */ new Map([
34
49
  {
35
50
  path: "executor.sources.list",
36
51
  name: "executor.sources.list",
37
- description: "List configured and built-in Executor sources.",
52
+ description: "List configured Executor integrations.",
38
53
  inputTypeScript: "{ query?: string; limit?: number; offset?: number; }",
39
54
  outputTypeScript: "{ items: ExecutorSourceListItem[]; total: number; hasMore: boolean; nextOffset: number | null; }",
40
55
  typeScriptDefinitions: {
41
- ExecutorSourceListItem: "{ id: string; name: string; kind: string; runtime?: boolean; canRemove?: boolean; canRefresh?: boolean; toolCount: number; }"
56
+ ExecutorSourceListItem: "{ id: string; name: string; kind: string; canRemove?: boolean; canRefresh?: boolean; toolCount: number; }"
42
57
  }
43
58
  }
44
59
  ],
@@ -64,19 +79,28 @@ var validationIssues = (value) => {
64
79
  const issues = value.issues;
65
80
  return Array.isArray(issues) ? issues : null;
66
81
  };
82
+ var credentialResolutionToolFailure = (input) => authToolFailure({
83
+ code: input.reauthRequired === true ? "oauth_reauth_required" : "oauth_refresh_failed",
84
+ message: input.reauthRequired === true ? `OAuth connection "${input.label}" requires reauthorization: ${input.message}` : `OAuth connection "${input.label}" could not be resolved: ${input.message}`,
85
+ credential: {
86
+ kind: "oauth",
87
+ label: input.label
88
+ }
89
+ });
67
90
  var expectedToolFailure = (value) => {
68
- if (Predicate.isTagged(value, "ToolNotFoundError") && "toolId" in value) {
69
- const suggestions = "suggestions" in value && Array.isArray(value.suggestions) ? value.suggestions : void 0;
91
+ if (Predicate.isTagged(value, "ToolNotFoundError") && "address" in value) {
92
+ const suggestions = "suggestions" in value && Array.isArray(value.suggestions) ? value.suggestions.map((suggestion) => addressToPath(String(suggestion))) : void 0;
93
+ const address = addressToPath(String(value.address));
70
94
  return {
71
95
  code: "tool_not_found",
72
- message: `Tool not found: ${String(value.toolId)}`,
73
- details: { toolId: value.toolId, ...suggestions ? { suggestions } : {} }
96
+ message: `Tool not found: ${address}`,
97
+ details: { path: address, ...suggestions ? { suggestions } : {} }
74
98
  };
75
99
  }
76
- if (Predicate.isTagged(value, "ToolBlockedError") && "toolId" in value) {
100
+ if (Predicate.isTagged(value, "ToolBlockedError") && "address" in value) {
77
101
  return {
78
102
  code: "tool_blocked",
79
- message: `Tool blocked by policy: ${String(value.toolId)}`,
103
+ message: `Tool blocked by policy: ${addressToPath(String(value.address))}`,
80
104
  details: value
81
105
  };
82
106
  }
@@ -92,17 +116,29 @@ var expectedToolFailure = (value) => {
92
116
  }
93
117
  return null;
94
118
  };
95
- var extractSourceNamespace = (path) => {
96
- const idx = path.indexOf(".");
97
- return idx === -1 ? path : path.slice(0, idx);
119
+ var extractNamespace = (path) => {
120
+ const normalized = addressToPath(path);
121
+ const idx = normalized.indexOf(".");
122
+ return idx === -1 ? normalized : normalized.slice(0, idx);
98
123
  };
99
124
  var makeExecutorToolInvoker = (executor, options) => ({
100
125
  invoke: Effect.fn("mcp.tool.dispatch")(function* ({ path, args }) {
101
126
  yield* Effect.annotateCurrentSpan({
102
127
  "mcp.tool.name": path,
103
- "mcp.tool.source_id": extractSourceNamespace(path)
128
+ "mcp.tool.integration": extractNamespace(path)
104
129
  });
105
- const result = yield* executor.tools.invoke(path, args, options.invokeOptions).pipe(
130
+ const address = pathToAddress(path);
131
+ const result = yield* executor.execute(address, args, options.invokeOptions).pipe(
132
+ Effect.catchTag(
133
+ "CredentialResolutionError",
134
+ (err) => Effect.succeed(
135
+ credentialResolutionToolFailure({
136
+ label: `${err.integration}.${err.owner}.${err.name}`,
137
+ message: err.message,
138
+ reauthRequired: err.reauthRequired
139
+ })
140
+ )
141
+ ),
106
142
  Effect.catchCause((cause) => {
107
143
  const err = cause.reasons.find(Cause.isFailReason)?.error;
108
144
  const expected = expectedToolFailure(err);
@@ -112,7 +148,7 @@ var makeExecutorToolInvoker = (executor, options) => ({
112
148
  if (isElicitationDeclinedError(err)) {
113
149
  return Effect.fail(
114
150
  new ExecutionToolError({
115
- message: `Tool "${err.toolId}" requires approval but the request was ${err.action === "cancel" ? "cancelled" : "declined"} by the user.`,
151
+ message: `Tool "${addressToPath(String(err.address))}" requires approval but the request was ${err.action === "cancel" ? "cancelled" : "declined"} by the user.`,
116
152
  cause: err
117
153
  })
118
154
  );
@@ -140,7 +176,7 @@ var makeExecutorToolInvoker = (executor, options) => ({
140
176
  return { ok: true, data: result };
141
177
  })
142
178
  });
143
- var isElicitationDeclinedError = (value) => Predicate.isTagged(value, "ElicitationDeclinedError") && value !== null && typeof value === "object" && "toolId" in value && typeof value.toolId === "string" && "action" in value && (value.action === "cancel" || value.action === "decline");
179
+ 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");
144
180
  var paginate = (all, offset, limit) => {
145
181
  const total = all.length;
146
182
  const start = Math.min(Math.max(offset, 0), total);
@@ -154,9 +190,15 @@ var paginate = (all, offset, limit) => {
154
190
  nextOffset: hasMore ? consumed : null
155
191
  };
156
192
  };
193
+ var toSearchableTool = (tool) => ({
194
+ path: addressToPath(String(tool.address)),
195
+ integration: String(tool.integration),
196
+ name: String(tool.name),
197
+ description: tool.description
198
+ });
157
199
  var SEARCH_FIELD_WEIGHTS = {
158
200
  path: 12,
159
- sourceId: 8,
201
+ integration: 8,
160
202
  name: 10,
161
203
  description: 5
162
204
  };
@@ -216,10 +258,10 @@ var matchesNamespace = (tool, namespace) => {
216
258
  if (namespaceTokens.length === 0) {
217
259
  return true;
218
260
  }
219
- const sourceTokens = tokenizeSearchText(tool.sourceId);
220
- const pathTokens = tokenizeSearchText(tool.id);
261
+ const integrationTokens = tokenizeSearchText(tool.integration);
262
+ const pathTokens = tokenizeSearchText(tool.path);
221
263
  const isPrefixMatch = (tokens) => namespaceTokens.every((token, index) => tokens[index] === token);
222
- return isPrefixMatch(sourceTokens) || isPrefixMatch(pathTokens);
264
+ return isPrefixMatch(integrationTokens) || isPrefixMatch(pathTokens);
223
265
  };
224
266
  var scoreToolMatch = (tool, query) => {
225
267
  const normalizedQuery = normalizeSearchText(query);
@@ -227,13 +269,13 @@ var scoreToolMatch = (tool, query) => {
227
269
  if (normalizedQuery.length === 0 || queryTokens.length === 0) {
228
270
  return null;
229
271
  }
230
- const path = prepareField(tool.id);
231
- const sourceId = prepareField(tool.sourceId);
272
+ const path = prepareField(tool.path);
273
+ const integration = prepareField(tool.integration);
232
274
  const name = prepareField(tool.name);
233
275
  const description = prepareField(tool.description);
234
276
  const fieldScores = [
235
277
  scorePreparedField(normalizedQuery, queryTokens, path, SEARCH_FIELD_WEIGHTS.path),
236
- scorePreparedField(normalizedQuery, queryTokens, sourceId, SEARCH_FIELD_WEIGHTS.sourceId),
278
+ scorePreparedField(normalizedQuery, queryTokens, integration, SEARCH_FIELD_WEIGHTS.integration),
237
279
  scorePreparedField(normalizedQuery, queryTokens, name, SEARCH_FIELD_WEIGHTS.name),
238
280
  scorePreparedField(normalizedQuery, queryTokens, description, SEARCH_FIELD_WEIGHTS.description)
239
281
  ];
@@ -263,14 +305,14 @@ var scoreToolMatch = (tool, query) => {
263
305
  if (path.tokens[0] === queryTokens[0] || name.tokens[0] === queryTokens[0]) {
264
306
  score += 8;
265
307
  }
266
- if (normalizeSearchText(tool.id) === normalizedQuery || normalizeSearchText(tool.name) === normalizedQuery) {
308
+ if (normalizeSearchText(tool.path) === normalizedQuery || normalizeSearchText(tool.name) === normalizedQuery) {
267
309
  score += 20;
268
310
  }
269
311
  return {
270
- path: tool.id,
312
+ path: tool.path,
271
313
  name: tool.name,
272
314
  description: tool.description,
273
- sourceId: tool.sourceId,
315
+ integration: tool.integration,
274
316
  score
275
317
  };
276
318
  };
@@ -299,7 +341,8 @@ var searchTools = Effect.fn("executor.tools.search")(function* (executor, query,
299
341
  })
300
342
  )
301
343
  );
302
- const ranked = all.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));
344
+ const searchable = all.map(toSearchableTool);
345
+ const ranked = 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));
303
346
  const page = paginate(ranked, offset, limit);
304
347
  yield* Effect.annotateCurrentSpan({
305
348
  "executor.search.candidate_count": all.length,
@@ -309,48 +352,53 @@ var searchTools = Effect.fn("executor.tools.search")(function* (executor, query,
309
352
  });
310
353
  return page;
311
354
  });
355
+ var defaultToolDiscoveryProvider = {
356
+ searchTools: ({ executor, query, namespace, limit, offset }) => searchTools(executor, query, limit, { namespace, offset })
357
+ };
312
358
  var listExecutorSources = Effect.fn("executor.sources.list")(function* (executor, options) {
313
359
  const normalizedQuery = normalizeSearchText(options?.query ?? "");
314
360
  const limit = options?.limit ?? 50;
315
361
  const offset = options?.offset ?? 0;
316
- const sources = yield* executor.sources.list().pipe(
362
+ const integrations = yield* executor.integrations.list().pipe(
317
363
  Effect.mapError(
318
364
  (cause) => new ExecutionToolError({
319
- message: "Failed to list executor sources",
365
+ message: "Failed to list executor integrations",
320
366
  cause
321
367
  })
322
368
  )
323
369
  );
324
- const filtered = normalizedQuery.length === 0 ? sources : sources.filter((source) => {
325
- const haystack = normalizeSearchText([source.id, source.name, source.kind].join(" "));
370
+ const filtered = normalizedQuery.length === 0 ? integrations : integrations.filter((integration) => {
371
+ const haystack = normalizeSearchText(
372
+ [String(integration.slug), integration.description, integration.kind].join(" ")
373
+ );
326
374
  return tokenizeSearchText(normalizedQuery).every((token) => haystack.includes(token));
327
375
  });
328
376
  const allTools = yield* executor.tools.list({ includeAnnotations: false }).pipe(
329
377
  Effect.mapError(
330
378
  (cause) => new ExecutionToolError({
331
- message: "Failed to list tools for source counts",
379
+ message: "Failed to list tools for integration counts",
332
380
  cause
333
381
  })
334
382
  )
335
383
  );
336
- const toolCountBySource = /* @__PURE__ */ new Map();
384
+ const toolCountByIntegration = /* @__PURE__ */ new Map();
337
385
  for (const tool of allTools) {
338
- toolCountBySource.set(tool.sourceId, (toolCountBySource.get(tool.sourceId) ?? 0) + 1);
386
+ const key = String(tool.integration);
387
+ toolCountByIntegration.set(key, (toolCountByIntegration.get(key) ?? 0) + 1);
339
388
  }
340
389
  const sortedWithCounts = filtered.map(
341
- (source) => ({
342
- id: source.id,
343
- name: source.name,
344
- kind: source.kind,
345
- runtime: source.runtime,
346
- canRemove: source.canRemove,
347
- canRefresh: source.canRefresh,
348
- toolCount: toolCountBySource.get(source.id) ?? 0
390
+ (integration) => ({
391
+ id: String(integration.slug),
392
+ name: String(integration.slug),
393
+ kind: integration.kind,
394
+ canRemove: integration.canRemove,
395
+ canRefresh: integration.canRefresh,
396
+ toolCount: toolCountByIntegration.get(String(integration.slug)) ?? 0
349
397
  })
350
398
  ).sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
351
399
  const page = paginate(sortedWithCounts, offset, limit);
352
400
  yield* Effect.annotateCurrentSpan({
353
- "executor.sources.candidate_count": sources.length,
401
+ "executor.sources.candidate_count": integrations.length,
354
402
  "executor.sources.match_count": sortedWithCounts.length,
355
403
  "executor.sources.result_count": page.items.length,
356
404
  "executor.sources.has_more": page.hasMore
@@ -361,7 +409,8 @@ var describeTool = Effect.fn("executor.tools.describe")(function* (executor, pat
361
409
  yield* Effect.annotateCurrentSpan({ "mcp.tool.name": path });
362
410
  const builtin = BUILTIN_TOOL_DESCRIPTIONS.get(path);
363
411
  if (builtin) return builtin;
364
- const schema = yield* executor.tools.schema(path);
412
+ const address = pathToAddress(path);
413
+ const schema = yield* executor.tools.schema(address);
365
414
  if (schema === null) {
366
415
  return { path, name: path };
367
416
  }
@@ -378,23 +427,38 @@ var describeTool = Effect.fn("executor.tools.describe")(function* (executor, pat
378
427
  // src/description.ts
379
428
  import { Effect as Effect2 } from "effect";
380
429
  var buildExecuteDescription = (executor) => Effect2.gen(function* () {
381
- const sources = yield* executor.sources.list().pipe(
430
+ const connections = yield* executor.connections.list().pipe(
382
431
  // oxlint-disable-next-line executor/no-effect-escape-hatch -- boundary: ExecutionEngine.getDescription currently exposes no error channel; engine typed-error widening is covered separately
383
432
  Effect2.orDie,
384
- Effect2.withSpan("executor.sources.list")
433
+ Effect2.withSpan("executor.connections.list")
385
434
  );
386
- const description = yield* Effect2.sync(() => formatDescription(sources)).pipe(
435
+ const description = yield* Effect2.sync(
436
+ () => formatDescription(connections.map((connection) => connectionPath(connection)))
437
+ ).pipe(
387
438
  Effect2.withSpan("schema.compile.description", {
388
- attributes: { "executor.source_count": sources.length }
439
+ attributes: { "executor.connection_count": connections.length }
389
440
  })
390
441
  );
391
442
  yield* Effect2.annotateCurrentSpan({
392
- "executor.source_count": sources.length,
393
- "schema.kind": "execute"
443
+ "executor.connection_count": connections.length,
444
+ "schema.kind": "execute",
445
+ // Connection inventory so a failing session build (which runs this during
446
+ // init) names the callable prefixes it resolved without listing tools.
447
+ "executor.connection_addresses": connections.map((connection) => connectionPath(connection)).slice(0, 50).join(","),
448
+ "executor.connection_integrations": [
449
+ ...new Set(connections.map((connection) => String(connection.integration)))
450
+ ].join(","),
451
+ "executor.connection_owners": [
452
+ ...new Set(connections.map((connection) => connection.owner))
453
+ ].join(",")
394
454
  });
395
455
  return description;
396
456
  }).pipe(Effect2.withSpan("schema.describe.execute"));
397
- var formatDescription = (sources) => {
457
+ var connectionPath = (connection) => {
458
+ const address = String(connection.address);
459
+ return address.startsWith("tools.") ? address.slice("tools.".length) : address;
460
+ };
461
+ var formatDescription = (connectionPrefixes) => {
398
462
  const lines = [
399
463
  "Execute TypeScript in a sandboxed runtime with access to configured API tools.",
400
464
  "",
@@ -404,35 +468,36 @@ var formatDescription = (sources) => {
404
468
  '2. `const path = matches[0]?.path; if (!path) return "No matching tools found.";`',
405
469
  "3. `const details = await tools.describe.tool({ path });`",
406
470
  "4. Use `details.inputTypeScript` / `details.outputTypeScript` and `details.typeScriptDefinitions` for compact shapes.",
407
- "5. Use `tools.executor.sources.list()` when you need configured source inventory.",
471
+ "5. Use `tools.executor.coreTools.connections.list({})` when you need live saved-connection inventory.",
408
472
  "6. Call the tool: `const result = await tools.<path>(input);`",
409
473
  "",
410
474
  "## Rules",
411
475
  "",
412
476
  "- `tools.search()` returns paginated, ranked matches: `{ items, total, hasMore, nextOffset }`. Best-first. Use short intent phrases like `github issues`, `repo details`, or `create calendar event`.",
413
477
  '- When you already know the namespace, narrow with `tools.search({ namespace: "github", query: "issues" })`.',
414
- "- `tools.executor.sources.list()` returns the same paged shape: `{ items: [{ id, toolCount, ... }], total, hasMore, nextOffset }`.",
478
+ "- `tools.executor.coreTools.connections.list({})` returns saved connections with `{ address, integration, owner, name, ... }`. The `address` field includes the leading `tools.` root.",
415
479
  "- Tool calls return a value union: `{ ok: true, data }` for success or `{ ok: false, error: { code, message, status?, details?, retryable? } }` for expected tool/domain failures. Branch on `result.ok`.",
416
- "- If `hasMore` is true and you didn't find what you need, fetch the next page: `tools.search({ query, offset: nextOffset, limit })`. Same `offset` parameter on `tools.executor.sources.list({ offset, limit })`.",
417
- "- Always use the namespace prefix when calling tools: `tools.<namespace>.<tool>(args)`. Example: `tools.home_assistant_rest_api.states.getState(...)` \u2014 not `tools.states.getState(...)`.",
418
- "- The `tools` object is a lazy proxy \u2014 `Object.keys(tools)` won't work. Use `tools.search()` or `tools.executor.sources.list()` instead.",
419
- '- Pass an object to system tools, e.g. `tools.search({ query: "..." })`, `tools.executor.sources.list()`, and `tools.describe.tool({ path })`.',
480
+ "- If `tools.search()` returns `hasMore: true` and you didn't find what you need, fetch the next page: `tools.search({ query, offset: nextOffset, limit })`.",
481
+ "- Always use the full address when calling tools: `tools.<integration>.<owner>.<connection>.<tool>(args)`. The `path` returned by `tools.search()` / `tools.describe.tool()` is already the exact path under `tools` \u2014 call `tools[path]` rather than guessing segments.",
482
+ "- The `tools` object is a lazy proxy \u2014 `Object.keys(tools)` won't work. Use `tools.search()` or `tools.executor.coreTools.connections.list({})` instead.",
483
+ '- Pass an object to system tools, e.g. `tools.search({ query: "..." })`, `tools.executor.coreTools.connections.list({})`, and `tools.describe.tool({ path })`.',
420
484
  "- `tools.describe.tool()` returns compact TypeScript shapes. Use `inputTypeScript`, `outputTypeScript`, and `typeScriptDefinitions`.",
421
485
  "- For tools that return large collections (e.g. `getStates`, `getAll`), filter results in code rather than calling per-item tools.",
422
486
  "- Do not use `fetch` \u2014 all API calls go through `tools.*`.",
423
487
  "- If execution pauses for interaction, resume it with the returned `resumePayload`.",
424
488
  "- TypeScript type syntax (`: T`, `as T`, generics, interfaces, type aliases) is stripped before execution \u2014 feel free to write idiomatic TypeScript using the shapes from `tools.describe.tool()`. Decorators and `enum` are not supported."
425
489
  ];
426
- if (sources.length > 0) {
490
+ if (connectionPrefixes.length > 0) {
427
491
  lines.push("");
428
- lines.push("## Available namespaces");
492
+ lines.push("## Available connection prefixes");
429
493
  lines.push("");
430
- const sorted = [...sources].sort((a, b) => a.id.localeCompare(b.id)).slice(0, 50);
431
- for (const source of sorted) {
432
- lines.push(`- \`${source.id}\``);
494
+ lines.push("These are paths under `tools.`; append the final tool segment.");
495
+ const sorted = [...connectionPrefixes].sort((a, b) => a.localeCompare(b)).slice(0, 50);
496
+ for (const prefix of sorted) {
497
+ lines.push(`- \`${prefix}\``);
433
498
  }
434
- if (sources.length > sorted.length) {
435
- lines.push(`- ... ${sources.length - sorted.length} more`);
499
+ if (connectionPrefixes.length > sorted.length) {
500
+ lines.push(`- ... ${connectionPrefixes.length - sorted.length} more`);
436
501
  }
437
502
  }
438
503
  return lines.join("\n");
@@ -506,7 +571,7 @@ instructions: ${instructions}`);
506
571
  kind: isUrlElicitation ? "url" : "form",
507
572
  message: req.message,
508
573
  instructions,
509
- toolId: String(paused.elicitationContext.toolId),
574
+ address: String(paused.elicitationContext.address),
510
575
  args: paused.elicitationContext.args,
511
576
  ...isUrlElicitation ? { url: req.url } : {},
512
577
  ...isFormElicitation ? { requestedSchema: req.requestedSchema } : {}
@@ -537,7 +602,7 @@ var readOptionalOffset = (value, toolName) => {
537
602
  }
538
603
  return Math.floor(value);
539
604
  };
540
- var makeFullInvoker = (executor, invokeOptions) => {
605
+ var makeFullInvoker = (executor, invokeOptions, toolDiscoveryProvider) => {
541
606
  const base = makeExecutorToolInvoker(executor, { invokeOptions });
542
607
  return {
543
608
  invoke: ({ path, args }) => {
@@ -571,7 +636,10 @@ var makeFullInvoker = (executor, invokeOptions) => {
571
636
  if (Predicate2.isTagged(offset, "ExecutionToolError")) {
572
637
  return Effect3.fail(offset);
573
638
  }
574
- return searchTools(executor, args.query ?? "", limit, {
639
+ return toolDiscoveryProvider.searchTools({
640
+ executor,
641
+ query: args.query ?? "",
642
+ limit,
575
643
  namespace: args.namespace,
576
644
  offset
577
645
  }).pipe(
@@ -652,7 +720,7 @@ var makeFullInvoker = (executor, invokeOptions) => {
652
720
  };
653
721
  };
654
722
  var createExecutionEngine = (config) => {
655
- const { executor, codeExecutor } = config;
723
+ const { executor, codeExecutor, toolDiscoveryProvider = defaultToolDiscoveryProvider } = config;
656
724
  const pausedExecutions = /* @__PURE__ */ new Map();
657
725
  let nextId = 0;
658
726
  const awaitCompletionOrPause = (fiber, pauseQueue) => Effect3.raceFirst(
@@ -684,7 +752,11 @@ var createExecutionEngine = (config) => {
684
752
  yield* Queue.offer(pauseQueue, paused);
685
753
  return yield* Deferred.await(responseDeferred);
686
754
  });
687
- const invoker = makeFullInvoker(executor, { onElicitation: elicitationHandler });
755
+ const invoker = makeFullInvoker(
756
+ executor,
757
+ { onElicitation: elicitationHandler },
758
+ toolDiscoveryProvider
759
+ );
688
760
  fiber = yield* Effect3.forkDetach(
689
761
  codeExecutor.execute(code, invoker).pipe(Effect3.withSpan("executor.code.exec"))
690
762
  );
@@ -708,9 +780,13 @@ var createExecutionEngine = (config) => {
708
780
  "mcp.execute.mode": "inline",
709
781
  "mcp.execute.code_length": code.length
710
782
  });
711
- const invoker = makeFullInvoker(executor, {
712
- onElicitation: options.onElicitation
713
- });
783
+ const invoker = makeFullInvoker(
784
+ executor,
785
+ {
786
+ onElicitation: options.onElicitation
787
+ },
788
+ toolDiscoveryProvider
789
+ );
714
790
  return yield* codeExecutor.execute(code, invoker).pipe(Effect3.withSpan("executor.code.exec"));
715
791
  });
716
792
  return {
@@ -726,6 +802,7 @@ export {
726
802
  ExecutionToolError,
727
803
  makeExecutorToolInvoker,
728
804
  searchTools,
805
+ defaultToolDiscoveryProvider,
729
806
  listExecutorSources,
730
807
  describeTool,
731
808
  buildExecuteDescription,
@@ -733,4 +810,4 @@ export {
733
810
  formatPausedExecution,
734
811
  createExecutionEngine
735
812
  };
736
- //# sourceMappingURL=chunk-NJPJNVEN.js.map
813
+ //# sourceMappingURL=chunk-Q2R6Q3HF.js.map