⏰feat: plugin IPC system — bidirectional Host<->Plugin + Agent ACP tunnel#95
Open
Rhythmarvin wants to merge 13 commits into
Open
⏰feat: plugin IPC system — bidirectional Host<->Plugin + Agent ACP tunnel#95Rhythmarvin wants to merge 13 commits into
Rhythmarvin wants to merge 13 commits into
Conversation
Complete redesign based on full_design.md VS Code-like bidirectional model.
New crates:
- ora-plugin-protocol: 5-byte binary frame codec, JSON-RPC envelope validation,
lifecycle DTOs ($/initialize handshake). 12 unit tests.
- ora-plugin-manager: PluginRuntime with Bun process spawn, handshake,
typed invoke/stop. Uses tokio async I/O.
Rewritten packages:
- @ora-space/plugin-sdk v0.2.0: definePlugin API, ExtensionContext,
internal transport/bootstrap/rpc. Single-package architecture.
HTTP API (apps/web/server):
- POST /api/plugins start plugin from path
- POST /api/plugins/{id}/invoke send JSON-RPC request
- POST /api/plugins/{id}/stop graceful shutdown
- GET /api/plugins list running plugins
Wire format: [length i32 BE][type i8][payload UTF-8 JSON]
Frame types: 1=Request, 2=Response, 3=Notification
- Move examples/demo-plugin/index.ts (was packages/plugin-sdk/examples/) - Remove 'examples' from plugin-sdk package.json files list - Keep plugin-sdk as a clean library package - Demo plugin uses definePlugin() to demonstrate SDK API
- Add registerHandler(method, handler) to ExtensionContext - Bootstrap loads plugin config after handshake, calls activate(ctx) - Demo plugin registers 'getInfo' handler returning demo string - Demo example tests full flow: ping + getInfo + notification + shutdown - Switch to std::process (blocking) for stable plugin process I/O - Dispatcher supports runtime handler registration from plugins
Lifecycle phases: 1. $/initialize → session handshake (echo identity) 2. $/activate → plugin activate(ctx) + register handlers 3. === Running === (business requests: ping, getInfo, etc.) 4. $/deactivate → plugin deactivate() + LIFO dispose subscriptions 5. $/exit → graceful process.exit(0) Bootstrap state machine: awaitingInitialize → awaitingActivate → running → deactivating → exiting Rust: spawn_and_activate() sends both $/initialize + $/activate Rust: shutdown() sends $/deactivate + $/exit
- [host] → invoke / ← response logs in PluginProcess - [host] starting plugin / plugin started logs in PluginRuntime - [host] $/deactivate acknowledged / plugin exited logs in shutdown - Remove unused bootstrap_path field from PluginRuntime
The old plugin-runtime was never tracked by git (was in .gitignore). Its function is now merged into plugin-sdk. Delete the directory entirely instead of hiding it behind .gitignore.
Rhythmarvin
force-pushed
the
mvp/plugin-ipc
branch
from
July 21, 2026 08:15
3bc2644 to
99428f7
Compare
- PluginProcess::spawn() — only $/initialize handshake - PluginProcess::activate() — $/activate, can be called lazily - PluginRuntime::initialize() + activate() — separate public API - PluginRuntime::start() — convenience method, calls both
Rhythmarvin
force-pushed
the
mvp/plugin-ipc
branch
from
July 21, 2026 08:18
99428f7 to
d453dbf
Compare
- PluginManagerConfig: derives managed paths from data_dir (plugins_dir, etc.)
- scanner: scans <data_dir>/plugins/ for subdirectories with valid package.json + ora manifest
- PluginRuntime::scan() returns discovered plugins
- PluginRuntime::start_by_id() starts a plugin by its scan ID
- REST: GET /api/plugins now shows running + available plugins
- REST: POST /api/plugins accepts {"id": "ora.demo-plugin"}
- REST: GET /api/plugins/scan lists discovered plugins
- 2 unit tests for scanner
- PluginEvent tagged union + PluginMetadata enum for typed discovery - Background reader thread with sync/stream dual pending maps - invoke_streaming() returns mpsc channel for long-running operations - Scanner parses ora.agent manifest block - runAgentBootstrap + AgentPeer: NDJSON <-> 5-byte frame tunnel - ACP initialize handshake with concurrent startup - REST: connect, forward, prompt (streaming NDJSON) endpoints - E2E verified with OpenCode v1.18.4
Contributor
Author
|
opencode-plugin.zip |
Rhythmarvin
force-pushed
the
mvp/plugin-ipc
branch
2 times, most recently
from
July 24, 2026 01:18
b8f9417 to
64a3b16
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
从零实现完整的插件 IPC 通信系统,支持 Host (Rust) 与 Plugin (Bun/TS) 之间的双向 JSON-RPC 通信,并扩展支持 Agent 类型插件作为 ACP 协议隧道。
架构
通用插件能力
[length i32 BE][type i8][payload UTF-8 JSON]$/initialize → $/activate → Running → $/deactivate → $/exit$/hello)definePlugin({ activate, deactivate })→ExtensionContextwithregisterHandler()ctx.registerHandler("getInfo", ...)让插件注册自己的 JSON-RPC method<data_dir>/plugins/目录,自动发现含 manifest 的插件initialize()和activate()拆分为独立步骤Agent 插件
Agent 插件作为一种特殊的插件类型,作为 Host 与实际 AI Agent(如 OpenCode、Claude Code)之间的薄隧道:北向与 Host 使用 5-byte 帧协议通信,南向通过 ACP 协议与 agent 进程通信。
插件 manifest
一般插件:
{ "ora": { "id": "ora.demo", "kind": "agent", "main": "index.ts" } }Agent 插件(多
agent字段声明 CLI 和展示信息):{ "ora": { "id": "ora.opencode", "kind": "agent", "main": "index.ts", "agent": { "cli": "opencode", "displayName": "OpenCode", "description": "OpenCode ACP agent" } } }REST API
GET/api/pluginsPOST/api/pluginsGET/api/plugins/scanPOST/api/plugins/{id}/invokePOST/api/plugins/{id}/connectPOST/api/plugins/{id}/forwardPOST/api/plugins/{id}/promptPOST/api/plugins/{id}/stop手动测试(目前为纯后端,仅支持rest接口测试)
一键测试脚本 (
test-rest.sh)