What this prevents
- I can have an agent reply to a Slack message that triggered it.
- I can have a scheduled workflow post a result to a Discord webhook.
- I can chat with my agent over Telegram and get the response back outside the desktop window.
On desktop the gateway runner skips its delivery consumer, reload listener, and heartbeat because there is no Redis to subscribe to. Inbound messages still trigger agents, but the worker has no place to enqueue outbound replies, so the response never reaches the platform. The user sees the agent "respond" inside the desktop UI but never receives the message in Slack / Discord / Telegram.
Surface
- The gateway runner's delivery consumer, reload listener, and heartbeat loop all skip their bodies when
redis_url is empty (the desktop default).
- Worker handlers XADD to the gateway-deliveries Redis stream when an outbound reply is queued. With no Redis, the stream write is silently dropped.
- The frontend chat path is unaffected because it polls the orchestrator directly. Only platform adapters break.
Expected vs Actual
Expected: an outbound reply queued by the worker reaches the gateway runner via some in-process pubsub when on desktop, and the platform adapter sends it.
Actual: the XADD is dropped. No log line, no error, no retry. The response is permanently lost from the user's perspective.
Suggested fix
Add a LocalDeliveryQueue (asyncio.Queue) shared across the single sidecar process. The worker pushes here instead of XADD when deployment_mode == "desktop". The gateway runner subscribes to it. Match the existing pubsub interface so cloud's multi-shard fanout still works upstream when the user later moves to k8s.
Touch points:
- Replace the no-op branch in the gateway runner with a local-delivery consumer.
- Add a new local-delivery module that owns the asyncio queue.
- Worker delivery handler picks queue vs Redis stream by
deployment_mode.
What this prevents
On desktop the gateway runner skips its delivery consumer, reload listener, and heartbeat because there is no Redis to subscribe to. Inbound messages still trigger agents, but the worker has no place to enqueue outbound replies, so the response never reaches the platform. The user sees the agent "respond" inside the desktop UI but never receives the message in Slack / Discord / Telegram.
Surface
redis_urlis empty (the desktop default).Expected vs Actual
Expected: an outbound reply queued by the worker reaches the gateway runner via some in-process pubsub when on desktop, and the platform adapter sends it.
Actual: the XADD is dropped. No log line, no error, no retry. The response is permanently lost from the user's perspective.
Suggested fix
Add a
LocalDeliveryQueue(asyncio.Queue) shared across the single sidecar process. The worker pushes here instead of XADD whendeployment_mode == "desktop". The gateway runner subscribes to it. Match the existing pubsub interface so cloud's multi-shard fanout still works upstream when the user later moves to k8s.Touch points:
deployment_mode.