| 2026-07-13 |
filesystem: strip trailing newline in _number_diff so diff lines aren't blank-separated
...
_unified_diff (used by edit_lines / smart_edit) feeds difflib with
"line + \n" so difflib does not emit a "\ No newline at end of file" marker.
With lineterm="" difflib keeps that trailing "\n" on content lines, so the
final "\n".join produced "\n\n" between every diff line — a blank line
between each, bloating the model-facing output and rendering with double
spacing in both clients. Strip the trailing "\n" in _number_diff before
processing; no-op for the _diff action, whose lines come from splitlines()
without trailing newlines.
Eugene Sukhodolskiy
committed
14 hours ago
|

Автономность мелких моделей: OUTPUT DISCIPLINE, milestone-todo, перехват финала хода
...
Два последовательных захода над одной проблемой — мелкие модели (12–30B) в navi_code
теряют автономность на трудных шагах: «остановился поболтать» вместо действия и
слишком большое расстояние между пунктами плана.
Заход 1 (З1–З3):
- З1: OUTPUT DISCIPLINE в системном промпте navi_code — «act, don't announce»,
без few-shot антипримеров. Контракт хода: ответ без tool_calls = конец хода, поэтому
объявление намерения текстом убивает автономность; правила заставляют вызывать
инструмент в том же ходе.
- З2: плоский todo + метка группы milestone + декомпозиция. _parse_plan_steps
возвращает list[tuple[milestone, text]]; milestone — метка группировки (не сущность,
без статуса), «done» вычисляется при рендеринге; подшаги = больше плоских шагов
(без вложенности). TUI side-panel группирует по milestone (плоский фолбэк при пустом
milestone). Plan depth: max 15→20 + правило декомпозиции.
- З3: adaptive re-plan «длинный шаг» — nudge «разбей шаг» при in_progress ≥ порога
итераций без смены todo (порог 4, раньше общего anti-stall warning на 8).
Заход 2 (шаги 1–3, после cloud-теста 31b vs 12b):
Корневая структурная причина: весь спасательный механизм (anti-stall warning с явным
предложением reflect, adaptive re-plan) живёт только внутри tool-цикла — nudge
инжектируется в pre_turn *следующей* итерации, которой при «остановился поболтать»
нет (ход закрылся по return до post_turn). 31b застревала через «продолжаю
tool-итерации» → дожала до warning → спаслась; 12b — через «замолчала текстом» → мимо
всех nudge.
- Шаг 1: перехват финала хода. Если модель выдала bare-text, но в todo есть открытые
шаги (pending/in_progress) и лимит не исчерпан — НЕ эмитить StreamEnd, а сохранить
ассистентский текст в session.context, поставить системный nudge и continue
(без StreamEnd, без workers — консистентно с multi-iteration tool-турами). Счётчик
final_interceptions на AgentTurnContext, лимит final_intercept_limit (default 2),
эскалация жёсткости (мягкий → «second stop»). has_open_steps в todo.py: пустой
todo → False (защита casual-сообщений), failed/skipped терминальны. Профильные
флаги final_intercept_enabled/limit в base.py + loader + admin.
- Шаг 2: жёсткий reflect-триггер в промпте — «~3 tool attempts on the same step
without progress → call reflect IN THIS TURN (tool call, not reasoning aloud)».
- Шаг 3: открыть replan для застревания — «call replan when reflect showed the whole
approach is dead (not one failed step, but the approach itself)».
Тесты: 874 passed, 1 skipped. Новые — has_open_steps (5), final intercept (5),
milestone-группировка, adaptive long-step nudge, парсер шагов с milestone-маркером.
cloud: navi_code model → gemma4:31b-cloud для тестирования догадки (31b признала
застревание, 12b — нет); .env cloud-host уже gitignored.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
17 hours ago
|
| 2026-07-12 |
config: raise context compression threshold 0.70 -> 0.90
...
Midturn compression now splits the in-flight turn correctly, so it's safe to
let the context grow closer to the limit before compressing. 0.90 gives the
model more raw context per turn and trips the compressor less often.
Worker test context_tokens bumped 50_000 -> 60_000 (76% -> 91.5%) to stay
above the new threshold; other compression tests pass the threshold as an
explicit argument and are unaffected.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

filesystem: show real file line numbers in unified diffs
...
_number_diff parses @@ hunk headers and prefixes each content line with its
real file line number: removed/context lines use the old (from) number,
added lines use the new (to) number, in `{marker} {num}│ {content}` form
(marker first so existing startswith highlighting stays valid; column
right-aligned to the largest line number). Hunk/file headers and the
`\ No newline` marker are left unchanged.
Applied in _unified_diff (covers edit / edit_lines / smart_edit) and the
`diff` action — a single server-side change, so both the TUI and the web
client receive numbered diffs in the tool result text.
TUI highlight_unified_diff renders the `{marker} {num}│` column dim and the
content in the marker color, so the number reads as meta, not as part of the
added/removed text. Lines without the prefix (standalone `diff` event, older
callers) are still colored whole.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

