feat(tui): wire normal text input to LLM via A2A facade #11230

Closed
opened 2026-05-15 14:15:13 +00:00 by hamza.khyari · 0 comments
Member

Metadata

  • Commit Message: feat(tui): wire normal text input to LLM via A2A facade
  • Branch: feature/m8-tui-llm-dispatch

Background and Context

The TUI shell is built and running but the normal text input path is not wired to the LLM. When a user types a message and presses Enter in NORMAL mode, the handler (on_input_submitted in tui/app.py:204–210) simply echoes the expanded text back into the conversation widget and stops. No A2A dispatch, no SessionWorkflow.tell(), no LLM call is made.

The A2A facade (a2a/facade.py) and SessionWorkflow.tell() are fully implemented and already used by the CLI (cli/commands/session.py). This is a pure wiring gap — no new infrastructure is needed.

Current Behaviour

User types a message in the TUI → text is echoed back in the conversation widget → nothing sent to an LLM.

# tui/app.py:204–210 — current dead end
preview = result.expanded_text
conversation.update(preview)   # ← just echoes the text, no LLM call

Expected Behaviour

User types a message → message is sent to the LLM via A2aLocalFacade.dispatch() → assistant response is rendered in the conversation widget → UI remains responsive during the LLM call.

Acceptance Criteria

  • Typing a message in NORMAL mode and pressing Enter dispatches the message to the LLM via A2aLocalFacade.dispatch(A2aRequest(method="message/send", ...))
  • The assistant response is displayed in the conversation widget after the LLM returns
  • The Textual event loop is not blocked during the LLM call (worker thread used)
  • If no actor is configured for the session, a user-friendly error is shown instead of an unhandled exception
  • A real database-backed session is created at TUI startup (not hardcoded "default")
  • All existing TUI behaviour (slash commands, shell exec, @ references) is unchanged

Supporting Information

Five concrete implementation steps:

  1. Real session creation — replace hardcoded session_id="default" (app.py:108) with SessionService.create() at startup in run_tui() (commands.py)
  2. Inject A2aLocalFacade — build and register a fully-wired facade in run_tui() (register session_service + session_workflow + provider_registry), pass it into CleverAgentsTuiApp.__init__
  3. Dispatch NORMAL mode input — replace dead-end conversation.update(preview) with facade.dispatch(A2aRequest(method="message/send", params={"session_id": ..., "message": ...})) and render response.result["assistant_message"]
  4. Handle SessionActorNotConfiguredError — display friendly message: "No actor configured. Use /persona set <name> or select an actor first."
  5. Async worker — wrap the blocking facade.dispatch() call in self.run_worker() to prevent freezing the Textual event loop

Key files:

  • src/cleveragents/tui/app.pyon_input_submitted, __init__
  • src/cleveragents/tui/commands.pyrun_tui()
  • src/cleveragents/a2a/cli_bootstrap.pyget_facade()
  • src/cleveragents/a2a/facade.pyA2aLocalFacade._handle_message_send()
  • src/cleveragents/application/services/session_workflow.pySessionWorkflow.tell()

Subtasks

  • Create real session via SessionService.create() in run_tui() and pass session_id to CleverAgentsTuiApp
  • Build and wire A2aLocalFacade in run_tui() and inject into CleverAgentsTuiApp
  • Replace dead-end NORMAL mode handler with facade.dispatch() call and response rendering
  • Catch SessionActorNotConfiguredError and display user-friendly message
  • Wrap LLM dispatch in self.run_worker() for non-blocking execution
  • Tests (Behave): add scenarios covering the normal message flow and error paths
  • Verify coverage >= 97% via nox -s coverage_report
  • Run nox (all default sessions), fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line matches the Commit Message in Metadata exactly.
  • The commit is pushed to the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a PR to master, reviewed, and merged.
