@executor-js/execution 1.4.32 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,56 @@ 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;
33
+ var BUILTIN_TOOL_DESCRIPTIONS = /* @__PURE__ */ new Map([
34
+ [
35
+ "search",
36
+ {
37
+ path: "search",
38
+ name: "search",
39
+ description: "Search available Executor tools.",
40
+ inputTypeScript: "{ query: string; namespace?: string; limit?: number; offset?: number; }",
41
+ outputTypeScript: "{ items: ToolDiscoveryResult[]; total: number; hasMore: boolean; nextOffset: number | null; }",
42
+ typeScriptDefinitions: {
43
+ ToolDiscoveryResult: "{ path: string; name: string; description?: string; integration: string; score: number; }"
44
+ }
45
+ }
46
+ ],
47
+ [
48
+ "executor.sources.list",
49
+ {
50
+ path: "executor.sources.list",
51
+ name: "executor.sources.list",
52
+ description: "List configured Executor integrations.",
53
+ inputTypeScript: "{ query?: string; limit?: number; offset?: number; }",
54
+ outputTypeScript: "{ items: ExecutorSourceListItem[]; total: number; hasMore: boolean; nextOffset: number | null; }",
55
+ typeScriptDefinitions: {
56
+ ExecutorSourceListItem: "{ id: string; name: string; kind: string; canRemove?: boolean; canRefresh?: boolean; toolCount: number; }"
57
+ }
58
+ }
59
+ ],
60
+ [
61
+ "describe.tool",
62
+ {
63
+ path: "describe.tool",
64
+ name: "describe.tool",
65
+ description: "Describe a tool's compact TypeScript input and output shapes.",
66
+ inputTypeScript: "{ path: string; }",
67
+ outputTypeScript: "DescribedTool",
68
+ typeScriptDefinitions: {
69
+ DescribedTool: "{ path: string; name: string; description?: string; inputTypeScript?: string; outputTypeScript?: string; typeScriptDefinitions?: { [k: string]: string; }; }"
70
+ }
71
+ }
72
+ ]
73
+ ]);
18
74
  var newCorrelationId = () => {
19
75
  return Math.floor(Math.random() * 4294967296).toString(16).padStart(8, "0");
20
76
  };
@@ -23,19 +79,28 @@ var validationIssues = (value) => {
23
79
  const issues = value.issues;
24
80
  return Array.isArray(issues) ? issues : null;
25
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
+ });
26
90
  var expectedToolFailure = (value) => {
27
- if (Predicate.isTagged(value, "ToolNotFoundError") && "toolId" in value) {
28
- 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));
29
94
  return {
30
95
  code: "tool_not_found",
31
- message: `Tool not found: ${String(value.toolId)}`,
32
- details: { toolId: value.toolId, ...suggestions ? { suggestions } : {} }
96
+ message: `Tool not found: ${address}`,
97
+ details: { path: address, ...suggestions ? { suggestions } : {} }
33
98
  };
34
99
  }
35
- if (Predicate.isTagged(value, "ToolBlockedError") && "toolId" in value) {
100
+ if (Predicate.isTagged(value, "ToolBlockedError") && "address" in value) {
36
101
  return {
37
102
  code: "tool_blocked",
38
- message: `Tool blocked by policy: ${String(value.toolId)}`,
103
+ message: `Tool blocked by policy: ${addressToPath(String(value.address))}`,
39
104
  details: value
40
105
  };
41
106
  }
@@ -51,17 +116,29 @@ var expectedToolFailure = (value) => {
51
116
  }
52
117
  return null;
53
118
  };
54
- var extractSourceNamespace = (path) => {
55
- const idx = path.indexOf(".");
56
- 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);
57
123
  };
