You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a concrete, spiked implementation plan for the proposal in #444 (Asynchronous Agent Coordination & Typed Guardrails via OpenCode Background Listeners). It reflects a design pass against our current architecture (forge/Replicator swarm, /uf.unleash, the Divisor review council).
TL;DR
We already have a working multi-agent swarm. The right move is extend, don't rebuild — the delta needed to deliver #444 is narrow:
Real-time push (Replicator comms/forge_broadcast are SQLite polling today).
Mid-flight typed interruptions (the Divisor council escalates only at phase gates, not during execution).
Headless cross-repo/batch control (the scripted/ SDK driver's niche).
Replicator talks to OpenCode over stdio JSON-RPC (request/response) — a plugin can't receive push there. So we add a second, opt-in transport: a local HTTP SSE sidecar. Push is one-way (broker → listener); the listener acts back through OpenCode's own SDK client.
The listener plugin (@opencode-ai/plugin) holds the SSE stream open, runs the typed-interruption router, and enacts decisions via the OpenCode client (session.command / prompt / abort).
Native substrate (feature-detected via experimental.capabilities.get())
experimental.session.background({sessionID}) — detach blocking subagents to background.
SSE sidecar in Replicator (alongside stdio; polling stays for back-compat)
Isolation
Hybrid by goal — in-session subagents + session.background for the interactive swarm; separate server per issue for headless batch
Batch parallelism
Cross-issue only + one global cap (3); reuse /uf.unleash's internal swarm
Recursion
Depth-1 only (guarded via session metadata)
Registration
Per-project
Phased plan
P1 — Native spike: confirm experimental.capabilities.get() + session.background; resolve the one real unknown — injection admission into a busy session (does client.session.prompt land mid-turn or at next idle?).
P2 — Replicator push:internal/events/ bus + SSE sidecar + comms_subscribe; emit on message/reservation/forge events; polling untouched.
P3 — Listener plugin + typed schema: SSE consumer, active-session map via the event hook, typed router, bail-on-unknown.
P4 — Wire into pipeline:/uf.unleash workers emit typed signals; listener enacts self-heal vs. human escalation.
P5 — Headless batch:scripted/ cross-issue loop (/opsx-propose → /uf.unleash → /uf.finale) under the global cap; scoped permission profiles replace allow-all; add --resume.
Feedback wanted
P1 unknown: does OpenCode admit mid-turn injected prompts, or only at turn boundaries? This decides whether interruptions are truly mid-flight.
Is Ideas the right home, or should this become a tracking issue once accepted?
Any objection to the SSE sidecar vs. keeping everything on stdio?
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Context
This is a concrete, spiked implementation plan for the proposal in #444 (Asynchronous Agent Coordination & Typed Guardrails via OpenCode Background Listeners). It reflects a design pass against our current architecture (forge/Replicator swarm,
/uf.unleash, the Divisor review council).TL;DR
We already have a working multi-agent swarm. The right move is extend, don't rebuild — the delta needed to deliver #444 is narrow:
comms/forge_broadcastare SQLite polling today).scripted/SDK driver's niche).What already exists (reused as-is)
coordinator.md,worker.md,/forgebackground-worker.md/uf.unleashviaswarm_worktree_*comms/forge_broadcastESCALATEDverdict (phase-gated)divisor-*)/uf.unleash/uf.finaleArchitecture
Replicator talks to OpenCode over stdio JSON-RPC (request/response) — a plugin can't receive push there. So we add a second, opt-in transport: a local HTTP SSE sidecar. Push is one-way (broker → listener); the listener acts back through OpenCode's own SDK client.
The listener plugin (
@opencode-ai/plugin) holds the SSE stream open, runs the typed-interruption router, and enacts decisions via the OpenCodeclient(session.command/prompt/abort).Native substrate (feature-detected via
experimental.capabilities.get())experimental.session.background({sessionID})— detach blocking subagents to background.experimental.session.list(...)— cross-project session discovery.controlPlane.moveSession(...),experimental.workspace.warp(...)— session mobility.Typed interruption contract
send_messagerequires structured JSON:{ interruption: { type, severity, context, action_required } }. Bail on unknown type (safety property mirrored from the headless driver's answer table).IMPLEMENTATION_DEVIATION→ inject "halt, discard tasks.md, re-propose" (self-heal).GOVERNANCE_BLOCKER→ abort the/uf.unleashloop, escalate to a human architect.This extends the Divisor council's escalation from phase-gated → real-time.
Design decisions (locked)
@unbound-force/coordination-plugin), referenced per-projectsession.backgroundfor the interactive swarm; separate server per issue for headless batch/uf.unleash's internal swarmPhased plan
experimental.capabilities.get()+session.background; resolve the one real unknown — injection admission into a busy session (doesclient.session.promptland mid-turn or at next idle?).internal/events/bus + SSE sidecar +comms_subscribe; emit on message/reservation/forge events; polling untouched.eventhook, typed router, bail-on-unknown./uf.unleashworkers emit typed signals; listener enacts self-heal vs. human escalation.scripted/cross-issue loop (/opsx-propose→/uf.unleash→/uf.finale) under the global cap; scoped permission profiles replace allow-all; add--resume.Feedback wanted
Ideasthe right home, or should this become a tracking issue once accepted?All reactions