Skip to content

fix(tools): register image_generate under the plugin namespace - #103

Closed
Arrosam wants to merge 1 commit into
V1ki:mainfrom
Arrosam:fix/host-owned-image-tool
Closed

Arrosam wants to merge 1 commit into
V1ki:mainfrom
Arrosam:fix/host-owned-image-tool

Conversation

@Arrosam

@Arrosam Arrosam commented Sep 23, 2026

Copy link
Copy Markdown

Problem

DSH Desktop mounts dsh-image-generation as its one shared image_generate tool, and that plugin registers it unconditionally — a duplicate throws inside its apply. This plugin also prefers the canonical image_generate name (#76), and loader entries are applied concurrently, so whichever side applies first decides the outcome. When this plugin wins, the host's apply throws and the whole plugin tree fails to boot:

[harness-node] plugin failures: {"stage":"apply","entryId":"dsh-image-generation", ... "message":"tool \"image_generate\" is already registered ..."}
[harness-node] DSH entry failed: Error: dsh: plugin tree failed to load: failed to apply loader entry dsh-image-generation (dsh-image-generation): tool "image_generate" is already registered

The plugin's own collision fallback cannot help here, because the failure happens on the host side, not inside registerWithAlias. In practice this is intermittent: some launches boot (the host registers first and the plugin falls back to its alias) and some abort, which makes it look like a random startup failure.

Fix

Treat image_generate as host-owned — the same call as f839fdb for web_search: register directly under dsh_subscriptions_image_generate and never attempt the canonical name. x_search and video_generate have no host owner, so they keep the canonical-first rule from #76.

  • src/tools/registration.ts: add HOST_OWNED_TOOLS and route those names straight to registerScoped; every other name behaves exactly as before.
  • src/client/index.ts, src/client/ImageGenerateToolview.tsx: the keyed toolview key and title follow TOOL_ALIASES.image_generate, so inline rendering keeps working.
  • src/index.ts: comment only.
  • README.md / README.zh.md: document the registered name.
  • Tests: new host-owned registration coverage, and the provider-settings RPC expectation now asserts the alias plus the fact that the deny list never names the host's image_generate.

Behaviour notes

  • On a host that ships no image tool the plugin's image tool is now namespaced too. The registry cannot tell whether the host's unprotected registration is still coming, so the alias is the only order-independent choice. If keeping the canonical name on older hosts matters, that wants a host capability seam (or an explicit opt-in) rather than a registration race.
  • Turning the subscription image switch off now denies only dsh_subscriptions_image_generate; the host's image_generate tool stays visible.

Verification

  • tsc -p tsconfig.test.json clean.
  • node --test "lib-test/test/**/*.spec.js": 507 tests, 500 pass, 0 fail, 7 skipped.
  • tsc + tsdown build clean; lib/client.js carries the new toolview key.

DSH Desktop mounts `dsh-image-generation`, which claims the global
`image_generate` tool unconditionally and throws when the name is already
taken. This plugin also prefers the canonical name (V1ki#76), and entries are
applied concurrently, so whichever side reaches the registry first decides
the outcome. When this plugin wins, the host's `apply` throws and the whole
plugin tree fails to load (`plugin tree failed to load: ... tool
"image_generate" is already registered`). The plugin's own collision
fallback cannot help: the failure happens on the host side.

Treat `image_generate` as host-owned, the same way `web_search` was left to
the host's seam in f839fdb: register directly under
`dsh_subscriptions_image_generate` and never attempt the canonical name.
`x_search` and `video_generate` have no host owner, so they keep the
canonical-first rule. On a host that ships no image tool the plugin's image
tool is now namespaced too; the registry cannot tell whether the host's
unprotected registration is still coming, so the alias is the only
order-independent choice.

The keyed toolview key and title follow the registered name through
TOOL_ALIASES, and the per-agent deny list now scopes to that alias, so
turning the subscription image switch off no longer hides the host's
`image_generate` tool.

Tests: new host-owned registration coverage in tool-registration.spec.ts and
updated provider-settings RPC expectations.
@V1ki

V1ki commented Sep 28, 2026

Copy link
Copy Markdown
Owner

感谢 @Arrosam 的 PR,也感谢把重名问题和启动顺序的竞争分析清楚。不过这次决定不改名,原因如下:

  • 影响所有用户。 改名后,每个人的工具名都会变成 dsh_subscriptions_image_generate,包括大多数没有安装其他图片插件、根本不会遇到冲突的用户。已保存会话里之前的 image_generate 调用会对不上工具视图,内联图片预览会失效;恢复旧会话时,模型也可能继续调用旧名字。
  • 已有重名兜底。 插件目前的做法是先用原名 image_generate 注册,这个名字已经被其他插件占用时,再改用 dsh_subscriptions_image_generate(1b7ea75,当时讨论 fix: dsh 0.1.5 compatibility — auth endpoints 405, /fast menu crash, tool-name collisions #83 后定下的方案)。所以不改名也不会注册失败。
  • 改名还不完整。 给模型看的提示文字里仍然写着 image_generate(image-generate.ts 第 310、332 行,resolved.ts 第 117 行)。装了另一个插件时,这些提示会把模型引向那个插件的工具;没装时,则指向一个不存在的工具。

两个插件谁先加载谁拿到原名,这个问题确实存在。如果你同时在用 dsh-image-generation,欢迎开一个 issue 说明具体场景,我们可以考虑一个默认不改名的方案,比如可选开关。这个 PR 先关闭。


Thanks @Arrosam for the PR and for spelling out the collision and the startup race. I've decided not to rename, for these reasons:

  • It affects everyone. The rename changes the tool name for every user, including the majority who have no other image plugin and never hit a collision. Past image_generate calls in saved conversations would no longer match the tool view and would lose their inline image preview, and a resumed session may keep calling the old name.
  • Collisions are already handled. The plugin registers the canonical image_generate first and falls back to dsh_subscriptions_image_generate when another plugin already holds the name (1b7ea75, settled after fix: dsh 0.1.5 compatibility — auth endpoints 405, /fast menu crash, tool-name collisions #83), so keeping the name never fails registration.
  • The rename is incomplete. Model-facing hints still say image_generate (image-generate.ts lines 310 and 332, resolved.ts line 117). With the other plugin installed they would steer the model to its tool; without it, to a tool that doesn't exist.

Which plugin loads first does decide who gets the canonical name. If you run dsh-image-generation alongside this plugin, please open an issue describing your setup; I'm open to a fix that keeps the canonical name by default, such as an opt-in. Closing this one for now.

@V1ki V1ki closed this Sep 28, 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

Development

Successfully merging this pull request may close these issues.

2 participants