| 2026-07-12 |
navi_code: prefer isolated project envs and bootstrap NAVI.md when absent
...
Add a "Project environments" section steering the agent toward the
project's existing isolated env (venv/uv/node_modules/target) — use it,
don't duplicate or bypass it — and only create a project-local one when
deps are needed and none exists; never install system-wide (system-wide
changes still require explicit confirmation). Also nudge the agent to
create NAVI.md when it's absent after real orientation work on a
non-trivial task, seeding the pointer structure so the next session
starts oriented.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
4 hours ago
|
navi_code: enable planning phase 2 (structured review)
...
phase1 already emits REFLECT: yes|no, but with phase2 off the flag was a
dead-end. Enabling phase2 runs a Critic/Pragmatist/Detailer review pass
before phase3 whenever phase1 flags the task as complex (REFLECT: yes),
feeding "Plan Adjustments" into the execution plan. Skipped for simple
tasks and subagents (gated on `needs_reflect and not is_subagent`), so
the extra LLM call only lands on complex parent-agent work.
Resolves F8 machinery-gap G.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
5 hours ago
|
navi_code: frame planning machinery in system prompt (F8)
...
Workflow "Plan" no longer duplicates the planner (phase1/3 already build a
structured plan and auto-populate todo); adds an observe/act carve-out so
observe requests don't get pushed to create a todo. New "System signals
you'll see" subsection names the runtime-injected messages ([Goal anchor],
[Scope boundary], [Anti-stall warning], [Iteration N/M]) so the model
recognises them as machinery and responds correctly — and corrects the
factual error that the goal anchor reads scratchpad `goal` (it reads the
original request + live todo). Closing paragraph warns that thinking isn't
re-injected and the plan's per-step executor assignments are lost to
compression, so conclusions/assignments should go in scratchpad.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
5 hours ago
|

navi_code: give the sub-agent operational wisdom (editing/reading/todo)
...
By default (inherit_system_prompt=False) the sub-agent got only the 17-line
subagent_system_prompt — none of the parent's editing/reading/context
discipline. So a sub-agent with a clean context would still burn it: pulling
whole files into read, over-using smart_edit, not tracking steps. The clean
context is the sub-agent's main advantage — keep it clean.
Expand subagent_system_prompt.txt with a compact operational core:
- Editing: prefer edit/edit_lines (deterministic); smart_edit as last resort
(extra LLM call, reads the whole file) — the same policy the parent now has.
- Reading: info before read, offset/limit to the region, grep/find/query to
locate — don't read a file just to search it.
- Track steps with todo and record findings in scratchpad (durable within the
run; context can be compressed).
inherit_system_prompt stays False: the full parent prompt carries orchestration
and spawn_agent sections that are irrelevant to a sub-agent and references
tools it lacks. The sub-agent keeps its own focused, self-contained prompt.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
5 hours ago
|

navi_code: brief sub-agent on its restricted toolset + context_transfer
...
The sub-agent briefing section told the model to "give the sub-agent
everything it needs" but never said which tools the sub-agent lacks or how
context actually reaches it — so briefings could ask the sub-agent to use
memory, spawn further agents, or switch profiles, all of which it can't.
- context_transfer: write the context the sub-agent needs (files, snippets,
how to verify) into the scratchpad `context_transfer` section before
spawning — it is injected automatically; the sub-agent does not inherit
short-term memory or conversation history.
- Restricted toolset: the sub-agent has todo/scratchpad/reflect/filesystem/
code_exec/terminal/list_tools but NOT memory/switch_profile/spawn_agent/
schedule_recall/manage_recall — brief it to use only what it has (e.g.
record findings in scratchpad, not memory).
The profile list is not duplicated — spawn_agent's own description carries it.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
5 hours ago
|

