Skip to content

feat: wire plugin-contributed channels end to end (boot bridge + API + dashboard) - #1823

Open
ideshun wants to merge 3 commits into
TencentCloud:developfrom
ideshun:feat/plugin-channels
Open

ideshun wants to merge 3 commits into
TencentCloud:developfrom
ideshun:feat/plugin-channels

Conversation

@ideshun

@ideshun ideshun commented Oct 8, 2026

Copy link
Copy Markdown

Summary

Completes the plugin-channel story on the host side: kind: channel plugins installed in Octop now become real channels — creatable, probeable, persistable, rebuildable, and fully manageable from the dashboard. This is the enabling infrastructure for the upstream Roadmap's Plugin marketplace: any third-party channel plugin, once installed, behaves like a builtin channel with zero core code changes.

What's included

1. Boot bridge — src/octop/infra/gateway/plugin_channels.py (new)

  • apply_plugin_channels(registry=...): replays every ChannelRegistration from the harness PluginRegistry into octop_gateway.register_channel_kind(). Idempotent (first-wins per kind), per-plugin failure isolation (a broken plugin logs and is skipped, never blocks boot).
  • plugin_channel_kinds(): summarizes registered plugin kinds for the API/dashboard catalogue.
  • Called in server.py immediately after plugin_manager.load_installed(), before gateway.boot() rebuilds DB channel rows — so plugin channels are ready to rebuild exactly like builtins. Also safe to re-run on plugin reload.

2. API — src/octop/api/routers/channels.py

  • Channel kind in create/patch/probe bodies was a closed ChannelKind enum (Pydantic would 422 any plugin kind). It is now a validated string: builtin kinds still accepted (case-normalized), plus any kind registered by an installed channel plugin — read live from the harness registry. Defensive: with harness releases that predate all_channels(), validation falls back to builtin-only instead of crashing.
  • New GET /channels/plugin-kinds endpoint (auth: current_user): returns the plugin channel catalogue (kind / label / icon / intro_url / fields / plugin_id) for UI consumption.

3. Dashboard — src/pages/Agent/Channels/*

  • ChannelsPanel fetches /channels/plugin-kinds once per mount and registers each kind into the catalogue at runtime (registerPluginChannelKind() in constants.ts): card keys, display labels, icons, accent colors, and per-kind config form schemas. Builtins remain compile-time constants; registration is idempotent and builtin kinds always win.
  • Plugin kinds without a declared field schema fall back to the drawer's existing raw-JSON config editor, so every channel plugin is configurable with no further frontend work.
  • Fetch failure (e.g. backend older than this PR) keeps a builtin-only catalogue — the panel never breaks.

Test plan

  • uv run --extra dev pytest tests/unit/test_plugin_channels.py → 7 passed, 1 skipped. Stub-based coverage of the bridge (registration, idempotency, first-wins, no-builtin-shadowing, summary, API validator) runs everywhere; the real-fixture end-to-end test auto-skips until the updated harness (octop-harness#52) is installed.
  • Regression: tests/unit/test_plugins.py + test_plugin_manager.py + test_bundled_plugins_layout.py → 35 passed; tests/integration/test_channels_api.py + tests/unit/db/test_repo_channels.py → 14 passed.
  • Frontend: tsc -b clean; vitest src/pages/Agent/Channels → 16 passed (existing suite unchanged).

Compatibility & rollout

  • With released (pre-PR#1/feat: add SkillHub expert market #2) harness/gateway packages: plugin kinds simply don't appear (validator falls back to builtin-only, catalogue fetch degrades, bridge apply is skipped when all_channels is absent). No behavior change for existing users.
  • After octop-harness#52 + octop-gateway#22 land and dependencies bump: the full path activates with no further code changes here.

Example: a minimal channel plugin

# plugin.yaml
id: acme-channel
version: 0.1.0
name: Acme IM
kind: channel
entry: main.py
# main.py
def setup(ctx):
    from acme_channel_impl import AcmeChannel
    ctx.channel(
        "acme",
        AcmeChannel,
        label="Acme IM",
        fields=[{"name": "token", "label": "Token", "type": "password", "required": True}],
    )

Install → restart → an "Acme IM" card appears in the dashboard channel grid, with a real token input in the drawer.

Chovy added 3 commits October 8, 2026 11:22
Octop-harness plugins can now contribute channel kinds (kind: channel)
that become first-class channels in the gateway and dashboard.

- infra/gateway/plugin_channels.py: apply_plugin_channels() replays
  plugin channel registrations into octop_gateway.register_channel_kind
  (idempotent, first-wins, per-plugin failure isolation);
  plugin_channel_kinds() summarizes them for the dashboard catalogue.
- server.py: apply plugin channel kinds right after load_installed()
  so kinds exist before gateway boot rebuilds DB channels.
- api/routers/channels.py: kind validation now accepts plugin kinds
  (case-normalized) in create/patch/probe bodies, tolerant of harness
  releases without all_channels(); new GET /channels/plugin-kinds
  endpoint exposing the plugin channel catalogue.

Tests: 8 cases in tests/unit/test_plugin_channels.py (7 run on released
harness; the fixture end-to-end test auto-skips until the updated
harness is installed).
…logue

Fetch GET /channels/plugin-kinds once per mount and register each
returned kind into the channel catalogue at runtime (keys, labels,
icons, colors, per-kind config schema). Builtin kinds stay compile-time
constants; registration is idempotent and builtin kinds always win.

Kinds registered without a schema fall back to the drawer's existing
raw-JSON editing path, so any kind:channel plugin is configurable
without further frontend work. Fetch failure keeps a builtin-only
catalogue (older backends without the endpoint).

This branch has not been deployed

No deployments
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.

1 participant