Navi Code: force context compression via typed /compact control message
Forced /compact previously sent a chat message, which ran a full agent turn
that only produced summary text instead of running the real context compressor.
Worse, even when wired correctly, forced compact always reported "Nothing to
compact yet — the context is still small" regardless of context size: the typical
navi_code shape is a single long autonomous turn (1 user message + many tool
iterations = one turn), and partition_messages finds nothing to summarize when
turns <= keep_recent. The midturn auto-compress path already bypassed this via
keep_recent_messages (intra-turn split), but compact_stream passed
keep_recent_messages=None, so the fallback was disabled.

Changes:
- WS protocol: {"type":"compact"} control message (distinct from {"type":
  "message"}); rejected while an agent turn is active to avoid racing the agent.
- Agent.compact_stream: forced compression that bypasses the token threshold
  but still runs the real compressor; passes keep_recent_messages=max(12,
  context_keep_recent*2) so a single long turn compresses via intra-turn split
  (mirrors midturn auto-compress). Raises NothingToCompactError when context is
  genuinely too small.
- Orchestrator.run_compact + clear_run: broadcast agent events to subscribers,
  end with done marker, surface NothingToCompactError as an error event.
- Terminal client: ws_client.send accepts str|dict; CompactCommand enqueues
  {"type":"compact"}; TUI distinguishes forced compact (no stream_start) from
  in-turn auto-compress via the _streaming flag.
- Tests: compact_stream (incl. single-long-turn regression), WS handler
  dispatch/rejection, run_compact event/error broadcasting, ws_client send,
  compact command.

Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b89445c commit 8615de3578db4302a8859eb36435de8ad2b90185
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 18 hours ago
Showing 11 changed files
View
clients/terminal/tui/commands/builtin.py
View
clients/terminal/tui/tui_app.py
View
clients/terminal/ws_client.py
View
navi/api/websocket.py
View
navi/core/agent.py
View
navi/core/orchestrator.py
View
navi/exceptions.py
View
tests/clients/test_compact_command.py 0 → 100644
View
tests/clients/test_ws_client_send.py 0 → 100644
View
tests/unit/api/test_websocket.py
View
tests/unit/core/test_agent.py