Skip to content

fix(dsh-plugin): register browser tools one by one so a name collision cannot take the whole suite down (#444) - #445

Closed
Jay0130-a wants to merge 1 commit into
Tencent:mainfrom
Jay0130-a:fix/dsh-plugin-tool-name-collision
Closed

Jay0130-a wants to merge 1 commit into
Tencent:mainfrom
Jay0130-a:fix/dsh-plugin-tool-name-collision

Conversation

@Jay0130-a

Copy link
Copy Markdown

Summary

When another plugin already owns a browser_* tool name, registerBrowserTools fails all six registrations instead of the one that collided, and the failure is invisible on a desktop host.

registerBrowserTools did:

const disposers = BROWSER_TOOL_SPECS.map((spec) =>
  deps.ctx.tools.register(defineBrowserTool(spec, definitions)),
).filter(/* ... */);

The host tool registry rejects a duplicate name by throwing. Array.prototype.map therefore aborts at the first rejection, so:

Fixes #444.

Change

packages/dsh-plugin-browserskill/src/browser-tools.ts registers the specs one at a time:

  • every tool whose name is still free is registered, so a collision costs one tool instead of the suite;
  • the disposers of the tools that did register are collected and returned, so ctx.effect teardown releases them;
  • each conflict is reported with the offending tool name, e.g. 1 of 6 browser tools failed to register — another tool already owns the name in this scope: browser_session ("browser_session" is already registered).

When nothing could register, it still throws, so armLazyTools keeps its existing "keep the invocation proof and retry at the next lifecycle boundary" behaviour. The eager (lazyTools: false) path still fails loudly.

I deliberately did not rename any tool: that changes the public tool contract and is the maintainers' call. This PR only makes the failure survivable and diagnosable.

Test plan

Four cases added to packages/dsh-plugin-browserskill/tests/tools.test.ts, driving the real registerBrowserTools with a register stub that throws for chosen names:

case assertion
collision on spec 0 the other five register; one warning naming browser_session
two collisions the remaining four register; warning names both
collision on a later spec dispose() still releases every tool that registered
all six collide throws with every name in the message

Mutation check — with the source change reverted, all four fail (4 failed | 70 skipped), including expected [Function] to throw error matching /browser_session.*browser_assist/s but got 'duplicate'.

$ pnpm exec vitest run
Test Files  1 failed | 22 passed (23)
     Tests  2 failed | 496 passed (498)

$ pnpm exec tsc --noEmit
(clean)

The 2 remaining failures are in tests/runner.test.ts (settles after the kill grace…, settles killAll and killFor…) and are pre-existing on Windows — they reproduce on a clean tree with this branch stashed (2 failed | 28 passed). The client suites need the repo's build:css step first; with it run, they pass.

  • Added/updated tests
  • Updated CHANGELOG.md

…llision cannot take the whole suite down

`registerBrowserTools` mapped over BROWSER_TOOL_SPECS and called
ctx.tools.register for each entry. The host tool registry rejects a duplicate
name by throwing, so the `.map()` aborted at the first rejection: on a profile
that also mounts `dsh-builtin-browser` -- which already owns `browser_session`,
spec 0 -- zero of the six tools registered, while the plugin still looked
healthy. The only trace was a console.warn inside armLazyTools, which an
Electron desktop host never shows (Tencent#444).

A collision on a later spec was quieter still: the tools before it DID
register, but their disposers were discarded along with the pending entries,
so unloading the plugin could not release them.

Register each spec on its own, keep the disposers of the ones that succeeded,
and report every conflict by tool name. When nothing registered we still
throw, preserving armLazyTools' retry-at-the-next-lifecycle-boundary path.
@iuyo5678

Copy link
Copy Markdown
Collaborator

Superseded by #446, which resolves #444. Thanks for identifying the registration and cleanup issues

@iuyo5678 iuyo5678 closed this Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants