Repository navigation
Replies: 3 comments
|
I based this proposal from a clanked plan here https://gist.github.com/airtonix/a038219ae8f8793d2b6d0fcb1d48272f |
|
@airtonix Thank you for this contribution! I like the concept and would like to create a draft. |
|
@airtonix Thanks for the thorough proposal. It is now implemented on main and shipped in 0.0.45. Here is what landed, where it follows your plan, and where it deliberately diverges. What is inPlugins are supervised external processes speaking newline-delimited JSON over stdin/stdout, exactly the transport you suggested. The protocol is independently versioned (manifest schema 1, wire protocol 1) and does not claim zot compatibility. zot was the design inspiration, not a compatibility target. Everything is behind explicit flags: Discovery is separate from execution. Capabilities are named grant given at approval time and rechecked on every request, never inferred from the manifest. The vocabulary is close to your list: Contributions: Lifecycle: Where it diverges from the proposalPlugins are trusted code, not sandboxed code. Grants restrict what a plugin can ask hrdx to do. They cannot stop a same-user executable from reading files, spawning processes, or using the existing control socket. The docs say this plainly instead of implying isolation that does not exist. Contributions are declared in the manifest, not registered dynamically during the handshake. That keeps inventory and diagnostics possible without running code and removes a class of registration races. There is no automatic restart or backoff. A crashed or stopped plugin stays stopped until someone restarts it. Reload is explicit and only accepts a reapproved package at the same path. Views are host-rendered plain text rows, never raw ANSI and never PTY-backed. Docked views reserve an edge strip outside the split tree rather than becoming tree nodes, so every persistent pane still appears exactly once in the tree and persisted layouts are unchanged. Plugin-created splits and tabs become ordinary host-owned panes that survive plugin disconnect. Floats created by a plugin are temporary and close with the connection. Pane input from plugins is bounded and ordered behind keyboard input. To make that safe, pane writes now go through a per-pane queue drained by one go routine, so a child that stops reading its PTY can no longer block the update loop. Keyboard input is never dropped. Grants are per workspace or instance. Per tab and per pane grants are not implemented. Tabs have no durable identity and pane IDs are process local. Where to look
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Feature proposal: establish a rich plugin platform for hrdx
Summary
I’d like to propose a first-class plugin platform for hrdx.
The goal is to let external tools integrate with hrdx workspaces, tabs, panes, agents, and UI surfaces without requiring plugins to run inside the hrdx process or depend on private Go types. Plugins should be independently versioned, supervised, capability-limited participants that communicate with hrdx through a stable protocol.
This proposal is intentionally trying to follow the plugin protocol and extension shape established by Zot, where that provides a useful baseline for manifests, external plugin processes, registration, lifecycle, and plugin-owned TUI views. hrdx would adapt those ideas to its own PTY, holder, layout, persistence, and Bubble Tea ownership boundaries rather than importing Zot internals or coupling to its implementation.
This would extend hrdx beyond custom agent harness registration while preserving its core identity:
Proposed plugin model
A plugin would be a package containing:
The manifest would describe what the plugin provides, but would not itself execute arbitrary shell hooks. hrdx would be able to discover, validate, inspect, enable, or disable packages without starting them.
Possible discovery locations would include:
Discovery should be deterministic and produce useful diagnostics for malformed manifests, duplicate IDs, unsupported protocol versions, missing entrypoints, and permission errors.
What plugins could provide
The initial contribution model could support:
Plugin-owned views would be hosted inside hrdx-owned floating frames or grid-cell frames. The plugin could own the view's content, internal layout, rendering, and content-area input, while hrdx would retain ownership of:
A plugin view would not be a terminal pane. It would not have a PTY, shell process, holder session, or terminal persistence identity.
What plugins would not receive
Plugins would not receive:
ui.Model;state.json;Plugins would use semantic protocol objects rather than screen scraping, title-string conventions, or private hrdx implementation details.
External process and protocol boundary
Plugins should run as supervised external processes rather than embedded Go plugins.
The initial transport could be local stdin/stdout using newline-delimited JSON, following the practical shape of Zot's external plugin communication while keeping hrdx's protocol independently versioned. A bidirectional protocol would provide:
hello,ready, and graceful shutdown messages;The existing hrdx control socket should remain separate. A plugin connection should be associated with a known package identity and an hrdx-issued session or transport credential.
Future Unix socket or Windows named-pipe support could be added if independently managed plugin daemons, multiple hrdx instances, or restart-surviving services create a concrete need. It should reuse the same protocol and capability broker rather than create a second plugin API.
Capability and security model
Requested capabilities would never be granted automatically.
The host should mediate every plugin operation through a capability broker. Capabilities should distinguish:
Potential capability families include:
High-risk operations such as sending input to a shell or agent, creating panes, reading terminal content, or spawning processes should require separate explicit grants.
Capability checks must happen at request time, not only during the initial handshake.
Failure and lifecycle requirements
A plugin should move through an explicit lifecycle such as:
The host should provide:
A plugin crash should remove the plugin's contributions and pending requests without affecting existing workspaces, PTYs, agents, or holder sessions.
Persistence and compatibility
Only host-owned plugin preferences should be persisted with hrdx state, for example:
{ "plugins": { "example.git-tools": { "enabled": true, "grants": [ "workspace.read", "ui.status.contribute" ], "config": { "show_clean": false } } } }Live connections, process IDs, subscriptions, callbacks, and plugin-private data should remain outside
state.json.Existing state files must remain loadable. Missing plugin fields should default safely, unknown optional fields should be ignored, and unsupported optional contributions should degrade without affecting core hrdx state.
Suggested first milestone
The first milestone should freeze the public design boundary before runtime implementation:
This milestone would not launch plugins or change user-facing behavior. It would give maintainers and plugin authors a stable contract to review before implementation begins.
Questions for discussion
The central design rule is:
All reactions