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).
1 parent 3780695 commit 01752de0d7df2964185507a93f9922839fd9da73
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored on 13 Jul
Showing 7 changed files
View
docs/terminal_tool_plan.md 0 → 100644
View
navi/core/container.py
View
navi/core/events.py
View
navi/core/orchestrator.py
View
navi/tools/_internal/terminal_manager.py
View
navi/tools/terminal.py
View
tests/unit/tools/test_terminal.py