recall: carry self-instruction (message) on the recall_update wire
...
The recall card could show call_type/trigger_at but not the self-instruction
(additional_context_message) — it was absent from RecallUpdate.to_wire, so
the user couldn't see what future-self was about to do at the scheduled or
fired moment. Extend the wire payload.
- events.RecallUpdate: add `message` field; to_wire emits "message".
- scheduler._publish_recall_update: accept and forward `message`.
- Publish sites carry message=recall.additional_context_message:
schedule_recall (scheduled) and orchestrator._finalize_recall
(rescheduled / fired / cancelled). manage_recall cancel/skip omit it
(no recall object handy; already visible in the prior scheduled card).
- TUI RecallRenderer: preview the message (first line + "(+N lines)",
capped at 80) on a `msg:` body line when present.
- Tests: RecallUpdate.to_wire carries message (defaults None); renderer
preview (single/multiline/truncate/empty) and scheduled-card renders it.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
5 hours ago
|

navi-code TUI: render schedule_recall lifecycle (recall_update)
...
The scheduler/orchestrator already push RecallUpdate (wire type
"recall_update") for the schedule_recall lifecycle, but the TUI had no
mapping or renderer — every event fell to the unknown-event catch-all and
rendered as a raw dict dump. Wire it into a dedicated card.
- chat_model: add a "recall" ChatItem kind and a recall_update branch
(before the catch-all) that keeps the wire fields in meta.
- chat_panel _item_msg: reconstruct {"type": "recall_update", **meta}
for a recall item so the registry sees it.
- renderers/recall.py: new RecallRenderer — a compact, color-coded card
per action (⏰ scheduled/info, ▶ fired·resuming/accent, ✕ cancelled/error,
↻ skipped/warning, ↻ rescheduled/info) showing call_type and a trimmed
trigger_at. Registered before the generic renderers.
- tests: 10 tests covering accepts, each action's title/border color,
trigger formatting, empty body, chat_model mapping, and _item_msg
round-trip.
MVP only — the self-instruction (additional_context_message) is not on the
wire, so it remains visible in the schedule_recall tool-call card above;
extending RecallUpdate.to_wire to carry it is a follow-up.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
6 hours ago
|