compressor: split in-flight turn in midturn mode + flush incrementally
...
Two related fixes for long autonomous turns in planning profiles (navi_code):
A1 — partition_messages turn-based branch held the in-flight (current) turn
verbatim when turns > keep_recent, so midturn compression was shallow
(e.g. 150 -> 141 messages). Now, when keep_recent_messages is set, the
in-flight turn is split like partition_current_turn_messages: head (user
request) + tail (recent tool steps) kept, middle summarized. The adaptive
swap is disabled in midturn mode (it could move the in-flight turn into
old_turns and summarize the current request whole). First branch
(len(turns) <= keep_recent) is untouched, so locked-in midturn tests hold.
B1 — agent.run_stream had no try/finally around the for-loop, so an
asyncio.CancelledError (server restart/shutdown) unwound the stack with no
flush: all in-memory turn messages (sequence_number < 0) were lost, only the
user message survived. Add incremental save() after planning, after the
assistant tool-call decision, and after each tool result, so a crash loses at
most the single in-flight tool call, not the whole turn. B2 (try/except
safety-net) was dropped: B1 leaves no window where an append is unsaved.
Tests: midturn split with many turns (partition + compress_session), and
crash/cancel persistence via a snapshot session store that mirrors the DB
boundary.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|

permissions: remove broken client-side gate; add backend design doc
...
The terminal-TUI permission gate was a race: _execute_tools_with_sink
emits ToolStarted and starts the tool on the backend immediately, so by
the time the client dialog appeared the destructive action had already
run -- 'deny' could only stop the session, not un-execute the tool. It
also existed only in the terminal TUI (webclient/android had nothing),
and the system-prompt 'Strict Confirmation' nudge was an unreliable
duplicate.
Remove the broken machinery for a clean slate and plan the real
replacement from zero:
- delete clients/terminal/tui/permissions.py, screens/permission_dialog.py
and their tests
- strip tui_app.py (engine, _deny_tool, _show_permission_dialog,
_confirm_shell_command, _stop_session_worker, on_permission_request,
tool_started gate) and the PermissionRequest TUI event; !cmd now runs
directly (user-typed, no agent gate)
- drop the 'Strict Confirmation' prompt rule from navi_code/developer/
tool_developer
- add docs/permissions.md: authoritative backend gate design (engine +
registry + event + endpoint + agent gate + postgres policy + global
rules config, sub-agents gated) -- design only, not yet implemented
- update docs/index.md, navi_code_cli.md, profiles.md, testing.md
Until Phase 1 lands there is no destructive-action confirmation -- a
conscious period without the false security of the old race-gate.
Eugene Sukhodolskiy
committed
1 day ago
|
replan: integrated mid-task re-planning tool
...
Add a replan tool that re-runs the planner over the live session context
+ todo + scratchpad when the plan's structure is stale due to discoveries
(NOT failed steps -- that stays [Adaptive re-plan]). Integrated approach:
PlanningEngine.run gains is_replan/replan_context (suppresses DIRECT
shortcut and observe-skip, frames Phase 1 as a revision); ReplanRunner
packs reason/goal/todo/findings/errors and captures PlanReady; the tool
is exposed to navi_code/developer/tool_developer via a
current_replan_runner ContextVar set per-iteration in run_stream (correct
after switch_profile). New plan replaces the todo. Lazy events import
breaks the navi.tools -> navi.core -> navi.tools cycle.
Eugene Sukhodolskiy
committed
1 day ago
|

profiles: port context-org/planning instructions to developer & tool_developer
...
Port the context-organization and planning-machinery framing accumulated
in navi_code to the developer and tool_developer profiles, adapted per
profile:
- Working state & memory (todo/scratchpad/context_transfer/schedule_recall/
reflect/memory) replaces the stale "Context drift recovery" section.
- System signals subsection names the runtime-injected messages each profile
actually receives: [Goal anchor], [Anti-stall warning], [Adaptive re-plan]
(adaptive_replan is on for both), [Iteration N/M]. [Scope boundary] is
omitted — scope_boundary_enabled is off on these profiles.
- Reading & searching, Editing policy (tool_developer), Safety Rules, Git
discipline, and Project environments (isolated venv) added.
- developer: Workflow rewritten planner-aware with an observe carve-out
(observe_skips_plan now on); Project knowledge replaced by docs-first
Documentation; sub-agent briefing gains context_transfer + restricted-toolset
bullets.
- tool_developer: keeps its MCP-specific 10-step workflow and prerequisites;
sub-agent briefing gains context_transfer + restricted-toolset bullets
(sub-agent cannot reload_tools/test_mcp_tool/mcp_status — run those inline).
- config: observe_skips_plan_enabled=true on both, so observe requests skip
phase3 (no plan/todo for read/explain/inspect) — enables the Workflow
observe carve-out and saves an LLM call on info requests.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
1 day ago
|
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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
1 day 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
2 days 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
2 days ago
|

