Wires the TUI normal text input path to the LLM via A2aLocalFacade,
closing the gap where typing a message produced no LLM call. All
infrastructure (facade, SessionWorkflow, ProviderRegistry) already
existed in the CLI; this PR adds the TUI wiring on top.
Key changes:
- _run_llm_dispatch(): module-level, Textual-free dispatch function
covering all domain exception paths with user-friendly messages
- _format_worker_outcome(): testable worker result/error translator
- _create_tui_session(): DB-backed session with persona actor binding
- _build_tui_facade(): wired A2aLocalFacade + SessionWorkflow singleton
- run_worker(thread=True, exclusive=True): non-blocking, serialised
- _dispatch_gen counter: prevents cancelled worker callbacks from
corrupting the multi-turn transcript
- SessionView.transcript: accumulated conversation history, pre-escaped
- Markup escaping: _escape() applied before transcript storage
- on_mount focus: prompt.focus() so keyboard input is received
Tests: 18 BDD scenarios + 6 Robot Framework integration tests with
FakeListLLM (no real API keys required).
ISSUES CLOSED: #11230