58
124
  var makeExecutorToolInvoker = (executor, options) => ({
59
125
  invoke: Effect.fn("mcp.tool.dispatch")(function* ({ path, args }) {
60
126
  yield* Effect.annotateCurrentSpan({
61
127
  "mcp.tool.name": path,
62
- "mcp.tool.source_id": extractSourceNamespace(path)
128
+ "mcp.tool.integration": extractNamespace(path)
63
129
  });
64
- 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
+ ),
65
142
  Effect.catchCause((cause) => {
66
143
  const err = cause.reasons.find(Cause.isFailReason)?.error;
67
144
  const expected = expectedToolFailure(err);
@@ -71,7 +148,7 @@ var makeExecutorToolInvoker = (executor, options) => ({
71
148
  if (isElicitationDeclinedError(err)) {
72
149
  return Effect.fail(
73
150
  new ExecutionToolError({
74
- 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.`,
75
152
  cause: err
76
153
  })
77
154
  );
@@ -99,7 +176,7 @@ var makeExecutorToolInvoker = (executor, options) => ({
99
176
  return { ok: true, data: result };
100
177
  })
101
178
  });
102
- 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");
103
180
  var paginate = (all, offset, limit) => {
104
181
  const total = all.length;
105
182
  const start = Math.min(Math.max(offset, 0), total);
@@ -113,9 +190,15 @@ var paginate = (all, offset, limit) => {
113
190
  nextOffset: hasMore ? consumed : null
114
191
  };
115
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
+ });
116
199
  var SEARCH_FIELD_WEIGHTS = {
117
200
  path: 12,
118
- sourceId: 8,
201
+ integration: 8,
119
202
  name: 10,
120
203
  description: 5
121
204
  };
@@ -175,10 +258,10 @@ var matchesNamespace = (tool, namespace) => {
175
258
  if (namespaceTokens.length === 0) {
176
259
  return true;
177
260
  }
178
- const sourceTokens = tokenizeSearchText(tool.sourceId);
179
- const pathTokens = tokenizeSearchText(tool.id);
261
+ const integrationTokens = tokenizeSearchText(tool.integration);
262
+ const pathTokens = tokenizeSearchText(tool.path);
180
263
  const isPrefixMatch = (tokens) => namespaceTokens.every((token, index) => tokens[index] === token);
181
- return isPrefixMatch(sourceTokens) || isPrefixMatch(pathTokens);
264
+ return isPrefixMatch(integrationTokens) || isPrefixMatch(pathTokens);
182
265
  };
183
266
  var scoreToolMatch = (tool, query) => {
184
267
  const normalizedQuery = normalizeSearchText(query);
@@ -186,13 +269,13 @@ var scoreToolMatch = (tool, query) => {
186
269
  if (normalizedQuery.length === 0 || queryTokens.length === 0) {
187
270
  return null;
188
271
  }
189
- const path = prepareField(tool.id);
190
- const sourceId = prepareField(tool.sourceId);
272
+ const path = prepareField(tool.path);
273
+ const integration = prepareField(tool.integration);
191
274
  const name = prepareField(tool.name);
192
275
  const description = prepareField(tool.description);
193
276
  const fieldScores = [
194
277
  scorePreparedField(normalizedQuery, queryTokens, path, SEARCH_FIELD_WEIGHTS.path),
195
- scorePreparedField(normalizedQuery, queryTokens, sourceId, SEARCH_FIELD_WEIGHTS.sourceId),
278
+ scorePreparedField(normalizedQuery, queryTokens, integration, SEARCH_FIELD_WEIGHTS.integration),
196
279
  scorePreparedField(normalizedQuery, queryTokens, name, SEARCH_FIELD_WEIGHTS.name),
197
280
  scorePreparedField(normalizedQuery, queryTokens, description, SEARCH_FIELD_WEIGHTS.description)
198
281
  ];
@@ -222,14 +305,14 @@ var scoreToolMatch = (tool, query) => {
222
305
  if (path.tokens[0] === queryTokens[0] || name.tokens[0] === queryTokens[0]) {
223
306
  score += 8;
224
307
  }
225
- if (normalizeSearchText(tool.id) === normalizedQuery || normalizeSearchText(tool.name) === normalizedQuery) {
308
+ if (normalizeSearchText(tool.path) === normalizedQuery || normalizeSearchText(tool.name) === normalizedQuery) {
226
309
  score += 20;
227
310
  }
228
311
  return {
229
- path: tool.id,
312
+ path: tool.path,
230
313
  name: tool.name,
231
314
  description: tool.description,
232
- sourceId: tool.sourceId,
315
+ integration: tool.integration,
233
316
  score
234
317
  };
235
318
  };
@@ -258,7 +341,8 @@ var searchTools = Effect.fn("executor.tools.search")(function* (executor, query,
258
341
  })
259
342
  )
260
343
  );
261
- 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));
262
346
  const page = paginate(ranked, offset, limit);
263
347
  yield* Effect.annotateCurrentSpan({
264
348
  "executor.search.candidate_count": all.length,
@@ -268,48 +352,53 @@ var searchTools = Effect.fn("executor.tools.search")(function* (executor, query,
268
352
  });
269
353
  return page;
270
354
  });
355
+ var defaultToolDiscoveryProvider = {
356
+ searchTools: ({ executor, query, namespace, limit, offset }) => searchTools(executor, query, limit, { namespace, offset })
357
+ };
271
358
  var listExecutorSources = Effect.fn("executor.sources.list")(function* (executor, options) {
272
359
  const normalizedQuery = normalizeSearchText(options?.query ?? "");
273
360
  const limit = options?.limit ?? 50;
274
361
  const offset = options?.offset ?? 0;
275
- const sources = yield* executor.sources.list().pipe(
362
+ const integrations = yield* executor.integrations.list().pipe(
276
363
  Effect.mapError(
277
364
  (cause) => new ExecutionToolError({
278
- message: "Failed to list executor sources",
365
+ message: "Failed to list executor integrations",
279
366
  cause
280
367
  })
281
368
  )
282
369
  );
283
- const filtered = normalizedQuery.length === 0 ? sources : sources.filter((source) => {
284
- 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
+ );
285
374
  return tokenizeSearchText(normalizedQuery).every((token) => haystack.includes(token));
286
375
  });
287
376
  const allTools = yield* executor.tools.list({ includeAnnotations: false }).pipe(
288
377
  Effect.mapError(
289
378
  (cause) => new ExecutionToolError({
290
- message: "Failed to list tools for source counts",
379
+ message: "Failed to list tools for integration counts",
291
380
  cause
292
381
  })
293
382
  )
294
383
  );
295
- const toolCountBySource = /* @__PURE__ */ new Map();
384
+ const toolCountByIntegration = /* @__PURE__ */ new Map();
296
385
  for (const tool of allTools) {
297
- 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);
298
388
  }
299
389
  const sortedWithCounts = filtered.map(
300
- (source) => ({
301
- id: source.id,
302
- name: source.name,
303
- kind: source.kind,
304
- runtime: source.runtime,
305
- canRemove: source.canRemove,
306
- canRefresh: source.canRefresh,
307
- 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
308
397
  })
309
398
  ).sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
310
399
  const page = paginate(sortedWithCounts, offset, limit);
311
400
  yield* Effect.annotateCurrentSpan({
312
- "executor.sources.candidate_count": sources.length,
401
+ "executor.sources.candidate_count": integrations.length,
313
402
  "executor.sources.match_count": sortedWithCounts.length,
314
403
  "executor.sources.result_count": page.items.length,
315
404
  "executor.sources.has_more": page.hasMore
@@ -318,7 +407,10 @@ var listExecutorSources = Effect.fn("executor.sources.list")(function* (executor
318
407
  });
319
408
  var describeTool = Effect.fn("executor.tools.describe")(function* (executor, path) {
320
409
  yield* Effect.annotateCurrentSpan({ "mcp.tool.name": path });
321
- const schema = yield* executor.tools.schema(path);
410
+ const builtin = BUILTIN_TOOL_DESCRIPTIONS.get(path);
411
+ if (builtin) return builtin;
412
+ const address = pathToAddress(path);
413
+ const schema = yield* executor.tools.schema(address);
322
414
  if (schema === null) {
323
415
  return { path, name: path };
324
416
  }
@@ -335,23 +427,38 @@ var describeTool = Effect.fn("executor.tools.describe")(function* (executor, pat
335
427
  // src/description.ts
336
428
  import { Effect as Effect2 } from "effect";
337
429
  var buildExecuteDescription = (executor) => Effect2.gen(function* () {
338
- const sources = yield* executor.sources.list().pipe(
430
+ const connections = yield* executor.connections.list().pipe(
339
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
340
432
  Effect2.orDie,
341
- Effect2.withSpan("executor.sources.list")
433
+ Effect2.withSpan("executor.connections.list")
342
434
  );
343
- const description = yield* Effect2.sync(() => formatDescription(sources)).pipe(
435
+ const description = yield* Effect2.sync(
436
+ () => formatDescription(connections.map((connection) => connectionPath(connection)))
437
+ ).pipe(
344
438
  Effect2.withSpan("schema.compile.description", {
345
- attributes: { "executor.source_count": sources.length }
439
+ attributes: { "executor.connection_count": connections.length }
346
440
  })
347
441
  );
348
442
  yield* Effect2.annotateCurrentSpan({
349
- "executor.source_count": sources.length,
350
- "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(",")
351
454
  });
352
455
  return description;
353
456
  }).pipe(Effect2.withSpan("schema.describe.execute"));
354
- 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) => {
355
462
  const lines = [
356
463
  "Execute TypeScript in a sandboxed runtime with access to configured API tools.",
357
464
  "",
@@ -361,35 +468,36 @@ var formatDescription = (sources) => {
361
468
  '2. `const path = matches[0]?.path; if (!path) return "No matching tools found.";`',
362
469
  "3. `const details = await tools.describe.tool({ path });`",
363
470
  "4. Use `details.inputTypeScript` / `details.outputTypeScript` and `details.typeScriptDefinitions` for compact shapes.",
364
- "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.",
365
472
  "6. Call the tool: `const result = await tools.<path>(input);`",
366
473
  "",
367
474
  "## Rules",
368
475
  "",
369
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`.",
370
477
  '- When you already know the namespace, narrow with `tools.search({ namespace: "github", query: "issues" })`.',
371
- "- `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.",
372
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`.",
373
- "- 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 })`.",
374
- "- 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(...)`.",
375
- "- The `tools` object is a lazy proxy \u2014 `Object.keys(tools)` won't work. Use `tools.search()` or `tools.executor.sources.list()` instead.",
376
- '- 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 })`.',
377
484
  "- `tools.describe.tool()` returns compact TypeScript shapes. Use `inputTypeScript`, `outputTypeScript`, and `typeScriptDefinitions`.",
378
485
  "- For tools that return large collections (e.g. `getStates`, `getAll`), filter results in code rather than calling per-item tools.",
379
486
  "- Do not use `fetch` \u2014 all API calls go through `tools.*`.",
380
487
  "- If execution pauses for interaction, resume it with the returned `resumePayload`.",
381
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."
382
489
  ];
383
- if (sources.length > 0) {
490
+ if (connectionPrefixes.length > 0) {
384
491
  lines.push("");
385
- lines.push("## Available namespaces");
492
+ lines.push("## Available connection prefixes");
386
493
  lines.push("");
387
- const sorted = [...sources].sort((a, b) => a.id.localeCompare(b.id)).slice(0, 50);
388
- for (const source of sorted) {
389
- 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}\``);
390
498
  }
391
- if (sources.length > sorted.length) {
392
- lines.push(`- ... ${sources.length - sorted.length} more`);
499
+ if (connectionPrefixes.length > sorted.length) {
500
+ lines.push(`- ... ${connectionPrefixes.length - sorted.length} more`);
393
501
  }
394
502
  }
395
503
  return lines.join("\n");
@@ -463,7 +571,7 @@ instructions: ${instructions}`);
463
571
  kind: isUrlElicitation ? "url" : "form",
464
572
  message: req.message,
465
573
  instructions,
466
- toolId: String(paused.elicitationContext.toolId),
574
+ address: String(paused.elicitationContext.address),
467
575
  args: paused.elicitationContext.args,
468
576
  ...isUrlElicitation ? { url: req.url } : {},
469
577
  ...isFormElicitation ? { requestedSchema: req.requestedSchema } : {}
@@ -494,7 +602,7 @@ var readOptionalOffset = (value, toolName) => {
494
602
  }
495
603
  return Math.floor(value);
496
604
  };
497
- var makeFullInvoker = (executor, invokeOptions) => {
605
+ var makeFullInvoker = (executor, invokeOptions, toolDiscoveryProvider) => {
498
606
  const base = makeExecutorToolInvoker(executor, { invokeOptions });
499
607
  return {
500
608
  invoke: ({ path, args }) => {
@@ -528,7 +636,10 @@ var makeFullInvoker = (executor, invokeOptions) => {
528
636
  if (Predicate2.isTagged(offset, "ExecutionToolError")) {
529
637
  return Effect3.fail(offset);
530
638
  }
531
- return searchTools(executor, args.query ?? "", limit, {
639
+ return toolDiscoveryProvider.searchTools({
640
+ executor,
641
+ query: args.query ?? "",
642
+ limit,
532
643
  namespace: args.namespace,
533
644
  offset
534
645
  }).pipe(
@@ -609,7 +720,7 @@ var makeFullInvoker = (executor, invokeOptions) => {
609
720
  };
610
721
  };
611
722
  var createExecutionEngine = (config) => {
612
- const { executor, codeExecutor } = config;
723
+ const { executor, codeExecutor, toolDiscoveryProvider = defaultToolDiscoveryProvider } = config;
613
724
  const pausedExecutions = /* @__PURE__ */ new Map();
614
725
  let nextId = 0;
615
726
  const awaitCompletionOrPause = (fiber, pauseQueue) => Effect3.raceFirst(
@@ -641,7 +752,11 @@ var createExecutionEngine = (config) => {
641
752
  yield* Queue.offer(pauseQueue, paused);
642
753
  return yield* Deferred.await(responseDeferred);
643
754
  });
644
- const invoker = makeFullInvoker(executor, { onElicitation: elicitationHandler });
755
+ const invoker = makeFullInvoker(
756
+ executor,
757
+ { onElicitation: elicitationHandler },
758
+ toolDiscoveryProvider
759
+ );
645
760
  fiber = yield* Effect3.forkDetach(
646
761
  codeExecutor.execute(code, invoker).pipe(Effect3.withSpan("executor.code.exec"))
647
762
  );
@@ -665,9 +780,13 @@ var createExecutionEngine = (config) => {
665
780
  "mcp.execute.mode": "inline",
666
781
  "mcp.execute.code_length": code.length
667
782
  });
668
- const invoker = makeFullInvoker(executor, {
669
- onElicitation: options.onElicitation
670
- });
783
+ const invoker = makeFullInvoker(
784
+ executor,
785
+ {
786
+ onElicitation: options.onElicitation
787
+ },
788
+ toolDiscoveryProvider
789
+ );
671
790
  return yield* codeExecutor.execute(code, invoker).pipe(Effect3.withSpan("executor.code.exec"));
672
791
  });
673
792
  return {
@@ -683,6 +802,7 @@ export {
683
802
  ExecutionToolError,
684
803
  makeExecutorToolInvoker,
685
804
  searchTools,
805
+ defaultToolDiscoveryProvider,
686
806
  listExecutorSources,
687
807
  describeTool,
688
808
  buildExecuteDescription,
@@ -690,4 +810,4 @@ export {
690
810
  formatPausedExecution,
691
811
  createExecutionEngine
692
812
  };
693
- //# sourceMappingURL=chunk-OLPHXJSA.js.map
813
+ //# sourceMappingURL=chunk-Q2R6Q3HF.js.map