You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TUI previously buffered messages in a local Inbox and used agent_idle as a
routing decision: when agent was busy, messages were queued locally and an
interrupt signal was used to wake the agent to drain the queue. This was
TUI concept leakage into the agent runtime — agent internal state was
being directly mutated by the display layer, and interrupt was overloaded
with "cancel work" AND "new message arrived" semantics.
The worst symptom: ephemeral agents exited immediately on user input
because the yield_now idle check fired before the TUI's delayed inbox
forward could complete the IPC round-trip.
Architecture principles applied:
- Agent owns its internal state (idle/busy); external actors interact only
via mailbox delivery + explicit interrupt
- TUI state (observable.status, is_idle) is derived solely from agent
events, never set directly
- InterruptSignal carries only cancel/shutdown semantics
Key changes:
- Remove agent_idle field; derive via AgentViewState::is_idle() from status
- Remove Inbox struct entirely (dead after routing bypass)
- TUI calls append_user_display() + route_message() directly — no buffering
- Ephemeral idle check: drop yield_now(), drain_pending() is reliable now
- drain_pending() returns Vec<AgentInput> so Control commands are not
silently dropped
- Merge InputFromClient into AgentInput (Interrupt variant was vestigial)
- Status transition rolls back on emit failure for consistency
- Upgrade observation-channel try_send drops from debug to warn for prod
visibility
49/49 tests pass, zero clippy warnings, rustfmt clean.
0 commit comments