## Metadata - **Commit Message**: `feat(tui): wire normal text input to LLM via A2A facade` - **Branch**: `feature/m8-tui-llm-dispatch` ## Background and Context The TUI shell is built and running but the normal text input path is not wired to the LLM. When a user types a message and presses Enter in `NORMAL` mode, the handler (`on_input_submitted` in `tui/app.py:204–210`) simply echoes the expanded text back into the conversation widget and stops. No A2A dispatch, no `SessionWorkflow.tell()`, no LLM call is made. The A2A facade (`a2a/facade.py`) and `SessionWorkflow.tell()` are **fully implemented** and already used by the CLI (`cli/commands/session.py`). This is a pure wiring gap — no new infrastructure is needed. ## Current Behaviour User types a message in the TUI → text is echoed back in the conversation widget → nothing sent to an LLM. ```python # tui/app.py:204–210 — current dead end preview = result.expanded_text conversation.update(preview) # ← just echoes the text, no LLM call ``` ## Expected Behaviour User types a message → message is sent to the LLM via `A2aLocalFacade.dispatch()` → assistant response is rendered in the conversation widget → UI remains responsive during the LLM call. ## Acceptance Criteria - Typing a message in NORMAL mode and pressing Enter dispatches the message to the LLM via `A2aLocalFacade.dispatch(A2aRequest(method="message/send", ...))` - The assistant response is displayed in the conversation widget after the LLM returns - The Textual event loop is not blocked during the LLM call (worker thread used) - If no actor is configured for the session, a user-friendly error is shown instead of an unhandled exception - A real database-backed session is created at TUI startup (not hardcoded `"default"`) - All existing TUI behaviour (slash commands, shell exec, `@` references) is unchanged ## Supporting Information Five concrete implementation steps: 1. **Real session creation** — replace hardcoded `session_id="default"` (`app.py:108`) with `SessionService.create()` at startup in `run_tui()` (`commands.py`) 2. **Inject `A2aLocalFacade`** — build and register a fully-wired facade in `run_tui()` (register `session_service` + `session_workflow` + `provider_registry`), pass it into `CleverAgentsTuiApp.__init__` 3. **Dispatch NORMAL mode input** — replace dead-end `conversation.update(preview)` with `facade.dispatch(A2aRequest(method="message/send", params={"session_id": ..., "message": ...}))` and render `response.result["assistant_message"]` 4. **Handle `SessionActorNotConfiguredError`** — display friendly message: "No actor configured. Use `/persona set <name>` or select an actor first." 5. **Async worker** — wrap the blocking `facade.dispatch()` call in `self.run_worker()` to prevent freezing the Textual event loop Key files: - `src/cleveragents/tui/app.py` — `on_input_submitted`, `__init__` - `src/cleveragents/tui/commands.py` — `run_tui()` - `src/cleveragents/a2a/cli_bootstrap.py` — `get_facade()` - `src/cleveragents/a2a/facade.py` — `A2aLocalFacade._handle_message_send()` - `src/cleveragents/application/services/session_workflow.py` — `SessionWorkflow.tell()` ## Subtasks - [ ] Create real session via `SessionService.create()` in `run_tui()` and pass `session_id` to `CleverAgentsTuiApp` - [ ] Build and wire `A2aLocalFacade` in `run_tui()` and inject into `CleverAgentsTuiApp` - [ ] Replace dead-end NORMAL mode handler with `facade.dispatch()` call and response rendering - [ ] Catch `SessionActorNotConfiguredError` and display user-friendly message - [ ] Wrap LLM dispatch in `self.run_worker()` for non-blocking execution - [ ] Tests (Behave): add scenarios covering the normal message flow and error paths - [ ] Verify coverage >= 97% via `nox -s coverage_report` - [ ] Run `nox` (all default sessions), fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the first line matches the Commit Message in Metadata exactly. - The commit is pushed to the branch matching the Branch in Metadata exactly. - The commit is submitted as a PR to master, reviewed, and merged.
hamza.khyari added this to the v3.7.0 milestone 2026-05-15 14:15:13 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#11230
No description provided.