| 2026-10-08 |

tools: a non-admin may reach their own session directory
...
A non-admin was confined to user_data/<user_id>/ alone, which made four
tools reject the directory the user's own uploads land in: share_file
refused the file it was asked to send back, filesystem could not read it,
and terminal/code_exec could not be pointed at it. In a prod session the
agent worked around the refusal by copying the uploaded file into its
sandbox with code_exec — the very bypass the injected security policy
forbids — and the copy collided with a same-named file, so the user got a
link to Project_1.mp3 instead of their own upload, plus an 8.6 MB duplicate.
navi/tools/_internal/areas.py now names the two roots a user owns, and the
four call sites share it. Relative paths still resolve into the sandbox,
and each tool keeps its own way of refusing: terminal returns
sandbox_violation, code_exec silently falls back to the sandbox root. The
share_file refusal lists both roots instead of "outside user sandbox", so
the agent retries in the right one instead of routing around it.
The session directory belongs to the same user: its id arrives from the
runtime context, never from tool arguments, and only the session's owner
can open it.
Eugene Sukhodolskiy
committed
3 hours ago
|
| 2026-09-26 |
agent parallelism: background tools, bg spawn_agent, parallel tool batches, message queue
...
- backgroundable tools (terminal/ssh_exec/peer/spawn_agent/code_exec) detach
via TaskManager with per-session/global/spawn caps, rate limit and TTL
- completion delivery both ways: task_update event (out-of-band) + pending
notes injected into the next turn; tasks tool (list/check/wait/cancel)
- parallel tool-call batches (profile-level gate, off by default) with
ToolStarted up-front, tagged event mux, call-order results, one save,
plus dangling tool_call repair on session load
- user message queue instead of busy error: message_queued frame,
back-to-back drain on the same socket, headless fallback on disconnect
- pin mcp<2 (v2 renames FastMCP with breaking API changes)
- docs: tasks.md (new), websocket/api/agent/config updates, tasks manual,
spawn_agent background param, persona contract section
Eugene Sukhodolskiy
committed
12 days ago
|
| 2026-07-13 |

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
|
| 2026-06-26 |
Navi Code: stop via Esc + project cwd propagation
...
- TUI: Esc stops active stream cooperatively via POST /sessions/{id}/stop
- TUI: render stream_stopped as status message
- CLI/WebSocket: send shell cwd in client->server message field
- Orchestrator stores cwd in session.session_metadata
- ContextBuilder injects [Working directory] into LLM context
- Agent sets current_working_directory ContextVar per turn
- tools/base: ToolContext gains cwd field
- filesystem/terminal/code_exec resolve relative paths against session cwd
- Add bin/navi-code wrapper for PATH symlink; document in README.md
- Update docs/websocket.md and tests
Full pytest: 544 passed, 1 skipped.
Co-Authored-By: Claude <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 Jun
|
| 2026-05-26 |
Clarify terminal description param and profile prompts
...
- terminal.py: stronger description for the 'description' field so
the model knows it is REQUIRED for action=open and what to write
- server_admin and developer system prompts: add a short section
explaining persistent terminal usage (open/close/list/status/send_input)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
Move terminal_manager to _internal subpackage
...
- terminal_manager is an internal helper, not a tool
- Update imports in terminal.py, container.py, test_terminal.py
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
Fix terminal review issues: security, lifecycle, reactivity
...
- TerminalManager.open now accepts exec_tokens to use create_subprocess_exec
for restricted commands instead of always using shell
- Fix kill/terminate order: SIGTERM first, SIGKILL fallback
- Pop closed sessions from _sessions dict to prevent memory leak
- Add terminal_manager.shutdown() to AppContainer.shutdown()
- Wait for reader tasks in foreground open before returning output
- Add _MAX_TERMINALS_PER_SESSION limit (10)
- Wrap cleanup_idle tasks in _close_one_safe with error logging
- send_input catches BrokenPipeError/ConnectionResetError specifically
- Foreground terminals auto-close after gathering output
- Vue reactivity: replace terminals object immutably instead of mutating
- onTerminalClosed marks matching tool card as no longer pending
- Update tests for new behavior (foreground auto-close, max limit)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
Add persistent multi-session terminal tool with background support
...
- New TerminalManager module: named subprocess sessions per Navi session,
background readers, event-sink streaming, idle auto-cleanup
- Refactor terminal tool to multi-action: run, open, close, list,
status, send_input
- Add TerminalOutputDelta and TerminalClosed events for streaming
- Wire TerminalManager into AppContainer, orchestrator, and registry
- Persist session_metadata in Session model and pg_session_store
- Close all session terminals on session delete
- Webclient: handle terminal_output/terminal_closed WS events,
display live terminal output in tool cards
- Update unit tests for new terminal actions
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 26 May
|
| 2026-05-23 |
Pass explicit ToolContext to tools instead of hidden ContextVars
...
Add ToolContext dataclass (session_id, event_sink, stop_event, model,
user_id, user_role, user_info) and thread it through the execution chain:
Agent._execute_tools_with_sink → ToolExecutor → tool.execute().
All ~25 tools updated to accept ctx parameter. Tools that previously
read ContextVar now prefer ctx when provided, falling back to
ContextVar for backward compatibility.
Tests updated to pass ToolContext explicitly — no more test fixtures
that set current_session_id / current_user_id ContextVars.
ContextVar setters remain as fallback for non-tool consumers
(ai_helper, context_builder, planning) and will be removed in a
follow-up refactor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 23 May
|
| 2026-05-15 |
Refactor: move tool helper modules into _internal subpackage
...
Moves non-tool infrastructure out of navi/tools/ root so that only
actual tool classes live there:
base.py → _internal/base.py
loader.py → _internal/loader.py
middleware.py → _internal/middleware.py
logging_middleware.py → _internal/logging_middleware.py
_time_parser.py → _internal/time_parser.py
All imports updated across core/, api/, mcp/, tools/, and tests/.
No proxy files remain in navi/tools/ root.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 15 May
|
fix: filesystem empty path validation and terminal timeout defaults
...
filesystem:
- Reject empty string paths in _check_path to prevent writing to cwd
- Clear error message when 'write' action uses 'destination' instead of 'path'
terminal:
- Reduce default timeout from 300s to 20s
- Clamp user-provided timeout between 1 and 300s
- Update parameter description to mention max and common use-cases
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 15 May
|
| 2026-05-08 |
Add multi-user sandbox: filesystem, terminal, code_exec, security policy
...
- filesystem, share_file: sandbox non-admin users to user_data/<user_id>/
- terminal: working_dir sandbox + allowlist + dangerous pattern block for users
- code_exec: sandbox CWD and temp files to user_data/<user_id>/ for users
- context_builder: inject dynamic security policy into LLM context (user/admin)
- config: terminal_user_allowed_commands setting
- agent: wire user_id/user_role through ContextBuilder.build()
- base: add current_user_role ContextVar; run_ephemeral inherits role
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 8 May
|
| 2026-04-17 |

