The agent console (TUI)¶
The Limestone agent runs in a terminal-style console in the bottom dock (ADR-0100).
It opens on the [[limestone]] banner and renders a turn as structured output:
> your prompt— the turn you sent.⏺ answer— the agent's answer.- tool-use blocks —
⏺ tool(args)then⎿ output(pretty-printed structured output). - thinking — the agent's intermediate reasoning, dimmed and collapsible.
- a status line with a braille spinner while a turn runs (
thinking… / running {tool}…).
While the model streams, its text types out live with a blinking cursor, and Stop interrupts the turn.
It's a theme-aware terminal (mono + the --ground surface + hairline frames),
so it follows light/dark like the rest of the app — not a hardcoded dark panel.
Testing streaming + interrupt¶
Token streaming (B3) and the interrupt (B4) have both automated and manual coverage, because the test fake agent returns whole messages near-instantly (no token deltas, no long turn to cancel mid-flight):
Automated (CI):
- Streaming —
tests/unit/agent/test_service.py::test_run_turn_streams_content_deltasscripts a smolagents stream (content deltas → a final) and assertsrun_turnemitsassistant_delta+final. Frontend:AgentChat.test.tsxasserts deltas merge into one live message. - Interrupt —
test_interrupt_stops_the_turn_cleanlyinterrupts mid-turn via a fake model that callsinterrupt()after step 1, asserting a clean stop (no answer, no error).test_agent_ws.py::test_chat_survives_an_interrupt_frameproves the socket survives an{"interrupt": true}frame and stays usable. Frontend:Stop sends a real interrupt frame.
Manual (needs a real model — configure a provider first):
make desktop-dev(or the web dev server), open the agent dock.- Ask something substantive ("summarize my Experiments collection and suggest a next step"). Streaming: watch the reasoning text type out token-by-token with the cursor. Interrupt: on a long turn, click Stop — it should halt at the next step boundary and return to an idle prompt, socket still live; send another message to confirm the session continues.
If a provider's model has no generate_stream, streaming falls back to whole-step
text (correct, just no cursor) — that's expected.