The prompt template contains a literal JSON example block ({"needs_clarification": ...})
which str.format() tried to interpret as format fields, raising KeyError on every
Discuss DM. Switch to .replace() so braces in the template are taken literally.
Without exc_info we only see the bare exception string, which has been
unhelpful for debugging Discuss DM failures (e.g. a KeyError whose
message is just a JSON key, with no clue where it was raised).
Odoo's bot model serialises user_id as a string (str(uid)) over the
HTTP boundary, but the asyncpg memory queries ($1) expect an integer.
This caused 'str object cannot be interpreted as an integer' on every
Discuss DM. Cast at the entry point so downstream stores get an int.
Fixes all errors reported in docker compose logs agent-service:
1. config.py: add ollama_max_concurrent, claude_timeout, claude_max_concurrent
fields so LLMRouter(config=settings) can read them without AttributeError.
2. main.py - LLM router: drop manual OllamaBackend/ClaudeBackend construction;
call LLMRouter(config=settings, pg_pool=pool) to match class signature.
Fixes: OllamaBackend.__init__() unexpected kwarg 'base_url'.
3. main.py - DB: add 5-attempt retry with 2s backoff and redacted DSN logging.
Fixes: connection refused race on startup before Postgres accepts connections.
4. main.py - AgentRegistry: call AgentRegistry() with no args (class takes none),
then await agent_registry.load_from_odoo(odoo) to populate active agents.
Fixes: AgentRegistry.__init__() unexpected kwarg 'odoo'.
5. main.py - PeerBus: pass registry=agent_registry at construction; register
specialist agents on agent_registry (not peer_bus, which has no register()).
peer_bus.py: make directive_id optional (default None) — bus is a singleton
at startup; directive_id is only needed per-request.
Fixes: PeerBus.__init__() missing positional args 'registry' and 'directive_id'.
6. main.py - MasterAgent: drop unexpected peer_bus= kwarg from constructor call.
Fixes: MasterAgent.__init__() unexpected kwarg 'peer_bus'.
7. mcp_router.py: pass NotificationOptions() instance instead of None.
Fixes: AttributeError 'NoneType' has no attribute 'tools_changed' (was applied
in running container but not committed; now committed).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>