Planning phases, context compression, and tool improvements
...
Agent:
- Planning now a 3-phase async generator: Analysis → Execution plan → AIHelper critic
- Yield PlanningStatus events before each phase (UI progress labels)
- Phase 1 runs with think=True for deeper analysis
- Phase 2 includes available tool list so executor assignments are accurate
- Phase 3: independent critic pass validates and corrects TOOL: names against real tool list
- Planning converted from list return to async generator (fixes token accounting)
Backend:
- Context compression threshold: 80% → 70% to trigger earlier
- Compressor summary prompt: structured sections (goal, work state, key facts, outputs, errors)
- Terminal output capped at 5000 chars to prevent context flooding
- Web search: region=wt-wt for DDG, country=ALL for Brave, language=all for SearxNG
- Scratchpad: mandate writing a 'goal' section at start of multi-step tasks
- secretary max_iterations: 40→25, temperature: 0.7→0.5
- server_admin max_iterations: 40→20
Webclient:
- ThinkingCard strips <thought> XML tags leaked by Ollama
- planning_status WS event wired to chat.onPlanningStatus()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 17 Apr
|
| 2026-04-10 |

Add stop button and fix context compression hang
...
Stop generation:
- Client: send button toggles to red ■ during streaming; sends {type:stop} via WS
- Server: _stream_recv concurrently reads incoming messages during streaming using
asyncio.wait — stop signal is handled immediately without polling
- Cooperative stop via asyncio.Event (current_stop_event ContextVar): agent breaks
out of LLM async-for cleanly so aclose() fires → Ollama stream closes gracefully,
model stays in VRAM. No task.cancel() which would eject the model.
- StreamStopped event propagates through run_stream/run_ephemeral; sub-agents stop
via the same shared stop_event inherited through task context
Context compression fix:
- compress_context passes think=False to llm.complete() — no extended reasoning
during summarization which caused GPU hang
- Input truncated to 12k chars before sending to summarizer
- LLMBackend.complete() / OllamaBackend.complete() accept think: bool | None override
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 10 Apr
|

Major feature batch: visibility, planning, file uploads, streaming
...
- stream_complete(): streaming with tools for all LLM turns — thinking
now streams as ThinkingDelta/ThinkingEnd in real-time during tool-
selection turns, not just on the final response
- todo built-in tool: session-scoped plan manager (set/view/update/clear);
persona + all profiles updated with mandatory planning instructions
- TurnThinking event: sub-agent thinking forwarded to parent sink as a
collapsible block in the spawn_agent card
- File uploads: non-image files uploaded via XHR, shown as badges in
message bubble; SVG treated as regular file (not base64 image)
- session_files: POST /sessions/{id}/files, TTL cleanup, forbidden exts
- WebSocket reconnect: _AgentRun broadcast pattern, re-attach mid-stream
- UI: favicon, sidebar logo, turn-thinking cards, subagent thinking blocks,
token counter, draft persistence, file progress bar
- Removed AgentNote (content is always None alongside tool_calls)
- Ollama stream_complete: tool_calls captured from non-final chunk (done=False)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 10 Apr
|
| 2026-04-08 |
Wire self-extension tools into all profiles; improve tool descriptions
...
All profiles now include write_tool, list_tools, tool_manual, reload_tools.
User tools from enabled.json merged in at runtime via Agent._tool_list().
Built-in tool descriptions rewritten to be more LLM-actionable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eugene Sukhodolskiy
committed
on 8 Apr
|
Unrestricted terminal mode and SSH tool
...
- Terminal: TERMINAL_ALLOWED_COMMANDS=* (default) runs via shell,
supports pipes, redirects, subshells; allowlist mode still available
- FS_ALLOWED_PATHS expanded to cover /home /etc /var /opt
- New ssh_exec tool: execute commands on remote hosts via asyncssh,
supports named connections (ssh_hosts.json) and inline user@host
- ssh_hosts.json gitignored, ssh_hosts.json.example added as reference
- ssh_exec added to server_admin and smart_home profiles
Eugene Sukhodolskiy
committed
on 8 Apr
|
Initial implementation of the agent system core
...
- FastAPI server with REST API and WebSocket streaming
- Modular LLM backend abstraction (Ollama implemented, OpenAI stub)
- Tool system: web_search (ddgs), filesystem, http_request, code_exec, terminal
- Agent profiles: smart_home, server_admin, secretary
- Tool-calling loop with concurrent tool execution
- In-memory session store with SessionStore ABC for future persistence
- Registry pattern for tools, profiles, and backends
- Orchestrator stub as foundation for multi-agent scenarios
Eugene Sukhodolskiy
committed
on 8 Apr
|