2026 03 23 Command Center Single Pipe Design
Documentation for Plans
Command Center Single-Pipe Architecture — Option B Design
Date: 2026-03-23 Session: S262 Status: Approved Scope: Consolidate Command Center to use Vitto SSE as the single real-time pipe. Remove WebSocket. Forward MCP decision events from ai-api into Vitto's SSE stream.
Problem
The Command Center has two real-time connections: SSE (Vitto engine events) + WebSocket (MCP decision events). The WebSocket path is effectively dead — nothing reliably pushes events to it. The MCP workflow panels show stale autoFlashState data from previous sessions, not live data.
Meanwhile, 12K+ MCP decisions in ai-api's MongoDB are valuable but invisible in real-time because nothing bridges them to the SSE stream.
Solution
1. Add event injection endpoint to vitto-api
New endpoint: POST /engine/events/external
Accepts events from trusted internal services (ai-api) and injects them into the SSE subject. Protected by a system token (not user JWT — this is service-to-service).
// vitto-core.controller.ts
@Post('events/external')
injectExternalEvent(@Body() event: { type: string; data: Record<string, unknown> }) {
this.engine.injectExternalEvent(event);
return { success: true };
}
// vitto-engine.service.ts
injectExternalEvent(event: { type: string; data: Record<string, unknown> }) {
this.sseSubject.next({
type: event.type as SSEEventType,
taskId: 'external',
data: event.data,
timestamp: new Date().toISOString(),
});
}
2. Forward decision events from ai-api to vitto-api
In decision.service.ts, after persisting a decision, POST to vitto-api's new endpoint:
// After this.decisionModel.create(...)
this.forwardToVittoSSE('decision.created', {
decisionId, question, chosen, confidence, type, agentId
});
Uses HttpService (Axios) to call http://localhost:3003/vitto-api/engine/events/external.
3. Add decision event handlers to VittoMonitorService
New SSE event types handled:
decision.created→ updates a newrecentDecisionssignallearning.stats→ updates a newlearningStatssignal
4. Remove WebSocket from Command Center component
Delete:
- Socket.io import and
socketproperty connectWebSocket()method and allsocket.on(...)handlers- All
autoFlashStatesignals for MCP workflow data (mcpInitStatus, preflightStatus, pipelineStatus, memoryTierStats, timelineEvents, liveDecisions) wsConnectedsignalisConnectedchanges fromcomputed(sseConnected || wsConnected)back tothis.monitor.isConnected- MCP Workflow Pipeline panel from template
- Memory Tiers panel from template
- Live Timeline panel from template
Keep:
recentDecisions— now fed by SSEdecision.createdeventslearningStats— now fed by SSE or REST on initisPausedautoFlashState — user preference, unrelated to transport- Approval workflow — moves to REST-based (VittoMonitorService already has checkpoint approval)
5. Add REST init calls for decision data
On component init, fetch current stats from ai-api:
GET /ai-api/command-center/stats/comprehensive→ learning statsGET /ai-api/command-center/decisions?limit=10→ recent decisions
These provide initial state. SSE provides real-time updates thereafter.
Files Modified
| File | Change |
|---|---|
APIs/backend-vitto-api/src/app/engine/vitto-engine.service.ts |
Add injectExternalEvent() method |
APIs/backend-vitto-api/src/app/engine/controllers/vitto-core.controller.ts |
Add POST events/external endpoint |
APIs/backend-vitto-api/src/app/types/vitto.types.ts |
Extend SSEEventType with decision.* types |
APIs/backend-ai-api/src/app/infrastructure/services/decision.service.ts |
Add forwarding to vitto-api after persist |
libs/libs-ai/vitto-hub/src/lib/services/vitto-monitor.service.ts |
Add handlers for decision.created |
libs/libs-ai/gograb-ai-lib/src/lib/services/vitto-monitor.service.ts |
Same (duplicate copy) |
libs/libs-ai/gograb-ai-lib/src/lib/components/command-center/command-center.component.ts |
Remove WebSocket, remove stale autoFlashState signals, simplify |
libs/libs-ai/gograb-ai-lib/src/lib/components/command-center/command-center.component.html |
Remove MCP pipeline/memory/timeline panels |