Isolate sub-agent todo + render live todo in TUI side panel
...
Phase 1 — sub-agent todo isolation (backend):
- Add current_todo_session_id ContextVar; subagent_runner scopes it to the
sub-agent's ephemeral run id so its auto-populated plan and todo updates
land in an isolated KV row instead of clobbering the parent session's todo
(which the parent's goal-anchoring reads every iteration).
- todo._sid() and planning.set_tasks prefer current_todo_session_id; the
parent run leaves it unset, so all existing todo consumers (anti-stall,
goal anchor, get_progress_message) behave exactly as before.
Phase 2 — live todo in the TUI right column:
- TodoUpdated event + emit it from the agent loop after planning auto-populate
and after each tool-execution turn.
- GET /sessions/{id}/todos reads the parent session's todo KV row (explicit
user_id/session_id, optional injected kv).
- api.get_todos + TodoList/TodoPanel widgets: status-coloured markers
(pending dim, in_progress accent+bold, done success, failed error, skipped
dim), progress header, scrollable panel below the auto-height info block.
- Hybrid delivery: REST seeds the panel on attach/switch, todo_updated WS
events update it live.
Sub-agent todos as nested sub-lists is deferred to a later phase.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
2 days ago
|
| 2026-07-10 |

tui: show the currently-served model in the status panel
...
The status panel's Model line was fed the global ollama_default_model, not the
session/profile model, and the server never told the client which model
actually served a call. Now:
- Backends stamp the resolved model onto LLMChunk (first chunk) / LLMResponse.
The fallback backend reports the model that survived its server+model
priority list (may differ from the profile's first choice).
- New ModelInfo event ({"type":"model_info","model":...}) emitted once per
turn from agent._consume_stream, re-emitted only when the model changes
across iterations. Additive WS event — old clients ignore it.
- TUI: attach_session/switch fetch the profile's configured model (first of
profile.model) via api.get_profile_model so the panel shows a value before
the first request; model_info then refines it to the actually-served model.
Not forwarded to the chat panel. raw CLI prints "[model] ...".
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
3 days ago
|
| 2026-07-09 |
agent: cwd-aware memory fact filter for bounded autonomy
...
Long-term memory stored context-dependent path facts (e.g. "project_root
→ /home/.../navi-1") as global user facts. search_facts injected them into
any session, so when working in another project the agent was told "the
project root is navi-1" and drifted there.
When scope_boundary_enabled and a session cwd is set, _memory_facts_msg now
drops facts whose value is an absolute path outside the session cwd tree.
Facts are kept when working inside that path (then they are correct), and
non-path/relative facts always pass. Free flight stays reproducible by
toggling the flag off. No facts deleted, extractor untouched.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
4 days ago
|

agent: bounded autonomy — scope boundary + observe-vs-act
...
navi_code had unwanted "free flight": an observe request ("look at a
directory") triggered the full Phase 3 plan with milestones + auto-todo,
and goal_anchoring then drove the agent to finish those steps, climbing
into sibling projects and executing milestone docs it found.
Two toggleable, default-off profile flags (on for navi_code):
- scope_boundary_enabled: injects a standing system message keeping the
agent within the literally requested scope; forbids acting on
discovered TODO/roadmap/milestone docs (report only).
- observe_skips_plan_enabled: Phase 1 classifies MODE: observe|act; an
observe request skips Phase 2/3 — no multi-step plan, no auto-todo, no
"execute step by step" prompt. The agent just gathers info and answers.
Independent of force_plan (observe on the first message still skips).
Free flight stays reproducible by flipping both flags off.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
4 days ago
|
fix(ws): include session_id/profile_id in session_sync, guard renderer
...
The server sent {"type": "session_sync"} without session_id/profile_id,
crashing the terminal client (render.py did None[:8]). Add the fields to
both session_sync sends and guard the renderer against a missing id.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
4 days ago
|
session store: lazy persistence — no empty sessions in DB
...
POST /sessions no longer inserts a DB row. The Session is registered in an
in-memory _pending registry on PgSessionStore and only upserted on the first
save() (first user message / meaningful state change). Empty sessions that
never receive a message never reach the DB and vanish on server restart or
via the hourly pending sweep.
- pg_session_store: _pending dict + lock; create() registers, get() checks
_pending first, save() upserts the sessions row (INSERT ... ON CONFLICT)
and pops _pending so the session_messages FK is satisfied; sweep_pending()
drops abandoned entries; pending_sweep_loop() background task.
- main.py: start/cancel pending_sweep_loop in lifespan.
- tests: 7 new tests for create/get/save/list/sweep semantics; updated
existing save() test comments for the upsert.
Eugene Sukhodolskiy
committed
4 days ago
|
| 2026-07-08 |
profiles: add gemma4:12b-it-qat-128k as top-priority model
...
New local QAT model added at the head of the model priority list across
all profiles, so it is preferred first with cloud fallback.
Eugene Sukhodolskiy
committed
5 days ago
|