Root cause: runLoop() had no recover() — panics killed the goroutine while
leaving the session queue's activeRuns entry orphaned. Subsequent messages
to the same session would hang indefinitely (zombie state).
Fix: Two-layer panic recovery:
1. loop.go: runLoop() now returns error on panic instead of crashing goroutine,
allowing normal cleanup flow (session queue, traces, events)
2. queue.go: executeRun() defense-in-depth recovery ensures activeRuns cleanup
and scheduleNext() even if agent-level recovery fails
Multi-agent AI gateway with WebSocket RPC, HTTP API, and messaging channel integrations.
Go port of OpenClaw with multi-tenant PostgreSQL, per-user isolation, security hardening,
and production observability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>