fix: dispose tool runtimes on conflict error and document builtinToolNames guards

This commit is contained in:
Davanum Srinivas 2026-03-19 10:16:42 -04:00
parent 643a7633f0
commit 65dc9d06b1
No known key found for this signature in database
GPG Key ID: 6DEA177048756885
2 changed files with 9 additions and 0 deletions

View File

@ -1595,6 +1595,8 @@ export async function runEmbeddedAttempt(
// Collect exact raw names of non-plugin OpenClaw tools. Passed to the
// subscriber so filterToolResultMediaUrls only trusts MEDIA: paths from
// the concrete built-in tool registrations for this run.
// Built from `tools` (not `effectiveTools`) so bundle-injected MCP/LSP
// tools are deliberately excluded and never receive local-path trust.
const builtinToolNames = new Set(
tools.flatMap((tool) => {
const name = tool.name.trim();
@ -1609,6 +1611,10 @@ export async function runEmbeddedAttempt(
existingToolNames: builtinToolNames,
});
if (clientToolNameConflicts.length > 0) {
// Dispose runtimes before throwing — the inner try/finally that normally
// handles disposal hasn't been entered yet at this point.
await bundleMcpRuntime?.dispose();
await bundleLspRuntime?.dispose();
throw createClientToolNameConflictError(clientToolNameConflicts);
}
await params.onPreflightPassed?.();

View File

@ -183,6 +183,9 @@ export function filterToolResultMediaUrls(
// an exact raw-name match. This prevents MCP/client tools from bypassing
// the local-path filter via aliases (bash -> exec), case variants, or
// other normalized-name collisions with trusted built-ins.
// NOTE: when builtinToolNames is omitted (undefined), the guard is skipped
// and the weaker normalized-name check applies. All production call paths
// (attempt.ts) supply this set; omitting it preserves legacy behavior only.
if (builtinToolNames !== undefined) {
const registeredName = toolName?.trim();
if (!registeredName || !builtinToolNames.has(registeredName)) {