terminal: session-broadcast sink + lifecycle events (Etap 1)
...
Persistent terminals emitted their stream only into a per-tool-call sink, which
closed once the open action returned — so a background dev server's output
stopped reaching the client immediately after open. TerminalClosed was never
emitted at all (the handler existed, the emitter did not).
Replace the per-tool event_sink with a single callback the orchestrator wires to
the session's WebSocket(s):
- TerminalManager: set_event_callback(cb) + _emit(session_id, event). Reader
tasks and open/close deliver TerminalOutputDelta / TerminalOpened /
TerminalClosed through the callback, which lives for the terminal's (and
session's) lifetime — not the tool call's.
- New TerminalOpened event (terminal_opened: name/description/pid/background).
TerminalClosed now actually emitted from _close_one (explicit/idle/shutdown/
session_ended). Background output keeps streaming after open returns.
- terminal.py: drop the per-tool event_sink from _tm.open (no longer needed).
- orchestrator._on_terminal_event → _notify_session (per-session WS fan-out,
same path recall/mcp updates use). container wires the callback after building
the orchestrator.
- Events imported lazily inside terminal_manager methods to avoid a circular
import (navi.core.events → navi.core.__init__ → agent → registry → navi.tools).
Tests: open emits TerminalOpened; background output streams via the callback
after open returns (gap-3 fix); close emits TerminalClosed; no callback does not
break readers. Existing terminal tests unchanged. Full suite: 968 passed, 1 skipped.
Plan: docs/terminal_tool_plan.md (5 etaps, this is Etap 1).
Eugene Sukhodolskiy
committed
on 13 Jul