Real-world test triggered a Sidekiq worker hang on conv 67 after a
message was routed through Daniela: two ResponseBuilderJobs (msg 1318
and 1319) started, emitted typing_on, then never returned. Sidekiq
showed 2/12 workers stuck for 10+ minutes — indefinite.
Root cause likely: Agents::Runner evaluates the orchestrator
instructions lambda multiple times per turn, and our wrapped lambda
calls MemoryPromptInjector#append_memory_block each time. Inside,
RecallService invokes OpenAI embedding API (2s timeout) and pgvector.
Ruby's Timeout.timeout has documented holes on net/http syscalls — if
the embedding API stalls at the socket level, the worker hangs forever
even though the timeout "fired".
Two fixes:
1. Per-message cache in the injector instance: the same
message_text is embedded + queried once, not N times per turn.
Dramatic reduction in network calls + DB queries during a single
agent run. Every call after the first returns the cached block
instantly.
2. Absolute rescue at append_memory_block top level:
rescue StandardError => e; return base_prompt. Even if the whole
memory pipeline throws, the base system prompt passes through and
the agent keeps responding. Memory is NEVER allowed to block a
response — that was already the design intent but the lambda caller
path didn't honor it rigorously enough.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>