navi_code: leverage context-org/planning tools in system prompt
...
Wire the KV-backed context/planning tools into the profile so the agent
uses them instead of relying on lossy conversation memory under compression.
- Workflow Plan: create a todo for non-trivial tasks.
- Workflow Test & verify: record the verification in the todo `validation`
field when marking done (structural form of "never claim done without
verification").
- Replace "Context drift recovery" with "Working state & memory":
- todo: plan + verification tracking.
- scratchpad: durable working memory across compression (sections
goal/findings/errors/artifacts); read before final report.
- Sub-agent handoff: write context to the scratchpad `context_transfer`
section before spawn_agent — it is injected into the sub-agent
automatically (the sub-agent does not inherit short-term memory).
- schedule_recall: continue after the iteration limit, offload heavy
work headlessly, poll builds/logs, chain multi-phase work.
- reflect: selectively, before complex plans or when stuck (3 LLM calls).
- memory: global cross-project facts, not a scratchpad/docs substitute.
- Drift recovery folded into a closing paragraph aligned with the
goal_anchoring machinery.
TUI visualization for schedule_recall is a follow-up task.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
6 hours ago
|

navi_code: fix review findings 1-5 in system prompt
...
- spawn_agent default profile: correct the false "omit profile_id to use
this developer profile" — omitting profile_id inherits navi_code (the
parent), not developer; set profile_id explicitly to pick another profile.
- Safety Rules: scope strict confirmation to destructive/irreversible
operations (rm, delete, wholesale write overwrite, drop table, force-push);
state explicitly that routine edit/edit_lines do not require confirmation.
- Workflow Understand: soften the hard "start with docs/index.md" to a soft
"read notes/docs first (see NAVI.md and Documentation sections below)" so
it no longer conflicts with the NAVI.md-first entry point.
- Documentation: add a branch for projects with no docs/ — propose creating
one (with user approval) or record in NAVI.md that docs are absent/not
needed.
- Execution environment: document terminal action="run" for one-off commands
(tests, git status, lint, py_compile); persistent terminals only for
long-running processes.
Findings 6 (subagent briefing toolset), 7 (subagent delegation wording),
8 (planning/context-org tools) deferred for later work.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
6 hours ago
|
navi_code: docs as living spec + NAVI.md hints file
...
Replace the Project knowledge section with two clearer sections:
- Documentation: docs/ is the project's living specification, not just
human reference — keep it current with code, and for non-trivial changes
align docs to the intended end state before implementing (docs-first).
- NAVI.md: a lightweight, pointer-shaped hints file at project root (not a
source of truth) that tells the agent where to look; capped at ~150 lines
with a defined structure (Project / Commands / Where to start / Docs index
/ Gotchas / Open decisions), kept distinct from docs/ (spec) and memory
(global cross-project facts).
Instruction-only: the agent reads NAVI.md via filesystem; no auto-inject.
Applies to navi_code only.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
filesystem edit: render red/green diff in TUI without changing model context
...
edit is now the primary editing method, yet its output was a dry one-line
status ("Edited …: replaced X B with Y B") while edit_lines/smart_edit showed a
highlighted unified diff. Carry the unified diff in ToolResult.metadata["diff"]
(kept out of the model-facing output, so the agent's context is unchanged) and
render it in the TUI: dim summary line plus highlighted diff (green +, red -,
dim @@). Falls back to plain text when no diff metadata is present.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
7 hours ago
|
filesystem: make edit/edit_lines the default, smart_edit a last-resort fallback
...
Agent overused smart_edit (whole-file LLM call, weak context). Rewrite the
FilesystemTool description decision tree so edit (exact text) is the default and
edit_lines (by line numbers) is the deterministic option; smart_edit is reserved
for genuinely semantic changes that cannot be expressed as exact text or line
numbers. Strengthen the old_not_unique hint to steer back to edit/edit_lines
before smart_edit. Add an "Editing policy" section to the navi_code and
developer profile prompts reinforcing the same priority.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
8 hours ago
|
tui: move filesystem tool_started info into the card body
...
Previously the action and primary path were crammed into the title
("→ filesystem read src/main.py") and the body only held the extra args,
so for simple actions (info/delete/mkdir/exists) the card body was empty.
Title is now compact ("→ filesystem <action>"). The body always carries
the information: "path: <path>" (accent) leads, "destination: <dest>"
(accent) follows for move/copy/diff, then the per-action args (range/
content/old+new/operations/instruction/pattern/question …) as before.
The card is never empty.
Tests updated: title carries action only (path is in the body),
destination lands in the body for move/copy/diff, info shows path in body,
path value is styled accent. 42 filesystem-renderer tests total. Full
suite 827 passed, 1 skipped.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 hours ago
|

tui: styled filesystem tool_started card — compact args, action in title
...
The generic ToolStartedRenderer dumped all of args as JSON, so filesystem
cards showed "→ filesystem" with no action in the title and the full
content/old/new text for write/edit/smart_edit flooding the card.
New FilesystemToolStartedRenderer (clients/terminal/tui/renderers/filesystem.py):
- Title carries the action and primary path: "→ filesystem read src/main.py";
move/copy/diff also append "→ <destination>".
- Body is a compact per-action summary instead of JSON:
- read: range (offset–limit) + numbered flag
- write/append: content preview (first line + "(+N lines)" hint, not full)
- edit: old + new previews
- edit_lines: operations count + op kinds (replace/delete/insert, first 5)
- smart_edit: instruction preview
- grep: pattern + glob + regex flag
- find/find_up: pattern
- query: question preview
- list: recursive flag
- info/delete/mkdir/exists: empty body (path is in the title)
- Body keys render dim, values in text.
- Sub-agent cards indented, matching ToolStartedRenderer.
Registered before the generic ToolStartedRenderer (first-match wins). The
filesystem tool, WS protocol, and events are untouched.
13 new tests cover accepts, title (action/path/destination), empty body for
info, and per-action body summaries (read/write/edit/edit_lines/grep/
smart_edit/query), content preview truncation, subagent indent, and body key
styling. 42 filesystem-renderer tests total. Full suite 827 passed, 1 skipped.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
9 hours ago
|
tui: styled filesystem find + find_up output — stage 3
...
find: the "[N matches for 'pattern' in path ⠠ …]" header plaque is dim
(warning color on ⠠ truncated); "No matches for …" renders dim; each match
line "{path} ({size})" renders the path in text, " (" and the size in
dim, and the "<dir>" marker in info.
find_up: a resolved path renders in accent; "not found (searched: …)"
renders dim.
Fallback narrowed to write/edit/append/move/copy/delete/mkdir/exists/
query/unknown. 5 new tests (find header/matches/truncated/no-matches,
find_up found/not-found); 29 filesystem-renderer tests total. Full suite
814 passed, 1 skipped.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
10 hours ago
|

tui: styled filesystem read + info output — stage 2
...
read: the "[path | N lines | size]" header plaque is now dim with the
path highlighted in accent (offset/limit plaque "… lines A–B of N …"
likewise); the "⚠ Large file …" warning line renders in warning color; the
file body renders in text (was dim — readable now) with no syntax
highlighting (rejected by design); numbered reads dim the line numbers and
render line content in text. read errors (not_found, file_too_large) render
in tool_error.
info: each "key: value" line highlights the key (incl. colon) in accent,
the alignment padding in dim, and the value in text.
Fallback narrowed to write/edit/append/move/copy/delete/mkdir/exists/find/
find_up/query/unknown (statuses and find/query intentionally untouched).
Diff/list/grep from stage 1 unchanged.
6 new tests (read plaque/offset/warn/numbered/error, info keys+values);
24 filesystem-renderer tests total. Full suite 809 passed, 1 skipped.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
10 hours ago
|

tui: styled filesystem tool output (diff / list / grep) — stage 1
...
The TUI rendered every tool result as a single dim Text in a Panel — no
special handling for filesystem, so unified diffs from edit_lines/smart_edit/
diff were shown as plain grey text (the existing DiffRenderer only fires on
type:"diff", which filesystem never emits), directory listings were an
unaligned text blob, and grep matches had no pattern highlighting.
New FilesystemToolResultRenderer (clients/terminal/tui/renderers/filesystem.py)
intercepts filesystem tool_call events by tool=="filesystem" + args.action:
- diff / edit_lines / smart_edit: reuse a shared highlight_unified_diff()
helper (extracted from DiffRenderer) — + green, - red, @@ dim; the
"Applied …" summary line of edit_lines/smart_edit is separated from the
diff body via a Group; "Files are identical." renders dim.
- list: header plaque dim (warning color on ⚠ truncated); directories
shown as "▸ name/ (n items)" in info color; files as name (text) +
size (accent) + time (dim), preserving the tool's fixed-width alignment;
"?" entries in warning; "(empty directory)" dim.
- grep: header plaque dim; "rel:line:" location dim; matched pattern
occurrences highlighted in accent (case-insensitive, re.escape'd);
"No matches …" dim.
- read / info / write / edit / append / move / copy / delete / mkdir /
exists / find / find_up / query / unknown: plain dim fallback (unchanged
for now — stage 2). Errors (success=False): plain in tool_error color.
Registered before the generic ToolResultRenderer (first-match wins). The
filesystem tool, WS protocol, events, and webclient are untouched.
22 new tests in tests/clients/test_filesystem_renderer.py cover accepts,
diff/edit_lines/identical/error, list (dir/file/truncated), grep
(pattern/no-matches/no-pattern-arg), fallback, title+status, subagent
indent. DiffRenderer behavior unchanged (existing test still passes).
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
10 hours ago
|

docs: index, profiles, tools, api actualization + archive historical docs
...
index.md: expand the file map to reference navi_code, navi_code_cli, store,
context_providers, eval_system, android-client, testing; point at archive/
for historical notes.
profiles.md: add the Compression section (compression_keep_recent /
compression_max_tokens / compression_prompt_file overrides), the
subagent_system_prompt field, and a note that AgentProfile allows unknown
keys (model_config extra=allow).
tools.md: rewrite the user-tools self-extension section — write_tool is
deprecated and removed; write the file via filesystem/code_exec and call
reload_tools instead.
api.md: add missing REST endpoints (GET /sessions/{id}/todos, GET
/sessions/{id}/files list, GET /sessions/{id}/messages/archive, POST
/sessions/{id}/stop) and the admin MCP management block (config CRUD,
reconnect, status, test, profile-mcp mapping, admin recalls); fix the
WS client→server table (type: message|compact, image limits 8/50MB);
add model_info / todo_updated / compression_started events; fix
recall_update action (add skipped), session_sync payload, context_compressed
threshold (70%).
Archive: move plan_01_god_object_agent.md, tech_debt_review_2026-04-29.md,
architecture_weak_spots.md, future_headless_nodes.md into docs/archive/
(historical design notes, no inbound links).
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|

docs(navi-code-cli): actualize TUI to current code
...
Rewrite the TUI section for the post-Jul-10 feature avalanche:
- Layout: five regions (ChatPanel + Vertical(StatusPanel, TodoPanel) +
InputBox + bottom StatusBar); SessionsPanel removed, sessions via modal
picker. StatusPanel now shows Profile/Session/Model/Connection/Backend/
Theme/hint (no Tokens/Iter).
- StatusBar: activity indicator + live elapsed timer + context-fill gauge.
- TodoPanel: live, sorted (in_progress → pending → failed/skipped → done),
subagent-isolated.
- Per-message widgets, markdown rendering, bounded history render cost,
session history load on resume, WebSocket auto-reconnect.
- Slash-command hints (Up/Down/Enter/Tab), command palette (Ctrl+P),
permission dialog, !shell passthrough, @file refs.
- Compression summary card, request-duration metadata, model_info display.
- Keybindings table (Ctrl+P / Ctrl+X C|T|Q / Esc).
- Commands: add aliases (clear, resume/continue, exit/q, save); fix /clear
(alias of /new), /sessions (picker), /switch (hybrid); add /compact.
- --resume <session_id> flag + post-exit resume hint.
- Full event rendering list (stream_start/end/stopped, model_info,
todo_updated, compression_started, context_compressed, …).
- Full file map (ws_bridge, chat_model, screens/, permissions, shell_runner,
file_refs, duration, …).
navi_code.md left as-is (already current with bounded autonomy).
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|
docs(websocket): /compact control message, new events, fixes
...
- Document the {"type":"compact"} forced-compression control message
(bypasses threshold, no stream_start, rejected if a run is active,
TUI /compact + Ctrl+X C).
- Fix image limits: 8 images, 50 MB total payload (was 10 / 5 MB each).
- tool_started/tool_call: document metadata + tool_call_id fields.
- recall_update action: add skipped (scheduled|cancelled|skipped|fired|
rescheduled).
- session_sync: document the session_id/profile_id payload.
- Add missing events: model_info, todo_updated, terminal_output,
terminal_closed.
- Document close code 4003 (auth/access denied) alongside 4004.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|

docs: actualize compression mechanics + real-token baseline
...
mechanics.md: rewrite the Context Compression catalog (adaptive turn
importance, intra-turn fallback, would_compress guard, token-budget
hard-truncate, per-message view truncation, profile-aware overrides,
real-token baseline estimator, meta-summary, archive-on-compress,
CompressionStarted/ContextCompressed events); fix Agent Loop rows
(run() delegates to run_stream, planning entry condition, pre/mid-turn
gates, check_context_size, forced /compact, estimate chars//3 in
compressor.py); fix WS image limits (8 / 50MB) and add /compact control
message; rebuild the Undocumented Mechanics Summary from current ❌ rows;
refresh the cross-reference index.
config.md: LLM_STREAM_FIRST_CHUNK_TIMEOUT 180→90; add
CONTEXT_MESSAGE_TOKEN_BUDGET.
agent.md: run() runs planning; full run_ephemeral signature + compact_stream
entry point; cwd ContextVar; planning entry condition; loop pseudocode
(midturn compression, check_context_size, ModelInfo, real baseline);
streaming guard 90s; workers carry profile; pre-turn estimate-based gate;
per-message view truncation.
sessions.md: full Message flag table (is_context/is_display/is_compression_critical
…); three compression trigger points + forced /compact; real-baseline
estimate; check_context_size guard; expanded algorithm (adaptive partitioning,
intra-turn fallback, meta-summary, 24k cap, 4000-char critical preview,
token-budget hard-truncate fallback, archive-on-compress, baseline clear);
compression events; SessionStore archive methods + list_page profile_id.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|
compression: profile-aware worker + real-token baseline estimator
...
Item 2 — thread the active profile into CompressionWorker so
compress_context applies per-profile overrides (compression_keep_recent,
compression_max_tokens, compression_prompt_file). navi_code now compresses
with keep_recent=12 instead of the global 8.
Item 1 — estimate the next LLM call's context from the *real* prompt_tokens
of the previous call (bulk) plus a heuristic delta for messages appended
since, replacing the chars//3 estimate that undercounts code-heavy tool
output and fired midturn compression too late (Navi kept working until the
window was exhausted). Baseline is recorded after each stream and cleared
after compression; check_context_size and the midturn gate use it, with a
heuristic fallback when no baseline exists or the context shrank.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|

compression: fix auto-compress no-op on few-huge-messages + honest status
...
Root cause: the compression gate (should_compress) measures tokens, but the
partition measures message/turn count, and CompressionStarted was emitted
before the attempt. For navi_code's "few very large messages" shape (one big
file read = 1 user + assistant + 1 huge tool result, 66k tokens in 3 messages)
the gate fired, the UI showed "compression", but partition returned
to_summarize=[] -> compress_context None -> nothing shrank. The agent kept
going until the window overflowed. It wasn't running "during" compression —
there was no compression, just a no-op the user mistook for one.
A. Per-message head/tail truncation in context_builder.build(): oversized
tool/assistant messages (over context_message_token_budget, 0=num_ctx//6)
are capped head+marker+tail in the LLM view only (model_copy — stored
history and reloads are never affected). A single huge tool result can no
longer alone blow the window; user/system messages are never truncated.
B. Token-budget hard-truncate fallback in compress_session: when partition
no-ops but tokens exceed the threshold, drop oldest turns to num_ctx*0.5.
_hard_truncate is now token-aware (was a fixed message-count floor that
no-oped on <=6 messages even when huge). New would_compress() predicts
compress_session's real outcome with no LLM call.
C. Honest CompressionStarted: _compression_events_midturn/_preturn emit it
only after would_compress() confirms the partition (or token-budget
fallback) can actually shrink the stored context — no more "compression"
status with no ContextCompressed to follow.
Bonus: post-turn CompressionWorker now passes keep_recent_messages=
max(12, context_keep_recent*2), matching the midturn path, so a single long
autonomous turn compresses post-turn too (was always a no-op).
Tests (+14): would_compress agreement, token-budget fallback, token-aware
hard_truncate, build() truncation (preserves user, no mutation, head+tail),
agent no-CompressionStarted-when-nothing-to-compress, worker single-long-turn.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|

Navi Code TUI: show context-compression summary in the chat
...
The server already sent the summary text in ContextCompressed.summary, but the
TUI never showed it — forced /compact printed a bare "N → M messages" line and
mid-turn auto-compress produced no chat feedback at all. Now both render a
distinct bordered block headed "Context compressed: N → M messages" with the
summary text (markdown) inside, so the user can see what the compressor kept.
- chat_model: new context_summary kind; context_compressed creates a chat item
carrying the summary + before/after counts (was explicitly ignored).
- renderers/summary: ContextSummaryRenderer — dim bordered panel, markdown body.
- chat_panel: _item_msg mapping for context_summary.
- tui_app: emit the summary item on context_compressed for both forced and
mid-turn cases; drop the now-redundant one-line status (the block header
carries the counts). Spinner start/stop/label logic unchanged.
- Tests: chat_model mapping + renderer (accepts/counts/text/missing-counts).
Summaries are live-only — not rebuilt on session reload (the compressor stores
them is_display=False, same as before for compression events).
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
11 hours ago
|
| 2026-07-11 |

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>
Eugene Sukhodolskiy
committed
23 hours ago
|
tui: remove the useless NaviCodeTui Header bar
...
The default Textual Header just shows the class name — no information value,
and it costs the top screen line. Drop it from compose (and the now-unused
import) so the chat panel starts at the very top.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
tui: per-message chat widgets — render only the streaming bubble
...
ChatPanel used to hold a single Static rebuilt via update(Group(...)) on every
event, so a per-token stream_delta re-rendered all visible items (up to 200).
Now each message is its own _ChatItemView(Static); _sync() mounts new widgets at
the end, removes dropped ones, and signature-gated maybe_update re-renders only
the changed widget. On a stream only the streaming bubble repaints — the rest
stay mounted untouched. Public API stays synchronous (mount/remove are sync in
this Textual version), so tui_app is unchanged. Truncation hint is now a
dedicated top widget toggled via styles.display.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
tui: fix 'Connection: online online' duplication
...
The status panel renders 'Connection: online {detail}'; the supervisor was
passing detail='online' on a successful connect, so the label read
'online online'. The label already conveys the state — send an empty
detail on success (matching the pre-reconnect behaviour). Reconnecting/offline
details are unaffected since they carry a reason distinct from the label.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
tui: auto-reconnect WebSocket with exponential backoff
...
A dropped server socket no longer leaves the TUI stranded. WsBridge now
runs a supervisor that connects, forwards events until the socket closes,
then backs off and reconnects indefinitely — the only thing that ends it is
stop(). The status panel flips to 'reconnecting…' and the next message the
user sends is held in the input queue until a fresh socket is live, so a
brief blip doesn't eat their input (send failures requeue + force a
reconnect; nothing is lost).
Backoff: 1s→2s→4s… cap 30s, reset on success; stop() cancels it promptly.
Interface preserved (start/stop/.client/.connected); an optional client=
param lets tests inject a fake client.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

tui: show the changed step's text in the todo update call card
...
The todo update call card (→ todo · #3 → done) now shows the text of the
step being changed, plus the validation when present. The LLM's update args
carry only the index, not the task text, so the text is read from the
current plan row before the tool runs and attached to ToolStarted.metadata
(client-rendering only, backward-compatible).
Backend:
- events.ToolStarted gains a metadata dict (mirrors ToolEvent) → to_wire.
- navi/tools/todo: step_text_for_update(index, ctx) resolves the step text
via _sid/_uid (so sub-agent isolation holds), started_metadata_for_call
wraps it for both emit sites.
- agent.py (parent) and subagent_runner.py (sub-agent) enrich ToolStarted
via the shared helper before emitting.
Renderer:
- TodoStartedRenderer update card reads msg.metadata.step_text → shows the
step text + validation; falls back to 'no validation' on history replay.
- Removed the step-text line from the result card (it now lives in the call
card) — result card is back to the compact 'plan · X/N done' summary.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|