Architecture
Plugins extend behavior without becoming the source of truth for chain state. Harnesses act through delegated API credentials, tools return results, hooks react asynchronously, and compactors produce derived chain seeds.
Package — Immutable versioned OCI artifact containing manifest, binaries, schemas, and assets. Distributed via container registries. Never contains tenant secrets.
Binding — Control-plane record that binds a package export to org or project scope. Supplies config, secret slots, rollout state, and policy overrides.
Invocation — One runtime execution of a bound export under a deadline and resource budget. Produces runtime events and a terminal outcome.
Delegated Credentials — Short-lived, scope-limited gateway API tokens injected into plugin invocations. Plugins call VAI APIs without broad ambient credentials.
Export Kinds
A single package can export multiple items across these four kinds.
harness — Orchestration programs that coordinate multi-step AI workflows. Act through delegated VAI API credentials to manage sessions, chains, and runs. Default class: isolated . Examples: composite bug triage, multi-agent coding pipelines, research and synthesis workflows.
tool — Result-producing functions invoked during model execution. Return structured output without directly mutating chain history. Default class: isolated (third-party) . Examples: Jira search, database queries, web scraping, code execution.
hook — Async event handlers triggered by semantic events like run completion or chain compaction. Idempotent and retried by delivery ID. Default class: worker . Examples: Slack notifications, metrics collection, audit logging, webhook forwarding.
compactor — Transform plugins that produce derived chain seeds for context compaction. Never rewrite existing chains, only output new seed data. Default class: worker . Examples: sliding window summarization, task-aware compaction, code-context preservation.
Execution Classes
inline | Lowest | Very restricted | Trusted first-party or tightly reviewed code |
|---|---|---|---|
worker | Moderate | Background on gateway-workers | Default for hooks and compactors |
isolated | Higher | Isolated execution plane | Third-party tools and hosted harnesses |
Package Manifest
Every plugin package requires a vai-plugin.toml manifest at the root of the OCI artifact. The manifest declares package metadata, runtime configuration, and one or more exports.
1/
2 vai-plugin.toml
3 bin/
4 schemas/
5 assets/
6 README.md
Manifest Example
A complete manifest with all four export kinds. Each export declares its execution class, permissions, timeout, and kind-specific configuration.
1api_version = "vai.plugin.v1"
2package_id = "acme/devkit"
3version = "0.1.0"
4name = "Acme Dev Kit"
5description = "Harnesses, tools, hooks, and compaction strategies"
6license = "Apache-2.0"
7source = "oci://ghcr.io/acme/devkit:0.1.0"
8
9[runtime]
10protocol = "vai.rpc.v1"
11transport = "stdio"
12entrypoint = ["./bin/plugin"]
13os = ["linux", "darwin"]
14arch = ["amd64", "arm64"]
15
16# --- Harness export ---
17[[exports]]
18id = "triage"
19kind = "harness"
20display_name = "Triage Harness"
21description = "Composite bug triage harness"
22execution_class = "isolated"
23default_timeout_ms = 1800000
24max_concurrency = 4
25config_schema = "schemas/triage_config.json"
26
27[exports.permissions]
28network = "egress"
29filesystem = "scratch"
30gateway_api_scopes = [
31 "sessions:read", "chains:read",
32 "chains:write", "runs:write",
33 "compactions:write"
34]
35secret_slots = ["anthropic_key", "openai_key"]
36
37[exports.harness]
38mode = "composite"
39fidelity = "checkpoint"
40supports = ["continue", "branch_session", "compact", "tool"]
41
42# --- Tool export ---
43[[exports]]
44id = "jira_search"
45kind = "tool"
46display_name = "Jira Search"
47execution_class = "isolated"
48default_timeout_ms = 20000
49config_schema = "schemas/jira_search_config.json"
50
51[exports.permissions]
52network = "egress"
53filesystem = "none"
54gateway_api_scopes = []
55secret_slots = ["jira_token"]
56
57[exports.tool]
58effect_class = "read_only"
59streaming = false
60input_schema = "schemas/jira_search_input.json"
61output_schema = "schemas/jira_search_output.json"
62
63# --- Hook export ---
64[[exports]]
65id = "slack_notify"
66kind = "hook"
67display_name = "Slack Notifier"
68execution_class = "worker"
69default_timeout_ms = 15000
70config_schema = "schemas/slack_notify_config.json"
71
72[exports.permissions]
73network = "egress"
74filesystem = "none"
75gateway_api_scopes = ["runs:read"]
76secret_slots = ["slack_webhook"]
77
78[exports.hook]
79subscribes_to = ["run.completed", "chain.compacted"]
80
81# --- Compactor export ---
82[[exports]]
83id = "coder_window"
84kind = "compactor"
85display_name = "Coder Window"
86execution_class = "worker"
87default_timeout_ms = 120000
88config_schema = "schemas/coder_window_config.json"
89
90[exports.permissions]
91network = "none"
92filesystem = "scratch"
93gateway_api_scopes = []
94secret_slots = []
95
96[exports.compactor]
97target_context = "coding"
98supports_pinned_items = true
Permissions Model
The manifest declares capability needs. The host decides the final granted permissions. The effective permission set may be narrower than what the package requests.
network | none \| egress | Network access policy for the invocation |
|---|---|---|
filesystem | none \| scratch \| assets_ro | Filesystem access level |
gateway_api_scopes | string[] | Delegated VAI API scopes for this export |
secret_slots | string[] | Operator-bindable named secrets |
Runtime Protocol
The vai.rpc.v1 protocol uses framed JSON over stdio, language-agnostic, easy to debug locally, and independent of language-specific ABI issues. Framing follows a content-length envelope similar to LSP.
1Content-Length: <n>\r\n
2\r\n
3<json bytes>
Protocol Frames
Every frame contains protocol and type fields. The lifecycle follows: hello -> request/event/response -> shutdown.
Hello
Exchanged at startup. The host verifies that the plugin's reported identity matches the installed artifact.
Host -> Plugin:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "hello",
4 "role": "host",
5 "host": {
6 "name": "vai-runner",
7 "version": "0.1.0"
8 }
9}
Plugin -> Host:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "hello",
4 "role": "plugin",
5 "plugin": {
6 "package_id": "acme/devkit",
7 "version": "0.1.0"
8 }
9}
Request
1{
2 "protocol": "vai.rpc.v1",
3 "type": "request",
4 "id": "req_123",
5 "op": "tool.invoke",
6 "body": {}
7}
Event
Zero or more events may be emitted for a given request before its terminal response.
1{
2 "protocol": "vai.rpc.v1",
3 "type": "event",
4 "request_id": "req_123",
5 "event": {
6 "type": "progress",
7 "message": "working"
8 }
9}
Response
ok=false is for transport or protocol failure, not for plugin business outcomes. Semantic failures use ok=true with a structured status in the body.
Success:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "response",
4 "id": "req_123",
5 "ok": true,
6 "body": {}
7}
Protocol Error:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "response",
4 "id": "req_123",
5 "ok": false,
6 "error": {
7 "code": "protocol.invalid_frame",
8 "message": "missing body"
9 }
10}
Cancel
1{
2 "protocol": "vai.rpc.v1",
3 "type": "cancel",
4 "request_id": "req_123",
5 "reason": "deadline_exceeded"
6}
Shutdown
1{
2 "protocol": "vai.rpc.v1",
3 "type": "shutdown"
4}
Invocation Context
Every request body includes a context object with identity, scope, tracing, deadline, scratch directory, and delegated gateway credentials.
1{
2 "invocation_id": "inv_123",
3 "trace_id": "tr_123",
4 "org_id": "org_123",
5 "project_id": "proj_123",
6 "session_id": "sess_123",
7 "chain_id": "ch_123",
8 "run_id": "run_123",
9 "deadline_at": "2026-03-12T20:00:00Z",
10 "scratch_dir": "/sandbox/scratch/inv_123",
11 "gateway": {
12 "base_url": "https://api.rhone.dev",
13 "delegation_token": "vai_dlg_...",
14 "scopes": ["chains:read", "runs:write"]
15 }
16}
Execution Contracts
Tool Contract
Tools are result-producing functions. They do not mutate chain history directly; the host records the execution and inserts the canonical tool_result.
Request:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "request",
4 "id": "req_123",
5 "op": "tool.invoke",
6 "body": {
7 "export_id": "jira_search",
8 "context": { "..." : "..." },
9 "tool_execution_id": "toolx_123",
10 "input": {
11 "query": "billing outage",
12 "project": "ENG"
13 }
14 }
15}
Response:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "response",
4 "id": "req_123",
5 "ok": true,
6 "body": {
7 "status": "succeeded",
8 "result": {
9 "content": [
10 {"type": "text", "text": "Found 3 issues."}
11 ],
12 "structured_output": {
13 "issues": [{"key": "ENG-123"}]
14 },
15 "is_error": false,
16 "retryable": false
17 }
18 }
19}
Effect classes: read_only, idempotent_write, non_idempotent_write, unknown
Hook Contract
Hooks are async-first event handlers. They are delivered from durable semantic events, retried by delivery_id, and should be idempotent.
Request:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "request",
4 "id": "req_123",
5 "op": "hook.handle",
6 "body": {
7 "export_id": "slack_notify",
8 "context": { "..." : "..." },
9 "delivery_id": "del_123",
10 "attempt": 2,
11 "event": {
12 "type": "run.completed",
13 "occurred_at": "2026-03-12T19:00:00Z",
14 "subject": {"kind": "run", "id": "run_123"},
15 "summary": {
16 "task": "Fix auth redirect bug",
17 "output": "Patched route handler"
18 }
19 }
20 }
21}
Response:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "response",
4 "id": "req_123",
5 "ok": true,
6 "body": {
7 "status": "ack"
8 }
9}
Compactor Contract
Compactors are transform plugins. They produce derived chain seeds, never rewrite existing chains. Large projections can be delivered inline or via artifact references in the scratch directory.
Request:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "request",
4 "id": "req_123",
5 "op": "compactor.compact",
6 "body": {
7 "export_id": "coder_window",
8 "context": { "..." : "..." },
9 "compaction_id": "cmp_123",
10 "source": {
11 "chain_id": "ch_123",
12 "projection": {
13 "transport": "inline",
14 "blocks": [],
15 "pinned_items": [],
16 "token_estimate": 182000
17 }
18 },
19 "target": {
20 "max_input_tokens": 32000,
21 "target_model": "gpt-5.4"
22 },
23 "policy": {
24 "retain_system": true,
25 "retain_open_tasks": true
26 }
27 }
28}
Response:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "response",
4 "id": "req_123",
5 "ok": true,
6 "body": {
7 "status": "succeeded",
8 "result": {
9 "seed_blocks": [
10 {
11 "kind": "system",
12 "content": [{"type": "text", "text": "You are a coding agent."}]
13 },
14 {
15 "kind": "input",
16 "content": [{"type": "text", "text": "Summary of prior work."}]
17 }
18 ],
19 "summary": {
20 "text": "Auth redirect fix in progress. Tests pending."
21 },
22 "diagnostics": {
23 "input_tokens_estimate": 182000,
24 "output_tokens_estimate": 5400
25 }
26 }
27 }
28}
Harness Contract
Harnesses are orchestration programs supervised by the plugin runner. They act through canonical VAI APIs using delegated credentials, with no hidden privileged mutation paths.
Request:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "request",
4 "id": "req_123",
5 "op": "harness.run",
6 "body": {
7 "export_id": "triage",
8 "context": { "..." : "..." },
9 "mode": "start",
10 "target": {
11 "session_id": "sess_123",
12 "chain_id": "ch_123",
13 "execution_id": "exec_123"
14 },
15 "trigger": {
16 "type": "user_request",
17 "input": [
18 {"type": "text", "text": "Investigate the bug and fix it"}
19 ]
20 },
21 "policy": {
22 "max_depth": 4,
23 "max_child_runs": 12,
24 "allow_compaction": true
25 }
26 }
27}
Response:
1{
2 "protocol": "vai.rpc.v1",
3 "type": "response",
4 "id": "req_123",
5 "ok": true,
6 "body": {
7 "status": "succeeded",
8 "result": {
9 "checkpoint": {
10 "kind": "checkpoint",
11 "data": {"cursor": 12}
12 },
13 "summary": {
14 "text": "Gathered context, planned, and executed fix."
15 },
16 "artifacts": []
17 }
18 }
19}
Harness fidelity classes: lossless, checkpoint, prompt_replay, summary_reseed
Binding Record
Bindings apply org- and project-specific configuration without modifying the immutable package artifact. They own config values, secret slot mappings, rollout state, and enable/disable state.
1{
2 "binding_id": "bind_123",
3 "package_id": "acme/devkit",
4 "package_version": "0.1.0",
5 "export_id": "jira_search",
6 "scope": {
7 "org_id": "org_123",
8 "project_id": "proj_123"
9 },
10 "config": {
11 "base_url": "https://acme.atlassian.net",
12 "max_results": 10
13 },
14 "secrets": {
15 "jira_token": "secret://org_123/jira_token"
16 },
17 "status": "active",
18 "rollout": {
19 "traffic_percent": 100
20 }
21}
Control Plane APIs
Marketplace
The platform plugin page separates marketplace discovery from installedinventory. Marketplace entries are curated first-party records that describeinstallable packages before a project binds them. Installed packages, bindings,and runtime inventory continue to come from the canonical plugin control plane.
Initial marketplace entries:
OpenAI Codex Hosted Harness — harness export for Codex App Server / SDK orchestration with delegated Rhone gateway credentials. Contract-ready until the native adapter binary is packaged.
OpenCode Hosted Harness — harness export for OpenCode server workflows through @opencode-ai/sdk . Contract-ready until the native adapter binary is packaged.
Thread Weave Compactor — worker compactor export that keeps recent coding context directly and emits lineage-linked seed blocks for older task history. This entry can be installed into a project as an active thread_weave compactor binding.
Marketplace install actions are intentionally separate from runtime invocation:installing creates or updates package and binding state, while execution stillflows through ordinary harness, tool, hook, and compactor contracts.
Marketplace cards also expose publisher, review, signature, source, OCI, andinstall-policy posture. A reviewed available package can expose an installaction. A contract-ready harness shows its adapter contract and sourcereferences, but remains non-installable until Rhone publishes a pinned signedruntime artifact and dependency lock.
Signed package releases use marketplace.signature.json. Release automation cangenerate it with vai-plugin-sign, and production imports should require atrusted Ed25519 signature before marking a package available.
Package Management
POST /v1/plugin-packages — Upload a plugin package
GET /v1/plugin-packages — List installed packages
GET /v1/plugin-packages/{id} — Get package details
Bindings
POST /v1/plugin-bindings — Create a binding
GET /v1/plugin-bindings — List bindings
PATCH /v1/plugin-bindings/{id} — Update a binding
POST /v1/plugin-bindings/{id}:validate — Validate a binding
Export Discovery
GET /v1/plugin-harnesses — List available harness exports
GET /v1/plugin-tools — List available tool exports
GET /v1/plugin-hooks — List available hook exports
GET /v1/plugin-compactors — List available compactor exports
Security Model
Secret Slots — Plugins declare named secret slots in the manifest. The binding record maps slots to stored secrets. Raw secrets are never in package artifacts, chain records, or run records.
Host-Granted Permissions — The host computes effective granted permissions per invocation. The granted set may be narrower than what the package requests.
Network Policy — Egress policy enforced per invocation. Inline and isolated execution support domain allowlists.
Filesystem Policy — scratch means write access inside the invocation scratch dir only. assets_ro means read-only mounted assets. No plugin receives arbitrary host filesystem access.
Terminal Statuses
All completed invocations use one of these terminal statuses in the response body.
succeeded
failed
cancelled
retryable_failure
Observability
Plugin invocations are first-class operational events. Every invocation records:
Invocation ID
Export kind and ID
Binding ID
Package ID and version
Execution class
Start and end time
Terminal status
Retry count
Org and project scope
Related session, chain, and run
Trace ID
Execution ID