Skip to content

fix(ui): re-widen ToolCatalogEntry/Group source discriminator to preserve plugin tag — corrects #2522 narrowing #2543

@alexey-pelykh

Description

@alexey-pelykh

Summary

PR #2534 (closed as merge of issue #2522) narrowed ToolCatalogEntry.source and ToolCatalogGroup.source from "core" | "plugin" to "core" in the UI type system and dropped pluginId?: string. That change was authored on the false premise that the plugin subsystem was being gutted. Deeper investigation (after this batch closed) confirmed the plugin subsystem is kept — it is the fork's primary extensibility mechanism, and the backend actively emits source: "plugin" for plugin-contributed tools.

The UI types therefore lie about the protocol's actual shape.

Evidence

Backend emits "plugin" via live paths

src/gateway/server-methods/tools-catalog.ts:99:        source: "plugin",
src/gateway/server-methods/tools-catalog.ts:110:      source: "plugin",

Both are inside buildPluginGroups (lines 71-123), which is invoked on every tools.catalog RPC when includePlugins !== false (default true). buildPluginGroups calls resolvePluginTools(...) from src/plugins/tools.ts.

Plugins actively register tools

At the time of filing, 10 api.registerTool(...) calls across 5 bundled extensions:

Extension Calls Files
extensions/feishu 6 docx.ts (2), bitable.ts, perm.ts, chat.ts, wiki.ts, drive.ts
extensions/voice-call 1 index.ts:399
extensions/tlon 1 index.ts:138
extensions/zalouser 1 index.ts:16

UI narrowed type (after #2534)

ui/src/ui/types.ts:
  ToolCatalogEntry.source: "core"              // was: "core" | "plugin"
  ToolCatalogGroup.source: "core"              // was: "core" | "plugin"
  pluginId?: string deleted on both

Current UI runtime deserialization is untyped cast, so the type lie does not throw at runtime — it just makes the type system blind to plugin-sourced tools.

Why this is harmful now

Low immediate impact: #2520 (commit d65576be8a) removed the only UI consumer of the tool catalog (loadToolsCatalog and its panel). Nothing reads the catalog from the UI today.

High latent impact:

  1. Future tool-browsing UI will inherit the narrowed type and silently drop plugin-tool display capability.
  2. Type-aware schema validation (when added) will reject all backend responses containing source: "plugin" as invalid.
  3. Protocol schema at src/gateway/protocol/schema/agents-tools.ts likely still declares source: "core" | "plugin" — the backend wire shape is unchanged. The mismatch between UI types and protocol schema is a quiet contract divergence.

Changes

  1. Revert UI narrowing in ui/src/ui/types.ts:

    • ToolCatalogEntry.source: widen back to "core" | "plugin"
    • ToolCatalogGroup.source: widen back to "core" | "plugin"
    • Restore pluginId?: string on both types (preserve the original backend fidelity)
  2. Verify protocol schema alignment: check src/gateway/protocol/schema/agents-tools.ts still declares the "core" | "plugin" union and emits pluginId. If UI-only fidelity divergence was introduced somewhere else, fix that too.

  3. Do NOT restore ui/src/ui/views/agents.ts or the deleted panel body — that cleanup (gut(ui): remove dead Tools tab from Agents view #2520) was correct independent of the plugin-retention question. Those UI surfaces were removed because the Tools tab was dead UI, not because plugins don't exist.

AC

  • ToolCatalogEntry.source in ui/src/ui/types.ts is typed "core" | "plugin"
  • ToolCatalogGroup.source in ui/src/ui/types.ts is typed "core" | "plugin"
  • pluginId?: string is present on both types
  • pnpm check green
  • pnpm test green
  • pnpm test:ui:smoke green

Context

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions