feat: agent-to-agent integrations — activity stream, steer channel, plan gate
Three fork-side integrations that turn OpenChamber from a fire-and-forget
coder into a visible, steerable, plan-gated agent:
1. Activity stream (GET /api/openchamber/agent-activity SSE)
- agent-activity/runtime.js: subscribes to global hub, normalizes
message.updated parts into structured activity events (tool-call,
file-edit, text-part), rate-limited coalescing for tool calls
- Broadcasts openchamber:agent-activity and session-completed events
- SSE endpoint with heartbeat (25s), same shape as /api/openchamber/events
2. Steer channel (POST /api/openchamber/session/:id/steer)
- session-steer/runtime.js: interrupt mode (interrupt → inject
system-level directive → resume) and queue mode (deliver on next idle)
- Follows message-queue precedent for interrupt-safe dispatch
- Broadcasts openchamber:steer-delivered on queued delivery
3. Plan-first gate (plan-gate/runtime.js + approve/reject/status routes)
- State machine per session: pending → approved | rejected | timed_out
- Injects plan-gate reminder via openchamber-sessions create prompt
- Agent emits ## Plan, runtime detects and emits openchamber:plan-ready
- Approve sends 'Proceed' prompt, reject sends revision prompt
- Configurable timeout (default 5 min), auto-approve on timeout
4. P1 shared plumbing
- cardID accepted in session create payload, stored in session metadata
- All new events carry cardID when known
Files added:
- packages/web/server/lib/agent-activity/runtime.js + runtime.test.js
- packages/web/server/lib/session-steer/runtime.js + runtime.test.js
- packages/web/server/lib/plan-gate/runtime.js + runtime.test.js
Files modified:
- packages/web/server/lib/openchamber-sessions/routes.js (cardID, planGate)
- packages/web/server/lib/opencode/feature-routes-runtime.js (route wiring)
- packages/web/server/index.js (runtime creation, SSE_PATH_PREFIXES)
- packages/web/server/lib/ui-auth/ui-auth.js (auth allowlist)
- packages/web/server/lib/realtime-proxy.js (SSE allowlist)
28 new tests passing. All pre-existing tests unaffected.
This commit is contained in:
@@ -92,6 +92,9 @@ import { createApnsRuntime } from './lib/notifications/apns-runtime.js';
|
||||
import { createNotificationTemplateRuntime } from './lib/notifications/template-runtime.js';
|
||||
import { createPermissionAutoAcceptRuntime } from './lib/permission-auto-accept/runtime.js';
|
||||
import { createMessageQueueRuntime } from './lib/message-queue/runtime.js';
|
||||
import { createAgentActivityRuntime, registerAgentActivityRoutes } from './lib/agent-activity/runtime.js';
|
||||
import { createSessionSteerRuntime, registerSessionSteerRoutes } from './lib/session-steer/runtime.js';
|
||||
import { createPlanGateRuntime, registerPlanGateRoutes } from './lib/plan-gate/runtime.js';
|
||||
import { createGracefulShutdownRuntime } from './lib/opencode/shutdown-runtime.js';
|
||||
import { createProjectConfigRuntime } from './lib/projects/project-config.js';
|
||||
import { createProjectContextRuntime } from './lib/project-context/runtime.js';
|
||||
@@ -167,6 +170,7 @@ const SSE_PATH_PREFIXES = [
|
||||
'/api/notifications/stream',
|
||||
'/api/openchamber/events',
|
||||
'/api/openchamber/realtime-proxy/sse',
|
||||
'/api/openchamber/agent-activity',
|
||||
];
|
||||
|
||||
function shouldSkipCompression(req, res) {
|
||||
@@ -899,6 +903,28 @@ const messageQueueRuntime = createMessageQueueRuntime({
|
||||
});
|
||||
messageQueueRuntime.start();
|
||||
|
||||
const agentActivityRuntime = createAgentActivityRuntime({
|
||||
globalEventHub: globalMessageStreamHub,
|
||||
broadcastGlobalUiEvent,
|
||||
});
|
||||
agentActivityRuntime.start();
|
||||
|
||||
const sessionSteerRuntime = createSessionSteerRuntime({
|
||||
globalEventHub: globalMessageStreamHub,
|
||||
buildOpenCodeUrl,
|
||||
getOpenCodeAuthHeaders,
|
||||
broadcastGlobalUiEvent,
|
||||
});
|
||||
sessionSteerRuntime.start();
|
||||
|
||||
const planGateRuntime = createPlanGateRuntime({
|
||||
globalEventHub: globalMessageStreamHub,
|
||||
buildOpenCodeUrl,
|
||||
getOpenCodeAuthHeaders,
|
||||
broadcastGlobalUiEvent,
|
||||
});
|
||||
planGateRuntime.start();
|
||||
|
||||
const openCodeWatcherRuntime = createOpenCodeWatcherRuntime({
|
||||
waitForOpenCodePort: (...args) => waitForOpenCodePort(...args),
|
||||
buildOpenCodeUrl,
|
||||
@@ -1947,6 +1973,9 @@ async function main(options = {}) {
|
||||
writeSseEvent,
|
||||
permissionAutoAcceptRuntime,
|
||||
messageQueueRuntime,
|
||||
agentActivityRuntime,
|
||||
sessionSteerRuntime,
|
||||
planGateRuntime,
|
||||
});
|
||||
|
||||
const startupPipelineResult = await startupPipelineRuntime.run({
|
||||
|
||||
Reference in New Issue
Block a user